Repository navigation
Security: rate-limit the CSP report sink per IP - #79
Merged
Merged
Conversation
…e-quiet note POST /api/csp-report is unauthenticated by necessity and had no limiter, so anyone could flood the Worker's logs. It now takes 5 reports a minute per IP from FLAG_LIMITER's budget, under its own csp:<ip> keys, checked before the body is read; past that it answers 429 and logs nothing. The header comment no longer says to enforce the report-only CSP once the sink stays quiet: the policy allows inline scripts, so enforcing it as written adds little XSS protection.
Deicyde
force-pushed
the
claude/csp-report-rate-limit
branch
from
October 17, 2026 23:36
f47673d to
2939165
Compare
Owner
Author
|
Recommendation: merge, timed against release B (at
Release-affecting. Release B now has to be built at Posted by the WikiLean PR swarm (lead); updated 2026-10-18. |
# Conflicts: # wiki/src/security.ts
… shared limiter; test the key and order Re-applies the per-IP limit on top of #84's sink: FLAG_LIMITER under csp:<ip> keys, checked before readBodyCapped, 429 with no log line past 5 requests a minute. The header comment no longer says to enforce the report-only CSP once the sink stays quiet, since the policy allows inline scripts. FLAG_LIMITER's comments in env.ts and wrangler.jsonc say it also caps the sink, so retuning one retunes both. The test captures the limiter key with a CF-Connecting-IP header and sends malformed JSON while the limiter refuses: it fails if the prefix changes or the check moves after parsing.
This was referenced Oct 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
POST /api/csp-report(wiki: security headers on Worker responses + report-only CSP #47) is unauthenticated by necessity, since browsers send the reports. It caps the body size and the violations per log line, but not the request rate, so anyone can flood the Worker's logs. Every other unauthenticated write has a limiter:/api/flagusesFLAG_LIMITER.wiki/src/security.tsgives a misleading next step. It says to "tighten it, then enforce it, once /api/csp-report stays quiet". The report-only policy allows'unsafe-inline'scripts, so enforcing it as written would add little XSS protection.Change
FLAG_LIMITERbefore reading the body (ahead of Security hardening: agent sandbox, fail-closed auth, request caps, static headers, token sealing #84'sreadBodyCapped). It uses its owncsp:<ip>keys, so it gets 5 requests a minute per IP without touching the flag route's budget. A Reporting API request can batch several reports. Past the limit it answers 429 and logs nothing.FLAG_LIMITER's comments inwiki/src/env.tsandwiki/wrangler.jsoncnow say it also caps the sink: the counters are separate but the limit and period are shared, so retuning the flag limit retunes the sink too.main; Security hardening: agent sandbox, fail-closed auth, request caps, static headers, token sealing #84 rewrotesecurity.ts, and the change is re-applied on top of its sink.How tested
wiki/test/security-headers.test.ts. It captures the limiter key from a request withCF-Connecting-IP: 203.0.113.7and assertscsp:203.0.113.7. It sends malformed JSON while the limiter refuses, and expects 429 (not the 400 malformed JSON gets), the security headers, and no log line. It fails if the key prefix changes or the check moves after parsing. The existing sink tests use the fixture's always-allow limiter and are unchanged.test/security-headers.test.tspasses locally (13 tests); CI on this PR runs the full Worker suite.Risks
Release
site/ops/brain_release_authority.py):wiki/src/security.ts,wiki/src/env.tsandwiki/wrangler.jsonc. It ships only through a Brain release and promotion. Release B is being built at the currentmain(Attest the non-Brain public assets for Brain release B #82), so merge this after B is promoted; it rides the next release.What Jack must do