Keep the audit process owner in strict delivery checks

Both required CI and the local gate must treat the new subprocess owner as audit runtime, not an unchanged dependency graph. Carry the actual helper into the isolated local-gate fixture and test positive findings, missing results, large change lists and exact-path lookalikes through both consumers.

Contract-Neutral: Extend the existing strict audit-runtime path classification to its new owned helper; advisory rules, dependency graphs, public runtime, credentials and external permissions are unchanged.
Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot]
2026-10-13 05:37:14 +01:00
parent 25c811bad7
commit e41ce241ec
4 changed files with 28 additions and 9 deletions
+1 -1
View File
@@ -129,7 +129,7 @@ jobs:
# Consume the full list: grep -q can close the pipe on an early
# match, making printf fail with SIGPIPE under pipefail. A large
# dependency change must not enter the inherited-advisory mode.
if printf '%s\n' "$changed" | grep -E '^frontend-modern/package(-lock)?\.json$|^scripts/npm-audit-retry\.sh$' >/dev/null; then
if printf '%s\n' "$changed" | grep -E '^frontend-modern/package(-lock)?\.json$|^scripts/(npm-audit-retry\.sh|npm_audit_process\.py)$' >/dev/null; then
frontend_deps=true
else
frontend_deps=false
+1 -1
View File
@@ -57,7 +57,7 @@ CHANGED_GO=$(printf '%s\n' "$CHANGED" | grep -E '\.go$' || true)
# Unchanged graphs are left to CI's existing inherited-finding warning policy.
# Consume the whole list rather than using grep -q: under pipefail an early
# match in a large diff must not turn the producer's SIGPIPE into a false skip.
if printf '%s\n' "$CHANGED" | grep -E '^frontend-modern/package(-lock)?\.json$|^scripts/npm-audit-retry\.sh$' >/dev/null; then
if printf '%s\n' "$CHANGED" | grep -E '^frontend-modern/package(-lock)?\.json$|^scripts/(npm-audit-retry\.sh|npm_audit_process\.py)$' >/dev/null; then
step "Frontend dependency audit (changed graph or audit runner)"
if ! (cd frontend-modern && NPM_AUDIT_REQUIRE_RESULT=true bash "$ROOT_DIR/scripts/npm-audit-retry.sh" all); then
echo "Frontend dependency audit failed; remaining source checks have not run." >&2
@@ -5154,19 +5154,23 @@ func TestFrontendChangeClassificationPreservesStrictAudit(t *testing.T) {
{name: "manifest", files: "frontend-modern/package.json\n", code: "true", dependencies: "true"},
{name: "lock", files: "frontend-modern/package-lock.json\n", code: "true", dependencies: "true"},
{name: "audit_runner", files: "scripts/npm-audit-retry.sh\n", code: "true", dependencies: "true"},
{name: "audit_process", files: "scripts/npm_audit_process.py\n", code: "true", dependencies: "true"},
{name: "large_manifest_first", files: "frontend-modern/package.json\n" + docs, code: "true", dependencies: "true"},
{name: "large_lock_first", files: "frontend-modern/package-lock.json\n" + docs, code: "true", dependencies: "true"},
{name: "large_runner_first", files: "scripts/npm-audit-retry.sh\n" + docs, code: "true", dependencies: "true"},
{name: "large_process_first", files: "scripts/npm_audit_process.py\n" + docs, code: "true", dependencies: "true"},
{name: "large_process_last", files: docs + "scripts/npm_audit_process.py\n", code: "true", dependencies: "true"},
{name: "large_lock_last", files: docs + "frontend-modern/package-lock.json\n", code: "true", dependencies: "true"},
{name: "large_docs_only", files: docs, code: "false", dependencies: "false"},
{name: "large_unrelated_code", goCompile: true, files: docs + "internal/config/config.go\n", code: "true", dependencies: "false"},
{name: "exact_path_lookalikes", files: "frontend-modern/package.json.extra\nother/frontend-modern/package-lock.json\nscripts/npm-audit-retry.sh.extra\n", code: "true", dependencies: "false"},
{name: "exact_path_lookalikes", files: "frontend-modern/package.json.extra\nother/frontend-modern/package-lock.json\nscripts/npm-audit-retry.sh.extra\nscripts/npm_audit_process.py.extra\n", code: "true", dependencies: "false"},
{name: "pull_request_docs", files: "docs/FAQ.md\n", event: "pull_request", code: "false", dependencies: "false"},
{name: "zero_push_base", goCompile: true, base: strings.Repeat("0", 40), code: "true", dependencies: "true"},
{name: "missing_base", goCompile: true, missingBase: true, code: "true", dependencies: "true"},
{name: "manual_dispatch", goCompile: true, event: "workflow_dispatch", base: "empty", code: "true", dependencies: "true"},
{name: "failed_diff", diffStatus: 42},
{name: "large_changed_graph_critical_blocks", files: "frontend-modern/package-lock.json\n" + docs, code: "true", dependencies: "true", audit: true, auditStatus: 1},
{name: "process_critical_blocks", files: "scripts/npm_audit_process.py\n", code: "true", dependencies: "true", audit: true, auditStatus: 1},
{name: "unchanged_graph_critical_warns", goCompile: true, files: "internal/config/config.go\n", code: "true", dependencies: "false", audit: true, auditStatus: 0},
}
for _, tc := range cases {
+21 -6
View File
@@ -16,6 +16,7 @@ import yaml
ROOT = Path(__file__).resolve().parents[2]
PREPUSH = ROOT / "scripts/dev-prepush.sh"
AUDIT = ROOT / "scripts/npm-audit-retry.sh"
AUDIT_PROCESS = ROOT / "scripts/npm_audit_process.py"
class DevPrepushFixture(unittest.TestCase):
@@ -53,6 +54,7 @@ class DevPrepushFixture(unittest.TestCase):
)
self.write("scripts/dev-prepush.sh", PREPUSH.read_text())
self.write("scripts/npm-audit-retry.sh", AUDIT.read_text())
self.write("scripts/npm_audit_process.py", AUDIT_PROCESS.read_text())
self.write("frontend-modern/package.json", '{"private":true}\n')
self.write("frontend-modern/package-lock.json", '{"lockfileVersion":3}\n')
self.write("frontend-modern/src/example.ts", "export const value = 1;\n")
@@ -170,6 +172,17 @@ class DevPrepushFrontendTest(DevPrepushFixture):
self.env["PREPUSH_AUDIT_MODE"] = "unavailable"
self.assert_refused_before_source_checks(*self.run_gate(), reads=2)
def test_changed_audit_process_requires_a_result(self):
self.change("scripts/npm_audit_process.py")
self.env["PREPUSH_AUDIT_MODE"] = "unavailable"
self.assert_refused_before_source_checks(*self.run_gate(), reads=2)
def test_changed_audit_process_cannot_relax_positive_findings(self):
self.change("scripts/npm_audit_process.py")
self.env["PREPUSH_AUDIT_MODE"] = "critical"
self.env["NPM_AUDIT_REQUIRE_RESULT"] = "false"
self.assert_refused_before_source_checks(*self.run_gate())
def test_failed_zero_report_is_not_a_passing_local_gate(self):
self.change("frontend-modern/package-lock.json")
self.env["PREPUSH_AUDIT_MODE"] = "zero-failure"
@@ -251,16 +264,18 @@ class DevPrepushFrontendTest(DevPrepushFixture):
self.assertEqual(guards, [])
def test_audit_path_lookalike_does_not_query(self):
self.change("scripts/npm-audit-retry.sh.extra")
result, calls, guards = self.run_gate(dependencies=False)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertEqual(calls, [])
self.assertEqual(len(guards), 1)
for path in ("scripts/npm-audit-retry.sh.extra", "scripts/npm_audit_process.py.extra"):
with self.subTest(path=path):
self.change(path)
result, calls, guards = self.run_gate(dependencies=False)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.assertEqual(calls, [])
self.assertEqual(len(guards), 1 if path.endswith(".sh.extra") else 2)
def test_local_strict_paths_match_ci(self):
workflow = yaml.safe_load((ROOT / ".github/workflows/build-and-test.yml").read_text())
classification = next(step for step in workflow["jobs"]["changes"]["steps"] if step.get("id") == "filter")["run"]
paths = r"^frontend-modern/package(-lock)?\.json$|^scripts/npm-audit-retry\.sh$"
paths = r"^frontend-modern/package(-lock)?\.json$|^scripts/(npm-audit-retry\.sh|npm_audit_process\.py)$"
self.assertIn(paths, classification)
self.assertIn(paths, PREPUSH.read_text())