Skip to content

fix(deps): clear the critical websocket-driver advisory and unbreak npm ci#102

Open
Prekzursil wants to merge 1 commit into
mainfrom
fix/deps-clean-zero-websocket-driver-critical
Open

fix(deps): clear the critical websocket-driver advisory and unbreak npm ci#102
Prekzursil wants to merge 1 commit into
mainfrom
fix/deps-clean-zero-websocket-driver-critical

Conversation

@Prekzursil

Copy link
Copy Markdown
Owner

Why

Two separate things were wrong, and the first was hiding the second.

1. npm ci was broken on main itself, so no dependency PR in this repo could be verified or merged:

npm error code EUSAGE
npm error `npm ci` can only install packages when your package.json and package-lock.json
npm error or npm-shrinkwrap.json are in sync.
npm error Invalid: lock file's ws@7.5.11 does not satisfy ws@7.5.13

Reproduced on a clean clone of main (477b7ae).

What was out of sync: the ws: "^7.5.11" override forced the whole tree onto the ws 7.x line, but the lock still carried a nested webdriver/node_modules/ws@8.21.0, which violates that override. A scan of all 12 overrides against the lock found ws was the only one with a violating node — which is exactly why npm named only ws. That broken edge makes arborist re-resolve the ws family, and ^7.5.11 now resolves to 7.5.13 (7.5.12/7.5.13 were published after the lock was written), so the freshly-built ideal tree disagreed with the pinned lock.

The ws override is removed rather than re-pinned: no ws version in the tree is flagged by any advisory any more, and the override was actively downgrading consumers that ask for ws 8.x (@storybook/core wants ^8.2.3, webpack-dev-server wants ^8.18.0). The lock was regenerated, not hand-edited.

2. The required quality gate needs osv-scanner clean-zero, and the tree carried 25 known vulnerabilities. Closing only the 3 Dependabot alerts would still have left the gate red, so the whole flagged set is brought up.

Dependabot alerts closed

severity package advisory fix
CRITICAL (CVSS 9.2) websocket-driver GHSA-xv26-6w52-cph6 0.7.4 -> 0.7.5
medium websocket-driver GHSA-mp7j-qc5w-4988 0.7.4 -> 0.7.5
high fast-xml-parser GHSA-8r6m-32jq-jx6q 5.9.3 -> 5.10.1

Each verified against the advisory's own range, not a summary. All floors are bounded (>=X <NextMajor) — no exact pins, so Dependabot can still see and propose newer patches.

Also brought to clean-zero for the gate

Django 5.2.15 -> 5.2.16; new bounded-floor overrides for body-parser, fast-uri, find-my-way, sharp, shell-quote, svgo; raised floors for postcss, js-yaml, webpack-dev-server. brace-expansion reaches its patched 1.1.16 / 2.1.2 naturally once the stale lock is regenerated — no override needed.

react-router-dom -> react-router (the one behavioural change)

GHSA-jjmj-jmhj-qwj2 has no fix anywhere in the react-router-dom 6.x line (introduced 6.30.2, last_affected 6.30.4, no fixed event). v7 also deprecates the react-router-dom shim in favour of importing from react-router directly, so the dependency is replaced and the 37 module specifiers under src/ are rewritten.

The shim is additionally unusable here: its CJS entry requires the react-router/dom subpath export, which CRA 5's jest 27 resolver cannot resolve (Cannot find module 'react-router/dom' — 18 suites failed to load when it was tried). react-router's main entry exports all 10 APIs this app uses (BrowserRouter, MemoryRouter, Routes, Route, Link, NavLink, Navigate, useNavigate, useParams, useLocation) — verified at runtime.

setupTests.ts gains a branch-free TextEncoder/TextDecoder polyfill: react-router v7 constructs a new TextEncoder() at module scope and jest 27's jsdom does not expose it. Branch-free deliberately, because this project enforces a 100% branch coverage threshold and an if (!global.TextEncoder) guard would add an uncovered branch.

⚠️ Two new IgnoredVulns — these are the judgement calls, please review

Recorded in osv-scanner.toml per that file's existing policy (reasoned + dated + greppable), with the reachability argument inline. This is the part of the PR to push back on.

  • GHSA-qwww-vcr4-c8h2 (react-router 7.18.1) — the advisory text itself says "This only affects your application if you are using the unstable RSC APIs". This is a CRA client-side SPA; src/ has no match for rsc / matchRSCServerRequest / createStaticHandler / StaticRouter / renderToString / renderToPipeableStream / unstable_ / createBrowserRouter / RouterProvider, and no route loader:/action:. Genuinely unfixable on React 18: the fix is react-router 8.3.0, whose peers require react >= 19.2.7; this app is on React 18.3.1, and react-router-dom has no 8.x at all (E404). The union of the four open react-router advisories covers >=6.0.0 <8.3.0, so on React 18 no clean version exists — 7.18.x is the minimum-vulnerability choice, and it clears the other three.
  • GHSA-mh99-v99m-4gvg (brace-expansion) — single affected range introduced 0, fixed 5.0.8, so no patched release exists in any line below 5.0.8. Forcing >=5.0.8 <6 was attempted and broke the build outright: [eslint] expand is not a function, because 5.0.8 restructured to an exports map with no callable default while minimatch 3.x does require("brace-expansion")(). Reached only by build/test-time glob tooling on repo-authored patterns; not in the shipped bundle.

Both carry a re-review trigger (React 19 migration; a 1.x/2.x backport or moving off minimatch 3.x).

Local verification

Run in frontend/webcoder_ui unless noted. osv-scanner is 2.3.8, the version CI pins.

check result
npm ci exit 0 (was EUSAGE on main)
tsc --noEmit exit 0
react-scripts build Compiled successfully.
react-scripts test --coverage --watchAll=false --ci 25/25 suites, 236/236 tests, 100% stmts/branch/funcs/lines
oxlint@1.69.0 --deny-warnings exit 0
biome@2.5.0 ci (formatter) exit 0 — checked against the LF blob via git archive, since the CRLF worktree gives a false 73-file failure on Windows; main's blob was used as the control and also passes
osv-scanner --lockfile=frontend/webcoder_ui/package-lock.json No issues found, exit 0 — down from 25 vulnerabilities (1 critical, 14 high)

⚠️ Known CI risk that is NOT this PR

osv-scanner's PyPI transitive-dependency resolution is currently failing upstream:

failed resolution for backend/requirements.txt: rpc error: code = Unavailable desc = service unavailable

This is present on main too (it appears in the 2026-07-24 runs of #98 and #101). It matters because of osv-scanner's exit codes: 1 = vulnerabilities found, but 127 = HasErrored. While vulnerabilities existed the scan exited 1, so the error was masked. Now that the tree is clean, a full recursive scan locally reports Total 0 packages affected by 0 known vulnerabilities and still exits 127 purely because of that outage — and the gate's shell fails anything that is not 0 or 128.

So if gate-deps goes red on this PR, check the exit code and grep for service unavailable before reading it as a security finding. A 127 there is an infrastructure flake and should be re-run, not "fixed" by changing a dependency. Isolating the two sources locally: frontend lockfile alone -> exit 0; requirements.txt alone -> exit 127 with only that resolver error. There is no repo-side switch (--no-resolve lives on the scanner invocation in the reusable workflow, not in osv-scanner.toml).

Relationship to the open Dependabot PRs

This supersedes the content of #98 (websocket-driver 0.7.5) and #101 (fast-xml-parser 5.10.1) — both of which fail the required quality gate on their own, because each fixes one package while the gate demands clean-zero. I have deliberately not touched, closed or rebased #98, #99, #100 or #101 — that call is left to the owner.

…pm ci

`npm ci` was broken on main itself, which blocked every dependency PR from
being verifiable:

    npm error code EUSAGE
    npm error `npm ci` can only install packages when your package.json and
    npm error package-lock.json or npm-shrinkwrap.json are in sync.
    npm error Invalid: lock file's ws@7.5.11 does not satisfy ws@7.5.13

Root cause: the `ws: ^7.5.11` override forced the whole tree onto the ws 7.x
line, but the lock still carried a nested `webdriver/node_modules/ws@8.21.0`
that VIOLATES that override. ws was the only override in the tree with a
violating node, which is why it was the only package npm named. The broken edge
made arborist re-resolve the ws family, and `^7.5.11` now resolves to 7.5.13
(7.5.12/7.5.13 published after the lock was written), so the freshly-built
ideal tree disagreed with the pinned lock.

The `ws` override no longer serves a security purpose (no ws version in the tree
is flagged) and it was downgrading consumers that ask for ws 8.x
(@storybook/core, webpack-dev-server), so it is removed rather than re-pinned.
The lock is regenerated, not hand-edited.

Dependabot alerts closed:
  * websocket-driver 0.7.4 -> 0.7.5  GHSA-xv26-6w52-cph6 (CRITICAL, CVSS 9.2)
  * websocket-driver 0.7.4 -> 0.7.5  GHSA-mp7j-qc5w-4988 (medium)
  * fast-xml-parser 5.9.3 -> 5.10.1  GHSA-8r6m-32jq-jx6q (high)

The `quality` gate additionally requires osv-scanner clean-zero, so the rest of
the flagged tree is brought up too: Django 5.2.15 -> 5.2.16, and bounded-floor
overrides (never exact pins) for body-parser, fast-uri, find-my-way, sharp,
shell-quote, svgo, plus raised floors for postcss, js-yaml and
webpack-dev-server. brace-expansion resolves to 1.1.16 / 2.1.2 naturally once
the stale lock is regenerated.

react-router-dom 6.30.4 is replaced by react-router ^7.18.0. GHSA-jjmj-jmhj-qwj2
has NO fix anywhere in the react-router-dom 6.x line, and v7 deprecates the
react-router-dom shim in favour of importing from react-router directly, so the
37 module specifiers under src/ are rewritten. The shim also cannot be used
here: its CJS entry requires the `react-router/dom` subpath export, which CRA
5's jest 27 resolver cannot resolve. A branch-free TextEncoder/TextDecoder
polyfill is added to setupTests.ts because react-router v7 constructs a
`new TextEncoder()` at module scope and jest 27's jsdom does not expose it.

Two new reasoned, dated IgnoredVulns entries are recorded in osv-scanner.toml
per that file's existing policy. Both are genuinely unfixable here and are
argued for reachability inline. They are the two judgement calls in this change
and are the right place to push back:
  * GHSA-qwww-vcr4-c8h2 (react-router) - the advisory states it only affects the
    unstable RSC APIs; this is a CRA client-side SPA with no RSC/SSR/data-router.
    The fix, react-router 8.3.0, peer-requires React >= 19.2.7 and this app is on
    React 18.3.1.
  * GHSA-mh99-v99m-4gvg (brace-expansion) - no patched release exists below
    5.0.8 in any line, and forcing >=5.0.8 breaks the build outright
    ("[eslint] expand is not a function", measured).

Local verification (frontend/webcoder_ui): npm ci exit 0, tsc --noEmit exit 0,
react-scripts build "Compiled successfully", 25/25 test suites and 236/236 tests
pass at 100% statements/branches/functions/lines, oxlint@1.69.0
--deny-warnings exit 0, and osv-scanner 2.3.8 (the version CI pins) reports
"No issues found" exit 0 on the frontend lockfile, down from 25 vulnerabilities.
@Prekzursil

Copy link
Copy Markdown
Owner Author

CI is green — but the merge is blocked by a stale required-check name (owner action needed)

All 9 checks pass on f9c2b99, including both gates that actually run:

check result
quality / quality pass (3m11s, attempt 2)
CodeQL pass
Analyze (actions) / (javascript-typescript) / (python) pass
verify pass
build (3.10) / build (3.11) pass
Seer Code Review pass

gh pr view reports mergeable: MERGEABLE, isDraft: false — but mergeStateStatus: BLOCKED.

Why it is blocked (not caused by this PR)

Branch protection on main requires the status context codeql / CodeQL:

$ gh api repos/Prekzursil/WebCoder/branches/main/protection/required_status_checks
{"contexts":["codeql / CodeQL","quality / quality"],"strict":true}

That context no longer exists. This repo has been switched to GitHub's default CodeQL setup, which disables the advanced workflow-based scan. On this PR head the CodeQL run is dynamic/github-code-scanning/codeql ("PR #102"), and .github/workflows/codeql.yml did not run at all — so the checks are named CodeQL + Analyze (<lang>), with no codeql / prefix.

Evidence that this is pre-existing drift, not this branch:

  • On ci: exclude vendored/build-time tooling from CodeQL scanning #94 (the last merged PR, 2026-06-27) the contexts codeql / CodeQL and codeql / Resolve CodeQL Profile were present.
  • On main's own scans since 2026-07-14/21 the names are already Analyze (actions) / Analyze (javascript-typescript) / Analyze (python).
  • This branch changes no file under .github/.

Because codeql / CodeQL can never report, the combined commit status is stuck at pending (gh api .../status -> {"state":"pending","statuses":[]}), so every PR in this repo is permanently BLOCKED regardless of quality. The repository ruleset is already correct — it requires CodeQL, which passes; only the older branch protection entry is stale.

The fix (one line, owner-only)

Update branch protection's required contexts from codeql / CodeQL to the names default setup actually emits — CodeQL (matching the ruleset) — or re-enable advanced CodeQL setup so codeql / CodeQL returns.

I have deliberately not touched branch protection and have not merged with --admin. Handing the governance call to you.

One flake worth knowing about

gate-deps failed on the first attempt with:

Total 0 packages affected by 0 known vulnerabilities (0 Critical, 0 High, 0 Medium, 0 Low, 0 Unknown)
FAIL gate-deps: osv-scanner exited 127

preceded by failed resolution for .../backend/requirements.txt: rpc error: code = Unavailable desc = service unavailable.

Zero vulnerabilities, exit 127. For osv-scanner, 1 = vulnerabilities found; 127 = HasErrored. That failure was the upstream deps.dev PyPI resolver being down, not a security finding — the gate's message ("vulnerabilities found or scanner error") conflates the two. A plain re-run went green. If gate-deps goes red here again, check the exit code and grep for service unavailable before treating it as a real finding.

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.

1 participant