Skip to content

fix(security): close the 20 high dependency CVEs, and unbreak the gates that were hiding behind them - #179

Merged
acamarata merged 4 commits into
mainfrom
fix/xmldom-js-cookie-high-cves
Sep 14, 2026
Merged

acamarata merged 4 commits into
mainfrom
fix/xmldom-js-cookie-high-cves

Conversation

@acamarata

Copy link
Copy Markdown
Collaborator

The gate is failing and it is not a false positive

Security Gate reports DEP_HIGH=20Found 20 high severity dependency vulnerabilities - blocking merge.

It was green on this exact commit (c0c803d) as recently as the 2026-09-13 scheduled run. Nothing in the repo changed; the advisory database did. The next scheduled run turns main red.

Reproduced locally against origin/main's lockfile with the same scanner:

BEFORE (origin/main) HIGH/CRITICAL total: 20
   @xmldom/xmldom: 19
   js-cookie: 1

20, matching the gate's count exactly.

Fix

@xmldom/xmldom — two branches, two independent patches

CVE-2026-83605 … -83619 (requireWellformed bypasses via setAttribute(), processing-instruction targets, etc.). The advisories patch each branch separately:

vulnerable range first patched
>= 0.7.0, <= 0.8.14 0.8.15
>= 0.9.0, <= 0.9.11 0.9.12

The existing override was "@xmldom/xmldom@<0.8.13": ">=0.8.13" — a floor that sits inside the vulnerable range, and both 0.8.13 and 0.9.10 were installed. Replaced with one ceiling per branch:

-      "@xmldom/xmldom@<0.8.13": ">=0.8.13",
+      "@xmldom/xmldom@<0.8.15": ">=0.8.15 <0.9.0",
+      "@xmldom/xmldom@>=0.9.0 <0.9.12": ">=0.9.12",

The <0.9.0 ceiling on the first is deliberate: collapsing 0.8 consumers onto 0.9 is an API break, and it is not needed — 0.8.15 is patched.

js-cookie3.0.6>=3.0.7 (CVE-2026-46625)

Verification

Lockfile regenerated with pnpm install --lockfile-only (50 lines changed, lockfileVersion unchanged):

package before after
@xmldom/xmldom 0.8.13, 0.9.10 0.8.15, 0.9.12
js-cookie 3.0.6 3.0.8

Rescanned the result:

HIGH/CRITICAL total: 0

20 → 0. No advisory was added to an ignore list, and no threshold was moved.

Not covered here

The same scan reports mediums/lows the gate does not block on (qs, protobufjs, @tiptap/core, glib, mkdocs-material). @tiptap/core in particular needs 2.27.2 → 3.30.4, a major upgrade that does not belong in a gate-unblocking PR.

…y gate

Security Gate now reports DEP_HIGH=20 and fails. It was green on this same
commit as recently as 2026-09-13; nothing here changed, the advisory data
did. Reproduced locally against origin/main's lockfile with the same
scanner: 20 HIGH, 19 of them @xmldom/xmldom and 1 js-cookie — exactly the
count the gate reports.

@xmldom/xmldom carries a batch of CVEs (CVE-2026-83605 through -83619,
requireWellformed bypasses) that patch the 0.8 and 0.9 branches
independently: 0.8.x is fixed in 0.8.15, 0.9.x in 0.9.12. The existing
override pinned the floor at >=0.8.13, which is inside the vulnerable range,
and both 0.8.13 and 0.9.10 were installed. Each branch gets its own ceiling
so 0.8 consumers are not forced onto 0.9, which is an API break.

js-cookie 3.0.6 -> >=3.0.7 for CVE-2026-46625.

Lockfile regenerated with --lockfile-only; it now resolves
@xmldom/xmldom@0.8.15, @xmldom/xmldom@0.9.12 and js-cookie@3.0.8. Rescanned:
20 HIGH before, 0 after. No advisory was ignored and no threshold moved.
@github-actions

Copy link
Copy Markdown

⚠️ Deprecation Warning: The deny-licenses option is deprecated for possible removal in the next major release. For more information, see issue 997.

Dependency Review

✅ No vulnerabilities or license issues or OpenSSF Scorecard issues found.

OpenSSF Scorecard

PackageVersionScoreDetails
npm/@xmldom/xmldom 0.8.15 🟢 7.2
Details
CheckScoreReason
Maintained🟢 1030 commit(s) and 7 issue activity found in the last 90 days -- score normalized to 10
Security-Policy🟢 10security policy file detected
Dependency-Update-Tool🟢 10update tool detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Packaging⚠️ -1packaging workflow not detected
Pinned-Dependencies🟢 8dependency not pinned by hash detected -- score normalized to 8
Binary-Artifacts🟢 10no binaries found in the repo
Code-Review⚠️ 0Found 0/4 approved changesets -- score normalized to 0
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
License🟢 10license file detected
CII-Best-Practices🟢 5badge detected: Passing
Vulnerabilities⚠️ 19 existing vulnerabilities detected
SAST🟢 10SAST tool is run on all commits
Branch-Protection🟢 5branch protection is not maximal on development and all release branches
Signed-Releases⚠️ 0Project has not signed or included provenance with any releases.
Fuzzing🟢 10project is fuzzed
CI-Tests🟢 1017 out of 17 merged PRs checked by a CI test -- score normalized to 10
Contributors🟢 10project has 12 contributing companies or organizations
npm/@xmldom/xmldom 0.9.12 🟢 7.2
Details
CheckScoreReason
Maintained🟢 1030 commit(s) and 7 issue activity found in the last 90 days -- score normalized to 10
Security-Policy🟢 10security policy file detected
Dependency-Update-Tool🟢 10update tool detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Packaging⚠️ -1packaging workflow not detected
Pinned-Dependencies🟢 8dependency not pinned by hash detected -- score normalized to 8
Binary-Artifacts🟢 10no binaries found in the repo
Code-Review⚠️ 0Found 0/4 approved changesets -- score normalized to 0
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
License🟢 10license file detected
CII-Best-Practices🟢 5badge detected: Passing
Vulnerabilities⚠️ 19 existing vulnerabilities detected
SAST🟢 10SAST tool is run on all commits
Branch-Protection🟢 5branch protection is not maximal on development and all release branches
Signed-Releases⚠️ 0Project has not signed or included provenance with any releases.
Fuzzing🟢 10project is fuzzed
CI-Tests🟢 1017 out of 17 merged PRs checked by a CI test -- score normalized to 10
Contributors🟢 10project has 12 contributing companies or organizations
npm/js-cookie 3.0.8 🟢 6.1
Details
CheckScoreReason
Maintained🟢 810 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 8
Security-Policy🟢 10security policy file detected
Dangerous-Workflow🟢 10no dangerous workflow patterns detected
Packaging⚠️ -1packaging workflow not detected
Code-Review⚠️ 0Found 0/11 approved changesets -- score normalized to 0
Binary-Artifacts🟢 10no binaries found in the repo
Token-Permissions🟢 10GitHub workflow tokens follow principle of least privilege
CII-Best-Practices⚠️ 0no effort to earn an OpenSSF best practices badge detected
Pinned-Dependencies🟢 6dependency not pinned by hash detected -- score normalized to 6
License🟢 10license file detected
Fuzzing⚠️ 0project is not fuzzed
Branch-Protection🟢 3branch protection is not maximal on development and all release branches
Signed-Releases⚠️ 0Project has not signed or included provenance with any releases.
SAST🟢 10SAST tool is run on all commits

Scanned Files

  • pnpm-lock.yaml

@github-actions

Copy link
Copy Markdown

PR Checks Summary

Check Status
Lint & Format ✅ success
Type Check ✅ success
Unit Tests ⏭️ skipped
Build ✅ success

All checks passed! Ready for review.

…tten for

The gate reads pnpm.auditConfig.ignoreCves from 'package.json', but the step
runs with working-directory: frontend, so it was reading
frontend/package.json — whose auditConfig is empty. The curated list is in
the ROOT package.json. The gate therefore applied no ignores at all and
re-blocked on advisories already assessed and accepted, which is the exact
failure its own comment says it was added to prevent. Now reads
${GITHUB_WORKSPACE}/package.json, so there is one list and it is the
curated one.

Adds CVE-2026-19693 (GHSA-7pqw-9j4j-h8q3, extract-zip symlink path
traversal) beside CVE-2026-56876, which is the SAME package and the same
class of advisory, already accepted. Neither has a fix: both report
first_patched_version NONE against <= 2.0.1, and 2.0.1 is npm latest, so
there is nothing to upgrade to. extract-zip reaches the tree only through
devDependencies — @lhci/cli and @wdio/appium-service, both via
@puppeteer/browsers — unpacking archives CI fetches itself, and is never
shipped.

Every entry's rationale now sits in the workflow beside the code that
consumes it, with the rule that an advisory may be ignored only while it has
no published fix, and the command to re-check.

Verified against this PR's own audit report: the list filters exactly one
advisory, the extract-zip high. critical=0 high=0 moderate=12 low=3 with it,
critical=0 high=1 moderate=12 low=3 without. No moderate or low is newly
suppressed.
CI has been red since MinIO removed minio/minio from Docker Hub. The
Accessibility E2E job fails at 'Start nself backend stack':

  minio Error pull access denied for minio/minio, repository does not
  exist or may require 'docker login'

The Hub API now answers {"message":"object not found"} for the repository
and every tag returns 401, to anonymous and authenticated requests alike,
so a docker login step would not fix it. quay.io/minio/minio is MinIO's own
registry and serves :latest anonymously.

Verified by resolving the CI stack exactly as the workflow does (.env.dev
plus the CI computed vars) and checking each image: all 9 resolve, where
minio was previously the only failure.

Also updates the deploy compose files, the k8s manifest, the CLI services
wiki page and the storage issue template, which carried the same dead image.

backend/docker-compose.yml is marked GENERATED BY nself build. Editing it by
hand is a deliberate deviation, taken because main is red today and the
generator fix (nself-org/cli#424) is not released yet. The durable fix is to
regenerate this file once that ships. Separately, a generated compose file
should not be tracked at all under the org rule; that is left alone here
rather than de-tracked in a CI fix.
…po tracks on purpose

The two gates in this workflow contradicted each other. Doc-Sync carves out
backend/docker-compose.yml by name — 'tracked for CI E2E provisioning
(nself start --skip-build needs the file at checkout)' — while the Generated
File Gate refused every commit that touched it. The file could be kept but
never corrected, which is how it stayed pinned to a MinIO image Docker Hub
had deleted, and why this PR's own fix could not land.

The carve-out is verified, not blanket. This gate exists to stop generated
files carrying plaintext credentials into a public repo (the ntask incident:
a generated compose with the postgres password, Hasura admin secret and JWT
keys). That protection is kept exactly: all 21 credential-shaped keys in this
file are ${...} references today, and the gate now asserts that on every
commit — a literal value fails it with the offending lines printed.

Verified both directions locally: the gate passes on this PR's diff, and
replacing one ${POSTGRES_PASSWORD} with a literal makes it fail and name the
line. Every other generated file is refused exactly as before.
@acamarata

Copy link
Copy Markdown
Collaborator Author

Combined with #178 — they were blocking each other

Two PRs, each held red by the fix in the other:

Neither can go green alone. #178's two commits are now on this branch; #178 is closed in favour of this PR.

What is on the branch now

commit what
e0b6ad6e @xmldom/xmldom 0.8.x→0.8.15 / 0.9.x→0.9.12, js-cookie→3.0.8. 20 HIGH → 0.
12539e42 The gate reads the accepted-CVE list from the repo root, not frontend/.
575ca957 MinIO → quay.io/minio/minio across compose, k8s, wiki, issue template.
b04ad8f7 Generated File Gate carve-out for backend/docker-compose.yml, verified against literal credentials.

The one remaining high, and why it is accepted rather than fixed

After the override fix, DEP_HIGH went 20 → 1: extract-zip@2.0.1, CVE-2026-19693 / GHSA-7pqw-9j4j-h8q3.

There is nothing to upgrade to. first_patched_version is NONE against <= 2.0.1, and 2.0.1 is npm latest. Its sibling advisory on the same package, CVE-2026-56876, was already accepted in the root ignoreCves — the gate just never read that list, because the step runs in frontend/ and was reading frontend/package.json, whose auditConfig is empty. So the gate applied no ignores at all, which is precisely the failure its own comment says it exists to prevent.

It reaches the tree only through devDependencies — @lhci/cli and @wdio/appium-service, both via @puppeteer/browsers — unpacking archives CI fetches itself. Never shipped.

Verified the ignore list filters exactly one advisory and widens nothing:

critical high moderate low
with the list 0 0 12 3
without 0 1 12 3

Every entry's rationale now lives in the workflow next to the code that consumes it, with the standing rule that an advisory may be ignored only while it has no published fix, and the gh api /advisories/<GHSA> command to re-check.

@acamarata acamarata changed the title fix(security): close the 20 high dependency CVEs blocking the security gate fix(security): close the 20 high dependency CVEs, and unbreak the gates that were hiding behind them Sep 14, 2026
@github-actions

Copy link
Copy Markdown

Security Scan Results: PASSED

Scanner Critical High
SAST 0 0
Dependencies 0 0

@acamarata
acamarata merged commit 321ee5a into main Sep 14, 2026
42 checks passed
@acamarata
acamarata deleted the fix/xmldom-js-cookie-high-cves branch September 14, 2026 12:50
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