Skip to content

npm audit makes frontend fail whenever registry.npmjs.org does #367

Description

@vladimirrott

Line references corrected 2026-09-07 at adab560. The three line numbers
in this issue were wrong when it was filed, and the grep -n output pasted
under "Getting started" reproduces on no commit this repository has. My error,
not a change under you. The step is at 197-198, the surrounding numbers are
in the corrected block below, and the substance of the issue is unchanged:
frontend is still required, the step is still enforced, and it still gates
every pull request on registry.npmjs.org.

frontend is a required status check on main, and its fourth step is:

- name: Audit production dependencies
  run: npm audit --omit=dev --audit-level=high

That command POSTs to https://registry.npmjs.org/-/npm/v1/security/advisories/bulk.
When the endpoint is unavailable, npm exits 1, and the check fails exactly the way
it fails for a genuine high-severity advisory.

This morning that took every open pull request down for 45 minutes:

run PR started (UTC) error
33859810636 #365 09:44 npm warn audit 503 Service Unavailable - POST .../advisories/bulk
33862001249 #366 10:11 npm warn audit network timeout at: .../advisories/bulk
33863121379 #366 10:25 503 Service Unavailable

Each attempt ran for five minutes before npm gave up (10:25:37 → 10:30:37 on the
last one), so a bad morning at the registry costs runner minutes on top of the
blocked merges. Re-running the failed job cleared all three.

Two things make this worse than an ordinary flake.

The audit covers apps/sysknife-shell, the paused Tauri GUI. CONTRIBUTING.md
puts GUI work out of scope, so #366, which changes one line of CONTRIBUTING.md,
waited twice on that directory's dependency advisories.

And gh pr checks prints frontend fail either way. A contributor reading that
cannot separate an outage from a vulnerability without opening the log.

Three shapes, and I hold none of them strongly:

  • Move the audit into its own scheduled workflow against main, daily, and drop
    it from the pull-request path. Advisories appear on the registry's clock. A
    PR-time audit answers a question the pull request did not ask.
  • Keep it on pull requests and stop it blocking: continue-on-error: true, so
    the result is reported and not enforced.
  • Leave the PR-shaped half to dependency-review, which already runs here and
    already catches a dependency a pull request introduces.

Whoever picks this up: the step is .github/workflows/ci.yml:197-198. Branch
protection on main lists frontend among its required contexts, so deleting
the step is enough; deleting the job would need the protection setting changed
in the same breath or every pull request sits on a check that never reports.

Getting started

One workflow file. No VM, no daemon, no LLM provider, no credentials.

$ grep -n 'npm audit' .github/workflows/ci.yml
198:        run: npm audit --omit=dev --audit-level=high
$ awk 'NR>=170 && NR<=230 && /- name:/ {print NR": "$0}' .github/workflows/ci.yml
177:       - name: Lint GitHub issue templates
186:       - name: Checkout
189:       - name: Set up Node
194:       - name: Install dependencies
197:       - name: Audit production dependencies
200:       - name: Type check
203:       - name: Test
216:       - name: Checkout
219:       - name: Install Tauri system dependencies
229:       - name: Set up Rust

That window is wider than the frontend job, so it picks up the tail of
docs-and-hygiene at 177 and the head of the next job at 216. Steps 186 through
203 are the ones this issue is about.

The job runs with working-directory: apps/sysknife-shell, which is the paused
desktop app, so this step gates every pull request in the repository on the
availability of registry.npmjs.org for a component nobody is developing.

Confirm the branch-protection half before you touch anything, because it decides
which of the three options in the body is safe:

$ gh api repos/lacs-project/sysknife/branches/main/protection --jq '.required_status_checks.contexts'

frontend is in that list. Deleting the step leaves the job reporting. Deleting
the job leaves every pull request waiting on a context that never arrives.

Tests first

The awkward part is that the thing you are fixing is a network outage, and you
cannot make registry.npmjs.org fail on demand.

Break what the guard protects. Whichever option you take, assert on the
workflow text rather than on a run. A guard under tests/release/ that parses
the frontend job and asserts the audit step carries continue-on-error: true,
or that no npm audit invocation is enforced on the pull-request path, is
checkable offline and cannot pass by accident.

Break the guard's own input. This is the failure mode #346, #362 and #368 all
share, so it is worth care here. Assert on a count, not only on a match. If your
guard greps the job for a token, it passes just as happily when the job is renamed
out from under it and it reads zero steps. Give it a fixture whose frontend job
has the step enforced, and assert it goes red on that fixture. A guard that has
never been seen to fail is not a guard.

tests/release/postgres-contract-guard.test.sh is the closest existing model for
parsing a job out of ci.yml, and #362 is the open issue about the ways that
particular pattern can fool you.

Difficulty

easy. The change is a few lines of YAML. Choosing among the three options is the
judgement, and the body already lays them out.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingeasyDifficulty: self-contained, no deep context neededhelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions