Skip to content

Commit b558781

Browse files
crisbetoaaronshim
andcommitted
feat: AutoCSP + Trusted Types compatibility testing with browser
## Summary This PR introduces a new feature to automatically apply a strict Content Security Policy (CSP) to the generated web applications and report any violations that arise in the browser. While we have some *static analysis* checks for potential injection patterns-- we also rely on these powerful runtime defenses in 1p Google apps to provide a powerful guarantee that the injection-prone code will specifically not run. Therefore, we have in the past discussed the usefulness of a browser-based runtime check for compatibility with these runtime-based injection defenses as a complementary signal to our code-scanning raters. ## Motivation AutoCSP-- transformation of the HTML to load all `<script>` tags with `src` attributes with a dynamic inline loader script, and then make sure there is a [hash-only CSP](https://github.com/google/strict-csp) enforced on the site-- is a zero-config approach to systematically protect against injection attacks (XSS) with a strong runtime guarantee. AutoCSP is also a feature that is available in clientside Angular and a practice we would like to generalize. Compatibility with autoCSP-- especially violations arising from automatically generated code patterns-- is an important metric that we would like to collect to not only prove the robustness of the current autoCSP approach available in Angular but also to further promote this approach across the entire TS/JS frontend ecosystem as a best practice. ## High-level Approach 1. Define a new environment that can specify whether we will turn on autoCSP 2. If enabled, intercept the responses seen during the Puppeteer testing of the page so that we can attempt to add an autoCSP 3. Use the [strict-csp](https://github.com/google/strict-csp) NPM package to transform/rewrite the HTML to include hash-based CSPs and dynamic loader scripts. In the future, we can also figure out a sustainable way to bring in Angular's implementation of the autoCSP transformation to test for feature parity. (As of now, that logic is not exported as a separate package.) 4. Include the autoCSP as a `Content-Security-Report-Only` *header* in the intercepted/modified Puppeteer response. This has an advantage that the JS execution will not halt on the first violation, allowing us to collect as many violation reports in the interaction with the app as possible. Furthermore, sending the report-only header as a header rather than a `<meta>` tag allows us to specify a `report-uri` to collect this data. 5. Puppeteer will intercept all requests to the `report-uri` endpoint to collect the violation data. 6. A rater (only pulled in to our new environment that enables autoCSP) will count how many CSP and TT violations we have encountered. In the future, we hope to plug in the data from 6 to attempt a LLM auto-repair of the code and repeat this process. ## Additions * **Automated CSP Injection:** A new `auto-csp.ts` module has been created. This module leverages the `strict-csp` library to dynamically generate a strict, hash-based CSP for the application. It uses Puppeteer's request interception to inject the `Content-Security-Policy-Report-Only` header into the main HTML document response. * **Violation Reporting:** The system is configured to send CSP violation reports to a local endpoint (`/csp-report`), which is also intercepted by Puppeteer. These reports are collected and included in the final build results. * **Source Code Snippets:** To improve the actionability of the violation reports, the tool uses the Chrome DevTools Protocol to access the source code of the scripts that cause violations. It then includes a relevant code snippet in the report, making it easier to pinpoint the source of the issue. We will use this information to implement a future feature to attempt LLM auto-repairing of CSP + TT incompatibilities. * **Configuration:** The feature can be enabled or disabled on a per-environment basis using the new `enableAutoCsp` flag in the environment's configuration file. * **Dependencies:** Added `strict-csp` for CSP generation and `node-fetch` to handle server-side fetching of the initial HTML document to be used in the puppeteer intercept-and-autoCSP flow Co-authored-by: Aaron Shim <aaronshim@users.noreply.github.com>
1 parent a42917b commit b558781

File tree

15 files changed

+551
-16
lines changed

15 files changed

+551
-16
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,12 @@
5252
"handlebars": "^4.7.8",
5353
"limiter": "^3.0.0",
5454
"marked": "^16.1.1",
55+
"node-fetch": "^3.3.2",
5556
"p-queue": "^8.1.0",
5657
"puppeteer": "^24.10.1",
5758
"sass": "^1.89.2",
5859
"stylelint": "^16.21.1",
60+
"strict-csp": "^1.1.1",
5961
"stylelint-config-recommended-scss": "^15.0.1",
6062
"tinyglobby": "^0.2.14",
6163
"tsx": "^4.20.3",

pnpm-lock.yaml

Lines changed: 112 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

runner/builder/auto-csp-types.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* Describes the structure of a Content Security Policy (CSP) violation report
3+
* sent by the browser.
4+
* @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only#violation_report_syntax
5+
*/
6+
export interface CspViolation {
7+
/** The page where the violation occurred. */
8+
'document-uri': string;
9+
/** The directive that was violated (e.g., 'script-src-elem'). */
10+
'violated-directive': string;
11+
/** The full original policy. */
12+
'original-policy': string;
13+
/** The disposition of the policy ('enforce' or 'report'). */
14+
disposition: 'enforce' | 'report';
15+
/** The URL of the resource that violated the policy, if applicable. */
16+
'blocked-uri'?: string;
17+
/** The source file where the violation originated. */
18+
'source-file'?: string;
19+
/** The line number in the source file. */
20+
'line-number'?: number;
21+
/** The column number in the source file. */
22+
'column-number'?: number;
23+
/** A sample of the violating code. */
24+
'script-sample'?: string;
25+
/** A custom field we add containing a snippet of the source code around the violation. */
26+
codeSnippet?: string;
27+
}

0 commit comments

Comments
 (0)