audit-csrf
Skill106 starsChanged 7 months agoMITView on GitHub
Check CSRF protection on every form and mutation API, on top of the built-in same-origin check (`api.requireSameOrigin`). Use for "audit CSRF", "check CSRF protection", "are my forms safe", "review session security".
In this repository33 other files it gives its agents.
- AGENTS.mdAGENTS.md
- add-authSkill
- add-capabilitiesSkill
- add-contentSkill
- add-dbSkill
- add-i18nSkill
- add-imagesSkill
- add-observabilitySkill
- add-openapiSkill
- audit-a11ySkill
- audit-agent-surfaceSkill
- audit-authSkill
- audit-bundlesSkill
- audit-depsSkill
- audit-headersSkill
- audit-islandsSkill
- audit-loadersSkill
- audit-redirectsSkill
- audit-secretsSkill
- audit-seoSkill
- audit-shellsSkill
- configure-isgSkill
- migrate-nextjsSkill
- pracht-debugSkill
- pracht-deploySkill
- pracht-scaffoldSkill
- pracht-test-apiSkill
- pre-deploySkill
- scaffold-e2eSkill
- scaffold-testsSkill
- tune-render-modeSkill
- typed-routesSkill
- upgrade-prachtSkill
The file
We split this skill into the rules it gives an agent
6 rules. About 0 are common, 2 are distinctive, and 4 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
4
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 49
Read src/routes.ts (the app manifest) for defineApp({ api: { requireSameOrigin } }).
- Very specific: line 116
If Step 0 found requireSameOrigin: false, look for middleware that:
- Very specific: line 124
Verify the wiring: the middleware name must appear in defineApp({ api: { middleware: [...] } }) — that single global list applies to every …
- Distinctive: line 163
Do not flag GET-only forms or read-only API methods.
- Very specific: line 170
If the project sets SameSite=None, require either a token check or an origin-check in addition to the built-in guard — explain why in the …
- Distinctive: line 172
Do not auto-fix. CSRF strategy is a policy decision.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Pracht Audit CSRF
What the framework guarantees: by default (ApiConfig.requireSameOrigin,
true unless explicitly disabled), the runtime rejects state-changing API
requests (POST/PUT/PATCH/DELETE) with a 403 unless the browser signals
an exact same-origin fetch (Sec-Fetch-Site: same-origin) or the request's
Origin/Referer matches the request URL's origin. same-site is not
accepted (sibling subdomains can be attacker-controlled). Requests with no
browser provenance headers at all (curl, server-to-server) are allowed — the
threat model is browser-form CSRF, which cannot strip those headers. Page
routes reject unsafe methods outright, so the API surface is where mutations
live.
The same check also covers WebSocket upgrade requests (any request carrying
an Upgrade header), even though they are GET. Browsers do not apply CORS to
WebSocket, so an upgrade is a cross-site-reachable, cookie-carrying request —
cross-site WebSocket hijacking. requireSameOrigin: false therefore opens
sockets as well as mutations.
The audit therefore targets the OPT-OUTS and the remaining layers, in order of preference:
- Built-in same-origin enforcement (
requireSameOrigin, on by default). SameSite=Lax(orStrict) on session cookies — defense in depth.- Origin-check middleware — only needed when
requireSameOriginis disabled or for non-/apiendpoints. - Per-request tokens — only when
SameSite=Noneis required.
Prerequisites: pracht inspect requires a vite config that registers the
pracht plugin.
Step 0: Check the built-in guard
Read src/routes.ts (the app manifest) for
defineApp({ api: { requireSameOrigin } }). Absent means true (the
default). An explicit requireSameOrigin: false is a top finding (error):
the project has opted out of the built-in CSRF protection and MUST show
compensating layers (origin-check middleware or token protocol) — demand them
in the report.
Step 1: Inventory mutation surfaces
Forms
Grep for <Form across src/. For each occurrence:
- Capture
method(default isget— onlypost/put/patch/deleteare CSRF-relevant). - Capture
action.
API mutations
pracht inspect api --jsonMCP: if pracht dev-mcp is registered, prefer its inspect_*, doctor, and verify tools to the CLI.
For each API route, read the exported methods. Mutation methods: POST,
PUT, PATCH, DELETE. Caveat: a default-export handler serves ALL
methods but reports methods: [] — check the hasDefaultHandler field
(requires a current @pracht/cli) or, on older CLIs, grep the handler file
for export default. A default handler counts as exposing all mutation
methods unless it gates on request.method itself.
WebSocket upgrades
Grep API handlers for upgrade, WebSocketPair, and status: 101. A route
that returns a handshake is a mutation-equivalent surface: it is reachable
cross-site, carries cookies, and — once open — is not covered by any
per-request check. Treat it as a mutation surface in Step 5, and additionally
verify:
- The handler authenticates the handshake (session check in the handler or
in
api.middleware). The built-in origin check stops other websites; it does nothing about an unauthenticated client. - Authorization is re-checked per message where messages carry authority. The socket outlives the request that opened it, so a session revoked afterwards does not close it.
Step 2: Inspect the session cookie
Locate cookie issuance — typically src/server/session.ts, src/api/auth/*,
or anywhere Set-Cookie appears in a response. For every cookie set:
| Attribute | Required posture | Failure mode |
HttpOnly | Present | XSS can steal the session |
Secure | Present in production | Sniffable on HTTP |
SameSite | Lax or Strict | Cross-site sends the cookie |
Path | Set (usually /) | Scope confusion |
Flag any cookie missing HttpOnly, missing SameSite, or with
SameSite=None without an accompanying token check. A missing
Max-Age/Expires is info only — session cookies are legitimate and
strictly shorter-lived, not a failure.
Step 3: Origin-check middleware (only for opt-outs)
A manual origin-check middleware is only needed when requireSameOrigin is
disabled, or for mutation endpoints outside /api handled by custom code.
If Step 0 found requireSameOrigin: false, look for middleware that:
- Reads
request.headers.get('origin'). - Compares against
url.originand an allowlist. - Rejects unsafe-method requests on mismatch.
The canonical shape is the origin-check.ts example in https://pracht.resynapse.dev/docs/recipes/auth.md.
Verify the wiring: the middleware name must appear in
defineApp({ api: { middleware: [...] } }) — that single global list applies
to every API route and generated capability HTTP endpoint. There is no
per-group API middleware, and
pracht inspect api --json output has no middleware field, so the manifest is
the only place to check.
Step 4: Look for token-based CSRF
Grep for: csrf, csrfToken, hidden form fields with token values, headers
like x-csrf-token. Verify both sides of the protocol exist (issue + verify).
A token issuer with no verifier (or vice versa) is a bug.
Step 5: Score each mutation surface
For each <Form> and each mutation API, assign a severity (primary) and a
posture verdict (secondary):
info/ Strong — built-in same-origin enforcement intact (default) +SameSite=Lax/Strictcookies.info/ Adequate — built-in enforcement intact; cookie posture unknown orSameSiteunset (the runtime check still blocks browser CSRF).warn/ Compensated —requireSameOrigin: falsebut a verified origin-check middleware or token protocol covers the surface.warn/ Token-only — token verified, cookie hasSameSite=None.error/ Weak —requireSameOrigin: falseand no token / no origin-check.warn/ Unknown — cookie source not located — investigate.
Produce:
| Surface | File:Line | Method | requireSameOrigin | Cookie posture | Middleware/Token? | Severity | Verdict |
Rules
- Point users at https://pracht.resynapse.dev/docs/recipes/auth for cookie
and middleware patterns, and at
ApiConfig.requireSameOriginfor the built-in guard. - Do not flag GET-only forms or read-only API methods.
- Remember: a hydrated
<Form>intercepts unsafe-method submissions and issuesfetch(actionUrl, { method, body: formData })— not a document POST. Fetch's default credentials mode issame-origin, so a cross-originactiongets no cookies at all; same-origin submissions carry cookies and the browser'sSec-Fetch-Site/Originheaders, which the built-in check validates. - If the project sets
SameSite=None, require either a token check or an origin-check in addition to the built-in guard — explain why in the report. - Do not auto-fix. CSRF strategy is a policy decision; surface the gaps and let the user choose layers.
$ARGUMENTS
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

