Skip to content

Commit

Permalink
Support CORS
Browse files Browse the repository at this point in the history
  • Loading branch information
wille committed Jul 31, 2024
1 parent 225d3b3 commit efc595f
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 11 deletions.
11 changes: 3 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,6 @@ app.listen(8080);

> [!NOTE]
> The policy headers must be set before the reportingEndpointHeader middleware so the middleware is able to append the reporter to the policy headers.
> ***
> If the reporting endpoint is on another origin, you need to setup CORS
> ```ts
> import cors from 'cors';
> const corsMiddleware = cors();
> app.options('/reporting-endpoint', cors());
> app.post('/reporting-endpoint', cors(), ...);
> ```
### Response with a `Reporting-Endpoints` header created and reporter setup on the Policy headers
```
Expand Down Expand Up @@ -133,6 +125,9 @@ Hello World!
- [`reportingEndpoint`](./src/reporting-endpoint.ts)
- [`setupReportingHeaders`](./src/setup-headers.ts)
> [!NOTE]
> Set the `allowedOrigins` option on your reporting endpoint to allow cross origin reports.
## Resources
- [Permissions-Policy reporting](https://github.com/w3c/webappsec-permissions-policy/blob/main/reporting.md)
Expand Down
57 changes: 54 additions & 3 deletions src/reporting-endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ export interface ReportingEndpointConfig {
* Debug mode
*/
debug?: boolean;

/**
* Set this field to enable CORS for reports sent cross origin to other domains.
* A special value '*' can be set to allow any domain to send reports to your endpoint.
*
* @example 'https://example.com'
* @example /https:\/\/(.*)\.example.com$/
*/
allowedOrigins?: string | RegExp | (string | RegExp)[];
}

function filterReport(
Expand Down Expand Up @@ -69,8 +78,23 @@ function filterReport(
return true;
}

function isOriginAllowed(
origin: string,
allowedOrigin: ReportingEndpointConfig['allowedOrigins']
): boolean {
if (Array.isArray(allowedOrigin)) {
return allowedOrigin.some((o) => isOriginAllowed(origin, o));
} else if (allowedOrigin instanceof RegExp) {
return allowedOrigin.test(origin);
} else if (typeof allowedOrigin === 'string') {
return allowedOrigin === origin;
}

return false;
}

function createReportingEndpoint(config: ReportingEndpointConfig) {
const { onReport, onValidationError } = config;
const { onReport, onValidationError, allowedOrigins } = config;

if (config.debug) {
debug.enable('reporting-api:*');
Expand Down Expand Up @@ -103,11 +127,38 @@ function createReportingEndpoint(config: ReportingEndpointConfig) {
}
}

return (req: Request, res: Response, next: NextFunction) => {
if (req.method !== 'POST') {
return (req: Request, res: Response) => {
if (req.method !== 'POST' && req.method !== 'OPTIONS') {
return res.sendStatus(405);
}

// If cross origin reports are allowed, setup CORS on both OPTIONS and POST.
if (allowedOrigins) {
const originHeader = req.headers.origin;

if (config.allowedOrigins === '*') {
res.setHeader('Access-Control-Allow-Origin', '*');
} else if (
originHeader &&
isOriginAllowed(originHeader, allowedOrigins)
) {
res.setHeader('Access-Control-Allow-Origin', originHeader);
}

// Since reports are sent with a Content-Type header MIME type that is not considered 'simple' (https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#simple_requests)
// we will always get a preflight request
if (req.method === 'OPTIONS') {
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.setHeader('Access-Control-Allow-Methods', 'POST');

// Capped at 7200 in Chrome
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Max-Age#delta-seconds
res.setHeader('Access-Control-Max-Age', '7200');

return res.sendStatus(200);
}
}

const version =
typeof req.query.version === 'string'
? req.query.version
Expand Down

0 comments on commit efc595f

Please sign in to comment.