Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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.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
vulnerable hoisted or nested copies of either transitive package.
12 changes: 6 additions & 6 deletions scripts/ci/noema-document-reader/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions scripts/ci/noema-document-reader/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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.1"
}
}
63 changes: 63 additions & 0 deletions tests/test_noema_document_reader_dependency_security.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
"""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"
)
PACKAGE_FILE = LOCK_FILE.with_name("package.json")


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."""
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:
"""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.1",
}
Loading