Skip to content

fix(security): GT-657 fix the half of a js-yaml advisory that has a fix, and name the half that has none - #441

Merged
beyondnetPeru merged 2 commits into
developfrom
gt-657-js-yaml-quadratic-cpu
Aug 8, 2026
Merged

fix(security): GT-657 fix the half of a js-yaml advisory that has a fix, and name the half that has none#441
beyondnetPeru merged 2 commits into
developfrom
gt-657-js-yaml-quadratic-cpu

Conversation

@beyondnetPeru

Copy link
Copy Markdown
Contributor

Closes GT-657.

GHSA-5p4m-2wfm-xmqj / CVE-2026-59870 (js-yaml, quadratic CPU in !!omap) turned Security Audit red on every branch. Attribution first, because "not my PR" is a claim that needs evidence: the dependabot PRs of 2026-08-05 were green, PR #440 of the 8th was red, and #440 changed 8 documentation and JSON files with zero dependency files. Pre-existing branch debt on main and develop.

The half that had a fix, and why it was missed

The root overrides already carried js-yaml: 4.3.0, added for an earlier advisory. The new one is vulnerable through 4.0.0 - 4.3.0 inclusive — the existing pin was exactly one patch short of it. Bumped to 4.3.1; re-resolving also collapsed three nested copies into the single hoisted one.

The half that has none, proven rather than assumed

@nestjs/swagger pins js-yaml verdict
11.4.4 4.1.1 exact vulnerable (CVE-2026-59870)
11.4.5 4.3.0 exact vulnerable (CVE-2026-59870)
11.4.6 (latest stable) 5.2.1 exact vulnerable (GHSA-pm4m-ph32-ghv5)

There is no version to bump to, and npm overrides do not reach a nested exact pin — measured four ways, same tree every time: top-level override, scoped @nestjs/swagger override, both repeated with the unrelated nested override objects removed, and --package-lock-only versus a real npm install.

That last experiment refutes a generalisation this board already recorded: GT-636 concluded that a nested override object anywhere stops the top-level rule cascading. Removing them changed nothing — what blocks it is the consumer's exact pin. Corrected in the catalog rather than left standing.

Why an acceptance was not enough

Leaving the job red is precisely what #440 just closed GT-622 to remove: a permanently red check trains reviewers to discount red, and the next genuinely fixable advisory would land in a job nobody reads.

63-validate-npm-audit-gate keeps the same HIGH threshold and adds one requirement — an advisory with no upstream fix must be named, with the path it arrives by and what was checked upstream.

situation verdict
undeclared high/critical fails
declared, still present passes, reason printed
declared for a different id or path fails — an exception covers one hole, not a package
declared but the advisory is gone fails as stale — the good news arrives as a red check
registry unparseable or missing its array fails, never "no exemptions"

The gate's own fixtures run in the job, because a gate whose exception list can swallow anything is a green button.

Verification

node --test 19/19. npm audit now reports 0 critical / 2 high, both declared, 3 moderate untouched below the unchanged threshold. core-api 160/160 in 30 suites and core-domain 1704/1704 in 146 suites against the bumped tree; eslint 9.39.4 and jest 30.4.1 both boot. Guards 04, 08, 09 --check, 34, 39, 40, 41 --execute --strict, 42, 43, 46, 47, 49 and bilingual-terminology-lint all exit 0.

Board: 642 / 655 done, 3 in progress, 3 pending, 7 deferred.

🤖 Generated with Claude Code

…ix, and name the half that has none

Closes GT-657.

GHSA-5p4m-2wfm-xmqj / CVE-2026-59870 (js-yaml, quadratic CPU in `!!omap`) was
published between 2026-08-05 and 2026-08-08 and turned `Security Audit` red on
every branch. The dating is the attribution evidence: the dependabot PRs of the
5th were green, PR #440 of the 8th was red, and #440 changed 8 documentation and
JSON files with ZERO dependency files. Pre-existing branch debt, not a
regression.

The half that had a fix, and why it was missed
----------------------------------------------
The root overrides already carried `js-yaml: 4.3.0`, added for an EARLIER
advisory. The new one is vulnerable through 4.0.0-4.3.0 INCLUSIVE, so the
existing pin was exactly one patch short of it. Bumped to 4.3.1; re-resolving
also collapsed three nested copies (@commitlint/load, @istanbuljs/load-nyc-config,
cosmiconfig) into the single hoisted one.

The half that has none, proven rather than assumed
--------------------------------------------------
The remaining advisory arrives through @nestjs/swagger, which pins js-yaml
EXACTLY, and every published release pins a vulnerable one: 11.4.4 -> 4.1.1 and
11.4.5 -> 4.3.0 (CVE-2026-59870), 11.4.6 -> 5.2.1 (GHSA-pm4m-ph32-ghv5). 11.4.6
is the latest stable; 12.0.0 is alpha only. npm overrides do not reach it,
measured four ways with the same tree every time: a top-level override, a scoped
@nestjs/swagger override, both repeated with the unrelated nested override
objects removed, and --package-lock-only versus a real npm install.

That last experiment REFUTES the generalisation GT-636 recorded — that a nested
override object anywhere stops the top-level rule cascading. Removing them
changed nothing. What actually blocks the override is the consumer's exact pin.
Recorded rather than left standing: a wrong lesson inside a closed row is worse
than no lesson.

Why an acceptance was not enough
--------------------------------
Leaving the job red is what GT-622 was just closed to remove — a permanently red
check trains reviewers to discount red, and the next genuinely fixable advisory
would land in a job nobody reads. `63-validate-npm-audit-gate` keeps the same
HIGH threshold and adds one requirement: an advisory with no upstream fix must be
NAMED, with the path it arrives by and what was checked upstream. It fails on an
undeclared advisory, on a declaration for a different id or path, and — the rule
that stops a graveyard — on a declaration whose advisory has DISAPPEARED, so the
good news arrives as a red check asking for the entry's removal.

The gate's own fixtures run in the job, because a gate whose exception list can
swallow anything is a green button.

Verified
--------
node --test 63-validate-npm-audit-gate.test.mjs 19/19 (including the undeclared
advisory red, the wrong-path declaration covering nothing, the stale declaration
failing, and an unparseable registry stopping the run). `npm audit` now reports
0 critical / 2 high, both declared, 3 moderate untouched below the unchanged
threshold. core-api 160/160 in 30 suites and core-domain 1704/1704 in 146 suites
against the bumped tree; eslint 9.39.4 and jest 30.4.1 both boot. Guards 04, 08,
09 --check, 34, 39, 40, 41 (--execute --strict, 114 executed / 113 exit 0, the
one non-zero being GT-78's root-cleanliness objecting to an untracked local
.DS_Store absent on the runner), 42, 43, 46, 47, 49 and
bilingual-terminology-lint all exit 0.

Board: 642 / 655 done, 3 in progress, 3 pending, 7 deferred.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@beyondnetPeru
beyondnetPeru requested a review from a team as a code owner August 8, 2026 15:02
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown

📊 Bilingual Coverage Impact

PR Changes

  • Paired EN/ES files modified: 3
  • New EN files needing ES translation: 0

Repository Coverage

Metric Value
Total EN files 527
Total ES files 501
Paired files 0
Coverage 0%

Good: All EN changes have ES counterparts.


Generated by GitHub Actions

Comment thread package-lock.json
Comment on lines +2692 to +2713
"node_modules/@nestjs/swagger/node_modules/js-yaml": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.2.1.tgz",
"integrity": "sha512-zfLtNfQqxVqq3uaTqSkh4x4hZw3KHobGUA0fJUj4wawW8bsQLTVqpHdXSIzidh7o+4lEW36tANuAGdaFx6Zgnw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/puzrin"
},
{
"type": "github",
"url": "https://github.com/sponsors/nodeca"
}
],
"license": "MIT",
"dependencies": {
"argparse": "^2.0.1"
},
"bin": {
"js-yaml": "bin/js-yaml.mjs"
}
},
…wered by reverting

`Trivy` reported "1 new alert including 1 high severity security vulnerability"
on PR #441 — js-yaml 5.2.1 at package-lock.json. It reads as though this change
introduced a vulnerability. It did not, and the revert was attempted before that
was checked, so the check is recorded rather than the conclusion alone.

Measured at BOTH versions:

  @nestjs/swagger 11.4.4 -> js-yaml 4.1.1 -> THREE advisories
      GHSA-52cp-r559-cp3m   HIGH
      GHSA-5p4m-2wfm-xmqj   HIGH
      GHSA-h67p-54hq-rp68   moderate
  @nestjs/swagger 11.4.6 -> js-yaml 5.2.1 -> ONE advisory
      GHSA-pm4m-ph32-ghv5   HIGH

The bump removes two high advisories and leaves one. Trivy compares alerts on
the changed lines, so an advisory id changing at the same lockfile position is
"new" to it — the id changed, no hole opened.

Two instruments disagree with each other here and both are being read correctly:
npm reports 2 high ROWS for 11.4.6 (js-yaml and its parent) against 1 for
11.4.4, while 11.4.6 carries a third of the advisories. Counting rows says
revert; counting holes says do not.

No code or dependency change: the tree is byte-identical to the previous commit.
What is added is the reasoning, in the exceptions entry as
`doNotRevertTheSwaggerBump` and in GT-657's catalog section in both languages,
so the next reader who sees the red Trivy check does not undo the fix.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@beyondnetPeru
beyondnetPeru merged commit 658a7bb into develop Aug 8, 2026
35 of 36 checks passed
@beyondnetPeru
beyondnetPeru deleted the gt-657-js-yaml-quadratic-cpu branch August 8, 2026 16:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants