devops(ci): nightly npm-audit fails with an opaque exit 22 when the Gitea API rejects NIGHTLY_AUDIT_TOKEN #839

Closed
opened 2026-06-14 19:21:30 +02:00 by marcel · 0 comments
Owner

Problem

When the npm-audit job in .gitea/workflows/nightly.yml finds a high/critical advisory, it tries to file/patch a Gitea tracking issue via curl -sf. If the Gitea API rejects the request (HTTP >= 400 — e.g. the NIGHTLY_AUDIT_TOKEN secret is missing, expired, or lacks issue read+write scope), curl --fail exits 22 and set -e aborts the step with a bare:

exitcode '22': failure

No indication that the cause is the token. This already bit us: run #6707 failed with exit 22 because the audit correctly found the tmp high advisory (GHSA-ph9p-34f9-6g65, via patch-package), then could not authenticate to file the tracking issue. The failure was indistinguishable from a logic bug.

Root cause

curl -sf collapses every HTTP error into exit 22 with no message. The five API calls in the audit branch (list issues, patch, create, list labels, add labels) all share this failure mode.

Acceptance criteria

  • REQ-001: When a Gitea API call in the npm-audit step returns HTTP >= 400, the step MUST emit a ::error:: annotation naming the HTTP status and pointing at the NIGHTLY_AUDIT_TOKEN secret (value/scope) as the likely cause, instead of a bare exitcode 22.
  • REQ-002: The step MUST still fail (non-zero exit) on such an error — clarity must not mask the failure.
  • REQ-003: The NIGHTLY_AUDIT_TOKEN value MUST NOT appear in logs or in the error message.
  • REQ-004: On a successful (HTTP < 400) call, behaviour is unchanged — the response body is returned to the caller as before.
  • REQ-005: An in-workflow self-test (mirroring the existing jq self-test) MUST cover both the HTTP-error path (emits ::error::, fails) and the success path (returns body) using a mocked transport.

Out of scope

  • Fixing the tmp advisory itself (separate dependency bump).
  • Rotating/recreating the NIGHTLY_AUDIT_TOKEN PAT (operational, not code).
## Problem When the `npm-audit` job in `.gitea/workflows/nightly.yml` finds a high/critical advisory, it tries to file/patch a Gitea tracking issue via `curl -sf`. If the Gitea API rejects the request (HTTP >= 400 — e.g. the `NIGHTLY_AUDIT_TOKEN` secret is missing, expired, or lacks issue read+write scope), `curl --fail` exits **22** and `set -e` aborts the step with a bare: ``` exitcode '22': failure ``` No indication that the cause is the token. This already bit us: run #6707 failed with exit 22 because the audit correctly found the `tmp` high advisory (`GHSA-ph9p-34f9-6g65`, via `patch-package`), then could not authenticate to file the tracking issue. The failure was indistinguishable from a logic bug. ## Root cause `curl -sf` collapses every HTTP error into exit 22 with no message. The five API calls in the audit branch (list issues, patch, create, list labels, add labels) all share this failure mode. ## Acceptance criteria - REQ-001: When a Gitea API call in the npm-audit step returns HTTP >= 400, the step MUST emit a `::error::` annotation naming the HTTP status and pointing at the `NIGHTLY_AUDIT_TOKEN` secret (value/scope) as the likely cause, instead of a bare `exitcode 22`. - REQ-002: The step MUST still fail (non-zero exit) on such an error — clarity must not mask the failure. - REQ-003: The `NIGHTLY_AUDIT_TOKEN` value MUST NOT appear in logs or in the error message. - REQ-004: On a successful (HTTP < 400) call, behaviour is unchanged — the response body is returned to the caller as before. - REQ-005: An in-workflow self-test (mirroring the existing jq self-test) MUST cover both the HTTP-error path (emits `::error::`, fails) and the success path (returns body) using a mocked transport. ## Out of scope - Fixing the `tmp` advisory itself (separate dependency bump). - Rotating/recreating the `NIGHTLY_AUDIT_TOKEN` PAT (operational, not code).
marcel added the P2-mediumdevops labels 2026-06-14 19:21:30 +02:00
Sign in to join this conversation.
No Label P2-medium devops
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#839