Skip to content

Commit e2c8c08

Browse files
committed
Update README
1 parent d3ae1e7 commit e2c8c08

2 files changed

Lines changed: 107 additions & 108 deletions

File tree

README.md

Lines changed: 105 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,146 +1,145 @@
1-
[![GitHub release](https://img.shields.io/npm/v/reporting-api.svg?style=flat-square)](https://www.npmjs.com/package/reporting-api)
2-
31
# reporting-api
42

5-
This package provides a middleware for Express.js that automatically configures the [Reporting API](https://w3c.github.io/reporting/) on existing policy headers and an endpoint to help you collect your own reports using the Reporting API.
3+
[![npm](https://img.shields.io/npm/v/reporting-api?style=flat-square)](https://www.npmjs.com/package/reporting-api)
4+
[![license](https://img.shields.io/npm/l/reporting-api?style=flat-square)](https://github.com/wille/reporting-api/blob/master/LICENSE)
65

7-
Automatically sets up reporting for the following headers and features supporting the Reporting API
8-
- [`Content-Security-Policy`](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) (CSP)
9-
- [`Content-Security-Policy-Report-Only`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only)
10-
- [`Permissions-Policy`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Permissions_Policy)
11-
- [`Permissions-Policy-Report-Only`](https://github.com/w3c/webappsec-permissions-policy/blob/main/reporting.md
12-
)
13-
- [`Cross-Origin-Opener-Policy`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy) (COOP)
14-
- [`Cross-Origin-Opener-Policy-Report-Only`](https://github.com/camillelamy/explainers/blob/main/coop_reporting.md)
15-
- [`Cross-Origin-Embedder-Policy`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy) (COEP)
16-
- [`Cross-Origin-Embedder-Policy-Report-Only`](https://gist.github.com/yutakahirano/f14f15bd1595e1e913b0870649000470)
17-
- [`NEL` (Network Error Logging)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Network_Error_Logging)
18-
- [Deprecation Reports](https://wicg.github.io/deprecation-reporting/)
19-
- [Intervention Reports](https://wicg.github.io/intervention-reporting/)
20-
- [Crash Reports](https://wicg.github.io/crash-reporting/)
6+
Express.js middleware for the [Reporting API](https://w3c.github.io/reporting/). Automatically wires up `report-to` / `report-uri` on your existing policy headers and gives you a ready-made endpoint to collect violation, deprecation, crash, and network error reports.
217

22-
Supports "CSP Level 2 Reports" in browsers browsers not supporting the Reporting API.
8+
## Supported headers and report types
239

24-
## Core concepts
10+
| Header | Shorthand |
11+
|--------|-----------|
12+
| [`Content-Security-Policy`](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) | CSP |
13+
| [`Content-Security-Policy-Report-Only`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only) | |
14+
| [`Cross-Origin-Opener-Policy`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Opener-Policy) | COOP |
15+
| [`Cross-Origin-Opener-Policy-Report-Only`](https://github.com/camillelamy/explainers/blob/main/coop_reporting.md) | |
16+
| [`Cross-Origin-Embedder-Policy`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cross-Origin-Embedder-Policy) | COEP |
17+
| [`Cross-Origin-Embedder-Policy-Report-Only`](https://gist.github.com/yutakahirano/f14f15bd1595e1e913b0870649000470) | |
18+
| [`Permissions-Policy`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Permissions_Policy) | |
19+
| [`Permissions-Policy-Report-Only`](https://github.com/w3c/webappsec-permissions-policy/blob/main/reporting.md) | |
20+
| [`NEL` (Network Error Logging)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Network_Error_Logging) | NEL |
2521

26-
Retrofitting a policy on a large website is hard to get right first. The solution is to use `-Report-Only` policies that will not enforce them and break your website. These headers and their enforcing equivalents supports reporting, which makes all policy violations gets sent to you so you can adjust your policies to not break functionality.
22+
Plus [Deprecation](https://wicg.github.io/deprecation-reporting/), [Intervention](https://wicg.github.io/intervention-reporting/), and [Crash](https://wicg.github.io/crash-reporting/) reports.
2723

28-
### Setup a reporting endpoint and setup reporters on your policy headers
24+
Backwards-compatible with CSP Level 2 `report-uri` for browsers that don't yet support the Reporting API.
2925

26+
## Install
27+
28+
```bash
29+
npm install reporting-api
3030
```
31-
$ npm install reporting-api
32-
```
31+
32+
Peer dependencies: `express`, `zod`, `debug`.
33+
34+
## Quick start
3335

3436
```ts
35-
import { reportingEndpoint } from 'reporting-api';
3637
import express from 'express';
38+
import { reportingEndpoint, setupReportingHeaders } from 'reporting-api';
3739

3840
const app = express();
3941

40-
// The reporting endpoint.
41-
// Use `use` to support CORS preflight request if you are receiving reports from another origin
42+
// 1. Mount the reporting endpoint
4243
app.use('/reporting-endpoint', reportingEndpoint({
43-
allowedOrigins: '*', // Allow reports from all origins
44+
allowedOrigins: '*',
4445
onReport(report) {
45-
// Collect the reports and do what you want with them
46-
console.log('Report received', {
47-
isEnforced: report.body.type === 'enforce',
48-
type: report.type,
49-
body: report.body,
50-
});
51-
}
46+
console.log(report.type, report.body);
47+
},
5248
}));
5349

54-
// Set the security headers
55-
app.get('/*', (req, res, next) => {
56-
// Set a CSP that disallows inline scripts.
50+
// 2. Set your policy headers, then let the middleware attach reporters
51+
app.use((req, res, next) => {
5752
res.setHeader('Content-Security-Policy', "script-src 'self'");
58-
59-
// COOP policy that disallows link in new tab
6053
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
61-
62-
// COEP policy that disallows external resources that does not use CORS or CORP (Cross-Origin-Resource-Policy)
6354
res.setHeader('Cross-Origin-Embedder-Policy', 'require-corp');
64-
65-
// Setup headers alternative 1
66-
setupReportingHeaders('/reporting-endpoint')(req, res);
67-
return next();
68-
});
69-
// Setup headers alternative 2
70-
app.get('/*', setupReportingHeaders('/reporting-endpoint', {
71-
includeDefaultReporters: true,
72-
enableNetworkErrorLogging: true,
73-
version: '1',
74-
}));
75-
//
76-
app.get('/test', (req, res) => {
77-
res.writeHead(200, {
78-
'Content-Type': 'text/html; charset=utf8'
79-
});
80-
81-
// The script will not run and instead generate a csp-violation report
82-
// Clicking the link will trigger a COOP report
83-
// Loading the image will trigger a COEP report
84-
res.end(`Hello World!
85-
<script>alert(1)</script>
86-
<a href="https://google.com" target="_blank">Trigger COOP</>
87-
<img src="https://lh3.googleusercontent.com/wAPeTvxh_EwOisF8kMR2L2eOrIOzjfA5AjE28W5asyfGeH85glwrO6zyqL71dCC26R63chADTO7DLOjnqRoXXOAB8t2f4C3QnU6o0BA">
88-
`);
55+
next();
8956
});
57+
app.use(setupReportingHeaders('/reporting-endpoint'));
9058

9159
app.listen(8080);
9260
```
9361

9462
> [!NOTE]
95-
> The policy headers must be set before the reportingEndpointHeader middleware so the middleware is able to append the reporter to the policy headers.
63+
> Policy headers must be set **before** `setupReportingHeaders` runs so the middleware can append `report-to` and `report-uri` directives to them.
64+
65+
The resulting response headers will look like this:
9666

97-
### Response with a `Reporting-Endpoints` header created and reporter setup on the Policy headers
9867
```
99-
$ curl -I localhost:8080/test
100-
Reporting-Endpoints: default=/test-endpoint
101-
Content-Security-Policy: default-src 'self'; report-to default; report-uri /reporting-endpoint?src=report-uri
102-
Cross-Origin-Opener-Policy: same-origin; report-to="default"
103-
Cross-Origin-Embedder-Policy: require-corp; report-to="default"
104-
105-
Hello World!
106-
<script>alert(1)</script>
107-
<a href="https://google.com" target="_blank">Trigger COOP</>
108-
<img src="https://lh3.googleusercontent.com/wAPeTvxh_EwOisF8kMR2L2eOrIOzjfA5AjE28W5asyfGeH85glwrO6zyqL71dCC26R63chADTO7DLOjnqRoXXOAB8t2f4C3QnU6o0BA">
68+
Reporting-Endpoints: reporter="/reporting-endpoint"
69+
Content-Security-Policy: script-src 'self';report-uri /reporting-endpoint?disposition=enforce;report-to reporter
70+
Cross-Origin-Opener-Policy: same-origin;report-to="reporter"
71+
Cross-Origin-Embedder-Policy: require-corp;report-to="reporter"
10972
```
11073

111-
> [!TIP]
112-
>
113-
> The Reporting API is also accessible in some browsers using the [ReportingObserver](https://developer.mozilla.org/en-US/docs/Web/API/ReportingObserver)
114-
> ```js
115-
> if (typeof ReportingObserver !== 'undefined') {
116-
> const myObserver = new ReportingObserver(reportList => {
117-
> reportList.forEach(report => {
118-
> console.log(report.body);
119-
> });
120-
> });
121-
> myObserver.observe();
122-
> }
123-
>```
124-
125-
## Configuration options
126-
127-
- [`reportingEndpoint`](./src/reporting-endpoint.ts)
128-
- [`setupReportingHeaders`](./src/setup-headers.ts)
74+
## API
12975

130-
> [!NOTE]
131-
> Set the `allowedOrigins` option on your reporting endpoint to allow cross origin reports.
76+
### `reportingEndpoint(config)`
77+
78+
Returns Express middleware that accepts incoming reports.
79+
80+
| Option | Type | Description |
81+
|--------|------|-------------|
82+
| `onReport` | `(report, req) => void` | Called for every valid report. |
83+
| `onValidationError` | `(error, body, req) => void` | Called when a report fails Zod validation. |
84+
| `allowedOrigins` | `string \| RegExp \| Array` | Enable CORS for cross-origin reports. Use `'*'` to allow any origin. |
85+
| `ignoreBrowserExtensions` | `boolean` | Drop CSP violations originating from browser extensions. |
86+
| `ignoredDeprecationIds` | `string[]` | Deprecation report IDs to ignore (e.g. `['AttributionReporting', 'Topics']`). |
87+
| `maxAge` | `number` | Maximum report age in **seconds**. Older buffered reports are dropped. |
88+
| `debug` | `boolean` | Enable `debug` logging for the `reporting-api:*` namespace. |
89+
90+
### `setupReportingHeaders(url, config?)`
91+
92+
Returns Express middleware that appends `report-to` / `report-uri` to every policy header already set on the response and adds the `Reporting-Endpoints` header.
93+
94+
| Option | Type | Default | Description |
95+
|--------|------|---------|-------------|
96+
| `reportingGroup` | `string` | `"reporter"` | Reporting group name. |
97+
| `enableDefaultReporters` | `boolean` | `false` | Use the `default` group so you also receive Deprecation, Crash, and Intervention reports. |
98+
| `enableNetworkErrorLogging` | `boolean \| object` | `false` | Add `Report-To` + `NEL` headers (Reporting API v0, required for NEL). Accepts `{ success_fraction, failure_fraction, include_subdomains }`. |
99+
| `version` | `string \| number` || Appended as a `?version=` query param so you can correlate reports with policy revisions. |
100+
101+
## Report schema
102+
103+
Every report delivered to `onReport` is validated with Zod and has the shape:
104+
105+
```ts
106+
{
107+
type: 'csp-violation' | 'coop' | 'coep' | 'deprecation' | 'crash'
108+
| 'intervention' | 'network-error' | 'permissions-policy-violation'
109+
| 'potential-permissions-policy-violation';
110+
body: { /* type-specific fields */ };
111+
url: string;
112+
age: number;
113+
user_agent: string;
114+
report_format: 'report-uri' | 'report-to' | 'report-to-safari';
115+
version?: string;
116+
}
117+
```
118+
119+
Full type definitions are exported as `Report` and the individual body types (`ContentSecurityPolicyReport`, `CrossOriginOpenerPolicyReport`, etc.).
120+
121+
## Client-side observing
122+
123+
Reports can also be observed in the browser via [ReportingObserver](https://developer.mozilla.org/en-US/docs/Web/API/ReportingObserver):
124+
125+
```js
126+
if (typeof ReportingObserver !== 'undefined') {
127+
new ReportingObserver((reports) => {
128+
reports.forEach(r => console.log(r.body));
129+
}).observe();
130+
}
131+
```
132132

133133
## Resources
134134

135-
- [Permissions-Policy reporting](https://github.com/w3c/webappsec-permissions-policy/blob/main/reporting.md)
136-
- [Reporting API v0 and Reporting API v1 differences](https://chromium.googlesource.com/chromium/src/+/HEAD/net/reporting/README.md#supporting-both-v0-and-v1-reporting-in-the-same-codebase)
135+
- [Reporting API v1 spec (Reporting-Endpoints)](https://w3c.github.io/reporting/)
136+
- [Reporting API v0 spec (Report-To)](https://www.w3.org/TR/reporting/)
137137
- [Migrating from v0 to v1](https://developer.chrome.com/blog/reporting-api-migration)
138-
- [Reporting API v0 (Report-To)](https://www.w3.org/TR/reporting/)
139-
- [Reporting API v1 (Reporting-Endpoints)](https://w3c.github.io/reporting/)
138+
- [v0 vs v1 differences (Chromium)](https://chromium.googlesource.com/chromium/src/+/HEAD/net/reporting/README.md#supporting-both-v0-and-v1-reporting-in-the-same-codebase)
139+
- [Permissions-Policy reporting](https://github.com/w3c/webappsec-permissions-policy/blob/main/reporting.md)
140140

141141
### Notes
142142

143-
- `Permissions-Policy` reports to the `default` reporting group if `report-to` is not set.
144-
- `report-to` group MUST be in double quotes (eg. `report-to="group"`) in COOP AND COEP headers to be used.
145-
- [`Document-Policy`](https://wicg.github.io/document-policy/) and [`Document-Policy-Report-Only`](https://wicg.github.io/document-policy/) is doesn't look that supported or well documented, is it supersceded by Permissions-Policy?
146-
- Safari sends reports in the format `body: { ... }` instead of an array of reports and it doesn't include an `age`
143+
- `Permissions-Policy` reports to the `default` group when `report-to` is not set.
144+
- COOP and COEP require `report-to` values wrapped in double quotes (e.g. `report-to="group"`).
145+
- Safari sends reports as `{ body: { ... } }` instead of an array and omits `age`.

src/setup-headers.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ describe('Handles multiple headers and only updates the last occuring', () => {
2222
res.setHeader('Content-Security-Policy', inputCsp);
2323
res.setHeader(
2424
'Content-Security-Policy-Report-Only',
25-
"frame-src 'none'",
25+
"frame-src 'none'"
2626
);
2727

2828
setupReportingHeaders('/endpoint')(req, res, next);
@@ -34,7 +34,7 @@ describe('Handles multiple headers and only updates the last occuring', () => {
3434
}
3535

3636
expect(inputCsp.at(-1)).toBe(
37-
"frame-src 'self';report-uri /endpoint?disposition=enforce;report-to reporter",
37+
"frame-src 'self';report-uri /endpoint?disposition=enforce;report-to reporter"
3838
);
3939
});
4040
});

0 commit comments

Comments
 (0)