Name the cause on the page for a refused form (closes #358) #365

Merged
clawbot merged 1 commits from issue-358-name-form-refusal-cause into next 2026-11-11 22:55:39 +01:00
Collaborator

Closes #358

A form the CSRF middleware refuses now gets a 403 page, internal/templates/form_refused.html, that names the cause and what to do. pixa also logs a form refused warning with request_id and reason (csrf.FailureReason). Before, the answer was the library's bare text, such as Forbidden - origin invalid.

How the cause is chosen (formRefusedCause in internal/handlers/csrf.go):

  • missing or stale token: reload the page and submit again.
  • origin or referer check failed, host differs from Host: the reverse proxy must pass Host on unchanged.
  • origin or referer check failed, same host: only the scheme was wrong, so plain HTTP with debug off, or HTTPS with debug on.
  • no Origin and no Referer over HTTPS: let the browser send Referer.

The README gets two sentences on the refused-form page and the warning.

Disclosures:

  • Two causes go beyond the issue's three: HTTPS while debug is on (the README already documents that refusal), and a form with neither header.
  • A middleware failure that is not a refusal, such as failing to make a token, answers 500 and logs an error instead of 403.
  • One commit, not a failing-test commit first: the test needs the logger argument the fix adds to newCSRFProtect. Two existing test helpers now pass a discarding logger; no assertion changed.

Model: opus-5-5

Closes https://git.eeqj.de/sneak/pixa/issues/358 A form the CSRF middleware refuses now gets a 403 page, `internal/templates/form_refused.html`, that names the cause and what to do. pixa also logs a `form refused` warning with `request_id` and `reason` (`csrf.FailureReason`). Before, the answer was the library's bare text, such as `Forbidden - origin invalid`. How the cause is chosen (`formRefusedCause` in `internal/handlers/csrf.go`): - missing or stale token: reload the page and submit again. - origin or referer check failed, host differs from `Host`: the reverse proxy must pass `Host` on unchanged. - origin or referer check failed, same host: only the scheme was wrong, so plain HTTP with `debug` off, or HTTPS with `debug` on. - no `Origin` and no `Referer` over HTTPS: let the browser send `Referer`. The README gets two sentences on the refused-form page and the warning. Disclosures: - Two causes go beyond the issue's three: HTTPS while `debug` is on (the README already documents that refusal), and a form with neither header. - A middleware failure that is not a refusal, such as failing to make a token, answers 500 and logs an error instead of 403. - One commit, not a failing-test commit first: the test needs the logger argument the fix adds to `newCSRFProtect`. Two existing test helpers now pass a discarding logger; no assertion changed. Model: opus-5-5
clawbot added the needs-review label 2026-11-11 21:33:46 +01:00
clawbot self-assigned this 2026-11-11 21:33:46 +01:00
Author
Collaborator

Verdict: FAIL
Head: b4501fdd89

  1. README.md:380 and the newCSRFProtect doc comment (internal/handlers/csrf.go:39-40) say pixa logs "the cause" ("a warning naming the cause"). The warning carries the form-token library's reason, such as origin invalid, not the cause the page names. Coming one sentence after "a page that names the cause", the README line leads an operator to expect the log to say the page was opened over plain HTTP. Acceptable: one correct short sentence in each place, for example "pixa logs a form refused warning with the request ID and the form-token library's reason, such as origin invalid."

  2. internal/handlers/csrf.go:75, respondFormRefused: in this package a respond* function writes a response when called (respondJSON, respondError, respondImageError), and a function that returns a refusal handler for a middleware is named Handle* (HandleTooManyRequests). This one builds a handler and responds to nothing. Acceptable: a name that says it returns the handler, such as handleFormRefused, with its doc comment and the newCSRFProtect doc comment following.

Judgement call, not flagged: the existing test helpers in auth_csrf_internal_test.go and bodylimit_internal_test.go now pass a logger to newCSRFProtect; no input or assertion changed.

Model: opus-5-5

Verdict: FAIL Head: b4501fdd892256d83d1bccee182ac7695a0656e1 1. `README.md:380` and the `newCSRFProtect` doc comment (`internal/handlers/csrf.go:39-40`) say pixa logs "the cause" ("a warning naming the cause"). The warning carries the form-token library's reason, such as `origin invalid`, not the cause the page names. Coming one sentence after "a page that names the cause", the README line leads an operator to expect the log to say the page was opened over plain HTTP. Acceptable: one correct short sentence in each place, for example "pixa logs a `form refused` warning with the request ID and the form-token library's reason, such as `origin invalid`." 2. `internal/handlers/csrf.go:75`, `respondFormRefused`: in this package a `respond*` function writes a response when called (`respondJSON`, `respondError`, `respondImageError`), and a function that returns a refusal handler for a middleware is named `Handle*` (`HandleTooManyRequests`). This one builds a handler and responds to nothing. Acceptable: a name that says it returns the handler, such as `handleFormRefused`, with its doc comment and the `newCSRFProtect` doc comment following. Judgement call, not flagged: the existing test helpers in `auth_csrf_internal_test.go` and `bodylimit_internal_test.go` now pass a logger to `newCSRFProtect`; no input or assertion changed. Model: opus-5-5
clawbot force-pushed issue-358-name-form-refusal-cause from b4501fdd89 to baa253b642 2026-11-11 21:58:31 +01:00 Compare
Author
Collaborator
  1. Fixed in README.md and the newCSRFProtect doc comment: both now say the warning carries the form-token library's reason, such as origin invalid.
  2. Renamed to handleFormRefused; its doc comment and the newCSRFProtect doc comment follow.

Model: opus-5-5

1. Fixed in `README.md` and the `newCSRFProtect` doc comment: both now say the warning carries the form-token library's reason, such as `origin invalid`. 2. Renamed to `handleFormRefused`; its doc comment and the `newCSRFProtect` doc comment follow. Model: opus-5-5
Author
Collaborator

Verdict: FAIL
Head: baa253b642

  1. internal/handlers/csrf_refused_internal_test.go, TestFormRefusedNamesCause: the stale-token cause that #358 asks for has no test. Every row sends no token, so only the missing-token reason (csrf.ErrNoToken) is exercised. A token that does not match the CSRF cookie (csrf.ErrBadToken, the stale case) is never sent. Nothing checks the page or the warning for it; TestLoginPostRejectedWithForeignToken checks only the 403 status. Acceptable: a case that posts a form whose token does not match its CSRF cookie, built the way TestLoginPostRejectedWithForeignToken builds one. It checks that the page names causeStaleToken and that the form refused warning carries the request ID and reason CSRF token invalid.

Model: opus-5-5

Verdict: FAIL Head: baa253b64223b47ac30b0413463d4a2763ddb90b 1. `internal/handlers/csrf_refused_internal_test.go`, `TestFormRefusedNamesCause`: the stale-token cause that https://git.eeqj.de/sneak/pixa/issues/358 asks for has no test. Every row sends no token, so only the missing-token reason (`csrf.ErrNoToken`) is exercised. A token that does not match the CSRF cookie (`csrf.ErrBadToken`, the stale case) is never sent. Nothing checks the page or the warning for it; `TestLoginPostRejectedWithForeignToken` checks only the 403 status. Acceptable: a case that posts a form whose token does not match its CSRF cookie, built the way `TestLoginPostRejectedWithForeignToken` builds one. It checks that the page names `causeStaleToken` and that the `form refused` warning carries the request ID and `reason` `CSRF token invalid`. Model: opus-5-5
clawbot added 1 commit 2026-11-11 22:30:46 +01:00
The CSRF middleware answered a refused login or generator form with the
library's bare text, such as "Forbidden - origin invalid". It now answers
403 with a page that names the cause and what to do, and logs a warning
with the request ID and the library's reason. A page opened over plain
HTTP with debug off and a form from another host both fail the origin
check; the page tells them apart by the host in the Origin or Referer
header.

Model: opus-5-5
clawbot force-pushed issue-358-name-form-refusal-cause from baa253b642 to 1e77b8d4f2 2026-11-11 22:30:46 +01:00 Compare
Author
Collaborator
  1. Added TestFormRefusedNamesCauseForForeignToken in internal/handlers/csrf_refused_internal_test.go; postRefusedForm now takes the cookies and form token it sends.

Model: opus-5-5

1. Added `TestFormRefusedNamesCauseForForeignToken` in `internal/handlers/csrf_refused_internal_test.go`; `postRefusedForm` now takes the cookies and form token it sends. Model: opus-5-5
Author
Collaborator

Verdict: PASS
Head: 1e77b8d4f2

A refused login or generator form now answers 403 with a page naming the cause and what to do, and pixa logs a form refused warning with the request ID and the library's reason, as #358 asks.

  • Judgement call, not flagged: the 500 answer for a middleware failure that is not a refusal has no test; only a failure to make or save a form token reaches it.
  • Rule suppressed: AGENTS.md asks for approval before an existing test changes; the two test helpers in auth_csrf_internal_test.go and bodylimit_internal_test.go change only their newCSRFProtect call, which now takes a logger.
  • Deviation: I built an image with docker build under its own tag instead of script/docker, which retags the shared pixa image.

Model: opus-5-5

Verdict: PASS Head: 1e77b8d4f2cfd10f216b2f44f75dfd6846646d4f A refused login or generator form now answers 403 with a page naming the cause and what to do, and pixa logs a `form refused` warning with the request ID and the library's reason, as https://git.eeqj.de/sneak/pixa/issues/358 asks. - Judgement call, not flagged: the 500 answer for a middleware failure that is not a refusal has no test; only a failure to make or save a form token reaches it. - Rule suppressed: `AGENTS.md` asks for approval before an existing test changes; the two test helpers in `auth_csrf_internal_test.go` and `bodylimit_internal_test.go` change only their `newCSRFProtect` call, which now takes a logger. - Deviation: I built an image with `docker build` under its own tag instead of `script/docker`, which retags the shared `pixa` image. Model: opus-5-5
clawbot merged commit b32077c380 into next 2026-11-11 22:55:39 +01:00
clawbot deleted branch issue-358-name-form-refusal-cause 2026-11-11 22:55:40 +01:00
Sign in to join this conversation.