From 08d8506468ded357b7b1a2493dfad40fedb6758c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 1 Oct 2026 05:29:21 +0900 Subject: [PATCH 1/4] fix(security): patch document reader transitives --- ...1-noema-document-reader-transitive-cves.md | 8 ++++++ .../noema-document-reader/package-lock.json | 12 ++++---- ...ema_document_reader_dependency_security.py | 28 +++++++++++++++++++ 3 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 CHANGELOG.d/20261001-noema-document-reader-transitive-cves.md create mode 100644 tests/test_noema_document_reader_dependency_security.py diff --git a/CHANGELOG.d/20261001-noema-document-reader-transitive-cves.md b/CHANGELOG.d/20261001-noema-document-reader-transitive-cves.md new file mode 100644 index 0000000000..5cc44c3f1b --- /dev/null +++ b/CHANGELOG.d/20261001-noema-document-reader-transitive-cves.md @@ -0,0 +1,8 @@ +### Noema document reader transitive security updates + +- Updated the locked `fast-uri` dependency from 3.1.7 to 3.1.8 and + `ip-address` from 10.7.0 to 10.7.2. These are the first releases outside + the affected ranges for GHSA-hrr3-gc8f-f4qj, GHSA-j6r3-76f7-8jcv, and + GHSA-h3mg-xc3c-68pw. The direct dependency ranges are unchanged. +- Added a deterministic regression contract that rejects reintroduction of + either vulnerable transitive release. diff --git a/scripts/ci/noema-document-reader/package-lock.json b/scripts/ci/noema-document-reader/package-lock.json index 1026fd79a3..2a0b8215b0 100644 --- a/scripts/ci/noema-document-reader/package-lock.json +++ b/scripts/ci/noema-document-reader/package-lock.json @@ -458,9 +458,9 @@ "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.7.tgz", - "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.8.tgz", + "integrity": "sha512-GZMtZUTNRpOVIECoXwLNZS5xUGE+mVNbTB8h/7Rwh2TFWcBQiPzTgyZi05BF9UMZKkLJv8XBRJTlU7zg8+ZfMg==", "funding": [ { "type": "github", @@ -669,9 +669,9 @@ "license": "ISC" }, "node_modules/ip-address": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.0.tgz", - "integrity": "sha512-BGFsyJd5mpXp3rK6jIdADLNgpJUK1jnjzvYF8lK+VyDab9JAmqN0YOKDdP17HlgKb2+ehPgDc8EtnRLbGCAMhA==", + "version": "10.7.2", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.2.tgz", + "integrity": "sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==", "license": "MIT", "engines": { "node": ">= 12" diff --git a/tests/test_noema_document_reader_dependency_security.py b/tests/test_noema_document_reader_dependency_security.py new file mode 100644 index 0000000000..72b060a905 --- /dev/null +++ b/tests/test_noema_document_reader_dependency_security.py @@ -0,0 +1,28 @@ +"""Keep the Noema document reader above known vulnerable transitive releases.""" + +from __future__ import annotations + +import json +from pathlib import Path + + +LOCK_FILE = ( + Path(__file__).resolve().parents[1] + / "scripts" + / "ci" + / "noema-document-reader" + / "package-lock.json" +) + + +def _version_tuple(package_name: str) -> tuple[int, ...]: + """Return the numeric release tuple recorded for one locked package.""" + lock_data = json.loads(LOCK_FILE.read_text(encoding="utf-8")) + package_data = lock_data["packages"][f"node_modules/{package_name}"] + return tuple(int(part) for part in package_data["version"].split(".")) + + +def test_document_reader_transitives_include_security_fixes() -> None: + """Reject releases affected by the September 2026 URI and IP advisories.""" + assert _version_tuple("fast-uri") >= (3, 1, 8) + assert _version_tuple("ip-address") >= (10, 7, 2) From 29ebc1effc84d0915ba478bf49818b4c22c615c9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 1 Oct 2026 05:43:55 +0900 Subject: [PATCH 2/4] test(security): require Noema transitive source owner --- .../test_noema_document_reader_dependency_security.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_noema_document_reader_dependency_security.py b/tests/test_noema_document_reader_dependency_security.py index 72b060a905..cb124545be 100644 --- a/tests/test_noema_document_reader_dependency_security.py +++ b/tests/test_noema_document_reader_dependency_security.py @@ -13,6 +13,7 @@ / "noema-document-reader" / "package-lock.json" ) +PACKAGE_FILE = LOCK_FILE.with_name("package.json") def _version_tuple(package_name: str) -> tuple[int, ...]: @@ -26,3 +27,13 @@ def test_document_reader_transitives_include_security_fixes() -> None: """Reject releases affected by the September 2026 URI and IP advisories.""" assert _version_tuple("fast-uri") >= (3, 1, 8) assert _version_tuple("ip-address") >= (10, 7, 2) + + +def test_document_reader_source_owns_transitive_security_fixes() -> None: + """Require source overrides so lock regeneration preserves the repair.""" + package_data = json.loads(PACKAGE_FILE.read_text(encoding="utf-8")) + + assert package_data["overrides"] == { + "fast-uri": "3.1.8", + "ip-address": "10.7.2", + } From 91f20d2b42ad97dd9c3f0c95eaa084c7faf2cbf7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 1 Oct 2026 05:43:57 +0900 Subject: [PATCH 3/4] fix(security): own Noema transitive overrides --- scripts/ci/noema-document-reader/package.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/ci/noema-document-reader/package.json b/scripts/ci/noema-document-reader/package.json index aa4fc0d3ff..a565439215 100644 --- a/scripts/ci/noema-document-reader/package.json +++ b/scripts/ci/noema-document-reader/package.json @@ -5,5 +5,9 @@ "dependencies": { "@rhwp/core": "0.7.7", "hwp-mcp": "0.3.0" + }, + "overrides": { + "fast-uri": "3.1.8", + "ip-address": "10.7.2" } } From 9a4af5e438283a31dc05814d6bc2818caee782a3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 1 Oct 2026 05:53:37 +0900 Subject: [PATCH 4/4] fix(security): narrow transitive floor and scan nested locks --- ...1-noema-document-reader-transitive-cves.md | 4 +- .../noema-document-reader/package-lock.json | 6 +-- scripts/ci/noema-document-reader/package.json | 2 +- ...ema_document_reader_dependency_security.py | 40 +++++++++++++++---- 4 files changed, 38 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.d/20261001-noema-document-reader-transitive-cves.md b/CHANGELOG.d/20261001-noema-document-reader-transitive-cves.md index 5cc44c3f1b..4a19c4bb5e 100644 --- a/CHANGELOG.d/20261001-noema-document-reader-transitive-cves.md +++ b/CHANGELOG.d/20261001-noema-document-reader-transitive-cves.md @@ -1,8 +1,8 @@ ### Noema document reader transitive security updates - Updated the locked `fast-uri` dependency from 3.1.7 to 3.1.8 and - `ip-address` from 10.7.0 to 10.7.2. These are the first releases outside + `ip-address` from 10.7.0 to 10.7.1. These are the first releases outside the affected ranges for GHSA-hrr3-gc8f-f4qj, GHSA-j6r3-76f7-8jcv, and GHSA-h3mg-xc3c-68pw. The direct dependency ranges are unchanged. - Added a deterministic regression contract that rejects reintroduction of - either vulnerable transitive release. + vulnerable hoisted or nested copies of either transitive package. diff --git a/scripts/ci/noema-document-reader/package-lock.json b/scripts/ci/noema-document-reader/package-lock.json index 2a0b8215b0..fe692cdf0e 100644 --- a/scripts/ci/noema-document-reader/package-lock.json +++ b/scripts/ci/noema-document-reader/package-lock.json @@ -669,9 +669,9 @@ "license": "ISC" }, "node_modules/ip-address": { - "version": "10.7.2", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.2.tgz", - "integrity": "sha512-7H/2gFSIitxc0hG3nOI1glS8QLo/EHBFFLk8vEUjXY/xu0AdL8jZ9U1IzO2PUm0d2D/ofQcAifb0g6OBkt8U7w==", + "version": "10.7.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.7.1.tgz", + "integrity": "sha512-4OUAqU9Z1i3vCnS05hzGiFnEMDpQ+62pAD/MVQOp83fYyNC8GleCqaS0QikQBmcWCrKFiUs/B8ztRRiYOAXuCA==", "license": "MIT", "engines": { "node": ">= 12" diff --git a/scripts/ci/noema-document-reader/package.json b/scripts/ci/noema-document-reader/package.json index a565439215..2408b5854b 100644 --- a/scripts/ci/noema-document-reader/package.json +++ b/scripts/ci/noema-document-reader/package.json @@ -8,6 +8,6 @@ }, "overrides": { "fast-uri": "3.1.8", - "ip-address": "10.7.2" + "ip-address": "10.7.1" } } diff --git a/tests/test_noema_document_reader_dependency_security.py b/tests/test_noema_document_reader_dependency_security.py index cb124545be..77c28ae30c 100644 --- a/tests/test_noema_document_reader_dependency_security.py +++ b/tests/test_noema_document_reader_dependency_security.py @@ -16,17 +16,41 @@ PACKAGE_FILE = LOCK_FILE.with_name("package.json") -def _version_tuple(package_name: str) -> tuple[int, ...]: - """Return the numeric release tuple recorded for one locked package.""" - lock_data = json.loads(LOCK_FILE.read_text(encoding="utf-8")) - package_data = lock_data["packages"][f"node_modules/{package_name}"] - return tuple(int(part) for part in package_data["version"].split(".")) +def _locked_versions(lock_data: dict[str, object], package_name: str) -> list[tuple[int, ...]]: + """Return every hoisted or nested locked release for one package.""" + package_records = lock_data["packages"] + assert isinstance(package_records, dict) + path_suffix = f"node_modules/{package_name}" + release_versions: list[tuple[int, ...]] = [] + for package_path, package_data in package_records.items(): + if package_path != path_suffix and not package_path.endswith(f"/{path_suffix}"): + continue + assert isinstance(package_data, dict) + release_versions.append(tuple(int(part) for part in package_data["version"].split("."))) + return release_versions def test_document_reader_transitives_include_security_fixes() -> None: """Reject releases affected by the September 2026 URI and IP advisories.""" - assert _version_tuple("fast-uri") >= (3, 1, 8) - assert _version_tuple("ip-address") >= (10, 7, 2) + lock_data = json.loads(LOCK_FILE.read_text(encoding="utf-8")) + for package_name, minimum_version in { + "fast-uri": (3, 1, 8), + "ip-address": (10, 7, 1), + }.items(): + locked_versions = _locked_versions(lock_data, package_name) + assert locked_versions + assert all(version >= minimum_version for version in locked_versions) + + +def test_nested_vulnerable_transitive_is_detected() -> None: + """Do not let a patched hoisted package hide a vulnerable nested copy.""" + lock_data = { + "packages": { + "node_modules/ip-address": {"version": "10.7.1"}, + "node_modules/parent/node_modules/ip-address": {"version": "10.7.0"}, + } + } + assert _locked_versions(lock_data, "ip-address") == [(10, 7, 1), (10, 7, 0)] def test_document_reader_source_owns_transitive_security_fixes() -> None: @@ -35,5 +59,5 @@ def test_document_reader_source_owns_transitive_security_fixes() -> None: assert package_data["overrides"] == { "fast-uri": "3.1.8", - "ip-address": "10.7.2", + "ip-address": "10.7.1", }