From c47a29b87c8035af77acb093a6dfd3a8b97dbe91 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 27 Sep 2026 20:19:37 +0900 Subject: [PATCH 1/6] fix(ci): support gh pagination without newer slurp flag --- .github/workflows/codeql-pr.yml | 4 ++-- .github/workflows/codeql-scan-dispatch.yml | 6 +++--- tests/test_codeql_pr_workflow_contract.py | 21 ++++++++----------- ..._codeql_scan_dispatch_workflow_contract.py | 9 ++++---- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/.github/workflows/codeql-pr.yml b/.github/workflows/codeql-pr.yml index 95f1ecc288..5ab511b028 100644 --- a/.github/workflows/codeql-pr.yml +++ b/.github/workflows/codeql-pr.yml @@ -250,7 +250,7 @@ jobs: echo "::error::Could not validate required run creation time before CodeQL verdict lookup." exit 1 fi - runs_json="$(gh api --method GET --paginate --slurp -f per_page=100 -f event=repository_dispatch -f created=">=${required_created_at}" "repos/ContextualWisdomLab/.github/actions/workflows/codeql-scan-dispatch.yml/runs")" + runs_json="$(gh api --method GET --paginate -f per_page=100 -f event=repository_dispatch -f created=">=${required_created_at}" "repos/ContextualWisdomLab/.github/actions/workflows/codeql-scan-dispatch.yml/runs" | jq -s .)" run_id="$(printf '%s' "$runs_json" | jq -r --arg title "$expected_title" --arg path ".github/workflows/codeql-scan-dispatch.yml" ' [ .[] | .workflow_runs[] @@ -263,7 +263,7 @@ jobs: | .id // empty ')" if [[ "$run_id" =~ ^[1-9][0-9]*$ ]]; then - jobs_json="$(gh api --paginate --slurp "repos/ContextualWisdomLab/.github/actions/runs/${run_id}/jobs")" + jobs_json="$(gh api --paginate "repos/ContextualWisdomLab/.github/actions/runs/${run_id}/jobs" | jq -s .)" dispatch_job="$(printf '%s' "$jobs_json" | jq -c --arg name "$expected_job" ' [.[] | .jobs[] | select(.name == $name)] | if length == 1 then .[0] else empty end diff --git a/.github/workflows/codeql-scan-dispatch.yml b/.github/workflows/codeql-scan-dispatch.yml index 9c02817198..04656e5b34 100644 --- a/.github/workflows/codeql-scan-dispatch.yml +++ b/.github/workflows/codeql-scan-dispatch.yml @@ -1057,7 +1057,7 @@ jobs: exit 1 fi - if ! required_job_pages="$(github_api --paginate --slurp "repos/${TARGET_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}/jobs?per_page=100")"; then + if ! required_job_pages="$(github_api --paginate "repos/${TARGET_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}/jobs?per_page=100" | jq -s .)"; then echo "::error::CodeQL settlement could not read the required jobs." exit 1 fi @@ -1094,8 +1094,8 @@ jobs: exit 1 fi - if ! handler_job_pages="$(GH_TOKEN="$HANDLER_READ_TOKEN" gh api --paginate --slurp "repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/jobs?per_page=100")" || - ! handler_artifact_pages="$(GH_TOKEN="$HANDLER_READ_TOKEN" gh api --paginate --slurp "repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/artifacts?per_page=100")"; then + if ! handler_job_pages="$(GH_TOKEN="$HANDLER_READ_TOKEN" gh api --paginate "repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/jobs?per_page=100" | jq -s .)" || + ! handler_artifact_pages="$(GH_TOKEN="$HANDLER_READ_TOKEN" gh api --paginate "repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/artifacts?per_page=100" | jq -s .)"; then echo "::error::CodeQL settlement could not read exact handler evidence." exit 1 fi diff --git a/tests/test_codeql_pr_workflow_contract.py b/tests/test_codeql_pr_workflow_contract.py index 977a47deed..65c739015a 100644 --- a/tests/test_codeql_pr_workflow_contract.py +++ b/tests/test_codeql_pr_workflow_contract.py @@ -248,6 +248,7 @@ def _run_verdict_read( 'test "$1" = api\n' 'if [[ "$*" == *"/actions/runs/42 --jq .created_at" ]]; then printf \'%s\\n\' "$FAKE_REQUIRED_CREATED_AT"; exit 0; fi\n' 'endpoint="${@: -1}"\n' + 'if printf \'%s\\n\' "$@" | grep -qx -- --slurp; then exit 2; fi\n' 'case "$endpoint" in\n' " */pulls/*) printf '%s\\n' \"$FAKE_PULL_JSON\" ;;\n" " */compare/*) printf '%s\\n' \"$FAKE_COMPARE_JSON\" ;;\n" @@ -270,16 +271,12 @@ def _run_verdict_read( "status": "ahead", "behind_by": 0, "ahead_by": 1, }), "FAKE_STATUSES_JSON": json.dumps(statuses), - "FAKE_DISPATCH_RUNS_JSON": json.dumps( - dispatch_runs - if isinstance(dispatch_runs, list) - else [dispatch_runs if dispatch_runs is not None else {"workflow_runs": []}] - ), - "FAKE_DISPATCH_JOBS_JSON": json.dumps( - dispatch_jobs - if isinstance(dispatch_jobs, list) - else [dispatch_jobs if dispatch_jobs is not None else {"jobs": []}] - ), + "FAKE_DISPATCH_RUNS_JSON": "\n".join(map(json.dumps, + dispatch_runs if isinstance(dispatch_runs, list) + else [dispatch_runs if dispatch_runs is not None else {"workflow_runs": []}])), + "FAKE_DISPATCH_JOBS_JSON": "\n".join(map(json.dumps, + dispatch_jobs if isinstance(dispatch_jobs, list) + else [dispatch_jobs if dispatch_jobs is not None else {"jobs": []}])), "GH_TOKEN": "fake-token", "TARGET_REPOSITORY": "ContextualWisdomLab/naruon", "PR_NUMBER": "42", @@ -748,8 +745,8 @@ def test_codeql_pr_attempt_one_without_verdict_fails_pending_without_dispatch( } ), "FAKE_STATUSES_JSON": json.dumps([]), - "FAKE_DISPATCH_RUNS_JSON": json.dumps([{"workflow_runs": []}]), - "FAKE_DISPATCH_JOBS_JSON": json.dumps([{"jobs": []}]), + "FAKE_DISPATCH_RUNS_JSON": json.dumps({"workflow_runs": []}), + "FAKE_DISPATCH_JOBS_JSON": json.dumps({"jobs": []}), "FAKE_REQUIRED_CREATED_AT": "2026-09-27T11:08:00Z", "FAKE_POST_LOG": str(post_log), "GH_TOKEN": "fake-token", diff --git a/tests/test_codeql_scan_dispatch_workflow_contract.py b/tests/test_codeql_scan_dispatch_workflow_contract.py index 290211e85d..74c01d7989 100644 --- a/tests/test_codeql_scan_dispatch_workflow_contract.py +++ b/tests/test_codeql_scan_dispatch_workflow_contract.py @@ -1055,7 +1055,7 @@ def test_dispatch_settles_all_languages_with_one_run_wide_mutation() -> None: assert "actions: write" in settlement.split(" steps:\n", 1)[0] assert 'github_api "repos/${TARGET_REPOSITORY}/pulls/${PR_NUMBER}"' in settlement assert 'github_api "repos/${TARGET_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}"' in settlement - assert 'github_api --paginate --slurp "repos/${TARGET_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}/jobs?per_page=100"' in settlement + assert 'github_api --paginate "repos/${TARGET_REPOSITORY}/actions/runs/${REQUIRED_RUN_ID}/jobs?per_page=100" | jq -s .' in settlement assert "rerun-failed-jobs" in settlement assert '"rerun"' in settlement assert "actions/jobs/${REQUIRED_JOB_ID}/rerun" not in workflow @@ -1189,6 +1189,7 @@ def _run_settlement_step( "set -euo pipefail\n" 'test "$1" = api\n' 'endpoint="${!#}"\n' + 'if printf \'%s\\n\' "$@" | grep -qx -- --slurp; then exit 2; fi\n' 'if printf \'%s\\n\' "$@" | grep -qx POST; then\n' ' printf \'%s\\n\' "$endpoint" >>"$FAKE_POST_LOG"\n' ' if [ -n "${FAKE_WAKE_POST_FAIL_TOKEN:-}" ] && ' @@ -1226,10 +1227,10 @@ def _run_settlement_step( "PATH": f"{fake_bin}:{os.environ['PATH']}", "FAKE_PULL_JSON": json.dumps(pull), "FAKE_RUN_JSON": json.dumps(run), - "FAKE_REQUIRED_JOB_PAGES": json.dumps([{"jobs": required_jobs}]), - "FAKE_HANDLER_JOB_PAGES": json.dumps([{"jobs": handler_jobs}]), + "FAKE_REQUIRED_JOB_PAGES": "\n".join(json.dumps({"jobs": [job]}) for job in required_jobs), + "FAKE_HANDLER_JOB_PAGES": json.dumps({"jobs": handler_jobs}), "FAKE_HANDLER_ARTIFACT_PAGES": json.dumps( - [{"artifacts": handler_artifacts}] + {"artifacts": handler_artifacts} ), "FAKE_POST_LOG": str(post_log), "FAKE_POST_EXIT": "0", From 46b622e929c4656075e625bd25a1bde7d4b31b32 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 27 Sep 2026 19:29:53 +0900 Subject: [PATCH 2/6] fix(rust): bound head lock intake to validated base dependency records --- .../ci/materialize_base_rust_dependencies.py | 215 ++++++++++++++++-- .../test_materialize_rust_head_lock_intake.py | 128 +++++++++++ 2 files changed, 329 insertions(+), 14 deletions(-) create mode 100644 tests/test_materialize_rust_head_lock_intake.py diff --git a/scripts/ci/materialize_base_rust_dependencies.py b/scripts/ci/materialize_base_rust_dependencies.py index ef946f42ae..115c8ac02d 100644 --- a/scripts/ci/materialize_base_rust_dependencies.py +++ b/scripts/ci/materialize_base_rust_dependencies.py @@ -19,6 +19,12 @@ manifests are read (never the pull request's), and vendoring itself uses Cargo's own built-in per-package checksum verification (every ``[[package]]`` entry in a lock file carries a ``checksum``), so no separate hash-pin parser is needed the way ``requirements*.txt`` needed one. + +An explicit ``--head-sha`` opts into a narrower lock-repair intake: every Cargo manifest +remains byte-identical to base, every registry record (including resolved dependency edges) +must already occur in the base lock union, and local identities must already be base-pinned. +Only lock bytes are overlaid; Cargo vendor --locked still verifies the unchanged manifests +and package checksums. Separate provenance records the base manifests and head lock blobs. """ from __future__ import annotations @@ -120,7 +126,9 @@ def _select_vendor_roots( for path in cargo_paths if path.endswith("Cargo.lock") } - for manifest_path in sorted(path for path in cargo_paths if path.endswith("Cargo.toml")): + for manifest_path in sorted( + path for path in cargo_paths if path.endswith("Cargo.toml") + ): content = _git(repo_root, "show", f"{base_sha}:{manifest_path}") if not _is_workspace_manifest(content): continue @@ -131,7 +139,9 @@ def _select_vendor_roots( ) for lock_dir in sorted(locks): if lock_dir not in manifests: - raise RuntimeError(f"base Cargo.lock at {lock_dir} has no sibling Cargo.toml") + raise RuntimeError( + f"base Cargo.lock at {lock_dir} has no sibling Cargo.toml" + ) if not locks: return [] # Deterministic order with the repository root first when it is one of the roots, @@ -156,10 +166,16 @@ def _placeholder_target_paths(manifest_content: bytes) -> list[str]: if "package" not in parsed: return [] paths = {"src/lib.rs", "src/main.rs"} - lib_path = parsed.get("lib", {}).get("path") if isinstance(parsed.get("lib"), dict) else None + lib_path = ( + parsed.get("lib", {}).get("path") + if isinstance(parsed.get("lib"), dict) + else None + ) if isinstance(lib_path, str): paths.add(lib_path) - for bin_target in parsed.get("bin", []) if isinstance(parsed.get("bin"), list) else []: + for bin_target in ( + parsed.get("bin", []) if isinstance(parsed.get("bin"), list) else [] + ): bin_path = bin_target.get("path") if isinstance(bin_target, dict) else None if isinstance(bin_path, str): paths.add(bin_path) @@ -167,7 +183,10 @@ def _placeholder_target_paths(manifest_content: bytes) -> list[str]: def _reconstruct_base_tree( - repo_root: pathlib.Path, base_sha: str, cargo_paths: list[str], work_dir: pathlib.Path + repo_root: pathlib.Path, + base_sha: str, + cargo_paths: list[str], + work_dir: pathlib.Path, ) -> None: """Write every tracked base Cargo manifest into ``work_dir`` at its repository path. @@ -182,7 +201,10 @@ def _reconstruct_base_tree( if destination.name == "Cargo.toml": for target_path in _placeholder_target_paths(content): target_relative_path = pathlib.PurePosixPath(target_path) - if target_relative_path.is_absolute() or ".." in target_relative_path.parts: + if ( + target_relative_path.is_absolute() + or ".." in target_relative_path.parts + ): raise RuntimeError( "Cargo target path must stay inside its manifest root: " f"{target_path}" @@ -227,14 +249,150 @@ def _run_cargo_vendor( ) +def _normalized_lock_records(content: bytes) -> list[dict]: + """Compare bounded lock records with uniquely resolved dependency identities.""" + parsed = tomllib.loads(content.decode("utf-8")) + packages = parsed.get("package") + if ( + set(parsed) - {"version", "package"} + or parsed.get("version") not in {3, 4} + or not isinstance(packages, list) + or len(packages) > 10000 + ): + raise ValueError("head intake requires a bounded version 3/4 Cargo lock") + identities = {} + by_name = {} + for package in packages: + if not isinstance(package, dict): + raise ValueError("invalid Cargo lock package") + source = package.get("source") + allowed = {"name", "version", "dependencies"} + if source is not None: + allowed |= {"source", "checksum"} + if ( + source != "registry+https://github.com/rust-lang/crates.io-index" + or not isinstance(package.get("checksum"), str) + or not re.fullmatch(r"[0-9a-f]{64}", package["checksum"]) + ): + raise ValueError( + "head intake requires existing crates.io checksum pins" + ) + if set(package) - allowed or any( + not isinstance(package.get(k), str) or not package[k] + for k in ("name", "version") + ): + raise ValueError("unsupported Cargo lock package fields") + identity = (package["name"], package["version"], source) + if identity in identities: + raise ValueError("duplicate Cargo lock package identity") + identities[identity] = package + by_name.setdefault(identity[0], []).append(identity) + records = [] + for package in packages: + dependencies = package.get("dependencies", []) + if not isinstance(dependencies, list) or len(dependencies) > 10000: + raise ValueError("invalid Cargo lock dependencies") + edges = [] + for dependency in dependencies: + if not isinstance(dependency, str): + raise ValueError("invalid Cargo lock dependency identity") + parts = dependency.split() + if not 1 <= len(parts) <= 3: + raise ValueError("unsupported Cargo lock dependency identity") + candidates = [ + identity + for identity in by_name.get(parts[0], []) + if (len(parts) < 2 or identity[1] == parts[1]) + and (len(parts) < 3 or f"({identity[2]})" == parts[2]) + ] + if len(candidates) != 1: + raise ValueError("missing or ambiguous Cargo lock dependency identity") + edges.append(candidates[0]) + if len(edges) != len(set(edges)): + raise ValueError("duplicate Cargo lock dependency edge") + records.append({**package, "dependencies": sorted(edges, key=repr)}) + return records + + +def _validated_head_locks( + repo_root: pathlib.Path, base_sha: str, head_sha: str, cargo_paths: list[str] +) -> tuple[dict[str, bytes], dict]: + """Allow head locks only to recombine records pinned in the base lock union. + + Manifest bytes remain from base. Local-package closure still requires the + unchanged base manifests to pass Cargo vendor --locked; this function does + not authorize new registry records, git sources or package checksums. + """ + if not SHA_RE.fullmatch(head_sha): + raise ValueError("head SHA must be exactly 40 hexadecimal characters") + head_paths = _regular_cargo_blob_paths(repo_root, head_sha) + if head_paths != cargo_paths: + raise ValueError("head Cargo manifest/lock paths must equal base") + changed = _git(repo_root, "diff", "--name-only", "-z", base_sha, head_sha).split( + b"\0" + ) + if any( + path and pathlib.PurePosixPath(path.decode()).name == "Cargo.toml" + for path in changed + ): + raise ValueError("head Cargo manifests must remain byte-identical to base") + + def lock_bytes(revision: str, path: str) -> bytes: + size = int(_git(repo_root, "cat-file", "-s", f"{revision}:{path}")) + if size > 16 * 1024 * 1024: + raise ValueError("Cargo lock exceeds bounded size") + return _git(repo_root, "show", f"{revision}:{path}") + + paths = [path for path in cargo_paths if path.endswith("Cargo.lock")] + base_records = [] + for path in paths: + base_records.extend(_normalized_lock_records(lock_bytes(base_sha, path))) + registry_records = { + json.dumps(row, sort_keys=True) for row in base_records if row.get("source") + } + local_identities = { + (row["name"], row["version"]) for row in base_records if not row.get("source") + } + locks = {} + receipts = [] + for path in paths: + content = lock_bytes(head_sha, path) + for row in _normalized_lock_records(content): + if row.get("source"): + if json.dumps(row, sort_keys=True) not in registry_records: + raise ValueError( + "head registry record differs from base lock union" + ) + elif (row["name"], row["version"]) not in local_identities: + raise ValueError("head local identity differs from base lock union") + locks[path] = content + receipts.append( + { + "path": path, + "base_lock_blob": _git(repo_root, "rev-parse", f"{base_sha}:{path}") + .decode() + .strip(), + "lock_blob": _git(repo_root, "rev-parse", f"{head_sha}:{path}") + .decode() + .strip(), + } + ) + return locks, { + "manifest_revision": base_sha.lower(), + "lock_revision": head_sha.lower(), + "locks": receipts, + } + + def materialize( repo_root: pathlib.Path, base_sha: str, output_dir: pathlib.Path, *, vendor_dir_for_config: str | None = None, + head_sha: str | None = None, ) -> list[str]: - """Vendor the base commit's Cargo dependency closure into ``output_dir``. + """Vendor base manifests with base locks or explicitly bounded head locks. Returns the list of source-tree-relative ``Cargo.lock`` paths that were vendored. An empty list means no Rust project (or no lock file) exists at the base commit, which is not an @@ -256,6 +414,11 @@ def materialize( cargo_paths = _regular_cargo_blob_paths(resolved_repo, base_sha) vendor_roots = _select_vendor_roots(resolved_repo, base_sha, cargo_paths) manifest: list[str] = [] + head_locks, provenance = ( + _validated_head_locks(resolved_repo, base_sha, head_sha, cargo_paths) + if head_sha is not None + else ({}, None) + ) if vendor_roots: primary_root, *additional_roots = vendor_roots @@ -268,8 +431,12 @@ def _lock_for(root: str) -> str: with tempfile.TemporaryDirectory() as work_dir: work_path = pathlib.Path(work_dir) _reconstruct_base_tree(resolved_repo, base_sha, cargo_paths, work_path) + for path, content in head_locks.items(): + (work_path / path).write_bytes(content) manifest_path = _manifest_for(primary_root, work_path) - sync_manifests = [_manifest_for(root, work_path) for root in additional_roots] + sync_manifests = [ + _manifest_for(root, work_path) for root in additional_roots + ] # Every root's lock is reported, so a failure names the whole vendored set # rather than only the primary root. lock_path = ", ".join(_lock_for(root) for root in vendor_roots) @@ -284,10 +451,14 @@ def _lock_for(root: str) -> str: if completed.returncode != 0: stderr = completed.stderr.decode("utf-8", errors="replace") normalized_stderr = " ".join(stderr.split()) - detail = normalized_stderr[:500] if normalized_stderr else ( - f"exit status {completed.returncode}" + detail = ( + normalized_stderr[:500] + if normalized_stderr + else (f"exit status {completed.returncode}") + ) + raise RuntimeError( + f"cargo vendor failed for base lock {lock_path}: {detail}" ) - raise RuntimeError(f"cargo vendor failed for base lock {lock_path}: {detail}") config_text = completed.stdout if vendor_dir_for_config is not None: config_text = config_text.replace( @@ -297,6 +468,10 @@ def _lock_for(root: str) -> str: (output_dir / "cargo-config.toml").write_bytes(config_text) manifest = [_lock_for(root) for root in vendor_roots] + if provenance is not None: + (output_dir / "lock-provenance.json").write_text( + json.dumps(provenance, indent=2) + "\n" + ) (output_dir / "manifest.json").write_text( json.dumps(manifest, indent=2, sort_keys=True) + "\n", encoding="utf-8", @@ -309,6 +484,7 @@ def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser() parser.add_argument("--repo-root", required=True, type=pathlib.Path) parser.add_argument("--base-sha", required=True) + parser.add_argument("--head-sha", default=None) parser.add_argument("--output-dir", required=True, type=pathlib.Path) parser.add_argument("--vendor-dir-for-config", default=None) args = parser.parse_args(argv) @@ -319,17 +495,28 @@ def main(argv: list[str] | None = None) -> int: args.base_sha, args.output_dir, vendor_dir_for_config=args.vendor_dir_for_config, + head_sha=args.head_sha, ) except (OSError, RuntimeError, ValueError) as exc: print( - f"::error::Could not materialize base Rust dependencies: {exc}", file=sys.stderr + f"::error::Could not materialize base Rust dependencies: {exc}", + file=sys.stderr, ) return 1 if manifest: - print(f"Materialized trusted base Cargo vendor directory from {manifest[0]}.") + if args.head_sha: + print( + f"Materialized Cargo vendor directory from base manifests and bounded head locks: {manifest[0]}." + ) + else: + print( + f"Materialized trusted base Cargo vendor directory from {manifest[0]}." + ) else: - print("No tracked Cargo.lock exists at the validated base SHA; Rust vendoring skipped.") + print( + "No tracked Cargo.lock exists at the validated base SHA; Rust vendoring skipped." + ) return 0 diff --git a/tests/test_materialize_rust_head_lock_intake.py b/tests/test_materialize_rust_head_lock_intake.py new file mode 100644 index 0000000000..71f204cfaa --- /dev/null +++ b/tests/test_materialize_rust_head_lock_intake.py @@ -0,0 +1,128 @@ +"""Head locks may recombine base-pinned records without trusting head manifests.""" + +import json +import subprocess + +import pytest + +from scripts.ci import materialize_base_rust_dependencies as materializer +from tests.test_materialize_base_rust_dependencies import _init_repo, _commit_all + +REGISTRY = "registry+https://github.com/rust-lang/crates.io-index" + + +def lock(packages): + text = "version = 4\n" + for package in packages: + text += "\n[[package]]\n" + text += "".join( + f"{key} = {json.dumps(value)}\n" for key, value in package.items() + ) + return text + + +@pytest.mark.parametrize( + "mutation", + [ + None, + "checksum", + "version", + "git-source", + "manifest", + "manifest-symlink", + "missing-lock", + "new-lock", + "edge", + "missing-edge", + "duplicate", + "unknown-field", + "vendor-failure", + ], +) +def test_head_intake_keeps_base_inputs_and_rejects_untrusted_changes( + tmp_path, monkeypatch, mutation +): + repo = tmp_path / "repo" + _init_repo(repo) + manifest = '[package]\nname="local"\nversion="1.0.0"\n[workspace]\n' + (repo / "Cargo.toml").write_text(manifest) + (repo / "fuzz").mkdir() + (repo / "fuzz/Cargo.toml").write_text( + '[package]\nname="fuzz"\nversion="0.0.0"\n[workspace]\n' + ) + registry = { + "name": "itoa", + "version": "1.0.0", + "source": REGISTRY, + "checksum": "a" * 64, + } + other = { + "name": "other", + "version": "1.0.0", + "source": REGISTRY, + "checksum": "b" * 64, + } + local = {"name": "local", "version": "1.0.0", "dependencies": ["itoa"]} + fuzz = {"name": "fuzz", "version": "0.0.0", "dependencies": ["local"]} + (repo / "Cargo.lock").write_text(lock([local, registry, other])) + (repo / "fuzz/Cargo.lock").write_text(lock([{"name": "fuzz", "version": "0.0.0"}])) + base = _commit_all(repo) + rows = [fuzz, local, dict(registry), dict(other)] + if mutation == "checksum": + rows[2]["checksum"] = "0" * 64 + if mutation == "version": + rows[2]["version"] = "2.0.0" + rows[1]["dependencies"] = ["itoa 2.0.0"] + if mutation == "git-source": + rows[2]["source"] = "git+https://example.invalid/repo#" + "c" * 40 + if mutation == "edge": + rows[2]["dependencies"] = ["other"] + if mutation == "missing-edge": + rows[2]["dependencies"] = ["absent"] + if mutation == "duplicate": + rows.append(dict(registry)) + if mutation == "unknown-field": + rows[2]["replace"] = "other" + (repo / "fuzz/Cargo.lock").write_text(lock(rows)) + if mutation == "manifest": + (repo / "Cargo.toml").write_text(manifest + "# changed\n") + if mutation == "manifest-symlink": + (repo / "extra").mkdir() + (repo / "extra/Cargo.toml").symlink_to("../Cargo.toml") + if mutation == "missing-lock": + (repo / "fuzz/Cargo.lock").unlink() + if mutation == "new-lock": + (repo / "extra.lock/Cargo.lock").parent.mkdir() + (repo / "extra.lock/Cargo.lock").write_text(lock(rows)) + head = _commit_all(repo) + calls = [] + + def vendor(path, output, sync): + calls.append(path) + assert path.read_text() == manifest + assert (sync[0].parent / "Cargo.lock").read_text() == lock(rows) + output.mkdir() + return subprocess.CompletedProcess( + [], + 1 if mutation == "vendor-failure" else 0, + b'[source.vendored-sources]\ndirectory="vendor"\n', + b"resolution failed", + ) + + monkeypatch.setattr(materializer, "_run_cargo_vendor", vendor) + output = tmp_path / "output" + if mutation is not None: + with pytest.raises((ValueError, RuntimeError)): + materializer.materialize(repo, base, output, head_sha=head) + assert bool(calls) == (mutation == "vendor-failure") + assert not (output / "manifest.json").exists() + else: + assert materializer.materialize(repo, base, output, head_sha=head) == [ + "Cargo.lock", + "fuzz/Cargo.lock", + ] + receipt = json.loads((output / "lock-provenance.json").read_text()) + assert receipt["manifest_revision"] == base + assert receipt["lock_revision"] == head + assert receipt["locks"][1]["path"] == "fuzz/Cargo.lock" + assert len(receipt["locks"][1]["lock_blob"]) == 40 From 6a865c48a89b75db898bc9243cf0dd886dcc0795 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 27 Sep 2026 20:10:17 +0900 Subject: [PATCH 3/6] fix(ci): route unchanged Cargo manifests through bounded head locks --- .../workflows/opencode-review-dispatch.yml | 20 +++++++ .../test_materialize_rust_head_lock_intake.py | 58 +++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index 135fca5f84..a74d6eaf1a 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -733,10 +733,30 @@ jobs: # the generic Python coverage path failed at collection with `ImportError: cannot # import name '_core'`, both surfacing as an indistinguishable "Coverage gate: failure" # even when the pull request itself introduced no regression. + rust_lock_args=() + rust_change_files="${RUNNER_TEMP}/opencode-rust-change-files" + if ! git -C "$COVERAGE_SOURCE_WORKDIR" diff --no-renames --name-only -z \ + "$PR_BASE_SHA" "$PR_HEAD_SHA" >"$rust_change_files"; then + echo "::error::Could not classify exact-head Cargo changes." >&2 + exit 1 + fi + rust_lock_changed=0 + rust_manifest_changed=0 + while IFS= read -r -d '' changed_path; do + case "${changed_path##*/}" in + Cargo.lock) rust_lock_changed=1 ;; + Cargo.toml) rust_manifest_changed=1 ;; + esac + done <"$rust_change_files" + if [ "$rust_lock_changed" -eq 1 ] && [ "$rust_manifest_changed" -eq 0 ]; then + # The trusted materializer still rejects non-base pins and changed graphs. + rust_lock_args=(--head-sha "$PR_HEAD_SHA") + fi python3 -I "$GITHUB_WORKSPACE/scripts/ci/materialize_base_rust_dependencies.py" \ --repo-root "$COVERAGE_SOURCE_WORKDIR" \ --base-sha "$PR_BASE_SHA" \ --output-dir "$coverage_build_dir/base-rust-dependencies" \ + "${rust_lock_args[@]}" \ --vendor-dir-for-config /opt/base-rust-dependencies/vendor cat >"$coverage_build_dir/Dockerfile" <<'DOCKERFILE' FROM docker.io/library/python:3.14-slim@sha256:b877e50bd90de10af8d82c57a022fc2e0dc731c5320d762a27986facfc3355c1 diff --git a/tests/test_materialize_rust_head_lock_intake.py b/tests/test_materialize_rust_head_lock_intake.py index 71f204cfaa..504628e8ed 100644 --- a/tests/test_materialize_rust_head_lock_intake.py +++ b/tests/test_materialize_rust_head_lock_intake.py @@ -2,6 +2,9 @@ import json import subprocess +import os +import pathlib +import textwrap import pytest @@ -11,6 +14,61 @@ REGISTRY = "registry+https://github.com/rust-lang/crates.io-index" +@pytest.mark.parametrize("change", ["lock", "source", "manifest", "rename-manifest"]) +def test_workflow_opts_into_head_locks_only_with_unchanged_manifests(tmp_path, change): + repo = tmp_path / "repo" + _init_repo(repo) + (repo / "Cargo.toml").write_text("base manifest\n") + (repo / "Cargo.lock").write_text("base lock\n") + base = _commit_all(repo) + if change != "source": + (repo / "Cargo.lock").write_text("repaired lock\n") + if change == "source": + (repo / "source.rs").write_text("source change\n") + if change == "manifest": + (repo / "Cargo.toml").write_text("changed manifest\n") + if change == "rename-manifest": + (repo / "Cargo.toml").rename(repo / "renamed.txt") + head = _commit_all(repo) + workflow = ( + pathlib.Path(__file__).resolve().parents[1] + / ".github/workflows/opencode-review-dispatch.yml" + ).read_text() + start = workflow.index(" rust_lock_args=()") + end = workflow.index(' cat >"$coverage_build_dir/Dockerfile"', start) + block = textwrap.dedent(workflow[start:end]) + fake_bin = tmp_path / "bin" + fake_bin.mkdir() + fake = fake_bin / "python3" + fake.write_text('#!/bin/bash\nprintf "%s\\0" "$@" >"$ARGUMENT_RECEIPT"\n') + fake.chmod(0o755) + receipt = tmp_path / "arguments" + env = { + **os.environ, + "PATH": f"{fake_bin}:{os.environ['PATH']}", + "RUNNER_TEMP": str(tmp_path), + "COVERAGE_SOURCE_WORKDIR": str(repo), + "PR_BASE_SHA": base, + "PR_HEAD_SHA": head, + "GITHUB_WORKSPACE": str(tmp_path), + "coverage_build_dir": str(tmp_path), + "ARGUMENT_RECEIPT": str(receipt), + } + subprocess.run( + ["bash", "-euo", "pipefail"], + input=block, + text=True, + env=env, + check=True, + capture_output=True, + ) + arguments = receipt.read_bytes().decode().split("\0")[:-1] + assert ("--head-sha" in arguments) == (change == "lock") + if change == "lock": + assert arguments[arguments.index("--head-sha") + 1] == head + assert arguments[arguments.index("--base-sha") + 1] == base + + def lock(packages): text = "version = 4\n" for package in packages: From 7862c0e8e18d78f5741930d765374abead98ea66 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 27 Sep 2026 20:22:35 +0900 Subject: [PATCH 4/6] test(ci): cover bounded head lock rejection and CLI branches --- .../test_materialize_rust_head_lock_intake.py | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tests/test_materialize_rust_head_lock_intake.py b/tests/test_materialize_rust_head_lock_intake.py index 504628e8ed..3a1f7d8685 100644 --- a/tests/test_materialize_rust_head_lock_intake.py +++ b/tests/test_materialize_rust_head_lock_intake.py @@ -94,6 +94,7 @@ def lock(packages): "missing-edge", "duplicate", "unknown-field", + "local-version", "vendor-failure", ], ) @@ -141,6 +142,8 @@ def test_head_intake_keeps_base_inputs_and_rejects_untrusted_changes( rows.append(dict(registry)) if mutation == "unknown-field": rows[2]["replace"] = "other" + if mutation == "local-version": + rows[0]["version"] = "0.0.1" (repo / "fuzz/Cargo.lock").write_text(lock(rows)) if mutation == "manifest": (repo / "Cargo.toml").write_text(manifest + "# changed\n") @@ -184,3 +187,70 @@ def vendor(path, output, sync): assert receipt["lock_revision"] == head assert receipt["locks"][1]["path"] == "fuzz/Cargo.lock" assert len(receipt["locks"][1]["lock_blob"]) == 40 + + +@pytest.mark.parametrize( + "content", + [ + b"version = 2\npackage = []\n", + b"version = 4\npackage = [1]\n", + lock([{"name": "a", "version": "1", "dependencies": 1}]).encode(), + lock([{"name": "a", "version": "1", "dependencies": [1]}]).encode(), + lock([{"name": "a", "version": "1", "dependencies": [""]}]).encode(), + lock( + [{"name": "a", "version": "1", "dependencies": ["a 1 (source) extra"]}] + ).encode(), + lock([{"name": "a", "version": "1", "dependencies": ["a", "a"]}]).encode(), + ], +) +def test_malformed_lock_shapes_and_duplicate_edges_are_rejected(content): + with pytest.raises(ValueError): + materializer._normalized_lock_records(content) + + +def test_head_intake_rejects_nonexact_revision(tmp_path): + with pytest.raises(ValueError, match="40 hexadecimal"): + materializer._validated_head_locks(tmp_path, "a" * 40, "main", []) + + +def test_head_intake_bounds_lock_before_reading_content(tmp_path, monkeypatch): + monkeypatch.setattr( + materializer, "_regular_cargo_blob_paths", lambda *_: ["Cargo.lock"] + ) + + def git(_repo, *args): + if args[0] == "diff": + return b"" + assert args[:2] == ("cat-file", "-s") + return str(16 * 1024 * 1024 + 1).encode() + + monkeypatch.setattr(materializer, "_git", git) + with pytest.raises(ValueError, match="bounded size"): + materializer._validated_head_locks(tmp_path, "a" * 40, "b" * 40, ["Cargo.lock"]) + + +def test_cli_reports_distinct_head_lock_provenance(tmp_path, monkeypatch, capsys): + seen = {} + + def materialize(_repo, _base, _output, **kwargs): + seen.update(kwargs) + return ["Cargo.lock"] + + monkeypatch.setattr(materializer, "materialize", materialize) + assert ( + materializer.main( + [ + "--repo-root", + str(tmp_path), + "--base-sha", + "a" * 40, + "--head-sha", + "b" * 40, + "--output-dir", + str(tmp_path / "out"), + ] + ) + == 0 + ) + assert seen["head_sha"] == "b" * 40 + assert "base manifests and bounded head locks" in capsys.readouterr().out From 2d5e966495da500cb9924b4ced15d6e9bc23f452 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 27 Sep 2026 20:37:18 +0900 Subject: [PATCH 5/6] fix(ci): pair repair workflow blob and document lock helpers --- scripts/ci/materialize_base_rust_dependencies.py | 3 +++ tests/test_pr_review_autofix_nvidia_nim_contract.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/ci/materialize_base_rust_dependencies.py b/scripts/ci/materialize_base_rust_dependencies.py index 115c8ac02d..094ffd6e6c 100644 --- a/scripts/ci/materialize_base_rust_dependencies.py +++ b/scripts/ci/materialize_base_rust_dependencies.py @@ -338,6 +338,7 @@ def _validated_head_locks( raise ValueError("head Cargo manifests must remain byte-identical to base") def lock_bytes(revision: str, path: str) -> bytes: + """Read a revision-pinned lock after checking its bounded blob size.""" size = int(_git(repo_root, "cat-file", "-s", f"{revision}:{path}")) if size > 16 * 1024 * 1024: raise ValueError("Cargo lock exceeds bounded size") @@ -423,9 +424,11 @@ def materialize( primary_root, *additional_roots = vendor_roots def _manifest_for(root: str, base: pathlib.Path) -> pathlib.Path: + """Locate a vendor root manifest within the reconstructed base tree.""" return base / ("Cargo.toml" if root == "." else f"{root}/Cargo.toml") def _lock_for(root: str) -> str: + """Return the repository-relative lock path for a vendor root.""" return "Cargo.lock" if root == "." else f"{root}/Cargo.lock" with tempfile.TemporaryDirectory() as work_dir: diff --git a/tests/test_pr_review_autofix_nvidia_nim_contract.py b/tests/test_pr_review_autofix_nvidia_nim_contract.py index e724abc9ad..e6ddc239d7 100644 --- a/tests/test_pr_review_autofix_nvidia_nim_contract.py +++ b/tests/test_pr_review_autofix_nvidia_nim_contract.py @@ -17,7 +17,7 @@ DOCTORING_RECORD = Path("docs/doctoring/hourly-nvidia-nim-autofix.md") CHANGELOG = Path("CHANGELOG.md") REVIEW_DISPATCH_WORKFLOW = Path(".github/workflows/opencode-review-dispatch.yml") -REVIEW_DISPATCH_BLOB_SHA = "135fca5f848ef7829b016460f1c90ee66eeb55f4" +REVIEW_DISPATCH_BLOB_SHA = "a74d6eaf1a6c998b59d7db152cbb173bdfba7bf8" def _workflow_text(path: Path) -> str: From 3563a21f6ed870bfb7928e873d037046a608ad31 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 28 Sep 2026 00:30:45 +0900 Subject: [PATCH 6/6] test(codeql): retain scoped lookup with compatible page collection --- tests/test_codeql_pr_workflow_contract.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_codeql_pr_workflow_contract.py b/tests/test_codeql_pr_workflow_contract.py index 65c739015a..6f1ebb6944 100644 --- a/tests/test_codeql_pr_workflow_contract.py +++ b/tests/test_codeql_pr_workflow_contract.py @@ -1131,7 +1131,9 @@ def test_codeql_pr_scopes_dispatch_history_to_required_run_creation() -> None: script = _extract_run_block(WORKFLOW_PATH.read_text(), DISPATCH_STEP_NAME) assert '-f created=">=${required_created_at}"' in script assert '-f event=repository_dispatch' in script - assert '--paginate --slurp' in script + assert '--paginate' in script + assert '--slurp' not in script + assert '| jq -s .' in script assert 'select(.display_title == $title or .name == $title)' in script