Skip to content

chore(security): stage COOP/COEP report-only headers#3609

Open
lspassos1 wants to merge 1 commit into
koala73:mainfrom
lspassos1:fix/coop-coep-reporting
Open

chore(security): stage COOP/COEP report-only headers#3609
lspassos1 wants to merge 1 commit into
koala73:mainfrom
lspassos1:fix/coop-coep-reporting

Conversation

@lspassos1
Copy link
Copy Markdown
Collaborator

@lspassos1 lspassos1 commented May 5, 2026

Summary

Root cause

  • WorldMonitor currently ships CSP and other security headers, but no COOP/COEP signal. Enforcing COEP immediately could break existing cross-origin embeds/assets, so the project needs a report-only staging step with report delivery first.

Changes

  • Adds Reporting-Endpoints: wm-coop-coep="https://worldmonitor.app/api/security/report".
  • Adds Cross-Origin-Opener-Policy-Report-Only: same-origin; report-to="wm-coop-coep".
  • Adds Cross-Origin-Embedder-Policy-Report-Only: require-corp; report-to="wm-coop-coep".
  • Keeps Vercel and self-hosted nginx headers aligned.
  • Adds /api/security/report, an Edge endpoint that accepts Reporting API batches, rate-limits when Redis is configured, caps payload size, and logs only redacted report summaries.
  • Lists the endpoint in api/api-route-exceptions.json as an external protocol endpoint because its wire format is defined by the browser Reporting API.
  • Adds guardrail tests for report-only mode, report routing, nginx alignment, API-contract compliance, and report payload redaction.

Validation

  • node --test api/security/report.test.mjs tests/deploy-config.test.mjs
  • node --test tests/edge-functions.test.mjs
  • node --test api/security/report.test.mjs tests/deploy-config.test.mjs tests/edge-functions.test.mjs
  • npm run lint:api-contract
  • npm run lint:unicode
  • npm run lint:boundaries
  • node -e "JSON.parse(require('fs').readFileSync('vercel.json','utf8')); console.log('vercel.json ok')"
  • git diff --check -- api/api-route-exceptions.json api/security/report.js api/security/report.test.mjs vercel.json docker/nginx-security-headers.conf tests/deploy-config.test.mjs
  • npx biome check api/security/report.js api/security/report.test.mjs tests/deploy-config.test.mjs api/api-route-exceptions.json

Risk

  • Low from a runtime behavior perspective because the COOP/COEP headers remain report-only and should not block resource loading. The new report endpoint is public by design, but it is method-limited, payload-limited, optionally rate-limited through existing Redis rate limiting, and avoids logging raw URLs. Full COOP/COEP enforcement should remain a follow-up after reviewing reports and third-party asset compatibility.

Refs #3547

@vercel
Copy link
Copy Markdown

vercel Bot commented May 5, 2026

@lspassos1 is attempting to deploy a commit to the World Monitor Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e4e7f9188d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread vercel.json Outdated
Comment on lines +89 to +90
{ "key": "Cross-Origin-Opener-Policy-Report-Only", "value": "same-origin" },
{ "key": "Cross-Origin-Embedder-Policy-Report-Only", "value": "require-corp" },
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include reporting endpoint in COOP/COEP report-only headers

These report-only headers are configured without a report-to parameter, so browsers treat them as functionally inert and do not emit violation reports; that defeats the stated rollout goal of observing COOP/COEP breakage before enforcement. In practice this means production traffic will not generate the telemetry needed to validate readiness for enforced isolation. Add ; report-to="..." and a corresponding Reporting-Endpoints (or equivalent) header so violations are actually delivered.

Useful? React with 👍 / 👎.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 5, 2026

Greptile Summary

This PR stages COOP/COEP cross-origin isolation by adding Cross-Origin-Opener-Policy-Report-Only: same-origin and Cross-Origin-Embedder-Policy-Report-Only: require-corp to both the Vercel catch-all security header route and the self-hosted nginx config, along with guardrail tests to prevent accidental promotion to enforcement mode.

  • vercel.json and docker/nginx-security-headers.conf gain the two report-only headers; the change is low-risk since report-only never blocks resource loading.
  • tests/deploy-config.test.mjs adds three new test cases: presence of the headers, absence of their enforced counterparts, and nginx/vercel alignment.
  • Neither header value includes a report-to directive and no Reporting-Endpoints header is defined, so browsers will generate violation events but have no endpoint to POST them to — production reports are silently discarded and the monitoring goal of this rollout phase goes unmet.

Confidence Score: 3/5

Safe to merge from a breakage perspective — report-only headers never block requests — but the change does not achieve its own stated goal of collecting isolation violation data from production users.

The report-only headers are syntactically correct and won't regress anything. However, without a report-to directive in the header values and a corresponding Reporting-Endpoints header, every COOP/COEP violation report the browser generates is silently dropped. The entire rationale for this PR is observability before enforcement, and that observability is currently absent.

vercel.json (lines 89–90) and docker/nginx-security-headers.conf (lines 6–7) both need a report-to directive added to the header values, plus a new Reporting-Endpoints (or legacy Report-To) header on the same route pointing at a real collector.

Important Files Changed

Filename Overview
vercel.json Adds COOP/COEP report-only headers to the main catch-all security route; header values lack a report-to directive so reports are silently discarded in production
docker/nginx-security-headers.conf Adds matching COOP/COEP report-only headers for self-hosted deployments; same missing report-to issue as vercel.json
tests/deploy-config.test.mjs Adds guardrail tests: presence of report-only headers, absence of enforced headers, and nginx alignment; does not assert a report-to directive is set

Sequence Diagram

sequenceDiagram
    participant Browser
    participant Vercel/nginx as Vercel / nginx
    participant ReportCollector as Reporting Endpoint

    Browser->>Vercel/nginx: GET /page
    Vercel/nginx-->>Browser: 200 + COOP-Report-Only: same-origin\n+ COEP-Report-Only: require-corp\n(no report-to directive)

    Note over Browser: Loads cross-origin resource<br/>that would violate COEP
    Browser->>Browser: Generate COOP/COEP violation report

    Browser--xReportCollector: POST report\n(no endpoint configured — silently dropped)

    Note over Browser,ReportCollector: Without Reporting-Endpoints header +\nreport-to directive, reports never leave the browser
Loading

Reviews (1): Last reviewed commit: "chore(security): stage COOP COEP report-..." | Re-trigger Greptile

Comment thread vercel.json Outdated
Comment on lines +89 to +90
{ "key": "Cross-Origin-Opener-Policy-Report-Only", "value": "same-origin" },
{ "key": "Cross-Origin-Embedder-Policy-Report-Only", "value": "require-corp" },
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 No reporting endpoint — violations are silently discarded

Neither header includes a report-to directive, and there is no matching Reporting-Endpoints (or legacy Report-To) header anywhere in the config. Without a named reporting group, browsers generate the violation reports internally but have nowhere to POST them; they are silently dropped. Because collecting those violations is the entire stated purpose of this "safe first rollout step", the report-only phase as shipped delivers zero observability for production traffic. The Cross-Origin-Opener-Policy-Report-Only value should be same-origin; report-to="default" (and likewise for COEP), paired with a Reporting-Endpoints: default="https://<your-collector>/reports" header on the same route. The nginx config has the same omission.

@lspassos1 lspassos1 force-pushed the fix/coop-coep-reporting branch from e4e7f91 to 22c30e5 Compare May 5, 2026 20:02
@lspassos1
Copy link
Copy Markdown
Collaborator Author

Updated after the report-only review feedback.

The PR now wires report-to into both COOP and COEP report-only headers, adds a Reporting-Endpoints target, and includes a small /api/security/report Edge collector. The endpoint accepts Reporting API batches, caps request size, uses existing Redis-backed rate limiting when configured, and logs only redacted summaries so query strings/full URLs are not written to logs.

I also added tests for the collector, header/report endpoint wiring, nginx alignment, and the no-enforcement rollout guardrail.

@lspassos1 lspassos1 force-pushed the fix/coop-coep-reporting branch from 22c30e5 to 0cc6a37 Compare May 5, 2026 20:07
@lspassos1 lspassos1 requested a review from SebastienMelki as a code owner May 5, 2026 20:07
@lspassos1
Copy link
Copy Markdown
Collaborator Author

Follow-up to the CI failure: the failing job was lint:api-contract, not a style issue. Since /api/security/report is a browser Reporting API collector whose request format is dictated by the platform, I added it to api/api-route-exceptions.json as external-protocol with the rationale captured there.

Validated locally with npm run lint:api-contract, npm run lint:unicode, npm run lint:boundaries, the focused tests, and the Edge Function guardrail test.

@lspassos1
Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant