fix: keep Debian publish token out of curl argv #67
No reviewers
Labels
No labels
attention
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-ruling
needs-triage
offsite
post-merge
ready
release
scope:ci
scope:cli
scope:docs
scope:manifests
scope:packaging
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
4 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/stoke#67
Loading…
Reference in a new issue
No description provided.
Delete branch "build/62-publish-deb-token"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #62
Worklog
Acceptance criteria
curlonPATHthat appends its fullargvto a log, a run withSTOKE_TOKEN=<sentinel>produces a log in which the sentinel appears zero times.-H @<path>, where<path>is inside the script temp directory, contains exactlyAuthorization: token <sentinel>, and has mode0600.201path, on the failure path, and when the stub exits non-zero — the temp directory no longer exists, and no/tmp/stoke-publish-response.*file is left behind.409and201messages and the failure path stderr dump of the response body are byte-identical to today, proven by a test per status.STOKE_TOKENstill gets past the guard, and an empty one still exits 1 with #57 message — the guard was not disturbed.git difftouches exactlyscripts/publish-deb.sh,test/publish-deb.test.jsand onechangelog.d/*.md.npm testpasses andci / testis green on the PR head.TDD evidence
Before implementation,
node --test test/publish-deb.test.jsfailed 2/6 for the intended reasons: the sentinel appeared in curl argv, and curl exit 7 left/tmp/stoke-publish-response.<pid>behind. The existing empty-token, 201, 409, and HTTP-error behaviors remained green. After the implementation, the focused suite passes 6/6. Fresh final verification at4d17b8bpasses focused 6/6, full suite 147/147, governance, shell syntax, diff check, and remote-head equality. The current-head CI checkbox remains unchecked until the engine-observed check is green.Round log
Round at
4d17b8b1Round passed with no written reply.
fix: keep Debian publish token out of curl argvto WIP: fix: keep Debian publish token out of curl argvBuild complete at head
4d17b8b113.publish-deb.shnow creates one trap-owned temporary directory after token validation, writes the authorization header there at mode0600, passes curl-H @<path>, and stores the response body beside it./tmp/stoke-publish-response.*path is gone.RUNNER_TEMPdirectory, and every exit cleans up.changelog.d/62.md.TDD evidence: before implementation, the focused suite failed 2/6 because the sentinel was present in curl argv and curl exit 7 leaked
/tmp/stoke-publish-response.<pid>. After implementation, focused tests pass 6/6.Fresh final verification:
npm testpasses 147/147;npm run check:governancepasses with 4 identities and 5 scope rows;bash -n scripts/publish-deb.shandgit diff --check origin/main...HEADpass; the clean pushed diff contains exactlyscripts/publish-deb.sh,test/publish-deb.test.js, andchangelog.d/62.md. The draft head currently has no checks reported, which is the ruled no-check green case; marking ready may start them, and the engine owns waiting before requesting the panel.📣 round answered at head
4d17b8b113WIP: fix: keep Debian publish token out of curl argvto fix: keep Debian publish token out of curl argv🔎 reviewing head
4d17b8b113Acceptance criteria against #62, all settled:
assertat test/publish-deb.test.js:113-H @<path>inside the script temp dir, exact contents, mode0600— :114-:117/tmp/stoke-publish-response.*on 201, HTTP-failure, and stub-exit-7 —assertCleaned, :87-:92scripts/publish-deb.sh,test/publish-deb.test.js,changelog.d/62.mdnpm test147/147 here afternpm ci;ci / testandlabels / labelsboth success on this headVerification run: new tests against base
2230ca25fail 2/6 (argv, curl-failure leak); dropping theEXIT trap fails 5/6;
umask 022+chmod 0644fails the mode assertion; restoring the inline-Hfails the argv assertion.
shellcheckandbash -nclean,check:governancepasses.curl -H @fileconfirmed live over a loopback server (curl 8.14.1): the header is sent as written.Verdict submitted.
🔎 reviewing head
4d17b8b1134d17b8b113(verified via submit-verdict.sh exit 0)Approve — head
4d17b8b113, all 7 acceptance criteria of #62 verified.Acceptance criteria, each checked:
curlon PATH records its full argv; run with STOKE_TOKEN=deb-token-that-must-not-enter-argv; sentinel asserted absent from every recorded argument (test 2). Sentinel is named for meaning, mirroring publish-release.test.js.-H @<path>; asserted path is directly inside the RUNNER_TEMP-rooted temp dir, header content exactlyAuthorization: token <sentinel>\n, mode 0600 (umask 077 + explicit chmod)./tmp/stoke-publish-response.*legacy file (assertCleaned on all six tests).git diff origin/main...HEADtouches exactlyscripts/publish-deb.sh,test/publish-deb.test.js,changelog.d/62.md.npm test147/147 pass locally at this head;ci / test (pull_request)= success on this exact SHA (read from the commit statuses endpoint).What I ran: focused suite 6/6; full suite 147/147 (after linking node_modules into the throwaway worktree — the first run's 66 failures were all
Cannot find module 'commander', i.e. missing untracked deps, not code);npm run check:governance(4 identities, 5 scope rows);bash -n. Mutation check: the new test file against the pre-PR script fails exactly 2/6 — the sentinel in argv and the curl-failure temp leak — so the failure cases actually fail.Constraints: mirrors publish-release.sh's sanctioned pattern exactly (RUNNER_TEMP-conditional mktemp -d, EXIT trap installed before first write, umask 077 + chmod 0600); CommonJS, node:test, bash shebang conventions all held; changelog fragment present.
Could not verify: a real upload against a live registry (no network publish from this box) — relied on the issue's live
-H @fileverification (401/200 on this forge; form exists since curl 7.88 on the runner) and on the stub-based runtime assertions; signal-path cleanup beyond bash's EXIT trap (SIGKILL is inherently uncleansable) — relied on the trap semantics and the identical exemplar. The response file also becomes 0600 via umask — strictly safer, noted as intended.Non-blocking nit (builder's discretion): none material; the
curl failuretest's empty-stderr assertion reflects the stub (real curl -S would add its own error line), which is fine as the script's own transcript is what is pinned.🔎 reviewing head
4d17b8b113Approve at
4d17b8b1131e921dea43d6f04c3d45bb028d39e1.The change is exactly #62's spec: one trap-owned temp directory created after the L33 guard, the
authorization header written there with
printf(a bash builtin — no argv exposure of its own) atmode
0600,curl -H @"$HEADER_FILE", the response body moved into the same directory, and bothhand-rolled
rm -f /tmp/stoke-publish-response.$$calls gone. It mirrorspublish-release.shL19-31 line for line, which is what #54 decided and what the issue asked for. Nothing in the frozen
surface moved: the L29-31 token resolution, the #57 message,
--upload-file, the 201/409/otherstatus handling, the stderr dump and
exit 1are byte-identical.Acceptance criteria — all seven met
test/publish-deb.test.js:113, plus:119-:120for stdout/stderr-H @<path>in the script temp dir, exact contents, mode0600:114proves the dir is a child ofRUNNER_TEMP;:116the exactAuthorization: token <sentinel>\n;:117the mode/tmp/stoke-publish-response.*, on 201 / failure / stub-exit-non-zeroassertCleaned:87-:92, called from all six tests;remainingTempEntriesreadsRUNNER_TEMPdirectly:128,:137,:147— full-string equality, notmatch:94-:106empty token → exit 1 with #57's message and ordering; every other test passes a non-empty onegit diff --name-only 2230ca25...HEAD→changelog.d/62.md,scripts/publish-deb.sh,test/publish-deb.test.jsnpm testpasses,ci / testgreen on the headnpm ci;ci / testandlabels / labelsbothsuccesson4d17b8bvia the commit status APIWhat I ran
2230ca25: 2/6 fail — test 2 (thesentinel is in argv) and test 6 (curl exit 7 leaves
/tmp/stoke-publish-response.<pid>). Thismatches the TDD evidence in the PR body, independently reproduced.
construction:
trap ... EXITline → 5/6 fail,remainingTempEntriesreporting the orphanedstoke-publish.XXXXXXdirectory by name;umask 022+chmod 0644→ test 2 fails onheaderMode;-H "Authorization: token $TOKEN"while keeping the temp dir → test 2 failson the argv assertion.
Worktree restored clean after each.
curl -H @filesemantics, live. Against a loopback HTTP server,curl -sS -o … -w '%{http_code}' -X PUT -H @<file> --upload-file …sentauthorization: token SENTINEL-abc123and returned201.The form works alongside
-X PUT/--upload-file, which the stub cannot show.bash -nandshellcheckclean onscripts/publish-deb.sh;npm run check:governancepasses(4 identities, 5 scope rows).
changelog.d/62.mdmatches the wording and(#N).shape of itsneighbours
57.mdand60.md.What I could not verify
curlis stubbed in the tests by design (#62's test plan says no network), andI did not publish a
.debto the registry. Criterion 2's "the registry accepted it" half rests onthe live loopback check above plus the issue's own 401/200 measurement, not on a real publish.
debjob runs innode:22-bookworm(curl 7.88);-H @filehas existed since curl 7.55, so it is present there, butI did not run that image. CI's green
ci / testdoes not cover it either — nothing in the testsuite invokes the real curl.
also covers SIGINT/SIGTERM is untested here; it is the same exposure
publish-release.shalreadycarries, so this PR does not widen it.
Non-blocking notes
node's argv, at the lines #62 deliberately froze. L29always runs
node -e '…loadConfig()…'and L30-31 pass the resultingCONFIG_JSON— which containstoken— as an argument tonode -pe. I confirmed it with a PATH-stubbednodethat logs argv:the sentinel appears twice. This is not a regression and not this PR's job — the issue says
L29-31 "stay exactly as they are", and I am not re-litigating that. It is CI-harmless (no stoke
config on the runner, so
CONFIG_JSONis empty there) and local-only. Worth a proposal, since itis the same defect class in the same file.
$TOKENwould now inject extra headers.-H @filereads one header perline — verified: a two-line file produced both
authorizationandx-injected. Real Forgejotokens cannot contain one, and the old inline form was merely malformed rather than safe, so this
is a curiosity, not a defect.
npm testpasses andci / testis green") is stillunchecked; both are now green on this head. Tick it at your discretion.
Approving. Nits 1-3 are yours to take or leave.
Approved at head
4d17b8b1. Reviewed the whole PR against #62's acceptance criteria, the repo's constraints, and the code, with runtime verification on a detached worktree (node v22.23.2).Acceptance criteria — all seven verified:
deb-token-that-must-not-enter-argv) appears zero times. I reproduced this independently with my own PATH-stubbed bashcurlagainst the real script: the sentinel occurs 0 times in the recorded argv; curl is invoked as-H @/…/stoke-publish.XXXXXX/authorization-header.-H @<path>target sits inside the script's temp dir (itself underRUNNER_TEMP), contains exactlyAuthorization: token <sentinel>\n, andstats at mode0600at runtime. The script earns this withumask 077plus an explicitchmod 0600, mirroringpublish-release.sh's--token-filediscipline that #54 specified./tmp/stoke-publish-response.*left behind, and the two hand-rolledrm -flines are gone. My probe confirmedRUNNER_TEMPis empty after all four runs; curl's exit status (7) propagates unchanged.main's messages line for line.git diff main...HEAD --statshows onlyscripts/publish-deb.sh,test/publish-deb.test.js,changelog.d/62.md.npm testand CI green. Local full suite 147/147. Failure cases actually fail: running the new test file againstmain's script fails exactly 2/6 (the argv-leak and curl-failure-cleanup tests) for the intended reasons, matching the TDD evidence.ci / test (pull_request)andlabels / labelsboth report success on this head.Constraints: changelog fragment present and formatted like its
57.md/60.mdsiblings;scripts/check-governance.jsgreen;bash -nclean; no workflow or third-party-action surface touched; CommonJS/node:testconventions kept.Could not verify: the real upload against the live registry (needs the release secret; out of scope per the issue's own test plan — the stub never reaches the network). The
0600mode is proven by the test's in-runstat, not by my probe, since the trap removes the file before exit — that is the required behavior, not a gap.Converges with the panel; ready for the human.