Scripting Policy

Editor’s Draft,

This version:
https://wicg.github.io/csp-next/scripting-policy.html
Version History:
https://github.com/WICG/csp-next/commits/master/scripting-policy.bs
Feedback:
public-webappsec@w3.org with subject line “[scripting-policy] … message topic …” (archives)
Issue Tracking:
GitHub
Inline In Spec
Editor:
(Google Inc.)
Explainer:
https://github.com/WICG/csp-next/
Participate:
File an issue (open issues)

Abstract

This document defines a mechanism for controling script execution in a given context, with an eye towards mitigating injection attacks in a deployable manner.

Status of this document

This is a public copy of the editors’ draft. It is provided for discussion only and may change at any moment. Its publication here does not imply endorsement of its contents by W3C. Don’t cite this document other than as work in progress.

Changes to this document may be tracked at https://github.com/w3c/webappsec.

The (archived) public mailing list public-webappsec@w3.org (see instructions) is preferred for discussion of this specification. When sending e-mail, please put the text “scripting-policy” in the subject, preferably like this: “[scripting-policy] …summary of comment…

This document was produced by the Web Application Security Working Group.

This document was produced by a group operating under the W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

This document is governed by the 15 September 2020 W3C Process Document.

1. Introduction

Though cross-site scripting (XSS) is technically trivial to mitigate through proper escaping techniques, it remains a somewhat pervasive risk to web applications. Content Security Policy attempts to contain this risk by giving developers more control over the way script is executed on a given page, and developers have had moderate success at rolling it out. They’ve also expressed more than a little frustration with the complexity that’s crept into CSP over time.

Originally, CSP aimed to address script injection attacks by limiting the sources from which scripts could be loaded. Over time, we learned that limiting scripts' sources was not a terribly effective way of addressing the problem. [CSP-IS-DEAD] shifted experts' recommendations away from a list of allowed sources for script, and towards more robust verification of individual script elements via nonce attributes on the one hand, or integrity verification on the other (see Content Security Policy §8.4 Allowing external JavaScript via hashes).

The [ARTUR] proposal is a tounge-in-cheek proposal that’s a fairly bad idea as specified, but seems like an interesting foundation conceptually. If we step back from CSP’s current syntax, and the contortions it takes to remain backwards compatible, it seems like we could extract a concise and comprehensible mechanism that would encompass the kinds of requirements that hard experience has taught us can be effective (e.g. those expressed in [STRICT-CSP]):

  1. Turn off dangerous parts of the platform that influence scripting (e.g. base, embed, and object.

  2. Create an out-of-band signal that a given script is intended to execute (e.g. the aforementioned nonces and hashes).

  3. Allow developers to roll the above out in a report-only manner.

This document aims to build upon that conceptual foundation, defining a Scripting Policy that governs the way script executes within a given context, focused entirely on mitigating injection attacks in a way that developers can easily deploy.

1.1. Examples

Most users would be well-served with the following policy:

Scripting-Policy: nonce=number-used-once

This would have the effect of:

  1. Executing "parser-inserted" script iff it has a nonce attribute matching the header-specified nonce (e.g. <script nonce="number-used-once">...</script>), and executing all non-"parser-inserted" script in a manner similar to CSP’s 'strict-dynamic' keyword (see Content Security Policy §8.2 Usage of "'strict-dynamic'").

  2. Preventing base from changing the meaning of relative URLs.

  3. Blocking eval(DOMString), while allowing eval(TrustedScript).

  4. Blocking inline event handlers, XSLT, navigation to javascript:..., embed, and object.

This is a pretty reasonable set of hurdles to put in front of an attacker.

Users with complicated sites might need more complicated policies that take advantage of optional features, perhaps along the lines of:

Scripting-Policy: integrity=(hash1 hash2 hash3 hash4),
                  report-to=name,
                  trusted-types-policy=policyName
Scripting-Policy-Report-Only: integrity=(hash1 hash2 ...hash18 ... hash37),
                              eval=block,
                              dynamic-loading=checked,
                              report-to=name,
                              trusted-types-policy=policyName

1.2. Threat Model

Scripting Policy aims to deal soley with injection attacks that cause script execution. It does not try to affect resource loading, mitigate data exfiltration, nor does it gate access to any feature unrelated to script execution.

This mechanism assumes an attacker can:

  1. Cause a server to output unexpected content directly into the body of any given response, leading to "reflected" cross-site scripting.

  2. Manipulate the inputs to client-side code, causing "stored" or "DOM-based" cross-site scripting.

It does not address other, related attacks. In particular, it assumes that attackers cannot reliably inject headers into a server’s response.

Extend this description.

1.3. Overview

This section is non-normative (but hopefully helpful anyway).

At its core, this story this document wants to tell is the following:

  1. Scripting Policy objects define the ways in which script can be executed within a given Document, Worker, etc. They have a number of properties that influence different aspects of script execution, defined in some detail in § 2 Framework.

  2. These policies are delivered as HTTP response headers: Scripting-Policy defines a policy to be enforced in a given context, and Scripting-Policy-Report-Only a policy to be reported upon, but not enforced.

    The headers' syntax is defined in § 2.1 Scripting Policy HTTP Response Headers, and parsing rules are in § 2.2 Parsing Scripting Policy Headers.

  3. Policies are bound to Document, WorkerGlobalScope, and WorkletGlobalScope objects when each is initialized. Each object obtain a policy from the HTTP response used when initializing the document or worker, or from the context responsible for the document’s navigation or worker’s instantiation if that response has a local scheme (e.g. data:, about:, blob:, etc.). These details are spelled out in § 2.4 Initialize a Document’s Scripting Policy and § 2.5 Initialize a global object’s Scripting Policy.

  4. A policy’s effects are enforced in a given context through hooks in HTML:

The rest of the document’s structure follows this general outline: § 2 Framework defines the concepts we’ll need, and the header delivery mechanism. § 2.3 Policy Application shows how policies are associated with the contexts that love them. § 2.6 Policy Enforcement explains how HTML and Fetch make decisions about how code is executed.

Enjoy!

1.4. Dependencies

The rest of the document relies upon Structured Headers to define policies' header syntax [I-D.ietf-httpbis-header-structure]. It also depends upon the Infra Standard for a number of foundational concepts used in its algorithms and prose [INFRA].

2. Framework

A Scripting Policy defines the characteristics of script execution in a given context, and may have effect on a Document, WorkerGlobalScope, or WorkletGlobalScope, as described in § 2.4 Initialize a Document’s Scripting Policy and § 2.5 Initialize a global object’s Scripting Policy.

Each policy has a nonce member, which is either null or a string representing a cryptographic "number-used-once". Unless otherwise specified, its value is null.

Note: The nonce member defines a "number-used-once" which must be reflected in script elements' nonce attribute in order to enable script execution. This check is performed during HTML’s prepare a script algorithm, as described in § 2.6 Policy Enforcement.

Each policy has an integrity member, which is either null, or a list either null or a list of integrity metadata objects. Unless otherwise specified, its value is null.

Note: The integrity member is a list of integrity metadata that define the set of scripts which can be executed on a given page, layering on top of [SRI] for enforcement. This check is performed during Fetch’s main fetch algorithm, as described in § 2.6 Policy Enforcement.

Each policy has an eval member, which is either "allow", "blocked", or "allow-trustedscript". Unless otherwise specified, its value is "allow-trustedscript".

Note: The eval member influences the way eval() is processed (shocking, right?). "allow" places no restriction upon eval(). "blocked" causes both eval(TrustedScript) and eval(DOMString) to throw. "allow-trustedscript" causes eval(DOMString) to throw, but allows eval(TrustedScript) to execute. This is detailed in § 2.6.3 Checking String Compilation (eval() and friends).

Each policy has a report-to member, which is either null, or a string representing a reporting group, as defined in [REPORTING]. Unless otherwise specified, its value is null.

Note: The report-to member wires Scripting Policy enforcement up to the Reporting API in order to inform developers about violations on their pages. This is detailed in § 2.7 Reporting Violations.

Each policy has a trusted-types-required-for member, which is either null, or a list of strings representing categories for which Trusted Types are enforced. Unless otherwise specified, its value is null.

Note: The trusted-types-require-for member configures Trusted Type enforcement. The only currently valid category is "script".

Each policy has a dynamic-loading member, which is either "allow-non-parser-inserted", or "check-non-parser-inserted". Unless otherwise specified, its value is "allow-non-parser-inserted".

Note: The dynamic-loading member controls how script that executes in a given context can cause more script to execute. "allow-non-parser-inserted" means that non-[=script/"parser-inserted"=] scripts will execute, regardless of nonce or integrity enforcement. "check-non-parser-inserted" will not privilege non-[script/"parser-inserted"=] script in any way, applying all checks that were applied to the original script.

Names are hard, but {allow,check}-non-parser-inserted are terrible.

Scripting Policies are often found in a Scripting Policy pair, combining one enforced policy with one policy which is unenforced, and merely reported upon. This is a pair whose first item is named enforced and whose second item is named report-only. Both items are either null or a Scripting Policy object. Unless otherwise specified, their values are both null.

2.1. Scripting Policy HTTP Response Headers

Servers can serialize Scripting Policy objects, and deliver them to user agents via HTTP response headers that declare the scripting requirements for a given response. The Scripting-Policy HTTP response header informs a user agent about the scripting requirements which are to be enforced for a given response. The Scripting-Policy-Report-Only HTTP response header has the same grammar and semantics as Scripting-Policy, but declares a policy that is not enforced in a given context, but merely reported upon.

Both are Structured Headers whose value MUST be a dictionary [I-D.ietf-httpbis-header-structure]. Their ABNF is:

Scripting-Policy = scripting-policy-dict
Scripting-Policy-Report-Only = scripting-policy-dict
scripting-policy-dict = sh-dictionary

Note: The sh-dictionary type is defined along with Structured Header’s dictionary concept in Section 3.2 of [I-D.ietf-httpbis-header-structure].

The scripting-policy-dict dictionary MAY contain one or more of the following members:

nonce

The member’s value is a token, which maps to the Scripting Policy's nonce member.

integrity

The member’s value is a list of token values, which are processed as integrity metadata values, and map to the Scripting Policy's integrity member.

eval

The member’s value is an enum represented as one of the following tokens: "allow", "blocked", and "allow-trustedscript". It maps to the Scripting Policy's eval member.

report-to

The member’s value is a token, which maps to the Scripting Policy's report-to member.

trusted-types-required-for

The member’s value is a list of token values, which maps to the Scripting Policy's trusted-types-required-for member.

dynamic-loading

The member’s value is an enum represented as one of the following tokens: "allow-non-parser-inserted", and "check-non-parser-inserted". It maps to the Scripting Policy's dynamic-loading member.

Both headers are currently meaningful only for responses that result in a navigation to a new document, as well as for responses that are executed as Worker, SharedWorker, and ServiceWorker. Detailed processing rules are found in § 2.6 Policy Enforcement.

2.2. Parsing Scripting Policy Headers

To obtain a Scripting Policy pair from a response, given a response (r), execute the following steps. This algorithm will return a Scripting Policy pair.
  1. Let headers be r’s header list.

  2. Let result be a new Scripting Policy pair.

  3. Let header be the result of getting Scripting-Policy from headers.

  4. If header is not null, set resultenforced to the result of obtaining a Scripting Policy from header.

  5. Let header be the result of getting Scripting-Policy-Report-Only from headers.

  6. If header is not null, set resultreport-only to the result of obtaining a Scripting Policy from header.

  7. Return result.

To obtain a Scripting Policy from a Structured Header, given a serialized Structured Header (header), execute the following steps. The algorithm will return a Scripting Policy, or null if no policy can be parsed successfully.
  1. Let policy be a new Scripting Policy object.

  2. Let struct be the result of parsing a Structured Header with an input_bytes of header and a header_type of dictionary.

    If parsing fails, return null.

  3. For each keyvalue of struct, byte-lowercase key and switch on the result:

    "dynamic-loading"
    1. If value is not a token, continue.

    2. If value is not contained in the list « "allow-non-parser-inserted", "check-non-parser-inserted" », continue.

    3. Set policy’s dynamic-loading member to value.

    "eval"
    1. If value is not a token, continue.

    2. If value is not contained in the list « "allow", "allow-trustedscript", "block" », continue.

    3. Set policy’s eval member to value.

    "integrity"

    Parse the integrity metadata, similar to the definition SRI §3.3.3 Parse metadata.. This might be as simple as joining the list on space, and running it through exactly that algorithm.

    Ensure that we normalize the values to base64 from base64url.

    "nonce"
    1. If value is not a token, continue.

    2. Set policy’s nonce member to value.

    "report-to"
    1. If value is not a token, continue.

    2. Set policy’s report-to member to value.

    "trusted-types-required-for"
    1. If value is not a token, continue.

    2. If value is not contained in the list « "script" », continue.

    3. Set policy’s trusted-types-required-for member to value.

  4. Return policy.

Note: Parsing errors fail open for compatibility with future changes to the dictionary names and values, as well as the underlying syntax. In particular, if parsing fails because the header cannot be interpreted as a Structured Header per the parsing rules defined in [I-D.ietf-httpbis-header-structure], no policy will be applied. This explicitly prioritizes forward compatibility with future iterations of Structured Headers, which may be surprising. User agents are encouraged to warn developers in this case.

2.3. Policy Application

Each context that can execute script has at most one Scripting Policy that governs script execution in that context.

Monkeypatching [HTML]:

Document objects have a scripting policy, which is a Scripting Policy pair.

WorkerGlobalScope objects have a scripting policy, which is a Scripting Policy pair.

WorkletGlobalScope objects have a scripting policy, which is a Scripting Policy pair.

Environment settings objects have an algorithm to obtain a scripting policy, defined as follows:

A Window objects’s environment settings object's scripting policy algorithm returns the scripting policy of the Window's associated Document.

A WorkerGlobalScope objects’s environment settings object's scripting policy algorithm returns the scripting policy of the WorkerGlobalScope.

A WorkletGlobalScope objects’s environment settings object's scripting policy algorithm returns the scripting policy of the WorkletGlobalScope.

Monkey patching!

2.4. Initialize a Document’s Scripting Policy

A Document's scripting policy comes from one of two places: the response to which the user agent navigated, or the environment settings object responsible for a navigation to a local scheme (e.g. data:, blob:, about:). In particular, note that this latter case covers <iframe srcdoc>.

Given a Document (document), a response (response), and a request or null (request), the user agent can initialize a Document's Scripting Policy by executing the following steps:
  1. If request is not null, and response’s url's scheme is a local scheme:

    1. Set document’s scripting policy to a copy of request’s client's scripting policy.

    2. Return.

    Does this cover about:srcdoc? Presumably this will be simplified in the future. <https://github.com/whatwg/html/issues/4926>

  2. Set document’s scripting policy to the result of obtaining a Scripting Policy pair from response.

This algorithm should be called from HTML’s create and initialize a Document object algorithm, directly after the existing hook for CSP:

Monkeypatching [HTML]'s create and initialize a Document object algorithm:

  1. Initialize a Document's Scripting Policy given document, response, and request.

Monkey patching!

2.5. Initialize a global object’s Scripting Policy

A global object's scripting policy comes from one of two places: the response used to initialize a given worker, or the environment settings object responsible for intializing a worker from a local scheme (e.g. new Worker('data:...');)

Given a global object (global) and a response (response), the user agent can initialize a global object’s Scripting Policy by executing the following steps:
  1. If response’s url's scheme is a local scheme:

    1. Assert: global is a DedicatedWorkerGlobalScope, a WorkletGlobalScope, or a SharedWorkerGlobalScope with one item in its owner set.

    2. With the single owner in global’s owner set, set global’s scripting policy to a copy of owner’s relevant settings object's scripting policy.

    3. Return.

    Is the assertion above correct? Presumably this will be simplified in the future. <https://github.com/whatwg/html/issues/4926>

  2. Set global’s scripting policy to the result of obtaining a Scripting Policy pair from response.

This algorithm should be called from HTML’s run a worker algorithm, directly after the existing hook for CSP:

Monkeypatching [HTML]'s run a worker algorithm:

  1. Obtain script ...

    In both cases, ...

    1. ...

    2. Initialize a global object’s Scripting Policy given worker global scope and response.

Monkey patching!

2.6. Policy Enforcement

2.6.1. Checking Nonces and Hashes

Scripting Policy’s restrictions on script execution are generally performed during HTML’s prepare a script algorithm, before requests are fired for externalized scripts, and prior to evaluating inline scripts.

Note: If a policy sets requirements for both a nonce and some set of integrity, either will be sufficient to allow script execution. That is, the policy nonce=abcdefg, integrity=(sha256-123456) would allow execution for each of <script nonce="abcdefg"></script>, <script integrity="sha256-123456"></script>, and <script nonce="abcdefg" integrity="sha256-123456"></script>.

To determine whether a script element should be blocked by Scripting Policy, given an Element (el), execute the following algorithm. It will return "Blocked" or "Allowed".
  1. Assert: el is an HTMLScriptElement or an SVGScriptElement.

  2. Let enforced be el’s node document's scripting policy's enforced.

  3. Let report-only be el’s node document's scripting policy's report-only.

  4. If report-only is not null:

    1. If report-only’s nonce is not null, and el’s [[CryptographicNonce]] slot is report-only’s nonce, then continue.

    2. Let list be report-only’s integrity, or an empty list if report-only’s integrity is null.

    3. If handler matches list, continue.

    4. Report a Scripting Policy violation, using report-only and el.

  5. If enforced is not null:

    1. If enforced’s nonce is not null, and el’s [[CryptographicNonce]] slot is enforced’s nonce, then continue.

    2. Let list be enforced’s integrity, or an empty list if enforced’s integrity is null.

    3. If handler matches list, continue.

    4. Report a Scripting Policy violation, using enforced and el.

    5. Return "Blocked".

  6. Return "Allowed".

This relies on SRI doing something useful for inline script blocks. We should probably make that happen. <https://github.com/w3c/webappsec-subresource-integrity/issues/44>

2.6.2. Checking Inline Event Handlers

Inline event handlers (e.g. <a onclick="amazing_javascript();">...</a>) are allowed iff they match integrity metadata explicitly allowed via a policy’s integrity list.

A string (value) matches an integrity metadata list (list) if the following algorithm returns "Match":
  1. Set source to the result of executing UTF-8 encode on the result of executing JavaScript string converstion on value.

  2. For each integrity metadata in list:

    1. Let expected be integrity metadata’s digest.

    2. Switch on integrity metadata’s algorithm:

      "sha256"
      1. If expected is a case-sensitive match with the result of base64 encoding the result of applying SHA-256 to source, return "Matches".

      "sha384"
      1. If expected is a case-sensitive match with the result of base64 encoding the result of applying SHA-384 to source, return "Matches".

      "sha512"
      1. If expected is a case-sensitive match with the result of base64 encoding the result of applying SHA-512 to source, return "Matches".

  3. Return "Does Not Match".

To determine whether an event handler be blocked by Scripting Policy, given an element (el) and string (handler):
  1. Let enforced be el’s node document's scripting policy's enforced.

  2. Let report-only be el’s node document's scripting policy's report-only.

  3. If report-only is not null:

    1. Let list be report-only’s integrity, or an empty list if report-only’s integrity is null.

    2. If handler matches list, continue.

    3. Report a Scripting Policy violation, using report-only, handler, and el.

  4. If enforced is not null:

    1. Let list be enforced’s integrity, or an empty list if enforced’s integrity is null.

    2. If handler matches list, continue.

    3. Report a Scripting Policy violation, using report-only, handler, and el.

    4. Return "Blocked".

  5. Return "Allowed".

Monkeypatching [HTML]'s event handler content attribute's attribute change steps:

  1. Otherwise:

    1. ...

    2. If Should an event handler be blocked by Scripting Policy? returns "Blocked" when executed upon element and value, then return.

2.6.3. Checking String Compilation (eval() and friends)

To address eval(), Scripting Policy modifies HTML’s implementation of JavaScript’s HostEnsureCanCompileStrings abstract operation to include verification against the context’s policy.

Given two realms (callerRealm and calleeRealm), and a string (source), EnsureScriptingPolicyDoesNotBlockScriptExecution(callerRealm, calleeRealm, source) returns normally if string compilation is allowed, and throws an EvalError if not:
  1. Let globals be a list containing callerRealm’s global object and calleeRealm’s global object.

  2. For each global in globals:

    1. Let enforced be global’s relevant settings object's scripting policy's enforced.

    2. Let report-only be global’s relevant settings object's scripting policy's report-only.

    3. If report-only is not null, switch on its eval value:

      "allow"

      Continue.

      "allow-trustedscript"

      How does this work?

      "blocked"
      1. Report a Scripting Policy violation, using report-only, source, and global.

    4. If enforced is not null, switch on its eval value:

      "allow"

      Continue.

      "allow-trustedscript"

      How does this work?

      "blocked"
      1. Report a Scripting Policy violation, using enforced, source, and global.

      2. Throw an EvalError exception.

  3. Return.

HostEnsureCanCompileStrings() does not include the string which is going to be compiled as a parameter. We’ll also need to update HTML to pipe that value through. <https://github.com/tc39/ecma262/issues/938>

Monkeypatching [HTML]'s HostEnsureCanCompileStrings():

  1. Perform ? EnsureCSPDoesNotBlockStringCompilation(callerRealm, calleeRealm). [CSP]

  2. Perform ? EnsureScriptingPolicyDoesNotBlockStringCompilation(callerRealm, calleeRealm).

Monkey patching!

2.6.4. Checking <base>

Scripting Policy blocks <base> from pointing to any cross-origin URL, as this can change the meaning of a script tag in unexpected and dangerous ways.

To determine whether a base element should be blocked by Scripting Policy, given an Element (el) and a URL (url), execute the following algorithm. It will return "Blocked" or "Allowed".
  1. If url’s origin is same origin with el’s node document's relevant settings object's origin, return "Allowed".

  2. Let enforced be el’s node document's scripting policy's enforced.

  3. Let report-only be el’s node document's scripting policy's report-only.

  4. If report-only is not null:

    1. Report a Scripting Policy violation, using report-only, handler, and el.

  5. If enforced is not null:

    1. Report a Scripting Policy violation, using enforced, handler, and el.

    2. Return "Blocked".

  6. Return "Allowed".

Monkeypatching [HTML]'s set the frozen base URL algorithm:

Insert the algorithm above into the current step 3, as follows:

  1. Set element’s frozen base URL to document’s fallback base URL, if urlRecord is failure or running Is base allowed for Document? on the resulting URL record and document returns "Blocked" either Is base allowed by Content Security Policy? or Is base allowed by Scripting Policy? return "Blocked" when executed upon the resulting URL record and document, and to urlRecord otherwise.

Monkeypatching!

2.6.5. Checking <embed> and <object>.

Scripting Policy blocks <embed> and <object>, as they have capabilities in line with (and beyond the capabilities of) script execution.

To determine whether a plugin element should be blocked by Scripting Policy, given an Element (el), execute the following algorithm. It will return "Blocked" or "Allowed".
  1. Let enforced be el’s node document's scripting policy's enforced.

  2. Let report-only be el’s node document's scripting policy's report-only.

  3. If report-only is not null:

    1. Report a Scripting Policy violation, using report-only, handler, and el.

  4. If enforced is not null:

    1. Report a Scripting Policy violation, using enforced, handler, and el.

    2. Return "Blocked".

  5. Return "Allowed".

Monkeypatching [HTML]'s object type determination steps

Insert the algorithm above into the current step 2, as follows:

  1. If the element has an ancestor media element, or has an ancestor object element that is not showing its fallback content, or if the element is not in a document whose browsing context is non-null, or if the element’s node document is not fully active, or if the element is still in the stack of open elements of an HTML parser or XML parser, or if the element is not being rendered, or if the Should element be blocked a priori by Content Security Policy? algorithm returns "Blocked" when executed on the element, or if the Should a plugin element be blocked by Scripting Policy? algorithm returns "Blocked" when executed on the element, then jump to the step below labeled fallback.

Monkeypatching!

This might be too strict, and we might want to have something akin to CSP’s plugin-types so folks can allow PDF in user agents that use PDFium? Or have a carveout similar to the sandboxed plugins browsing context flag concept of a "secured plugin"?

2.7. Reporting Violations

Scripting Policy reports provide developers with context when a given page or worker violates its policy. These violations are reported to developers via the Reporting API, which exposes violations via the ReportingObserver interface, as well as via reports delivered to endpoints configured via the Report-To header as defined in [REPORTING].

Scripting Policy reports have a report type of "scripting-policy", and are visible to ReportingObservers.

A Scripting Policy report's body is represented in JavaScript by the ScriptingPolicyReportBody interface:

enum ScriptingPolicyViolationType {
  "externalScript",
  "inlineScript",
  "inlineEventHandler",
  "eval"
};

[Exposed=(Window,Worker), SecureContext]
interface ScriptingPolicyReportBody : ReportBody {
  [Default] object toJSON();
  readonly attribute DOMString     violationType;
  readonly attribute USVString?    violationURL;
  readonly attribute USVString?    violationSample;
  readonly attribute unsigned long lineno;
  readonly attribute unsigned long colno;
};
violationType, of type DOMString, readonly

This attribute, to your certain astonishment, reflects the violation’s type:

  • "externalScript" reflects a violation when fetching a script (e.g. <script src="...">).

  • "inlineScript" reflects a violation from an inline script block (e.g. <script>...</script>).

  • "inlineEventHandler" reflects a violation from an inline event handler (e.g. <a onclick="...">).

  • "eval" reflects a violation from eval(), new Function(), etc.

  • More for TT? <base>? <object>?

violationURL, of type USVString, readonly, nullable

When the violationType is "externalScript", this attribute will contain that script’s URL. It will be null for any other type.

violationSample, of type USVString, readonly, nullable

When the violationType is not "externalScript", this attribute will contain additional detail about the violation’s cause. When inline event handlers cause a violation, for example, this attribute will contain the first 40 characters of the handler’s source.

When the violationType is "externalScript", this attribute will be null.

lineno, of type unsigned long, readonly
colno, of type unsigned long, readonly

These attributes contain the line and column numbers, respectively, associated with a violation, if the user agent can obtain them. If no positional information can be obtained, these attributes will both be 0.

To report a Scripting Policy violation, given a Scripting Policy (policy), string (source), and global object (global):
  1. TODO (policy, source, global).

3. Security and Privacy Considerations

TODO.

4. IANA Considerations

The permanent message header field registry should be updated with the following registrations [RFC3864]:

4.1. Scripting-Policy Registration

Header field name

Scripting-Policy

Applicable protocol

http

Status

standard

Author/Change controller

Me

Specification document

This specification (See § 2.1 Scripting Policy HTTP Response Headers)

4.2. Scripting-Policy-Report-Only Registration

Header field name

Scripting-Policy-Report-Only

Applicable protocol

http

Status

standard

Author/Change controller

Me

Specification document

This specification (See § 2.1 Scripting Policy HTTP Response Headers)

5. Acknowledgements

This document owes much to those developers who have struggled with Content Security Policy deployments over the years. In particular, Google ( ) and Dropbox ( ) have done a nice job sharing their experiences and challenges.

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Conformant Algorithms

Requirements phrased in the imperative as part of algorithms (such as "strip any leading space characters" or "return false and abort these steps") are to be interpreted with the meaning of the key word ("must", "should", "may", etc) used in introducing the algorithm.

Conformance requirements phrased as algorithms or specific steps can be implemented in any manner, so long as the end result is equivalent. In particular, the algorithms defined in this specification are intended to be easy to understand and are not intended to be performant. Implementers are encouraged to optimize.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[CSP]
Mike West. Content Security Policy Level 3. 15 October 2018. WD. URL: https://www.w3.org/TR/CSP3/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[ENCODING]
Anne van Kesteren. Encoding Standard. Living Standard. URL: https://encoding.spec.whatwg.org/
[FETCH]
Anne van Kesteren. Fetch Standard. Living Standard. URL: https://fetch.spec.whatwg.org/
[HTML]
Anne van Kesteren; et al. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[I-D.ietf-httpbis-header-structure]
Mark Nottingham; Poul-Henning Kamp. Structured Headers for HTTP. ID. URL: https://tools.ietf.org/html/draft-ietf-httpbis-header-structure
[INFRA]
Anne van Kesteren; Domenic Denicola. Infra Standard. Living Standard. URL: https://infra.spec.whatwg.org/
[REPORTING]
Douglas Creager; et al. Reporting API. 25 September 2018. WD. URL: https://www.w3.org/TR/reporting-1/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://tools.ietf.org/html/rfc2119
[RFC3864]
G. Klyne; M. Nottingham; J. Mogul. Registration Procedures for Message Header Fields. September 2004. Best Current Practice. URL: https://tools.ietf.org/html/rfc3864
[RFC4648]
S. Josefsson. The Base16, Base32, and Base64 Data Encodings. October 2006. Proposed Standard. URL: https://tools.ietf.org/html/rfc4648
[SERVICE-WORKERS-1]
Alex Russell; et al. Service Workers 1. 19 November 2019. CR. URL: https://www.w3.org/TR/service-workers-1/
[SVG2]
Amelia Bellamy-Royds; et al. Scalable Vector Graphics (SVG) 2. 4 October 2018. CR. URL: https://www.w3.org/TR/SVG2/
[URL]
Anne van Kesteren. URL Standard. Living Standard. URL: https://url.spec.whatwg.org/
[WebIDL]
Boris Zbarsky. Web IDL. 15 December 2016. ED. URL: https://heycam.github.io/webidl/

Informative References

[ARTUR]
Mike West. Anti-XSS Response-Time Uniqueness Requirement. URL: https://mikewest.github.io/artur-yes/
[CSP-IS-DEAD]
Lukas Weichselbaum; et al. CSP Is Dead, Long Live CSP! On the Insecurity of Whitelists and the Future of Content Security Policy. 2016-10-24. URL: https://ai.google/research/pubs/pub45542
[SRI]
Devdatta Akhawe; et al. Subresource Integrity. 23 June 2016. REC. URL: https://www.w3.org/TR/SRI/
[STRICT-CSP]
Strict CSP. URL: https://csp.withgoogle.com/docs/strict-csp.html

IDL Index

enum ScriptingPolicyViolationType {
  "externalScript",
  "inlineScript",
  "inlineEventHandler",
  "eval"
};

[Exposed=(Window,Worker), SecureContext]
interface ScriptingPolicyReportBody : ReportBody {
  [Default] object toJSON();
  readonly attribute DOMString     violationType;
  readonly attribute USVString?    violationURL;
  readonly attribute USVString?    violationSample;
  readonly attribute unsigned long lineno;
  readonly attribute unsigned long colno;
};

Issues Index

Extend this description.
Trusted types integration.
Names are hard, but {allow,check}-non-parser-inserted are terrible.
Parse the integrity metadata, similar to the definition SRI §3.3.3 Parse metadata.. This might be as simple as joining the list on space, and running it through exactly that algorithm.
Ensure that we normalize the values to base64 from base64url.
Monkey patching!
Does this cover about:srcdoc? Presumably this will be simplified in the future. <https://github.com/whatwg/html/issues/4926>
Monkey patching!
Is the assertion above correct? Presumably this will be simplified in the future. <https://github.com/whatwg/html/issues/4926>
Monkey patching!
This relies on SRI doing something useful for inline script blocks. We should probably make that happen. <https://github.com/w3c/webappsec-subresource-integrity/issues/44>
How does this work?
How does this work?
HostEnsureCanCompileStrings() does not include the string which is going to be compiled as a parameter. We’ll also need to update HTML to pipe that value through. <https://github.com/tc39/ecma262/issues/938>
Monkey patching!
Monkeypatching!
Monkeypatching!
This might be too strict, and we might want to have something akin to CSP’s plugin-types so folks can allow PDF in user agents that use PDFium? Or have a carveout similar to the sandboxed plugins browsing context flag concept of a "secured plugin"?
More for TT? <base>? <object>?
TODO (policy, source, global).
TODO.