Process Content Security Policy violations with SQL, Send to Slack
@dylburger
code:
data:privatelast updated:4 years ago
today
Build integrations remarkably fast!
You're viewing a public workflow template.
Sign up to customize, add steps, modify code and more.
Join 800,000+ developers using the Pipedream platform
steps.
trigger
HTTP API
Deploy to generate unique URL
This workflow runs on Pipedream's servers and is triggered by HTTP / Webhook requests.
steps.
check_for_csp_report
auth
to use OAuth tokens and API keys in code via theauths object
code
Write any Node.jscodeand use anynpm package. You can alsoexport datafor use in later steps via return or this.key = 'value', pass input data to your code viaparams, and maintain state across executions with$checkpoint.
async (event, steps) => {
1
2
3
4
5
}
6
// Sometimes the CSP report is empty. Exit early
if (!event.body["csp-report"]) {
  $end("No CSP report present in body")
}
steps.
save_to_table
Pipedream automatically generates a table and schema based on the event shape.
params
Table Name

Enter the name of the table (e.g., my_table_name) to load the payload data into. Pipedream's SQL service automatically creates the table and adapts the schema to your data.

csp_violation_data
string ·params.table
Payload

Enter a reference to the data (for example, event.body or steps.step_name.return_value) you'd like to insert into the table. Pipedream’s SQL service automatically converts the data to JSON and maps the table schema to its keys.

{{event.body["csp-report"]}}
string ·params.payload
code
async params => {
1
2
3
4
5
}
6
  $send.sql({
    table: params.table,
    payload: params.payload,
  })
steps.
ignore_specific_csp_violations
auth
to use OAuth tokens and API keys in code via theauths object
code
Write any Node.jscodeand use anynpm package. You can alsoexport datafor use in later steps via return or this.key = 'value', pass input data to your code viaparams, and maintain state across executions with$checkpoint.
async (event, steps) => {
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
}
30
const includes = require("lodash.includes")

const cspReport = event.body["csp-report"]
const effectiveDirective = cspReport["effective-directive"]
const violatedDirective = cspReport["violated-directive"]
const blockedURI = cspReport["blocked-uri"]

// This shows how you can ignore violations for specific directives.
// If a directive is included on this list, the violation will not get sent to Slack.
// Of course, you can modify this code to ignore a more specific set of violations
// for a (directive, URI pattern), for example.

//const ignoreDirectives = ["object-src", "font-src", "style-src-elem"]
const ignoreDirectives = []


// You can also ignore specific blocked URIs 
// (or extend this to handle pattern matching)
//const ignoreURIs = ["chrome-extension"]
const ignoreURIs = []

if (includes(ignoreDirectives, effectiveDirective) || includes(ignoreDirectives, violatedDirective)) {
  $end(`${effectiveDirective || violatedDirective} is on our list of ignored directives. Exiting.`)
}

if (includes(ignoreURIs, blockedURI)) {
  $end(`${blockedURI} is on our list of ignored blocked URIs. Exiting.`)
}
steps.
send_slack_message
auth
to use OAuth tokens and API keys in code via theauths object
(auths.slack)
params
Slack channel
 
string ·params.channel
code
Write any Node.jscodeand use anynpm package. You can alsoexport datafor use in later steps via return or this.key = 'value', pass input data to your code viaparams, and maintain state across executions with$checkpoint.
async (event, steps, params, auths) => {
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
}
42
// Process a message to send to Slack
const { WebClient } = require('@slack/web-api')
const useragent = require('useragent')

const { channel } = params
const cspReport = event.body['csp-report']

let report_url
try {
  report_url = new URL(cspReport['document-uri'])
} catch (err) {
  // Some document-uris are not full URLs
  report_url = cspReport['document-uri']
}

// Retrieve browser + version, since that can be
// relevant for researching CSP violations
const ua = useragent.parse(event.headers["user-agent"])
const browserString = `${ua.family} ${ua.major}.${ua.minor} on ${ua.os.family}`

// Send a subset of the CSP violation fields to Slack
const cspSlackPayload = {
  "document-uri": cspReport["document-uri"],
  browser: browserString,
  referrer: cspReport.referrer,
  "effective-directive": cspReport["effective-directive"],
  "violated-directive": cspReport["violated-directive"],
  "blocked-uri": cspReport["blocked-uri"],
  "source-file": cspReport["source-file"],
  "script-sample": cspReport["script-sample"],
}

const text = `New CSP violation on ${report_url.hostname}${event.body['csp-report']['blocked-uri']} violated directive ${event.body['csp-report']['violated-directive']}
\`\`\`${JSON.stringify(cspSlackPayload, null, 2)}\`\`\``

const web = new WebClient(auths.slack.oauth_access_token)
await web.chat.postMessage({
  channel,
  text,
})