Skip to content

Parser and evaluator for Content Security Policy directives.

License

Notifications You must be signed in to change notification settings

northwood-labs/csp-parser

Repository files navigation

CSP Parser and Evaluator in Go

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).

Call-outs from the CSP specs

Directive Notes for implementors
base-uri

The base-uri directive restricts the URLs that can be used to specify the document base URL.

block-all-mixed-content (e)

The block-all-mixed-content directive disallows any http: resources from being used with https:-served websites.

Obsolete. Use upgrade-insecure-requests instead.

child-src

The child-src directive governs the creation of nested browsing contexts (e.g., frames) as well as Worker execution contexts.

connect-src

The connect-src directive restricts which URLs the protected resource can load using script interfaces.

Affects: processing XMLHttpRequest.send(); WebSocket; EventSource; Pinging during hyperlink auditing; the navigator.sendBeacon() method.

default-src

The default-src directive sets a default source list for a number of directives.

fenced-frame-src (e)
font-src

The font-src directive restricts from where the protected resource can load fonts.

Affects: the @font-face CSS rule.

form-action

The form-action restricts which URLs can be used as the action of HTML form elements.

frame-ancestors

The frame-ancestors directive restricts embedding the resource using a frame, iframe, object, embed, or applet element, or equivalent functionality in non-HTML resources.

Resources can use this directive to avoid many UI Redressing attacks by avoiding being embedded into potentially hostile contexts. The frame-ancestors directive obsoletes the X-Frame-Options header.

frame-src

The frame-src directive restricts the URLs which may be loaded into child navigables.

img-src

The img-src directive restricts from where the protected resource can load images.

Affects: the src or srcset attributes of an img element; the src attribute of an input element with a type of image, the poster attribute of a video element, the url(), image(), or image-set() values on any CSS property; or the href attribute of a link element with an image-related rel attribute, such as icon.

manifest-src
media-src

The media-src directive restricts from where the protected resource can load video, audio, and associated text tracks.

Affects: data for a video or audio clip, such as when processing the src attribute of a video, audio, source, or track element.

object-src

The object-src directive restricts from where the protected resource can load plugins.

Affects: data for a plugin, such as when processing the data attribute of an object element, the src attribute of an embed element, or the code or archive attributes of an applet element; requesting data for display in a nested browsing context in the protected resource created by an object or an embed element.

plugin-types

The plugin-types directive restricts the set of plugins that can be invoked by the protected resource by limiting the types of resources that can be embedded.

prefetch-src
referrer
report-to

The report-to directive specifies an identifier defined in the Reporting-Endpoints HTTP response header field to which to send reports. See Reporting API for more information.

Browser support is still nascent.

report-uri

The report-uri directive specifies a URL to which the user agent sends reports about policy violation.

Deprecated in CSP3, but will require transition time. Use this along with report-to in the interim.

require-trusted-types-for (e)
sandbox

The sandbox directive specifies an HTML sandbox policy that the user agent applies to the protected resource. The set of flags available to the CSP directive should match those available to the iframe attribute.

script-src-attr

The script-src-attr directive applies to event handlers and, if present, it will override the script-src directive for relevant checks.

script-src-elem

The script-src-elem directive applies to all script requests and script blocks. Attributes that execute script (inline event handlers) are controlled via script-src-attr.

script-src

The script-src directive restricts which scripts the protected resource can execute. The directive also controls other resources, such as XSLT style sheets, which can cause the user agent to execute script.

unsafe-inline SHOULD be avoided in favor of nonce-source or hash-source. unsafe-eval SHOULD be avoided in favor of passing callables (instead of strings) to setTimeout(), setInterval(), setImmediate(), or execScript().

style-src-attr

The style-src-attr directive governs the behaviour of style attributes.

style-src-elem

The style-src-elem directive governs the behaviour of styles except for styles defined in inline attributes.

style-src

The style-src directive restricts which styles the user may applies to the protected resource.

unsafe-inline SHOULD be avoided in favor of nonce-source or hash-source. Affects: the href of a link element where rel=stylesheet; the @import directive; a Link HTTP response header field.

trusted-types
upgrade-insecure-requests (e)

An extension to CSP; The upgrade-insecure-requests directive instructs the browser to load any http: resources over https: instead.

webrtc

The webrtc directive determines whether or not any WebRTC connectivity is allowed.

worker-src

Fallbacks

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…

Usage

These are the inputs:

  1. The current URL being evaluated. May be an empty string, but doing so will disable validation of 'self' expressions.

  2. The Reporting-Endpoints HTTP reponse header value. This is used to validate the report-to directive (CSP Level 3). If there is no report-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.

  3. 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`.

References