fix(ci)(security): pipe RFC_324_TEAM_READ_TOKEN to curl via -K/stdin instead of -H argv (token-in-process-table) #541

Closed
opened 2026-05-11 18:48:10 +00:00 by core-devops · 0 comments
Member

Context

Follow-up to PR #535 (RFC#324 Step 1 of 5 — qa-review + security-review workflow add). hongming-pc review #1421 nit 4.

In .gitea/workflows/qa-review.yml, .gitea/workflows/security-review.yml, and .gitea/scripts/review-check.sh, the Gitea API call shape is:

curl -sS -o "$PR_JSON" -w '%{http_code}' \
  -H "Authorization: token ${GITEA_TOKEN}" \
  "${API}/repos/${OWNER}/${NAME}/pulls/${PR_NUMBER}"

The expanded Authorization: token <secret> ends up in curl's argv — visible to any other process on the runner via /proc/<pid>/cmdline or ps -ef.

Why it matters here specifically

For most workflow tokens this is low-impact: GITHUB_TOKEN is ephemeral and scoped to a single workflow run. But RFC_324_TEAM_READ_TOKEN is a long-lived org-secret (provisioning tracked in internal#325) — an org-Owners-tier identity that's a member of both qa and security teams. Leak window from process-table is the entire token lifetime, not a single workflow run.

A co-tenant step (or — under pull_request_target — a future malicious step we forgot to guard) running ps -e -o args on the same runner during the curl invocation reads the secret. The runner is single-tenant per-job today, but defence-in-depth says don't put long-lived secrets in argv.

Fix options

Option 1 (preferred) — curl -K <(...) config-file via process-substitution.

curl_authed() {
  local url="$1" outfile="$2"
  curl -sS -o "$outfile" -w '%{http_code}' \
    -K <(printf 'header = "Authorization: token %s"\n' "$GITEA_TOKEN") \
    "$url"
}

The token lives in a transient FIFO file descriptor — never on the curl command line. bash only.

Option 2 — safe_curl helper per internal#178. If that helper has shipped, reuse it. Same effect, more shared maintenance.

Option 3 — --config - (stdin). printf '...' | curl -K - .... Portable to non-bash shells (sh) but trickier with the rest of the -sS -o ... -w ... flags; need to assemble the full config on stdin.

Recommendation: Option 1 unless safe_curl (option 2) is already available — it covers all six curl call sites in review-check.sh with one helper.

Acceptance

  • Introduce curl_authed() helper at the top of review-check.sh (or a sourced .gitea/scripts/lib/safe_curl.sh).
  • Replace all -H "Authorization: token ${GITEA_TOKEN}" invocations with curl_authed.
  • Same fix in the Privilege check step of qa-review.yml and security-review.yml (note: per RFC#324 v1.3, that step is now informational-only, but still shouldn't leak the token).
  • Bats test (per #540) verifies the helper still passes a syntactically valid Authorization header (mock-curl asserts on a config-file argument).

Tier

tier:medium — defence-in-depth on a long-lived org-secret. Not blocking the workflow's correctness, but should land before RFC_324_TEAM_READ_TOKEN is provisioned (internal#325) and the workflows start carrying live load.

Owner

core-security (token-handling owner) or core-devops (workflow author). Either persona.

Cross-refs

  • PR #535 (RFC#324 Step 1 workflow-add)
  • hongming-pc review #1421 nit 4
  • internal#325RFC_324_TEAM_READ_TOKEN provisioning
  • internal#178safe_curl helper origin (if already shipped, link the path)
## Context Follow-up to PR #535 (RFC#324 Step 1 of 5 — `qa-review` + `security-review` workflow add). hongming-pc review #1421 nit 4. In `.gitea/workflows/qa-review.yml`, `.gitea/workflows/security-review.yml`, and `.gitea/scripts/review-check.sh`, the Gitea API call shape is: ```bash curl -sS -o "$PR_JSON" -w '%{http_code}' \ -H "Authorization: token ${GITEA_TOKEN}" \ "${API}/repos/${OWNER}/${NAME}/pulls/${PR_NUMBER}" ``` The expanded `Authorization: token <secret>` ends up in `curl`'s **argv** — visible to any other process on the runner via `/proc/<pid>/cmdline` or `ps -ef`. ## Why it matters here specifically For most workflow tokens this is low-impact: `GITHUB_TOKEN` is ephemeral and scoped to a single workflow run. **But `RFC_324_TEAM_READ_TOKEN` is a long-lived org-secret** (provisioning tracked in `internal#325`) — an org-Owners-tier identity that's a member of both `qa` and `security` teams. Leak window from process-table is the entire token lifetime, not a single workflow run. A co-tenant step (or — under `pull_request_target` — a future malicious step we forgot to guard) running `ps -e -o args` on the same runner during the curl invocation reads the secret. The runner is single-tenant per-job today, but defence-in-depth says don't put long-lived secrets in argv. ## Fix options **Option 1 (preferred) — `curl -K <(...)` config-file via process-substitution.** ```bash curl_authed() { local url="$1" outfile="$2" curl -sS -o "$outfile" -w '%{http_code}' \ -K <(printf 'header = "Authorization: token %s"\n' "$GITEA_TOKEN") \ "$url" } ``` The token lives in a transient FIFO file descriptor — never on the curl command line. `bash` only. **Option 2 — `safe_curl` helper per `internal#178`.** If that helper has shipped, reuse it. Same effect, more shared maintenance. **Option 3 — `--config -` (stdin).** `printf '...' | curl -K - ...`. Portable to non-bash shells (`sh`) but trickier with the rest of the `-sS -o ... -w ...` flags; need to assemble the full config on stdin. Recommendation: **Option 1** unless `safe_curl` (option 2) is already available — it covers all six curl call sites in `review-check.sh` with one helper. ## Acceptance - [ ] Introduce `curl_authed()` helper at the top of `review-check.sh` (or a sourced `.gitea/scripts/lib/safe_curl.sh`). - [ ] Replace all `-H "Authorization: token ${GITEA_TOKEN}"` invocations with `curl_authed`. - [ ] Same fix in the `Privilege check` step of `qa-review.yml` and `security-review.yml` (note: per RFC#324 v1.3, that step is now informational-only, but still shouldn't leak the token). - [ ] Bats test (per #540) verifies the helper still passes a syntactically valid Authorization header (mock-curl asserts on a config-file argument). ## Tier `tier:medium` — defence-in-depth on a long-lived org-secret. Not blocking the workflow's correctness, but should land before `RFC_324_TEAM_READ_TOKEN` is provisioned (`internal#325`) and the workflows start carrying live load. ## Owner `core-security` (token-handling owner) or `core-devops` (workflow author). Either persona. ## Cross-refs - PR #535 (RFC#324 Step 1 workflow-add) - hongming-pc review #1421 nit 4 - `internal#325` — `RFC_324_TEAM_READ_TOKEN` provisioning - `internal#178` — `safe_curl` helper origin (if already shipped, link the path)
core-devops added the securitytier:medium labels 2026-05-11 18:48:10 +00:00
core-devops self-assigned this 2026-05-11 19:10:10 +00:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: molecule-ai/molecule-core#541