diff --git a/CHANGELOG.md b/CHANGELOG.md
index 97adcfc..187ea5f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,19 @@
All notable changes to `@ngbracket/a11y-devtools` are documented here.
This project adheres to [Semantic Versioning](https://semver.org/).
+## 0.18.3
+
+### Fixed
+
+- Report mode now scans pages served with a strict Content-Security-Policy or
+ Trusted Types. The scan runs as an injected script, which a policy such as
+ `script-src 'self'` or `require-trusted-types-for 'script'` blocked, so the
+ page was reported as an error with no findings. The scanning browser now
+ ignores the page's CSP, whether it comes from a header or a `` tag.
+ Your app's policy is unchanged. Because the scan runs without the policy,
+ anything the policy would block for real users (a web font, an inline style)
+ loads during the scan, so a problem caused by the CSP itself isn't reported.
+
## 0.18.2
### Fixed
diff --git a/package-lock.json b/package-lock.json
index 2cdf0fe..fb31bdb 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "@ngbracket/a11y-devtools",
- "version": "0.18.2",
+ "version": "0.18.3",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@ngbracket/a11y-devtools",
- "version": "0.18.2",
+ "version": "0.18.3",
"license": "MIT",
"dependencies": {
"axe-core": "^4.13.0"
diff --git a/package.json b/package.json
index 8e75ecb..0f5764f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@ngbracket/a11y-devtools",
- "version": "0.18.2",
+ "version": "0.18.3",
"description": "Finds accessibility problems in Angular apps and names the component behind each one. A dev overlay, or headless reports for CI.",
"license": "MIT",
"author": "Duncan Faulkner",
diff --git a/src/report/headless.ts b/src/report/headless.ts
index e6b8334..88f2ca2 100644
--- a/src/report/headless.ts
+++ b/src/report/headless.ts
@@ -214,7 +214,14 @@ async function scanPass(
config: PassConfig,
): Promise {
const { baseUrl, routes, labels, waitMs, axeOptions, inPageScript, frameworkPrefixes } = config;
- const context = await browser.newContext({ viewport: { width: 1280, height: 900 }, colorScheme });
+ // bypassCSP: the scan runs as an injected inline script (plus a style tag that
+ // hides the overlay), which a strict Content-Security-Policy or Trusted Types
+ // would block. It only lifts the policy in this scanning browser.
+ const context = await browser.newContext({
+ viewport: { width: 1280, height: 900 },
+ colorScheme,
+ bypassCSP: true,
+ });
const pages: PageReport[] = [];
try {
const page = await context.newPage();
diff --git a/src/testing/e2e-report.spec.ts b/src/testing/e2e-report.spec.ts
index c99b3bb..48cce21 100644
--- a/src/testing/e2e-report.spec.ts
+++ b/src/testing/e2e-report.spec.ts
@@ -57,6 +57,20 @@ const PAGES: Record = {
Help
`,
+ // Served with a strict Content-Security-Policy and Trusted Types (see the
+ // server below), which block the injected scan unless the browser bypasses CSP.
+ '/csp': `
+
+
Strict CSP
+
+ `,
+ // The same policy set by a tag instead (the server adds it
+ // to ), which is how many Angular apps ship a CSP.
+ '/csp-meta': `
+
+
Strict CSP in a meta tag
+
+ `,
// Healthy: native controls in order, plus an iframe (Tab inside a frame shows
// up as repeated focus on the