Public: anyone can read this; the crew can change it.

Holding a file from any page goes through one form handler that takes where to return, and refuses an unknown or unreadable repository in the same words #7071

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`fileHoldUpdate` now delegates to one exported function, `fileHoldForm(ctx, form, repo, to)`, in `src/web/pages/file_holds.ts`. A route that offers a hold passes in the form, the repository and where to return, and gets the same flash wording as every other route. The settings route still checks with `adminForm` first and passes its own `to`, `/:owner/:name/settings#file-holds`, so what it returns…

shipped production is serving it deploy fc82d1a

opened this

fileHoldUpdate now delegates to one exported function, fileHoldForm(ctx, form, repo, to), in src/web/pages/file_holds.ts. A route that offers a hold passes in the form, the repository and where to return, and gets the same flash wording as every other route. The settings route still checks with adminForm first and passes its own to, /:owner/:name/settings#file-holds, so what it returns is unchanged.

fileHoldForm accepts repo: Repo | undefined:

  • Missing repository: handled as the same not_found that actions/file_holds.ts gives a caller who cannot read a private one.
  • Hold: both cases answer 303 to to with "That file could not be held.", byte for byte the same.
  • Release: both cases answer "That path could not be released.". I kept release's existing wording for its refusal rather than reusing the hold sentence.

test/file-hold-form.test.ts checks three things:

  • Settings route works: posting hold and release returns to the settings card and changes the stored holds.
  • Settings route refuses alike: for a missing repository and an unreadable private one, it gives the same 404 to a stranger. The comparison ignores only the per-request CSP nonce and the requested path echoed in the page's alternate link; neither says whether the repository exists.
  • Shared function refuses alike: for a signed-in non-reader and for an anonymous caller, fileHoldForm gives identical status, location, flash cookie and body for a missing and an unreadable repository, for both hold and release.

Choices:

  • /boards is not wired to fileHoldForm here. Pull #7004 and the rest of #6846 do that.
  • There was one, unwanted, change to a test in this pull. A first draft compared the settings-route 404s as raw bytes, but the per-request nonce makes two pages never byte-equal, so the test now ignores the nonce and the echoed path.

To try it: bun test test/file-hold-form.test.ts test/crew-file-holds.test.ts passes, and so do bun x tsc --noEmit and bun scripts/design-lint.ts.

🤖 Generated with Claude Code

Closes #7055

commented

fileHoldUpdate now delegates to one exported function, fileHoldForm(ctx, form, repo, to), in src/web/pages/file_holds.ts. A route that offers a hold passes in the form, the repository and where to return, and gets the same flash wording as every other route. The settings route still checks with adminForm first and passes its own to, /:owner/:name/settings#file-holds, so what it returns is unchanged.

fileHoldForm accepts repo: Repo | undefined:

  • Missing repository: handled as the same not_found that actions/file_holds.ts gives a caller who cannot read a private one.
  • Hold: both cases answer 303 to to with "That file could not be held.", byte for byte the same.
  • Release: both cases answer "That path could not be released.". I kept release's existing wording for its refusal rather than reusing the hold sentence.

test/file-hold-form.test.ts checks three things:

  • Settings route works: posting hold and release returns to the settings card and changes the stored holds.
  • Settings route refuses alike: for a missing repository and an unreadable private one, it gives the same 404 to a stranger. The comparison ignores only the per-request CSP nonce and the requested path echoed in the page's alternate link; neither says whether the repository exists.
  • Shared function refuses alike: for a signed-in non-reader and for an anonymous caller, fileHoldForm gives identical status, location, flash cookie and body for a missing and an unreadable repository, for both hold and release.

Choices:

  • /boards is not wired to fileHoldForm here. Pull #7004 and the rest of #6846 do that.
  • There was one, unwanted, change to a test in this pull. A first draft compared the settings-route 404s as raw bytes, but the per-request nonce makes two pages never byte-equal, so the test now ignores the nonce and the echoed path.

To try it: bun test test/file-hold-form.test.ts test/crew-file-holds.test.ts passes, and so do bun x tsc --noEmit and bun scripts/design-lint.ts.

🤖 Generated with Claude Code

Approved

Clean. fileHoldForm adds no authority of its own: holdFileAs/releaseFileAs still run requireAdmin (read, then signed in, then admin), and a missing repository goes through the same err("not_found"), so the flash shows the same words whether the repository is missing or unreadable. The settings route still goes through adminForm first (same-origin, admin scope), its to is built on the server, and the flash text is escaped where the layout renders it. The new test exercises the missing-repository branch for a non-reader and for an anonymous caller, for both hold and release. It would fail without the change, because the function did not exist. On the preview, / answers 200, and an anonymous same-origin POST to /trevato/forge/settings/holds and to /nobody/nowhere/settings/holds both answer 404. No markup changes, so there is nothing for the design system to check; no tty surface changes. This is a small, single-purpose step towards #6846. One non-blocking note is inline.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs