Twenty credentials are passed in argv, where ps can read them, across 15 files in two distinct classes #848

Open
opened 2026-08-16 01:17:46 +00:00 by coilyco-ops · 0 comments
Member

Filed by Darren (director seat) at Kai's instruction, 2026-08-15. She asked for the full grep after the admin-token cleanup. Here it is.

The repo already treats this as a real hazard. AGENTS.md carries a scrubbing rule for ps, journalctl and /proc/*/cmdline output, scripts/test-forgejo-package-retention.sh asserts the token never reaches curl argv, and telegram-ci-alert-rollout.py has a test literally named test_git_env_keeps_the_token_out_of_argv. The discipline exists. These are the places it is not applied.

Class one: curl headers in shell argv - 11 sites, 6 files

scripts/grant-coilyco-ops-org-repo-create.sh:33      admin_auth=(-H "Authorization: token ${admin}")
scripts/provision-ci-release-token.sh:68             org_auth=(-H "Authorization: token ${admin}")
scripts/provision-ci-release-token.sh:106            -H "Authorization: token ${token}" ...
scripts/provision-coilyco-ops-bot.sh:339             admin_auth=(-H "Authorization: token ${admin}")
scripts/provision-coilyco-ops-bot.sh:340             admin_sudo_auth=(-H "Authorization: token ${admin}" -H "Sudo: ...")
scripts/provision-coilyco-ops-bot.sh:352             -H "Authorization: Bearer ${GITHUB_TOKEN}"
scripts/provision-coilyco-ops-bot.sh:356             -H "Authorization: Bearer ${GH_TOKEN}"
scripts/provision-forgejo-runner-registration-token.sh:65  -H "Authorization: token ${admin_token}"
scripts/provision-registry-token.sh:114              -H "Authorization: token ${admin}"
scripts/provision-sirens-echo-issue-token.sh:84,91,101    -H "Authorization: token ${token}"
scripts/provision-telegram-ci-secrets.sh:56          -H "Authorization: token ${admin_token}"

These have a known fix already in the tree. provision-tap-bump-token.sh writes the header to a 0600 file and passes -K:

printf 'header = "Authorization: token %s"\n' "${admin}" >"${work}/admin-auth"
chmod 600 "${work}/admin-auth"
curl -sS -K "${work}/admin-auth" ...

Every listed script already has a work dir with a cleanup trap, so this is a mechanical conversion. Three sites were converted in the admin-token PR because they sat in files that PR already edited; these are the rest.

The array forms (admin_auth=(-H ...)) are the same defect wearing a variable. The array expands into argv at call time.

Class two: runner registration tokens in container argv - 9 sites, 9 files

deploy/forgejo-runner.yml:111
deploy/forgejo-runner-build.yml:127
deploy/forgejo-runner-deploy.yml:243
deploy/forgejo-runner-publisher.yml:137
deploy/forgejo-runner-tap-writer.yml:75
deploy/forgejo-runners-ser8/general-bridge/kustomization.yaml:74
deploy/forgejo-runners-ser8/general-flight-deck/kustomization.yaml:74
deploy/forgejo-runners-ser8/general-flight-deck-canary/kustomization.yaml:74
deploy/forgejo-runners-ser8/general-gaming/kustomization.yaml:74

All forgejo-runner register --token "${RUNNER_TOKEN}". This is a different problem with a different fix and a different blast radius, which is why it is a separate class rather than a longer list:

  • the exposure is inside a pod rather than on an operator's host, so the reader set is whoever can exec into the namespace
  • the value is a registration token, which is single-use-ish and lower value than a site-admin PAT
  • the fix is whatever forgejo-runner register supports for file or stdin input, not curl's -K. If it supports neither, this is upstream and the honest outcome is a recorded accepted risk rather than a change

Class three: one flux call

scripts/flux-bootstrap.sh:50 passes --password="$TOKEN". Check whether the flux CLI takes a stdin or file form; if not, same accepted-risk outcome as class two.

Suggested order

  1. Class one, mechanically, using the -K pattern already in the tree. Highest value, lowest risk, and the fix is proven in this repo.
  2. Class three, one call, quick to answer either way.
  3. Class two last, because it needs an upstream capability answer before any code moves.

Acceptance

  • No shell script passes a credential to curl in argv. The grep in this issue returns only class two and three.
  • Class two and three are either converted or carry a recorded decision naming why the exposure is accepted and who reads it.
  • A test or hook keeps class one from regressing. test-forgejo-package-retention.sh already does this for one script and is the model.

Not claimed

Filed from a director seat as an inventory. The conversion is engineer work.


Found while removing scripts/forgejo-admin-token.py. Full grep is reproducible with the patterns above.

**Filed by Darren (director seat) at Kai's instruction, 2026-08-15.** She asked for the full grep after the admin-token cleanup. Here it is. The repo already treats this as a real hazard. `AGENTS.md` carries a scrubbing rule for `ps`, `journalctl` and `/proc/*/cmdline` output, `scripts/test-forgejo-package-retention.sh` asserts the token never reaches curl argv, and `telegram-ci-alert-rollout.py` has a test literally named `test_git_env_keeps_the_token_out_of_argv`. The discipline exists. These are the places it is not applied. ## Class one: curl headers in shell argv - 11 sites, 6 files ``` scripts/grant-coilyco-ops-org-repo-create.sh:33 admin_auth=(-H "Authorization: token ${admin}") scripts/provision-ci-release-token.sh:68 org_auth=(-H "Authorization: token ${admin}") scripts/provision-ci-release-token.sh:106 -H "Authorization: token ${token}" ... scripts/provision-coilyco-ops-bot.sh:339 admin_auth=(-H "Authorization: token ${admin}") scripts/provision-coilyco-ops-bot.sh:340 admin_sudo_auth=(-H "Authorization: token ${admin}" -H "Sudo: ...") scripts/provision-coilyco-ops-bot.sh:352 -H "Authorization: Bearer ${GITHUB_TOKEN}" scripts/provision-coilyco-ops-bot.sh:356 -H "Authorization: Bearer ${GH_TOKEN}" scripts/provision-forgejo-runner-registration-token.sh:65 -H "Authorization: token ${admin_token}" scripts/provision-registry-token.sh:114 -H "Authorization: token ${admin}" scripts/provision-sirens-echo-issue-token.sh:84,91,101 -H "Authorization: token ${token}" scripts/provision-telegram-ci-secrets.sh:56 -H "Authorization: token ${admin_token}" ``` **These have a known fix already in the tree.** `provision-tap-bump-token.sh` writes the header to a 0600 file and passes `-K`: ```sh printf 'header = "Authorization: token %s"\n' "${admin}" >"${work}/admin-auth" chmod 600 "${work}/admin-auth" curl -sS -K "${work}/admin-auth" ... ``` Every listed script already has a `work` dir with a cleanup trap, so this is a mechanical conversion. Three sites were converted in the admin-token PR because they sat in files that PR already edited; these are the rest. The array forms (`admin_auth=(-H ...)`) are the same defect wearing a variable. The array expands into argv at call time. ## Class two: runner registration tokens in container argv - 9 sites, 9 files ``` deploy/forgejo-runner.yml:111 deploy/forgejo-runner-build.yml:127 deploy/forgejo-runner-deploy.yml:243 deploy/forgejo-runner-publisher.yml:137 deploy/forgejo-runner-tap-writer.yml:75 deploy/forgejo-runners-ser8/general-bridge/kustomization.yaml:74 deploy/forgejo-runners-ser8/general-flight-deck/kustomization.yaml:74 deploy/forgejo-runners-ser8/general-flight-deck-canary/kustomization.yaml:74 deploy/forgejo-runners-ser8/general-gaming/kustomization.yaml:74 ``` All `forgejo-runner register --token "${RUNNER_TOKEN}"`. **This is a different problem with a different fix and a different blast radius**, which is why it is a separate class rather than a longer list: * the exposure is inside a pod rather than on an operator's host, so the reader set is whoever can `exec` into the namespace * the value is a registration token, which is single-use-ish and lower value than a site-admin PAT * the fix is whatever `forgejo-runner register` supports for file or stdin input, not curl's `-K`. If it supports neither, this is upstream and the honest outcome is a recorded accepted risk rather than a change ## Class three: one flux call `scripts/flux-bootstrap.sh:50` passes `--password="$TOKEN"`. Check whether the flux CLI takes a stdin or file form; if not, same accepted-risk outcome as class two. ## Suggested order 1. **Class one**, mechanically, using the `-K` pattern already in the tree. Highest value, lowest risk, and the fix is proven in this repo. 2. **Class three**, one call, quick to answer either way. 3. **Class two** last, because it needs an upstream capability answer before any code moves. ## Acceptance * No shell script passes a credential to `curl` in argv. The grep in this issue returns only class two and three. * Class two and three are either converted or carry a recorded decision naming why the exposure is accepted and who reads it. * A test or hook keeps class one from regressing. `test-forgejo-package-retention.sh` already does this for one script and is the model. ## Not claimed Filed from a director seat as an inventory. The conversion is engineer work. --- Found while removing `scripts/forgejo-admin-token.py`. Full grep is reproducible with the patterns above.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
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
coilyco-flight-deck/infrastructure#848
No description provided.