The goal of this project is to be able to take a URL and one or more CSP headers, understand them correctly, and ultimately be able to provide education and actionable feedback for ensuring CSP provide the appropriate and intended level of security. This is the underlying library intended to make that ultimate goal possible.
This code is not a web browser, so the parts of the spec about "blocking networking requests" aren't relevant. However, calling this out as what a web browser would do can be helpful.
You should consider web.dev: Content security policy required reading for understanding CSP.
Caution
Partially working. Code which parses the policy has complete support for CSP Level 2, and CSP Level 3 (Draft) support is mostly complete. Code which performs deeper evaluation of the content of the page against the CSP policy has not yet been started. PUBLIC INTERFACES ARE NOT STABLE YET.
Implements parsing and evaluation for CSP2 (2016) and the CSP3 working draft (April 2024).
Directive | Notes for implementors |
---|---|
base-uri | The |
block-all-mixed-content (e) | The Obsolete. Use |
child-src | The |
connect-src | The Affects: processing XMLHttpRequest.send(); WebSocket; EventSource; Pinging during hyperlink auditing; the navigator.sendBeacon() method. |
default-src | The |
fenced-frame-src (e) | |
font-src | The Affects: the |
form-action | The |
frame-ancestors | The Resources can use this directive to avoid many UI Redressing attacks by avoiding being embedded into potentially hostile contexts. The |
frame-src | The |
img-src | The Affects: the |
manifest-src | |
media-src | The Affects: data for a video or audio clip, such as when processing the |
object-src | The Affects: data for a plugin, such as when processing the |
plugin-types | The |
prefetch-src | |
referrer | |
report-to | The |
report-uri | The Deprecated in CSP3, but will require transition time. Use this along with |
require-trusted-types-for (e) | |
sandbox | The |
script-src-attr | The |
script-src-elem | The |
script-src | The
|
style-src-attr | The |
style-src-elem | The |
style-src | The
|
trusted-types | |
upgrade-insecure-requests (e) | An extension to CSP; The |
webrtc | The |
worker-src |
When the child nodes are undefined, their values will fall back to their parent's definition. Nodes at the root (all the way to the left) have no fallback.
Processing is right-to-left, and resolving the value of worker-src works slightly differently from resolving the value of frame-src.
See tree…
- base-uri
- block-all-mixed-content — Extension has no value; treat as boolean.
- default-src
- form-action
- frame-ancestors
- manifest-src
- plugin-types — Deprecated in browsers since plugins are now deprecated.
- report-uri — Will be deprecated, but still has high compatibility.
- sandbox
- upgrade-insecure-requests — Extension has no value; treat as boolean.
- webrtc
These are the inputs:
-
The current URL being evaluated. May be an empty string, but doing so will disable validation of
'self'
expressions. -
The
Reporting-Endpoints
HTTP reponse header value. This is used to validate thereport-to
directive (CSP Level 3). If there is noreport-to
directive, this value may be empty.NOTE: Browser support is still nascent as of 2024-06-04. CSP Level 3 and the Reporting API are still draft-level specifications.
-
One (or more) CSP policies. (One is typical, however multiple policies are allowed.)
The hashicorp/go-multierror
library is used extensively to collect errors and report them back at the same time.
parseTree, err := csp.Parse(
// Current URL
"https://ryanparman.com",
// Value of the `Reporting-Endpoints` response header
`endpoint-1="https://example.com/reports1" endpoint-2="https://example.com/reports2"`,
// One CSP policy, as an array of one.
// This is ONE long concatenated string, NOT several string values.
[]string{
`default-src 'self' blob: cdn.ryanparman.com; `+
`connect-src 'self' cdn.ryanparman.com embedr.flickr.com; frame-src cdn.ryanparman.com embed.music.apple.com platform.twitter.com syndication.twitter.com www.google.com www.instagram.com www.youtube.com; `+
`img-src 'self' 'unsafe-inline' data: cdn.ryanparman.com *.static.flickr.com *.staticflickr.com media.githubusercontent.com pbs.twimg.com platform.twitter.com s3.amazonaws.com stats.g.doubleclick.net syndication.twitter.com web.archive.org www.google-analytics.com www.google.com www.googletagmanager.com www.google.co.in; `+
`media-src 'self' blob: ryanparman.com cdn.ryanparman.com *.http.atlas.cdn.yimg.com s3.amazonaws.com www.flickr.com cdn.jsdelivr.net; `+
`script-src 'self' 'unsafe-inline' ajax.cloudflare.com cdn.ryanparman.com cdn.jsdelivr.net cdn.syndication.twimg.com embedr.flickr.com gist.github.com platform.twitter.com widgets.flickr.com www.google-analytics.com www.googletagmanager.com www.instagram.com; `+
`script-src-elem 'unsafe-inline' cdn.ryanparman.com ajax.cloudflare.com www.googletagmanager.com www.google-analytics.com; `+
`style-src 'self' 'unsafe-inline' cdn.ryanparman.com github.githubassets.com platform.twitter.com; `+
`font-src 'self' data: cdn.ryanparman.com; style-src-attr 'unsafe-inline'; `+
`report-uri wss://ryanparman.report-uri.com/r/d/csp/wizard; `+
`upgrade-insecure-requests; `+
`block-all-mixed-content; `+
`plugin-types application/pdf; `+
`sandbox allow-forms allow-popups; `+
`frame-ancestors https://alice.com https://bob.com; `+
`webrtc 'allow'; `+
`report-to endpoint-2`
}
)
// If we have an non-nil error...
if err != nil {
// See if it's a multi-error...
if merr, ok := err.(*multierror.Error); ok {
// If so, handle each nested error.
for _, e := range merr.Errors {
logger.Errorf("%v", e)
}
} else {
// Otherwise, this is a regular error.
logger.Errorf("%v", err)
}
}
// Do something with `parseTree`.
- Content Security Policy Level 2 (formal recommendation)
- Content Security Policy Level 3 (working draft)
- web.dev: Content security policy
- MDN: Content Security Policy (CSP)
- OWASP: Content security policy (outdated)
- content-security-policy.com
- Can I use: Content Security Policy?
- Mozilla HTTP Observatory
- csp-evaluator