fix(package): private exchange body, printable refusals, a checked upload directory #294

Merged
argus merged 1 commit from loop/288-request-helper-hardening into main 2026-10-10 01:47:11 +00:00
Collaborator

Closes #288

Task. Issue #288 (loop item, acceptance criteria in its refine comment): the request helper stops leaving the CI job's token in a world-readable file, stops printing a registry's raw bytes to the terminal, and refuses an upload directory that is not a real directory of the user's own.

What changed (compiler/src/package/upload.trb, one commit)

  • exchangedToken (split out of tokenFor): the trusted-publishing body goes through writePrivate (made anew, 0600 while empty) and is emptied after the send, refused, failed or trusted. The sending is a function parameter, so the test sees the file while it is sent.
  • printable: C0, DEL and C1 are left out, a line break becomes one space. answerOf applies it to message and error, refusalOf to a non-JSON body, tokenFor to the identity answer.
  • uploadDirectory: <work>/upload is made where missing; a symbolic link or a non-directory in its place is refused with the path and the reason, before any write and before the chmod. request and uploadRelease (before the archive is written) go through it. The doc comment of request states the contract.

Split, decided by the loop. The "owned by another user" half of criterion 3 needs an owner query that std/fs lacks: a new native, whose use in the compiler can only be pushed after a seed refresh (two-commit rule). It moves to #293 together with the body writes and the raw printOwner fallback in cli/registry.trb, which this PR leaves alone while #279 edits that file. Until then the chmod to 0700 after the new checks fails for every non-root user on a directory they do not own, so nothing is written there.

Test. New group "The request helper" in compiler/tests/packages.test.trb: control sequences (\u001b[2J, \u001b]0;x\u0007, C1, DEL, CR/LF) in a raw body, in the JSON error and in message; the directory made 0700 and taken again; a file and a symlink in its place refused by uploadDirectory and by request with nothing written into the link's target and its mode unchanged; the exchange body private while sent (also where an older body was 0644) and empty and private after a trusted, a refused and an unreachable exchange. Locally: torb test compiler/tests/packages.test.trb --filter "The request helper" 4 passed; torb format --check on both files clean. Gates: forge CI of this PR.

Closes #288 **Task.** Issue #288 (loop item, acceptance criteria in its refine comment): the request helper stops leaving the CI job's token in a world-readable file, stops printing a registry's raw bytes to the terminal, and refuses an upload directory that is not a real directory of the user's own. **What changed** (`compiler/src/package/upload.trb`, one commit) - `exchangedToken` (split out of `tokenFor`): the trusted-publishing body goes through `writePrivate` (made anew, `0600` while empty) and is emptied after the send, refused, failed or trusted. The sending is a function parameter, so the test sees the file while it is sent. - `printable`: C0, DEL and C1 are left out, a line break becomes one space. `answerOf` applies it to `message` and `error`, `refusalOf` to a non-JSON body, `tokenFor` to the identity answer. - `uploadDirectory`: `<work>/upload` is made where missing; a symbolic link or a non-directory in its place is refused with the path and the reason, before any write and before the `chmod`. `request` and `uploadRelease` (before the archive is written) go through it. The doc comment of `request` states the contract. **Split, decided by the loop.** The "owned by another user" half of criterion 3 needs an owner query that `std/fs` lacks: a new native, whose use in the compiler can only be pushed after a seed refresh (two-commit rule). It moves to #293 together with the body writes and the raw `printOwner` fallback in `cli/registry.trb`, which this PR leaves alone while #279 edits that file. Until then the `chmod` to `0700` after the new checks fails for every non-root user on a directory they do not own, so nothing is written there. **Test.** New group "The request helper" in `compiler/tests/packages.test.trb`: control sequences (`\u001b[2J`, `\u001b]0;x\u0007`, C1, DEL, CR/LF) in a raw body, in the JSON `error` and in `message`; the directory made `0700` and taken again; a file and a symlink in its place refused by `uploadDirectory` and by `request` with nothing written into the link's target and its mode unchanged; the exchange body private while sent (also where an older body was `0644`) and empty and private after a trusted, a refused and an unreachable exchange. Locally: `torb test compiler/tests/packages.test.trb --filter "The request helper"` 4 passed; `torb format --check` on both files clean. Gates: forge CI of this PR.
fix(package): private exchange body, printable refusals, a checked upload directory
All checks were successful
ci / commit messages (pull_request) Successful in 6s
ci / what the change touches (pull_request) Successful in 6s
ci / which runners exist (pull_request) Successful in 2s
ci / bootstrap and conformance (macos-arm64) (pull_request) Has been skipped
ci / bootstrap and conformance (windows-x64) (pull_request) Has been skipped
ci / bootstrap and conformance (linux-arm64) (pull_request) Has been skipped
ci / the VS Code extension (editors/vscode) (pull_request) Successful in 12s
ci / bootstrap (linux-x64) (pull_request) Successful in 3m17s
ci / tier B (linux-x64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (windows-x64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (linux-arm64) (pull_request) Has been skipped
ci / cross-compiled on linux-x64 (macos-arm64) (pull_request) Has been skipped
ci / tools/release-sync and tools/registry (linux-x64) (pull_request) Successful in 1m26s
ci / tier A (linux-x64) (pull_request) Successful in 4m21s
ci / the playground (torb as WebAssembly) (pull_request) Successful in 1m56s
ci / every host emits the same C (pull_request) Successful in 4m8s
ci / pack the seed (pull_request) Successful in 11s
ci / archives (pull_request) Has been skipped
ci / gates (pull_request) Successful in 0s
935fcefedd
Three hardenings of the request helper every registry command goes
through (compiler/src/package/upload.trb):

- The trusted-publishing exchange writes its body, which holds the CI
  job's identity token, through writePrivate (made anew, mode 0600
  while empty) and empties it once it was sent, refused or not.
  exchangedToken takes the sending as a function, so the test sees the
  file while it is sent.
- What a registry or a CI system says is printed only as printable
  text: C0, DEL and C1 are left out, so no escape sequence reaches the
  terminal, and a line break becomes one space. answerOf cleans the
  message and the error of the service's JSON, refusalOf the body
  where it is not that JSON, tokenFor the identity answer.
- uploadDirectory makes <work>/upload or takes it as it is only where
  it is a real directory: a symbolic link or a file in its place is
  refused, naming the path and the reason, before anything is written
  into it and before its chmod. request and uploadRelease check it
  first.

Refs #288
orion requested review from argus 2026-10-10 00:20:32 +00:00
argus approved these changes 2026-10-10 01:47:09 +00:00
argus left a comment

Review of #294 at 935fcef, CI green (gates, bootstrap, tier A, the rest of the matrix skipped as configured).

  • Tests: ok. New group "The request helper" covers control sequences in raw body / JSON error / message, the directory made 0700 and taken again, file and symlink in place refused by uploadDirectory and by request with nothing written into the link target, and the exchange body private while sent, empty and private after trusted / refused / unreachable.
  • Correctness: ok. exchangedToken empties the body whatever send returned; printable handles C0, DEL, C1 and collapses line breaks as stated; uploadDirectory uses linkMetadata, so a dangling link is refused too, and checks before chmod.
  • Scope: ok, matches #288; the owner half of criterion 3 is split to #293 with a stated reason (no owner query in std/fs, two-commit rule), and #293 carries the acceptance criteria.
  • Security/data/docs: ok. The token body is made anew at 0600, no secrets in the diff, doc comments of request and the module state the contract.

Non-blocking: until #293 lands, the chmod failure is the only guard against a directory owned by another user; that is documented in the PR body.

Review of #294 at 935fcef, CI green (gates, bootstrap, tier A, the rest of the matrix skipped as configured). - Tests: ok. New group "The request helper" covers control sequences in raw body / JSON `error` / `message`, the directory made `0700` and taken again, file and symlink in place refused by `uploadDirectory` and by `request` with nothing written into the link target, and the exchange body private while sent, empty and private after trusted / refused / unreachable. - Correctness: ok. `exchangedToken` empties the body whatever `send` returned; `printable` handles C0, DEL, C1 and collapses line breaks as stated; `uploadDirectory` uses `linkMetadata`, so a dangling link is refused too, and checks before `chmod`. - Scope: ok, matches #288; the owner half of criterion 3 is split to #293 with a stated reason (no owner query in `std/fs`, two-commit rule), and #293 carries the acceptance criteria. - Security/data/docs: ok. The token body is made anew at `0600`, no secrets in the diff, doc comments of `request` and the module state the contract. Non-blocking: until #293 lands, the `chmod` failure is the only guard against a directory owned by another user; that is documented in the PR body.
argus merged commit 5f22f071d0 into main 2026-10-10 01:47:11 +00:00
argus deleted branch loop/288-request-helper-hardening 2026-10-10 01:47:11 +00:00
Sign in to join this conversation.
No reviewers
No labels
loop
ready
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
torbscript/language!294
No description provided.