@biffo/cli 0.315.6 → 0.315.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -53,7 +53,14 @@ jobs:
53
53
  run:
54
54
  working-directory: apps/frontend
55
55
  steps:
56
+ # `fetch-depth: 0` (biffo-template#2040) — the Dependency audit step
57
+ # below needs `origin/${GITHUB_BASE_REF}` to resolve locally for its
58
+ # pre-existing-vs-introduced comparison (biffo-template#1673's Python
59
+ # design, now shared by the JS script too), and the default shallow
60
+ # checkout does not carry it.
56
61
  - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
62
+ with:
63
+ fetch-depth: 0
57
64
  - uses: pnpm/action-setup@v4
58
65
  with:
59
66
  version: 9.15.9
@@ -74,6 +81,27 @@ jobs:
74
81
  - name: Test
75
82
  if: ${{ !cancelled() }}
76
83
  run: pnpm run test
84
+ # Runs IN this required job again, rather than in a separate
85
+ # `continue-on-error` job, as of biffo-template#2040 — reverting the
86
+ # biffo-template#1880 split (see that repo's root `ci.yml` `js` job
87
+ # for the full reasoning this mirrors). #1880 moved this step out
88
+ # because a registry-side INCONCLUSIVE result could block real,
89
+ # unrelated PRs; that risk is unchanged and still accepted here. What
90
+ # changed: js-dependency-audit.sh now classifies findings as
91
+ # "pre-existing on the base branch" vs "introduced by this diff", so
92
+ # the routine case #1880 was reacting to — an advisory published
93
+ # against something already on `dev`, unrelated to any PR's diff — no
94
+ # longer blocks, and only a finding this diff actually introduced or
95
+ # upgraded into does.
96
+ #
97
+ # Path is relative: this job's working-directory is apps/frontend,
98
+ # which is exactly the tree to audit, and the script audits its own
99
+ # cwd — it is byte-identical to biffo-template's copy and to every
100
+ # other satellite's, distributed by that repo's shared-sync.sh, so it
101
+ # cannot assume a layout. Do not edit it here; edit it upstream.
102
+ - name: Dependency audit
103
+ if: ${{ !cancelled() }}
104
+ run: sh ../../scripts/js-dependency-audit.sh
77
105
  # Run with NO NEXT_PUBLIC_CORE_COGNITO_* in scope — this is now the
78
106
  # PERMANENT state, not a build-guard quirk. The frontend NEVER reads those
79
107
  # vars: it resolves the core's Cognito identity at runtime from
@@ -126,8 +154,8 @@ jobs:
126
154
  if: ${{ !cancelled() }}
127
155
  # This job's working-directory default is apps/frontend (top of this
128
156
  # job), but scripts/biffo.sh lives at the repo root -- same relative-
129
- # path convention as the js-audit job's own
130
- # `sh ../../scripts/js-dependency-audit.sh` below. A bare
157
+ # path convention as this job's own Dependency audit step's
158
+ # `sh ../../scripts/js-dependency-audit.sh` above. A bare
131
159
  # `scripts/biffo.sh` here fails unconditionally with "No such file",
132
160
  # on every run, regardless of whether there is a real
133
161
  # lambda-output/terraform-input violation (biffo-template#1983).
@@ -193,51 +221,6 @@ jobs:
193
221
  - name: E2E tests
194
222
  run: pnpm run e2e
195
223
 
196
- # Split out of the `js` job deliberately (biffo-template#1880): `pnpm
197
- # audit`'s registry call has been chronically, intermittently unreachable
198
- # since npm/GitHub retired the legacy audit endpoints on 2026-07-15, an
199
- # ongoing upstream infrastructure condition unrelated to this repo's own
200
- # code. Mirrors biffo-template's own root `ci.yml` split; see that repo's
201
- # `js-audit` job and #1880 for the full evidence. `js` (required) no
202
- # longer runs this step, so a registry hang can never again block this
203
- # required check; this job runs the exact same script informationally on
204
- # every push/PR (`continue-on-error: true`) and is deliberately NOT part
205
- # of DEFAULT_STATUS_CHECKS/SIBLING_STATUS_CHECKS.
206
- js-audit:
207
- name: JS Dependency Audit (non-blocking, #1880)
208
- runs-on: ${{ vars.RUNNER_LABEL || 'ubuntu-latest' }}
209
- timeout-minutes: 20
210
- continue-on-error: true
211
- defaults:
212
- run:
213
- working-directory: apps/frontend
214
- steps:
215
- - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
216
- - uses: pnpm/action-setup@v4
217
- with:
218
- version: 9.15.9
219
- - uses: actions/setup-node@v4
220
- with:
221
- node-version: ${{ env.NODE_VERSION }}
222
- cache: pnpm
223
- cache-dependency-path: apps/frontend/pnpm-lock.yaml
224
- - run: pnpm install --frozen-lockfile
225
- - name: Dependency audit
226
- if: ${{ !cancelled() }}
227
- # Fails on a real high/critical advisory, but treats a broken audit
228
- # registry (non-JSON response, or the chronic #1880 timeout) as
229
- # INCONCLUSIVE rather than blocking (#591, #743) — and this job's own
230
- # non-required status is what stops that inconclusive result from
231
- # blocking the merge queue in the first place.
232
- #
233
- # The relative path is deliberate. This job's working-directory is
234
- # apps/frontend, which is exactly the tree to audit, and the script
235
- # audits its own cwd — it is byte-identical to biffo-template's copy
236
- # and to every other satellite's, distributed by that repo's
237
- # shared-sync.sh, so it cannot assume a layout. Do not edit it here;
238
- # edit it upstream.
239
- run: sh ../../scripts/js-dependency-audit.sh
240
-
241
224
  python:
242
225
  name: Python (lint, types, test, security)
243
226
  runs-on: ${{ vars.RUNNER_LABEL || 'ubuntu-latest' }}
@@ -246,14 +229,14 @@ jobs:
246
229
  run:
247
230
  working-directory: services/api
248
231
  steps:
249
- # `fetch-depth: 0` is no longer required here (#1882): it existed
250
- # solely for py-dependency-audit.sh's pre-existing-vs-introduced
251
- # comparison (#1673), which needed `origin/${GITHUB_BASE_REF}` to
252
- # resolve locally -- that step, and the same full-depth checkout it
253
- # needs, moved to the `python-audit` job below when the audit was
254
- # split out of this required job. Nothing remaining in this job reads
255
- # `origin/` state, so the default shallow checkout is correct here now.
232
+ # `fetch-depth: 0` is required again (biffo-template#2040, reverting
233
+ # #1882's removal of it): the Dependency audit step below (folded back
234
+ # into this required job) needs `origin/${GITHUB_BASE_REF}` to resolve
235
+ # locally for py-dependency-audit.sh's pre-existing-vs-introduced
236
+ # comparison (#1673).
256
237
  - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
238
+ with:
239
+ fetch-depth: 0
257
240
  - uses: astral-sh/setup-uv@v5
258
241
  with:
259
242
  enable-cache: true
@@ -264,6 +247,19 @@ jobs:
264
247
  # committed lock just stops describing what actually ran
265
248
  # (biffo-template#1731).
266
249
  - run: uv sync --all-groups --locked
250
+ # Runs IN this required job again, rather than in a separate
251
+ # `continue-on-error` job, as of biffo-template#2040 — reverting the
252
+ # biffo-template#1882 split (mirroring the identical `js`-side revert
253
+ # above; see that job's own comment for the full reasoning). What
254
+ # changed: py-dependency-audit.sh has classified findings as
255
+ # "pre-existing on the base branch" vs "introduced by this diff"
256
+ # since #1673, so the routine case #1882 was reacting to no longer
257
+ # blocks, and only a finding this diff actually introduced or
258
+ # upgraded into does. cwd is services/api, which is the environment
259
+ # `uv sync` installed and so the one to audit.
260
+ - name: Dependency audit
261
+ if: ${{ !cancelled() }}
262
+ run: sh ../../scripts/py-dependency-audit.sh
267
263
  - name: Lint
268
264
  if: ${{ !cancelled() }}
269
265
  run: uv run ruff check .
@@ -367,39 +363,6 @@ jobs:
367
363
  # *.test.sh guard is added with no run: line anywhere.
368
364
  run: sh scripts/guard-self-test-wiring.sh
369
365
 
370
- # Split out of the `python` job deliberately (biffo-template#1882, mirroring
371
- # #1880/#1881's identical JS-side fix): `pip-audit`'s advisory lookups are
372
- # exactly as registry-dependent and chronically unreliable as `pnpm audit`'s
373
- # were. `python` (required) no longer runs this step, so a registry-side
374
- # advisory event can never again block that job; this job runs the exact
375
- # same script informationally on every push/PR (`continue-on-error: true`)
376
- # and is deliberately NOT part of DEFAULT_STATUS_CHECKS/SIBLING_STATUS_CHECKS.
377
- python-audit:
378
- name: Python Dependency Audit (non-blocking, #1882)
379
- runs-on: ${{ vars.RUNNER_LABEL || 'ubuntu-latest' }}
380
- timeout-minutes: 20
381
- continue-on-error: true
382
- defaults:
383
- run:
384
- working-directory: services/api
385
- steps:
386
- - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
387
- with:
388
- fetch-depth: 0
389
- - uses: astral-sh/setup-uv@v5
390
- with:
391
- enable-cache: true
392
- - run: uv sync --all-groups --locked
393
- - name: Dependency audit
394
- if: ${{ !cancelled() }}
395
- # Same treatment as the JS job's Dependency audit: fails on a real
396
- # advisory, INCONCLUSIVE on an unreachable PyPI/OSV (#591, #743) —
397
- # and this job's own non-required status is what stops that
398
- # inconclusive result from blocking the merge queue in the first
399
- # place. cwd is services/api, which is the environment `uv sync`
400
- # installed and so the one to audit.
401
- run: sh ../../scripts/py-dependency-audit.sh
402
-
403
366
  security-secrets:
404
367
  name: Secret Scan
405
368
  runs-on: ${{ vars.RUNNER_LABEL || 'ubuntu-latest' }}
@@ -0,0 +1,304 @@
1
+ #!/usr/bin/env sh
2
+ #
3
+ # Proves js-dependency-audit.sh (#2040) distinguishes "this PR's diff
4
+ # introduced or upgraded to a vulnerable package version" from "an advisory
5
+ # was published against a version already sitting on the base branch,
6
+ # unrelated to this diff" -- rather than reporting an identical red for both.
7
+ # This is the JS side of the fix py-dependency-audit.sh already got in
8
+ # #1673; see that file's own classification test for the Python case table
9
+ # this one mirrors.
10
+ #
11
+ # The real incident this guards against: two routine npm advisories
12
+ # (GHSA-p293-qw3h-jr36 / Next.js, GHSA-82fw-gwwq-j7x9 / vitest) were
13
+ # published against packages already on `dev` on 2026-09-08/09, with no code
14
+ # change anywhere -- and turned the JS lane red on every open PR at once,
15
+ # producing 89 fleet dispatches across 61 units in 8 repos and 10 duplicate
16
+ # tickets for one classifier bug (#2040's own cost accounting). Case 1 below
17
+ # reconstructs exactly that shape: base and PR both pinned at the flagged
18
+ # version, PR diff untouched.
19
+ #
20
+ # ## Real `pnpm audit --json` shape, captured live
21
+ #
22
+ # Run against this repo's own workspace (pnpm 9.15.9, 2026-09-10):
23
+ #
24
+ # $ pnpm audit --json
25
+ # {
26
+ # "advisories": {
27
+ # "1090893": {
28
+ # "severity": "low",
29
+ # "module_name": "cli",
30
+ # "github_advisory_id": "GHSA-6cpc-mj5c-m9rq",
31
+ # "findings": [{"version": "0.0.0", "paths": []}],
32
+ # ...
33
+ # }
34
+ # },
35
+ # "metadata": {
36
+ # "vulnerabilities": {"info": 0, "low": 1, "moderate": 0, "high": 0, "critical": 0},
37
+ # "totalDependencies": 929
38
+ # }
39
+ # }
40
+ #
41
+ # `.advisories` is an OBJECT keyed by an internal numeric id, not an array --
42
+ # `.advisories[]?` iterates its values the same as an array would. Each
43
+ # value carries `.severity`, `.module_name` and `.github_advisory_id`, which
44
+ # is exactly what js-dependency-audit.sh's classification reads. The fixture
45
+ # JSON below is shaped to match this real structure, not invented -- the
46
+ # OLD version of js-dependency-audit-parallel.test.sh's stub JSON carried
47
+ # only `.metadata.vulnerabilities` with no `.advisories` key at all, which
48
+ # classification silently read as zero findings (fixed alongside this file;
49
+ # see that test's own updated `_fail_json` comment).
50
+ #
51
+ # Also proves `pnpm audit` runs against a BARE lockfile copy with no install
52
+ # and no workspace context -- verified directly, not assumed:
53
+ #
54
+ # $ mkdir /tmp/probe && cp pnpm-lock.yaml /tmp/probe/ && cd /tmp/probe
55
+ # $ pnpm audit --json --ignore-workspace # exits 0, real advisory output
56
+ #
57
+ # which is why the base-branch side of this comparison is a second real
58
+ # `pnpm audit` call against a `git show`-extracted copy of the lockfile,
59
+ # not a hand-rolled pnpm-lock.yaml parser.
60
+ #
61
+ # Builds a real, tiny, throwaway git repo under mktemp (never under /tmp as
62
+ # a full worktree copy -- this is a from-scratch repo with a handful of
63
+ # files, not a copy of this repository's own object store -- see AGENTS.md's
64
+ # "never create a git worktree ... under /tmp", which this is not) with a
65
+ # base branch and a PR-head state, and stubs `pnpm` on PATH so `pnpm audit
66
+ # --json` returns a canned finding selected by a marker comment inside
67
+ # whichever pnpm-lock.yaml the stub is invoked against -- no network, no
68
+ # real registry. `jq`, `git` and `grep` are used for real -- they are
69
+ # exactly what the target script itself depends on, so stubbing them would
70
+ # test nothing.
71
+ #
72
+ # Run: sh scripts/js-dependency-audit-classification.test.sh
73
+
74
+ set -u
75
+
76
+ SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
77
+ TARGET="$SCRIPT_DIR/js-dependency-audit.sh"
78
+
79
+ REPO_DIR=$(mktemp -d)
80
+ STUB_DIR=$(mktemp -d)
81
+ TMPROOT_DIR=$(mktemp -d)
82
+ OUT_FILE=$(mktemp)
83
+ trap 'rm -rf "$REPO_DIR" "$STUB_DIR" "$TMPROOT_DIR"; rm -f "$OUT_FILE"' EXIT
84
+
85
+ FAILURES=0
86
+
87
+ # --- pnpm stub ---------------------------------------------------------------
88
+ # Selected by a `# STATE:<tag>` marker comment inside whatever
89
+ # pnpm-lock.yaml is in the CURRENT DIRECTORY when invoked -- the target
90
+ # script always `cd`s into the tree it is auditing (the repo's own working
91
+ # tree for the HEAD run, or a scratch dir holding a `git show`-extracted
92
+ # copy for the BASE run), so the marker travels with whichever commit's
93
+ # content is actually being read, exactly like a real lockfile diff would.
94
+ cat > "$STUB_DIR/pnpm" <<'STUB'
95
+ #!/usr/bin/env sh
96
+ if [ "$1" = "audit" ]; then
97
+ state=$(grep -o 'STATE:[A-Za-z0-9_-]*' pnpm-lock.yaml 2>/dev/null | head -1 | cut -d: -f2)
98
+ out_file="$STUB_DIR_ENV/output-${state:-clean}.json"
99
+ if [ -f "$out_file" ]; then
100
+ cat "$out_file"
101
+ else
102
+ cat "$STUB_DIR_ENV/output-clean.json"
103
+ fi
104
+ exit 0
105
+ fi
106
+ echo "pnpm stub: unexpected invocation: $*" >&2
107
+ exit 99
108
+ STUB
109
+ chmod +x "$STUB_DIR/pnpm"
110
+
111
+ # --- fixture JSON, one per STATE tag ----------------------------------------
112
+ cat > "$STUB_DIR/output-clean.json" <<'JSON'
113
+ {"metadata":{"vulnerabilities":{"critical":0,"high":0,"moderate":0,"low":0},"totalDependencies":5}}
114
+ JSON
115
+
116
+ # One high-severity advisory against vuln-pkg-a.
117
+ cat > "$STUB_DIR/output-vuln-a.json" <<'JSON'
118
+ {"metadata":{"vulnerabilities":{"critical":0,"high":1,"moderate":0,"low":0},"totalDependencies":5},"advisories":{"1":{"severity":"high","github_advisory_id":"GHSA-test-0001","module_name":"vuln-pkg-a","findings":[{"version":"1.2.3","paths":[]}]}}}
119
+ JSON
120
+
121
+ # Only vuln-pkg-b's advisory (used as the mixed case's BASE state -- pkg-a
122
+ # is absent entirely, pkg-b is present and already flagged).
123
+ cat > "$STUB_DIR/output-vuln-b-only.json" <<'JSON'
124
+ {"metadata":{"vulnerabilities":{"critical":0,"high":1,"moderate":0,"low":0},"totalDependencies":5},"advisories":{"2":{"severity":"high","github_advisory_id":"GHSA-test-0002","module_name":"vuln-pkg-b","findings":[{"version":"2.0.0","paths":[]}]}}}
125
+ JSON
126
+
127
+ # Both advisories together (the mixed case's HEAD state).
128
+ cat > "$STUB_DIR/output-vuln-a-b.json" <<'JSON'
129
+ {"metadata":{"vulnerabilities":{"critical":0,"high":2,"moderate":0,"low":0},"totalDependencies":5},"advisories":{"1":{"severity":"high","github_advisory_id":"GHSA-test-0001","module_name":"vuln-pkg-a","findings":[{"version":"1.2.3","paths":[]}]},"2":{"severity":"high","github_advisory_id":"GHSA-test-0002","module_name":"vuln-pkg-b","findings":[{"version":"2.0.0","paths":[]}]}}}
130
+ JSON
131
+
132
+ # --- repo scaffolding --------------------------------------------------------
133
+ # One workspace-level pnpm-lock.yaml so discovery finds exactly one tree,
134
+ # matching the real incident (the workspace root, not a vendored tree).
135
+ _write_lock() {
136
+ # $1 = destination file, $2 = STATE tag
137
+ cat > "$1" <<LOCK
138
+ lockfileVersion: '9.0'
139
+ # STATE:$2
140
+ LOCK
141
+ }
142
+
143
+ _init_repo() {
144
+ # $1 = base STATE tag (what "dev" / origin/<base> holds)
145
+ # $2 = head STATE tag (what the PR branch under test holds)
146
+ base_state=$1
147
+ head_state=$2
148
+
149
+ rm -rf "$REPO_DIR"
150
+ mkdir -p "$REPO_DIR"
151
+ ( cd "$REPO_DIR" \
152
+ && git init -q -b trunk \
153
+ && git config user.email test@example.com \
154
+ && git config user.name "Test" )
155
+
156
+ _write_lock "$REPO_DIR/pnpm-lock.yaml" "$base_state"
157
+ ( cd "$REPO_DIR" && git add -A && git commit -q -m base )
158
+ # A literal branch named "origin/<base>" -- git tolerates slashes in
159
+ # branch names, so this resolves via `git show origin/<base>:<path>`
160
+ # exactly like a real remote-tracking ref would, with no remote required.
161
+ ( cd "$REPO_DIR" && git branch -q "origin/dev" )
162
+
163
+ if [ "$head_state" != "$base_state" ]; then
164
+ _write_lock "$REPO_DIR/pnpm-lock.yaml" "$head_state"
165
+ ( cd "$REPO_DIR" && git add -A && git commit -q -m "pr change" )
166
+ fi
167
+ }
168
+
169
+ # --- runner ------------------------------------------------------------------
170
+ _run() {
171
+ # Runs the target script with GITHUB_BASE_REF set from $1 (empty string
172
+ # means unset -- a push/workflow_dispatch/merge_group context), cwd inside
173
+ # the synthetic repo, pnpm stubbed, PATH otherwise untouched (jq/git/grep
174
+ # are the real system tools, same as production).
175
+ base_ref=$1
176
+ (
177
+ cd "$REPO_DIR" || exit 97
178
+ PATH="$STUB_DIR:$PATH"
179
+ STUB_DIR_ENV="$STUB_DIR"
180
+ TMPDIR="$TMPROOT_DIR"
181
+ export PATH STUB_DIR_ENV TMPDIR
182
+ if [ -n "$base_ref" ]; then
183
+ GITHUB_BASE_REF="$base_ref"
184
+ export GITHUB_BASE_REF
185
+ else
186
+ unset GITHUB_BASE_REF
187
+ fi
188
+ sh "$TARGET"
189
+ ) >"$OUT_FILE" 2>&1
190
+ LAST_RC=$?
191
+ }
192
+
193
+ _assert_exit() {
194
+ name=$1
195
+ expected=$2
196
+ if [ "$LAST_RC" -eq "$expected" ]; then
197
+ echo "PASS: $name (exit $LAST_RC)"
198
+ else
199
+ echo "FAIL: $name -- expected exit $expected, got $LAST_RC"
200
+ echo "--- output ---"
201
+ cat "$OUT_FILE"
202
+ echo "--------------"
203
+ FAILURES=$((FAILURES + 1))
204
+ fi
205
+ }
206
+
207
+ _assert_output_contains() {
208
+ name=$1
209
+ needle=$2
210
+ if grep -qF "$needle" "$OUT_FILE"; then
211
+ echo "PASS: $name mentions '$needle'"
212
+ else
213
+ echo "FAIL: $name -- expected output to mention '$needle'"
214
+ echo "--- output ---"
215
+ cat "$OUT_FILE"
216
+ echo "--------------"
217
+ FAILURES=$((FAILURES + 1))
218
+ fi
219
+ }
220
+
221
+ # ==============================================================================
222
+ # Case table (must-NOT-block first, then must-block), each run against the
223
+ # real target script -- not a reimplementation of its logic.
224
+ # ==============================================================================
225
+
226
+ # 1. PRE-EXISTING, PR context, advisory UNCHANGED from base (the
227
+ # GHSA-p293-qw3h-jr36 / 2026-09-08 shape: same version on both sides of
228
+ # the diff). Must NOT block, and must say so as pre-existing rather than
229
+ # a flat red.
230
+ _init_repo "vuln-a" "vuln-a"
231
+ _run dev
232
+ _assert_exit "pre-existing, advisory unchanged by diff" 0
233
+ _assert_output_contains "pre-existing case names it as pre-existing" "pre-existing"
234
+ _assert_output_contains "pre-existing case cites #2040" "#2040"
235
+
236
+ # 2. INTRODUCED via upgrade: base had a clean lockfile, this diff's
237
+ # pnpm-lock.yaml moved it to the flagged version. Must block.
238
+ _init_repo "clean" "vuln-a"
239
+ _run dev
240
+ _assert_exit "introduced by upgrade" 1
241
+ _assert_output_contains "introduced-by-upgrade case names it as introduced" "introduced or upgraded by this diff"
242
+
243
+ # 3. INTRODUCED via new dependency: base's lockfile carries no such package
244
+ # at all; this diff added it at an already-vulnerable version. For the
245
+ # advisory-ID-diffing design this is mechanically identical to case 2
246
+ # (base's audit reports no matching id either way) -- kept as its own
247
+ # case for documentation, matching py-dependency-audit-classification's
248
+ # own case 3, which IS mechanically distinct there because Python
249
+ # classifies per-package lockfile version rather than by diffing two
250
+ # full audit runs.
251
+ _init_repo "clean" "vuln-a"
252
+ _run dev
253
+ _assert_exit "introduced via brand-new dependency" 1
254
+ _assert_output_contains "new-dependency case names it as introduced" "introduced or upgraded by this diff"
255
+
256
+ # 4. Non-PR context (push to the integration branch itself, or
257
+ # workflow_dispatch/merge_group -- GITHUB_BASE_REF unset). No diff exists
258
+ # to attribute the finding to, so the classification must NOT apply: any
259
+ # finding blocks, exactly as before this fix. Deliberately the SAME
260
+ # lockfile shape as case 1 (unchanged advisory) to prove the difference
261
+ # in outcome is driven by PR-context alone -- the scheduled base-branch
262
+ # scan added alongside this file (#2040) relies on exactly this: every
263
+ # current finding on `dev` must be reported, not just new ones.
264
+ _init_repo "vuln-a" "vuln-a"
265
+ _run ""
266
+ _assert_exit "no PR context -- always blocks" 1
267
+
268
+ # 5. PR context, but the base ref cannot be resolved locally (e.g. a shallow
269
+ # checkout, or a distributed copy running where fetch-depth: 0 was not
270
+ # honoured). Comparison fails CLOSED: blocks, same as before this fix,
271
+ # rather than silently waving a real regression through because the
272
+ # comparison itself could not be made.
273
+ _init_repo "vuln-a" "vuln-a"
274
+ _run "some-branch-that-was-never-fetched"
275
+ _assert_exit "base ref unresolvable -- fails closed to blocking" 1
276
+ _assert_output_contains "base ref unresolvable -- warns why" "does not resolve locally"
277
+
278
+ # 6. Mixed tree: one pre-existing finding (vuln-pkg-b, unchanged from base)
279
+ # and one introduced finding (vuln-pkg-a, new to this diff) in the same
280
+ # run. Must still block overall (the introduced one), while the
281
+ # pre-existing one is still named as such rather than folded into an
282
+ # undifferentiated total.
283
+ _init_repo "vuln-b-only" "vuln-a-b"
284
+ _run dev
285
+ _assert_exit "mixed tree -- introduced finding still blocks" 1
286
+ _assert_output_contains "mixed tree -- introduced vuln-pkg-a is named" "vuln-pkg-a advisory GHSA-test-0001"
287
+ _assert_output_contains "mixed tree -- pre-existing vuln-pkg-b is named separately" "vuln-pkg-b advisory GHSA-test-0002"
288
+ _assert_output_contains "mixed tree -- pre-existing vuln-pkg-b says so" "pre-existing"
289
+
290
+ # 7. Sanity: a clean run (no vulnerabilities at all) is unaffected by any of
291
+ # the above -- still exits 0, still audits normally with the new
292
+ # 5-arg audit_dir signature.
293
+ _init_repo "clean" "clean"
294
+ _run dev
295
+ _assert_exit "clean run unaffected" 0
296
+
297
+ echo
298
+ if [ "$FAILURES" -eq 0 ]; then
299
+ echo "js-dependency-audit-classification.test.sh: all checks passed."
300
+ exit 0
301
+ else
302
+ echo "js-dependency-audit-classification.test.sh: $FAILURES check(s) failed."
303
+ exit 1
304
+ fi