agentleFS
JoviDeCroockJoviDeCroock/pracht/skills/audit-csrf/SKILL.md

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".

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
  1. Very specific:

    Read src/routes.ts (the app manifest) for defineApp({ api: { requireSameOrigin } }).

    line 49
  2. Very specific:

    If Step 0 found requireSameOrigin: false, look for middleware that:

    line 116
  3. Very specific:

    Verify the wiring: the middleware name must appear in defineApp({ api: { middleware: [...] } }) — that single global list applies to every …

    line 124
  4. Distinctive:

    Do not flag GET-only forms or read-only API methods.

    line 163
  5. Very specific:

    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 …

    line 170
  6. Distinctive:

    Do not auto-fix. CSRF strategy is a policy decision.

    line 172

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

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:

  1. Built-in same-origin enforcement (requireSameOrigin, on by default).
  2. SameSite=Lax (or Strict) on session cookies — defense in depth.
  3. Origin-check middleware — only needed when requireSameOrigin is disabled or for non-/api endpoints.
  4. Per-request tokens — only when SameSite=None is 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 is get — only post/put/patch/delete are CSRF-relevant).
  • Capture action.

API mutations

pracht inspect api --json

MCP: 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:

AttributeRequired postureFailure mode
HttpOnlyPresentXSS can steal the session
SecurePresent in productionSniffable on HTTP
SameSiteLax or StrictCross-site sends the cookie
PathSet (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.origin and 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/Strict cookies.
  • info / Adequate — built-in enforcement intact; cookie posture unknown or SameSite unset (the runtime check still blocks browser CSRF).
  • warn / Compensated — requireSameOrigin: false but a verified origin-check middleware or token protocol covers the surface.
  • warn / Token-only — token verified, cookie has SameSite=None.
  • error / Weak — requireSameOrigin: false and no token / no origin-check.
  • warn / Unknown — cookie source not located — investigate.

Produce:

SurfaceFile:LineMethodrequireSameOriginCookie postureMiddleware/Token?SeverityVerdict

Rules

  1. Point users at https://pracht.resynapse.dev/docs/recipes/auth for cookie and middleware patterns, and at ApiConfig.requireSameOrigin for the built-in guard.
  2. Do not flag GET-only forms or read-only API methods.
  3. Remember: a hydrated <Form> intercepts unsafe-method submissions and issues fetch(actionUrl, { method, body: formData }) — not a document POST. Fetch's default credentials mode is same-origin, so a cross-origin action gets no cookies at all; same-origin submissions carry cookies and the browser's Sec-Fetch-Site/Origin headers, which the built-in check validates.
  4. 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.
  5. Do not auto-fix. CSRF strategy is a policy decision; surface the gaps and let the user choose layers.

$ARGUMENTS

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.