Skip to content

dependency-audit only detects a root package-lock.json — 35 nested lockfiles across 7 repos are never audited #137

Description

@chitcommit

reusable-governance-gates.yml's dependency-audit job detects lockfiles at the repository root only:

- name: Detect lockfile
  run: |
    if [[ -f package-lock.json ]]; then
      echo "present=true" >> "$GITHUB_OUTPUT"
    else
      echo "present=false" >> "$GITHUB_OUTPUT"
    fi

Nested lockfiles are never audited, and when no root lockfile exists the job skips entirely while reporting success.

Confirmed impact

Found while remediating a real alert: chittyregistry had a high-severity js-yaml advisory in packages/cli/package-lock.json that sat open while gates / dependency-audit reported green. Dependabot saw it; the gate could not. Fixed via #189, but only because the Dependabot alert surfaced it independently — the gate contributed nothing.

Seven repos that call this workflow carry nested lockfiles outside the audited root:

Repo Unaudited nested lockfiles
chittymcp 20
chittyapi 5
chittycommand 5
chittystorage 2
chittymonitor 1
chittyregistry 1
chittyserv 1

35 lockfiles across 7 repos are outside the gate's coverage. chittycommand — this repo — is among them.

One repo is in the worse state: chittyagent-helper has no root lockfile, so its dependency-audit job takes the skip branch and reports success without ever running npm audit. A green check that never performed the work.

Why this is the quiet kind of failure

There is no continue-on-error to grep for and no red job to notice. The gate reports success, the required check passes, and the coverage loss is invisible from the PR page. It is the "check you pay for and never receive" pattern arriving through scope rather than through masking.

Suggested direction

Discover lockfiles rather than assuming one at root:

- name: Detect lockfiles
  id: lockfile
  run: |
    set -euo pipefail
    mapfile -t LOCKS < <(find . -name package-lock.json -not -path '*/node_modules/*' | sort)
    printf 'count=%s\n' "${#LOCKS[@]}" >> "$GITHUB_OUTPUT"
    printf '%s\n' "${LOCKS[@]}" > /tmp/locks.txt

- name: Dependency Audit (High+)
  if: ${{ steps.lockfile.outputs.count != '0' }}
  run: |
    set -euo pipefail
    while read -r lock; do
      dir="$(dirname "$lock")"
      echo "::group::audit $dir"
      ( cd "$dir" && npm ci --ignore-scripts && npm audit --audit-level=high ${{ inputs.audit_omit_dev && '--omit=dev' || '' }} )
      echo "::endgroup::"
    done < /tmp/locks.txt

Two points worth deciding explicitly rather than inheriting:

  1. Make "no lockfile found" loud. Right now it is indistinguishable from "audited and clean." At minimum it should annotate; arguably it should fail for repos that are expected to have one.
  2. --ignore-scripts on the audit install. The audit job does not need lifecycle scripts to run, and skipping them removes an arbitrary-code-execution path from a job that installs untrusted dependency trees.

Context

Surfaced during a git-hygiene sweep of chittyregistry, where I first tried audit_omit_dev: true to clear a red gate. That was rejected by separated adversarial review as weakening rather than fixing, and reverted in chittyos/chittyregistry#195 — the advisory turned out to be resolvable outright via an overrides pin. This coverage gap is the adjacent finding: audit_omit_dev narrows what is audited, and root-only detection narrows where. The second is invisible.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions