From 6542e73c2784d1829f5ddba2ffd8dcfbe2d3325a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 22:56:54 +0900 Subject: [PATCH 01/20] test(coverage): specify LLVM 19 isolated runtime contract --- ...encode_rust_coverage_toolchain_contract.py | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 tests/test_opencode_rust_coverage_toolchain_contract.py diff --git a/tests/test_opencode_rust_coverage_toolchain_contract.py b/tests/test_opencode_rust_coverage_toolchain_contract.py new file mode 100644 index 000000000..43979a649 --- /dev/null +++ b/tests/test_opencode_rust_coverage_toolchain_contract.py @@ -0,0 +1,86 @@ +"""Permanent contract for the trusted Rust LLVM coverage toolchain.""" + +from __future__ import annotations + +import re +from pathlib import Path + + +_REPOSITORY_ROOT = Path(__file__).resolve().parents[1] +_WORKFLOW_PATH = _REPOSITORY_ROOT / ".github/workflows/opencode-review-dispatch.yml" +_LLVM_COV_PATH = "/usr/bin/llvm-cov-19" +_LLVM_PROFDATA_PATH = "/usr/bin/llvm-profdata-19" + + +def _workflow_text() -> str: + """Return the authoritative OpenCode review-dispatch workflow text.""" + + return _WORKFLOW_PATH.read_text(encoding="utf-8") + + +def _all_positions(text: str, fragment: str) -> list[int]: + """Return every start position of ``fragment`` in ``text``.""" + + return [match.start() for match in re.finditer(re.escape(fragment), text)] + + +def test_trusted_rust_coverage_image_provisions_verified_llvm_19_tools() -> None: + """Require explicit compatible LLVM tools before cargo-llvm-cov installation.""" + + workflow = _workflow_text() + + llvm_package = workflow.index("llvm-19") + llvm_cov_environment = workflow.index(f"ENV LLVM_COV={_LLVM_COV_PATH}") + llvm_profdata_environment = workflow.index( + f"ENV LLVM_PROFDATA={_LLVM_PROFDATA_PATH}" + ) + llvm_cov_checks = _all_positions(workflow, 'test -x "$LLVM_COV"') + llvm_profdata_checks = _all_positions(workflow, 'test -x "$LLVM_PROFDATA"') + cargo_llvm_cov_archive = workflow.index( + "cargo-llvm-cov-x86_64-unknown-linux-musl.tar.gz" + ) + + assert len(llvm_cov_checks) >= 2 + assert len(llvm_profdata_checks) >= 2 + assert ( + llvm_package + < llvm_cov_environment + < llvm_profdata_environment + < llvm_cov_checks[0] + < llvm_profdata_checks[0] + < cargo_llvm_cov_archive + ) + + +def test_isolated_runtime_receives_reviewed_llvm_constants() -> None: + """Require exact LLVM 19 path propagation through the Docker boundary.""" + + workflow = _workflow_text() + docker_run = workflow.index("docker run --rm") + llvm_cov_binding = workflow.index( + f"--env LLVM_COV={_LLVM_COV_PATH}", docker_run + ) + llvm_profdata_binding = workflow.index( + f"--env LLVM_PROFDATA={_LLVM_PROFDATA_PATH}", docker_run + ) + coverage_image = workflow.index('"$coverage_tool_image"', docker_run) + + assert docker_run < llvm_cov_binding < llvm_profdata_binding < coverage_image + + +def test_isolated_runtime_revalidates_llvm_tools_before_coverage() -> None: + """Require reviewed-path equality and executable checks before Rust coverage.""" + + workflow = _workflow_text() + docker_run = workflow.index("docker run --rm") + toolchain_start = workflow.index("ensure_rust_toolchain() {", docker_run) + toolchain_end = workflow.index("rust_coverage_manifests() {", toolchain_start) + toolchain = workflow[toolchain_start:toolchain_end] + cargo_coverage_invocation = workflow.index("cargo llvm-cov", toolchain_end) + llvm_cov_checks = _all_positions(workflow, 'test -x "$LLVM_COV"') + llvm_profdata_checks = _all_positions(workflow, 'test -x "$LLVM_PROFDATA"') + + assert f'"${{LLVM_COV:-}}" != "{_LLVM_COV_PATH}"' in toolchain + assert f'"${{LLVM_PROFDATA:-}}" != "{_LLVM_PROFDATA_PATH}"' in toolchain + assert docker_run < llvm_cov_checks[-1] < cargo_coverage_invocation + assert docker_run < llvm_profdata_checks[-1] < cargo_coverage_invocation From 09b7760e0dc179463642b1de409bbebe3f1b36f4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 22:59:50 +0900 Subject: [PATCH 02/20] fix(coverage): provision verified LLVM 19 tools --- .github/workflows/opencode-review-dispatch.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index 83f6830d5..b17cf3775 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -652,11 +652,15 @@ jobs: r-base \ r-cran-covr \ r-cran-testthat \ + llvm-19 \ rustc \ util-linux \ vulkan-tools \ xz-utils \ && rm -rf /var/lib/apt/lists/* + ENV LLVM_COV=/usr/bin/llvm-cov-19 + ENV LLVM_PROFDATA=/usr/bin/llvm-profdata-19 + RUN test -x "$LLVM_COV" && test -x "$LLVM_PROFDATA" RUN curl --proto '=https' --tlsv1.2 -fsSLo /tmp/node-linux-x64.tar.xz \ https://nodejs.org/dist/v24.18.0/node-v24.18.0-linux-x64.tar.xz \ && echo '55aa7153f9d88f28d765fcdad5ae6945b5c0f98a36881703817e4c450fa76742 /tmp/node-linux-x64.tar.xz' | sha256sum -c - \ From 99675b7f3c65365472b3d56848cc4925fe34dbb1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 7 Aug 2026 23:04:25 +0900 Subject: [PATCH 03/20] ci(coverage): add exact-head LLVM runtime quality gate --- ...ode-rust-coverage-toolchain-quality-ci.yml | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 .github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml diff --git a/.github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml b/.github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml new file mode 100644 index 000000000..ee4b283bd --- /dev/null +++ b/.github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml @@ -0,0 +1,56 @@ +name: OpenCode Rust Coverage Toolchain Quality CI + +on: + pull_request: + paths: + - ".github/workflows/opencode-review-dispatch.yml" + - ".github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml" + - "tests/test_opencode_rust_coverage_toolchain_contract.py" + - "docs/doctoring/opencode-rust-coverage-runtime-boundary.md" + - "CHANGELOG.md" + +permissions: + contents: read + +concurrency: + group: opencode-rust-coverage-toolchain-quality-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + quality: + name: quality + runs-on: ubuntu-24.04 + timeout-minutes: 15 + env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + steps: + - name: Harden runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + egress-policy: audit + + - name: Checkout exact pull request head + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.14" + cache: pip + cache-dependency-path: requirements-opencode-review-ci-hashes.txt + + - name: Install exact hash-locked test tooling + run: >- + python -m pip install --disable-pip-version-check --require-hashes + -r requirements-opencode-review-ci-hashes.txt + + - name: Run permanent LLVM runtime-boundary contract + run: | + set -euo pipefail + python -m pytest -q tests/test_opencode_rust_coverage_toolchain_contract.py + python -m compileall -q tests/test_opencode_rust_coverage_toolchain_contract.py + git diff --check "${{ github.event.pull_request.base.sha }}...${{ github.event.pull_request.head.sha }}" From 35e5aa4209627c8adff9f16c85bc6b3bea868c4f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 9 Aug 2026 19:24:39 +0900 Subject: [PATCH 04/20] fix(coverage): preserve LLVM 19 across sandbox runtime --- .github/workflows/opencode-review-dispatch.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index b17cf3775..de1c4800d 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -660,7 +660,8 @@ jobs: && rm -rf /var/lib/apt/lists/* ENV LLVM_COV=/usr/bin/llvm-cov-19 ENV LLVM_PROFDATA=/usr/bin/llvm-profdata-19 - RUN test -x "$LLVM_COV" && test -x "$LLVM_PROFDATA" + RUN test -x "$LLVM_COV" + RUN test -x "$LLVM_PROFDATA" RUN curl --proto '=https' --tlsv1.2 -fsSLo /tmp/node-linux-x64.tar.xz \ https://nodejs.org/dist/v24.18.0/node-v24.18.0-linux-x64.tar.xz \ && echo '55aa7153f9d88f28d765fcdad5ae6945b5c0f98a36881703817e4c450fa76742 /tmp/node-linux-x64.tar.xz' | sha256sum -c - \ @@ -773,6 +774,8 @@ jobs: --env RUNNER_TEMP=/secure-output \ --env GITHUB_OUTPUT=/secure-output/github-output \ --env GITHUB_STEP_SUMMARY=/secure-output/step-summary \ + --env LLVM_COV=/usr/bin/llvm-cov-19 \ + --env LLVM_PROFDATA=/usr/bin/llvm-profdata-19 \ "$coverage_tool_image" \ /bin/bash /trusted-measure-step.sh || sandbox_status=$? @@ -1713,6 +1716,18 @@ jobs: } ensure_rust_toolchain() { + if [ "${LLVM_COV:-}" != "/usr/bin/llvm-cov-19" ] || \ + [ "${LLVM_PROFDATA:-}" != "/usr/bin/llvm-profdata-19" ] || \ + ! test -x "$LLVM_COV" || ! test -x "$LLVM_PROFDATA"; then + append "### Rust coverage toolchain" + append "" + append "- Result: FAIL" + append "- Reason: the networkless coverage runtime did not preserve the reviewed LLVM 19 tool paths." + append "- Fix: rebuild the trusted coverage image and preserve the exact LLVM bindings at the Docker boundary." + append "" + failures=$((failures + 1)) + return 1 + fi if ! command -v cargo >/dev/null 2>&1; then append "### Rust coverage toolchain" append "" From 30f0f3c647c1d84c5cdee85b72e5fa99c73057f3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 10 Aug 2026 13:24:49 +0900 Subject: [PATCH 05/20] test(coverage): require live Rust toolchain trigger paths --- ...encode_rust_coverage_toolchain_contract.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/test_opencode_rust_coverage_toolchain_contract.py b/tests/test_opencode_rust_coverage_toolchain_contract.py index 43979a649..0e5c3d9a8 100644 --- a/tests/test_opencode_rust_coverage_toolchain_contract.py +++ b/tests/test_opencode_rust_coverage_toolchain_contract.py @@ -8,6 +8,9 @@ _REPOSITORY_ROOT = Path(__file__).resolve().parents[1] _WORKFLOW_PATH = _REPOSITORY_ROOT / ".github/workflows/opencode-review-dispatch.yml" +_QUALITY_WORKFLOW_PATH = ( + _REPOSITORY_ROOT / ".github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml" +) _LLVM_COV_PATH = "/usr/bin/llvm-cov-19" _LLVM_PROFDATA_PATH = "/usr/bin/llvm-profdata-19" @@ -84,3 +87,21 @@ def test_isolated_runtime_revalidates_llvm_tools_before_coverage() -> None: assert f'"${{LLVM_PROFDATA:-}}" != "{_LLVM_PROFDATA_PATH}"' in toolchain assert docker_run < llvm_cov_checks[-1] < cargo_coverage_invocation assert docker_run < llvm_profdata_checks[-1] < cargo_coverage_invocation + + +def test_quality_workflow_watched_paths_resolve_to_repository_files() -> None: + """Every exact-path trigger in the permanent quality workflow must exist.""" + + quality_workflow = _QUALITY_WORKFLOW_PATH.read_text(encoding="utf-8") + watched_section = quality_workflow.split(" paths:\n", 1)[1].split( + "\n\npermissions:\n", 1 + )[0] + watched_paths = [ + line.strip()[2:].strip('"') + for line in watched_section.splitlines() + if line.strip().startswith("- ") + ] + + assert watched_paths + for relative_path in watched_paths: + assert (_REPOSITORY_ROOT / relative_path).is_file(), relative_path From 2f397e6ad99fc7d145b5fc016a1c61cbe98b0378 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 10 Aug 2026 13:25:51 +0900 Subject: [PATCH 06/20] docs(coverage): document LLVM 19 runtime boundary --- ...opencode-rust-coverage-runtime-boundary.md | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 docs/doctoring/opencode-rust-coverage-runtime-boundary.md diff --git a/docs/doctoring/opencode-rust-coverage-runtime-boundary.md b/docs/doctoring/opencode-rust-coverage-runtime-boundary.md new file mode 100644 index 000000000..ea43dbc9a --- /dev/null +++ b/docs/doctoring/opencode-rust-coverage-runtime-boundary.md @@ -0,0 +1,114 @@ +# OpenCode Rust coverage LLVM runtime boundary + +## Decision + +The trusted OpenCode coverage sandbox binds Rust coverage to the reviewed LLVM +19 executables shipped by Debian's `llvm-19` package: + +- `LLVM_COV=/usr/bin/llvm-cov-19` +- `LLVM_PROFDATA=/usr/bin/llvm-profdata-19` + +These are compatibility and trust-boundary constants, not caller-selectable +configuration. The coverage image installs `llvm-19`, declares both exact paths, +and fails the image build unless both are executable before the pinned +`cargo-llvm-cov` archive is admitted. The isolated `docker run` passes the same +literal values through the networkless runtime boundary. Inside the container, +`ensure_rust_toolchain()` requires exact string equality and executable files +before the first `cargo llvm-cov` invocation. + +The runtime MUST NOT fall back to unversioned `llvm-cov` or `llvm-profdata`, a +host-runner tool, a pull-request-selected path, or a dynamically downloaded LLVM +binary. Missing, changed, or non-executable reviewed paths are coverage-evidence +failures rather than reasons to measure a different toolchain. + +## Why the boundary exists + +`cargo-llvm-cov` is a wrapper around Rust's LLVM source-based coverage and +explicitly supports `LLVM_COV` and `LLVM_PROFDATA` as path overrides. Its +current project documentation states that the LLVM tools must be compatible +with the LLVM version used by `rustc`. Allowing ambient `PATH` discovery would +therefore make a runner-image change capable of silently changing the coverage +producer. + +Debian bookworm currently publishes the versioned `llvm-19` package from +`llvm-toolchain-19`; Debian package file inventories expose versioned LLVM 19 +tool entry points including `llvm-cov-19`. Pinning the reviewed executable names +inside the image converts that mutable ambient dependency into an explicit +contract that can be checked before source execution. + +## Trust-boundary sequence + +```mermaid +flowchart LR + A["Digest-pinned coverage base image"] --> B["Install Debian llvm-19"] + B --> C["ENV exact LLVM_COV / LLVM_PROFDATA paths"] + C --> D["Build-time test -x for both executables"] + D --> E["Verify pinned cargo-llvm-cov archive"] + E --> F["docker run --network=none with literal LLVM env values"] + F --> G["ensure_rust_toolchain exact-value + executable checks"] + G --> H["cargo llvm-cov"] +``` + +Each arrow is fail-closed. A later stage does not repair or broaden an earlier +stage's failed trust decision. + +## Security and supply-chain implications + +The reviewed paths are fixed in trusted central workflow source. Pull-request +content cannot choose an LLVM package, executable path, download origin, or +runtime environment value. The existing coverage sandbox retains +`--network=none`, credential/Git isolation, exact-head/base materialization, +and the separately checksum-pinned `cargo-llvm-cov` archive. + +This binding narrows reproducibility risk but does not by itself attest Debian's +whole package supply chain or prove a future Rust toolchain is compatible with +LLVM 19. A future rustc or base-image upgrade must revalidate compatibility and +update this contract, its tests, and CHANGELOG in one reviewed change rather +than silently selecting a different binary. + +## Failure and recovery + +If the image cannot install `llvm-19`, either reviewed executable is missing or +non-executable, the runtime value differs from the literal reviewed path, or the +isolated runtime does not receive the values, Rust coverage fails closed before +`cargo llvm-cov` runs. The operator should identify whether the failure comes +from Debian package availability, the pinned image/base generation, a central +workflow regression, or an intentional Rust/LLVM compatibility change. + +Do not work around the failure by removing the exact-value check, using an +unversioned executable, adding network access to the PR runtime, or accepting a +host-provided path. A deliberate toolchain migration requires fresh authoritative +compatibility evidence and the same RED→GREEN exact-head verification sequence. + +## Verification contract + +`tests/test_opencode_rust_coverage_toolchain_contract.py` proves that: + +1. `llvm-19` is provisioned before the pinned `cargo-llvm-cov` archive; +2. the image binds the two exact LLVM 19 executable paths; +3. image construction verifies both executables; +4. the isolated `docker run` receives both literal values before the coverage + image argument; +5. `ensure_rust_toolchain()` revalidates exact values and executability before + Rust coverage; and +6. every exact path named by the permanent quality workflow's + `pull_request.paths` filter resolves to a repository file, preventing a + dangling documentation trigger from becoming invisible debt. + +The permanent quality workflow runs on Python 3.14, checks out the exact PR head, +executes the focused contract, compiles the test, and applies `git diff --check`. +Repository security and supply-chain workflows remain separate authorities. + +## References + +Debian Project. (2026). *Package: llvm-19 (1:19.1.7-3~deb12u1), bookworm*. +Debian Packages. Retrieved August 10, 2026, from +https://packages.debian.org/bookworm/llvm-19 + +Debian Project. (2026). *File list of package llvm-19*. Debian Packages. +Retrieved August 10, 2026, from +https://packages.debian.org/bookworm/amd64/llvm-19/filelist + +Taiki Endo. (2026). *cargo-llvm-cov: Cargo subcommand to use LLVM source-based +code coverage*. GitHub. Retrieved August 10, 2026, from +https://github.com/taiki-e/cargo-llvm-cov From 4e94ab11c3ab0cfc7204ab349aab16f70c77d146 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 13 Aug 2026 14:12:21 +0900 Subject: [PATCH 07/20] docs(coverage): cite NIST 800-218 PW.4.1 LLVM 19 pin Bind Rust coverage to reviewed llvm-cov-19 executables so a runner PATH change cannot silently replace the producer. Darwin trusted-uv tests exercise the linux x86_64 installer path. --- .../opencode-rust-coverage-runtime-boundary.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/doctoring/opencode-rust-coverage-runtime-boundary.md b/docs/doctoring/opencode-rust-coverage-runtime-boundary.md index ea43dbc9a..6aeb09e82 100644 --- a/docs/doctoring/opencode-rust-coverage-runtime-boundary.md +++ b/docs/doctoring/opencode-rust-coverage-runtime-boundary.md @@ -21,6 +21,12 @@ host-runner tool, a pull-request-selected path, or a dynamically downloaded LLVM binary. Missing, changed, or non-executable reviewed paths are coverage-evidence failures rather than reasons to measure a different toolchain. +NIST SP 800-218 PW.4.1 requires third-party software to come from expected, +trusted sources with integrity verification (Souppaya et al., 2022). Binding +coverage to the reviewed `/usr/bin/llvm-cov-19` and +`/usr/bin/llvm-profdata-19` executables is that verification; an ambient +`PATH` lookup would treat a runner-image change as a new producer. + ## Why the boundary exists `cargo-llvm-cov` is a wrapper around Rust's LLVM source-based coverage and @@ -109,6 +115,11 @@ Debian Project. (2026). *File list of package llvm-19*. Debian Packages. Retrieved August 10, 2026, from https://packages.debian.org/bookworm/amd64/llvm-19/filelist +Souppaya, M., Scarfone, K., & Dodson, D. (2022). *Secure Software Development +Framework (SSDF) version 1.1: Recommendations for mitigating the risk of +software vulnerabilities* (NIST Special Publication 800-218). National +Institute of Standards and Technology. https://doi.org/10.6028/NIST.SP.800-218 + Taiki Endo. (2026). *cargo-llvm-cov: Cargo subcommand to use LLVM source-based code coverage*. GitHub. Retrieved August 10, 2026, from https://github.com/taiki-e/cargo-llvm-cov From 86761ff02270ad094e647d6f9cda8bb64eaa73e1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 15 Aug 2026 19:21:01 +0900 Subject: [PATCH 08/20] ci: add verified PR 827 review repair --- .../ci/repair_pr827_coderabbit_comments.py | 372 ++++++++++++++++++ 1 file changed, 372 insertions(+) create mode 100644 scripts/ci/repair_pr827_coderabbit_comments.py diff --git a/scripts/ci/repair_pr827_coderabbit_comments.py b/scripts/ci/repair_pr827_coderabbit_comments.py new file mode 100644 index 000000000..267b45f02 --- /dev/null +++ b/scripts/ci/repair_pr827_coderabbit_comments.py @@ -0,0 +1,372 @@ +#!/usr/bin/env python3 +"""Apply the verified CodeRabbit repairs for pull request 827.""" + +from __future__ import annotations + +from pathlib import Path + + +def replace_once(path: str, old: str, new: str) -> None: + """Replace one exact fragment and fail closed on drift.""" + file_path = Path(path) + text = file_path.read_text(encoding="utf-8") + count = text.count(old) + if count != 1: + raise SystemExit(f"{path}: expected one replacement marker, found {count}") + file_path.write_text(text.replace(old, new, 1), encoding="utf-8") + + +def replace_between(path: str, start: str, end: str, replacement: str) -> None: + """Replace a uniquely delimited source section.""" + file_path = Path(path) + text = file_path.read_text(encoding="utf-8") + start_index = text.find(start) + if start_index < 0 or text.find(start, start_index + 1) >= 0: + raise SystemExit(f"{path}: start marker missing or ambiguous") + end_index = text.find(end, start_index) + if end_index < 0: + raise SystemExit(f"{path}: end marker missing") + file_path.write_text( + text[:start_index] + replacement + text[end_index:], encoding="utf-8" + ) + + +SCRIPT = "scripts/ci/materialize_base_python_requirements.py" +TEST = "tests/test_materialize_base_python_requirements.py" +DOC = "docs/doctoring/opencode-rust-coverage-runtime-boundary.md" +WORKFLOW = ".github/workflows/opencode-review-dispatch.yml" + +replace_between( + SCRIPT, + "def _is_bounded_requirement_include(line: str) -> bool:\n", + "def _requirement_lines(content: bytes) -> list[str]:\n", + '''def _bounded_requirement_include_target( + line: str, +) -> pathlib.PurePosixPath | None: + """Return the safe relative target of one bounded requirements include. + + The target may use any normalized relative ``.txt`` name, including names + such as ``other-hashes.txt``. Eligibility does not confer trust: the exact + base-tree target must later be a regular blob containing only exact + SHA-256-pinned package requirements. + """ + fields = line.split() + if len(fields) != 2 or fields[0] not in {"-r", "--requirement"}: + return None + target = fields[1] + if ( + target.startswith(("-", "~")) + or "\\\\" in target + or ":" in target + or "?" in target + or "#" in target + ): + return None + include_path = pathlib.PurePosixPath(target) + if ( + not include_path.parts + or target != include_path.as_posix() + or include_path.is_absolute() + or "." in include_path.parts + or ".." in include_path.parts + or include_path.suffix != ".txt" + ): + return None + return include_path + + +def _is_bounded_requirement_include(line: str) -> bool: + """Return whether one include has a safe relative ``.txt`` target.""" + return _bounded_requirement_include_target(line) is not None + + +''', +) + +replace_once( + SCRIPT, + " if _is_candidate_lock_name(candidate.name):\n", + " if _is_candidate_lock_path(candidate):\n", +) + +helpers = '''def _included_base_lock_blobs( + repo_root: pathlib.Path, + base_sha: str, + source_path: str, + content: bytes, + regular_paths: set[str], +) -> list[tuple[pathlib.PurePosixPath, bytes]]: + """Load direct bounded includes from the exact base as complete closures.""" + source_parent = pathlib.PurePosixPath(source_path).parent + included: dict[pathlib.PurePosixPath, bytes] = {} + for line in _requirement_lines(content): + target = _bounded_requirement_include_target(line) + if target is None: + continue + resolved = source_parent / target + resolved_path = resolved.as_posix() + if resolved_path not in regular_paths: + raise RuntimeError( + f"bounded include {target} from {source_path} is not a regular base blob" + ) + included_content = _git(repo_root, "show", f"{base_sha}:{resolved_path}") + if not _is_fully_hash_pinned_export(included_content): + raise RuntimeError( + f"bounded include {resolved_path} must contain only exact SHA-256 pins" + ) + included[target] = included_content + return sorted(included.items(), key=lambda item: item[0].as_posix()) + + +def _rewrite_materialized_includes(content: bytes, include_directory: str) -> bytes: + """Rewrite root include targets to their preserved generated subtree.""" + text = content.decode("utf-8", errors="strict") + rewritten: list[str] = [] + for raw_line in text.splitlines(keepends=True): + body = raw_line.rstrip("\\r\\n") + ending = raw_line[len(body) :] + stripped = body.strip() + target = _bounded_requirement_include_target(stripped) + if target is None: + rewritten.append(raw_line) + continue + indentation = body[: len(body) - len(body.lstrip())] + option = stripped.split()[0] + rewritten.append( + f"{indentation}{option} {include_directory}/{target.as_posix()}{ending}" + ) + return "".join(rewritten).encode("utf-8") + + +''' +replace_once(SCRIPT, "def materialize(\n", helpers + "def materialize(\n") + +replace_between( + SCRIPT, + "def materialize(\n", + "def main(argv: list[str] | None = None) -> int:\n", + '''def materialize( + repo_root: pathlib.Path, + base_sha: str, + output_dir: pathlib.Path, +) -> list[dict[str, str]]: + """Write base locks and resolvable bounded includes into a safe context.""" + if output_dir.exists() and output_dir.is_symlink(): + raise ValueError("output directory must not be a symlink") + output_dir.mkdir(parents=True, exist_ok=True) + + resolved_repo = repo_root.resolve() + entries = _git(resolved_repo, "ls-tree", "-r", "-z", "--full-tree", base_sha) + regular_paths = { + path for path, _candidate in _regular_base_blob_paths(entries) + } + manifest: list[dict[str, str]] = [] + for index, (source_path, content) in enumerate( + base_hash_locks(resolved_repo, base_sha) + ): + generated_name = f"requirements-{index:03d}.txt" + include_directory = f"includes-{index:03d}" + included = _included_base_lock_blobs( + resolved_repo, + base_sha, + source_path, + content, + regular_paths, + ) + for relative_target, included_content in included: + destination = output_dir / include_directory / Path(*relative_target.parts) + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_bytes(included_content) + destination = output_dir / generated_name + destination.write_bytes( + _rewrite_materialized_includes(content, include_directory) + ) + manifest.append({"file": generated_name, "source": source_path}) + + (output_dir / "manifest.json").write_text( + json.dumps(manifest, indent=2, sort_keys=True) + "\\n", + encoding="utf-8", + ) + (output_dir / "manifest.txt").write_text( + "".join(f"{entry['file']}\\n" for entry in manifest), + encoding="utf-8", + ) + return manifest + + +''', +) + +replace_once(TEST, "import tarfile\n", "import tarfile\nimport zipfile\n") +replace_once( + TEST, + ' assert not materializer._is_hash_pinned(b"-r other-hashes.txt\\n")\n', + ' assert materializer._is_hash_pinned(b"-r other-hashes.txt\\n")\n', +) +replace_once( + TEST, + ' assert not materializer._is_candidate_lock_name("pyproject.toml")\n', + ' assert not materializer._is_candidate_lock_name("pyproject.toml")\n' + ' assert materializer._is_candidate_lock_path(\n' + ' materializer.pathlib.PurePosixPath("requirements/ci.txt")\n' + ' )\n' + ' assert materializer._is_candidate_lock_path(\n' + ' materializer.pathlib.PurePosixPath("service/requirements/package.txt")\n' + ' )\n' + ' assert not materializer._is_candidate_lock_path(\n' + ' materializer.pathlib.PurePosixPath("service/config/ci.txt")\n' + ' )\n', +) +replace_once( + TEST, + ' (repo / "requirements-test.txt").write_text(\n' + ' "hypothesis==6 --hash=sha256:" + ("b" * 64) + "\\n",\n' + ' encoding="utf-8",\n' + ' )\n', + ' (repo / "requirements-test.txt").write_text(\n' + ' "hypothesis==6 --hash=sha256:" + ("b" * 64) + "\\n",\n' + ' encoding="utf-8",\n' + ' )\n' + ' requirements_dir = repo / "requirements"\n' + ' requirements_dir.mkdir()\n' + ' (requirements_dir / "ci.txt").write_text(\n' + ' "pytest==9 --hash=sha256:" + ("c" * 64) + "\\n",\n' + ' encoding="utf-8",\n' + ' )\n', +) +replace_once( + TEST, + ' "requirements-test.txt",\n' + ' "services/account_unification/requirements-dev.txt",\n', + ' "requirements-test.txt",\n' + ' "requirements/ci.txt",\n' + ' "services/account_unification/requirements-dev.txt",\n', +) + +integration_test = '''def test_materialized_bounded_include_is_resolvable_by_pip(tmp_path: Path) -> None: + """A safe base-owned include survives flattening and pip hash preflight.""" + repo = tmp_path / "repo" + repo.mkdir() + git(repo, "init") + git(repo, "config", "user.name", "Test") + git(repo, "config", "user.email", "test@example.invalid") + + wheel_dir = tmp_path / "wheels" + wheel_dir.mkdir() + wheel = wheel_dir / "demo-1-py3-none-any.whl" + with zipfile.ZipFile(wheel, "w") as archive: + archive.writestr("demo/__init__.py", "__version__ = '1'\\n") + archive.writestr( + "demo-1.dist-info/METADATA", + "Metadata-Version: 2.1\\nName: demo\\nVersion: 1\\n", + ) + archive.writestr( + "demo-1.dist-info/WHEEL", + "Wheel-Version: 1.0\\nGenerator: TEPP-test\\n" + "Root-Is-Purelib: true\\nTag: py3-none-any\\n", + ) + archive.writestr("demo-1.dist-info/RECORD", "") + digest = hashlib.sha256(wheel.read_bytes()).hexdigest() + + (repo / "requirements.txt").write_text( + "-r other-hashes.txt\\n", encoding="utf-8" + ) + (repo / "other-hashes.txt").write_text( + f"demo==1 --hash=sha256:{digest}\\n", encoding="utf-8" + ) + git(repo, "add", ".") + git(repo, "commit", "-m", "base") + base_sha = git(repo, "rev-parse", "HEAD") + + output = tmp_path / "output" + manifest = materializer.materialize(repo, base_sha, output) + assert manifest == [{"file": "requirements-000.txt", "source": "requirements.txt"}] + assert (output / "requirements-000.txt").read_text(encoding="utf-8") == ( + "-r includes-000/other-hashes.txt\\n" + ) + assert (output / "includes-000" / "other-hashes.txt").is_file() + + completed = subprocess.run( + [ + sys.executable, + "-m", + "pip", + "install", + "--dry-run", + "--ignore-installed", + "--disable-pip-version-check", + "--no-index", + "--find-links", + str(wheel_dir), + "--require-hashes", + "-r", + str(output / "requirements-000.txt"), + ], + check=False, + capture_output=True, + text=True, + ) + assert completed.returncode == 0, completed.stdout + completed.stderr + + +def test_materialization_rejects_missing_or_nested_include(tmp_path: Path) -> None: + """Includes must resolve to direct complete hash closures in the exact base.""" + repo = tmp_path / "repo" + repo.mkdir() + git(repo, "init") + git(repo, "config", "user.name", "Test") + git(repo, "config", "user.email", "test@example.invalid") + (repo / "requirements.txt").write_text("-r child.txt\\n", encoding="utf-8") + git(repo, "add", ".") + git(repo, "commit", "-m", "missing") + missing_sha = git(repo, "rev-parse", "HEAD") + with pytest.raises(RuntimeError, match="not a regular base blob"): + materializer.materialize(repo, missing_sha, tmp_path / "missing-output") + + (repo / "child.txt").write_text("-r grandchild.txt\\n", encoding="utf-8") + (repo / "grandchild.txt").write_text( + "demo==1 --hash=sha256:" + ("d" * 64) + "\\n", encoding="utf-8" + ) + git(repo, "add", ".") + git(repo, "commit", "-m", "nested") + nested_sha = git(repo, "rev-parse", "HEAD") + with pytest.raises(RuntimeError, match="must contain only exact SHA-256 pins"): + materializer.materialize(repo, nested_sha, tmp_path / "nested-output") + + +''' +replace_once(TEST, "def test_rejects_invalid_base_sha", integration_test + "def test_rejects_invalid_base_sha") +replace_once( + TEST, + ' assert not materializer._is_bounded_requirement_include("-r /abs/requirements.txt")\n', + ' assert not materializer._is_bounded_requirement_include("-r /abs/requirements.txt")\n' + ' assert not materializer._is_bounded_requirement_include("-r pyproject.toml")\n', +) + +replace_once( + "CHANGELOG.md", + "- Materialized base Python locks only when every package line is an exact SHA-256 pin or a bounded relative `-r`/`--requirement` include. A lone `--require-hashes` directive, a dotted include such as `./lock.txt`, or `-r other-hashes.txt` no longer enters the trusted build context.\n", + "- Materialized base Python locks only when every package line is an exact SHA-256 pin or a bounded relative `-r`/`--requirement` include. Includes such as `-r other-hashes.txt` remain allowed when the exact base-tree target is a regular, complete SHA-256-pinned closure; a lone `--require-hashes` directive, dotted `./lock.txt`, traversal, absolute, URL, and option-like targets fail closed.\n", +) + +replace_once( + DOC, + "NIST SP 800-218 PW.4.1 requires third-party software to come from expected,\ntrusted sources with integrity verification (Souppaya et al., 2022). Binding\ncoverage to the reviewed `/usr/bin/llvm-cov-19` and\n`/usr/bin/llvm-profdata-19` executables is that verification; an ambient\n`PATH` lookup would treat a runner-image change as a new producer.\n", + "NIST SP 800-218 PW.4.1 requires third-party software to come from expected,\ntrusted sources with integrity verification (Souppaya et al., 2022). The exact\n`/usr/bin/llvm-cov-19` and `/usr/bin/llvm-profdata-19` bindings are\nproducer-selection controls: they select reviewed paths and `test -x` verifies\nexecutability. They do not hash or signature-verify the Debian package or binary.\nPackage/image hashes, signatures, repository metadata, and attestations are\nseparate integrity controls and must not be inferred from path equality.\n", +) +replace_once( + DOC, + "Debian bookworm currently publishes the versioned `llvm-19` package from\n`llvm-toolchain-19`; Debian package file inventories expose versioned LLVM 19\ntool entry points including `llvm-cov-19`. Pinning the reviewed executable names\ninside the image converts that mutable ambient dependency into an explicit\ncontract that can be checked before source execution.\n", + "Debian publishes `llvm-19` from the `llvm-toolchain-19` source package; its\nofficial copyright record states `Apache-2.0 WITH LLVM-exception`. Debian package\nfile inventories expose versioned LLVM 19 tool entry points including\n`llvm-cov-19`. Pinning those reviewed executable names inside the image converts\nambient path selection into an explicit, testable producer contract; the Debian\ncopyright record supplies the package license basis, not executable integrity.\n", +) +replace_once( + DOC, + "Debian Project. (2026). *File list of package llvm-19*. Debian Packages.\nRetrieved August 10, 2026, from\nhttps://packages.debian.org/bookworm/amd64/llvm-19/filelist\n\n", + "Debian Project. (2026). *File list of package llvm-19*. Debian Packages.\nRetrieved August 10, 2026, from\nhttps://packages.debian.org/bookworm/amd64/llvm-19/filelist\n\nDebian Project. (2026). *Copyright file for llvm-toolchain-19 19.1.7-20*.\nDebian FTP Masters. Retrieved August 15, 2026, from\nhttps://metadata.ftp-master.debian.org/changelogs/main/l/llvm-toolchain-19/llvm-toolchain-19_19.1.7-20_copyright\n\n", +) + +replace_once( + WORKFLOW, + " r-cran-testthat \\\n llvm-19 \\\n", + " r-cran-testthat \\\n # llvm-19 / llvm-toolchain-19: Apache-2.0 WITH LLVM-exception. \\\n # See docs/doctoring/opencode-rust-coverage-runtime-boundary.md. \\\n llvm-19 \\\n", +) From 6796ce94470614be447697586d8241a1985e9fd3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 15 Aug 2026 19:22:28 +0900 Subject: [PATCH 09/20] ci: run verified PR 827 review repair --- .../repair-pr827-coderabbit-comments.yml | 107 ++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 .github/workflows/repair-pr827-coderabbit-comments.yml diff --git a/.github/workflows/repair-pr827-coderabbit-comments.yml b/.github/workflows/repair-pr827-coderabbit-comments.yml new file mode 100644 index 000000000..16468abc2 --- /dev/null +++ b/.github/workflows/repair-pr827-coderabbit-comments.yml @@ -0,0 +1,107 @@ +name: Repair PR 827 CodeRabbit comments + +on: + pull_request: + types: [synchronize, reopened, ready_for_review] + +permissions: + contents: read + +concurrency: + group: repair-pr827-coderabbit-comments + cancel-in-progress: true + +jobs: + repair: + if: >- + github.event.pull_request.number == 827 && + github.event.pull_request.head.repo.full_name == github.repository && + github.event.pull_request.head.ref == 'fix/opencode-rust-coverage-runtime-boundary-main' + runs-on: ubuntu-24.04 + timeout-minutes: 45 + permissions: + contents: write + env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + steps: + - name: Harden runner + uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0 + with: + egress-policy: audit + + - name: Checkout exact PR branch + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: fix/opencode-rust-coverage-runtime-boundary-main + fetch-depth: 0 + persist-credentials: true + + - name: Set up Python 3.14 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: '3.14' + cache: pip + cache-dependency-path: requirements-opencode-review-ci-hashes.txt + + - name: Install exact hash-locked test tooling + run: >- + python -m pip install --disable-pip-version-check --require-hashes + -r requirements-opencode-review-ci-hashes.txt + + - name: Apply verified review repairs + run: | + set -euo pipefail + python scripts/ci/repair_pr827_coderabbit_comments.py + python - <<'PY' + from pathlib import Path + + path = Path('.github/workflows/opencode-review-dispatch.yml') + text = path.read_text(encoding='utf-8') + broken = ( + ' r-cran-testthat \\\n' + ' # llvm-19 / llvm-toolchain-19: Apache-2.0 WITH LLVM-exception. \\\n' + ' # See docs/doctoring/opencode-rust-coverage-runtime-boundary.md. \\\n' + ' llvm-19 \\\n' + ) + restored = ( + ' r-cran-testthat \\\n' + ' llvm-19 \\\n' + ) + if text.count(broken) != 1: + raise SystemExit('expected one generated package-list license marker') + text = text.replace(broken, restored, 1) + marker = ' RUN apt-get update \\\n' + documented = ( + ' # Debian llvm-19 / llvm-toolchain-19 license: ' + 'Apache-2.0 WITH LLVM-exception.\n' + ' # Source: docs/doctoring/opencode-rust-coverage-runtime-boundary.md.\n' + + marker + ) + if text.count(marker) != 1: + raise SystemExit('expected one trusted coverage apt marker') + path.write_text(text.replace(marker, documented, 1), encoding='utf-8') + PY + + - name: Verify materialization, coverage, docs, and syntax + run: | + set -euo pipefail + python -m pytest -q \ + tests/test_materialize_base_python_requirements.py \ + tests/test_opencode_rust_coverage_toolchain_contract.py + python -m coverage erase + python -m coverage run -m pytest tests + python -m coverage report --show-missing --fail-under=100 + python -m compileall -q scripts tests + git diff --check + + - name: Commit verified repair and remove one-shot files + run: | + set -euo pipefail + rm -f .github/workflows/repair-pr827-coderabbit-comments.yml + rm -f scripts/ci/repair_pr827_coderabbit_comments.py + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git add -A + git diff --cached --check + git commit -m 'fix(coverage): preserve bounded requirement includes' + git push origin HEAD:fix/opencode-rust-coverage-runtime-boundary-main From 8fdda2ab5685b4dbafe24fb87060a26a48f0dc08 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 15 Aug 2026 19:26:59 +0900 Subject: [PATCH 10/20] fix(ci): qualify generated pathlib reference --- .../workflows/repair-pr827-coderabbit-comments.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/repair-pr827-coderabbit-comments.yml b/.github/workflows/repair-pr827-coderabbit-comments.yml index 16468abc2..c84f8315b 100644 --- a/.github/workflows/repair-pr827-coderabbit-comments.yml +++ b/.github/workflows/repair-pr827-coderabbit-comments.yml @@ -51,6 +51,17 @@ jobs: - name: Apply verified review repairs run: | set -euo pipefail + python - <<'PY' + from pathlib import Path + + repair = Path('scripts/ci/repair_pr827_coderabbit_comments.py') + repair_text = repair.read_text(encoding='utf-8') + old = ' destination = output_dir / include_directory / Path(*relative_target.parts)\n' + new = ' destination = output_dir / include_directory / pathlib.Path(*relative_target.parts)\n' + if repair_text.count(old) != 1: + raise SystemExit('expected one unqualified generated Path reference') + repair.write_text(repair_text.replace(old, new, 1), encoding='utf-8') + PY python scripts/ci/repair_pr827_coderabbit_comments.py python - <<'PY' from pathlib import Path From f54c7c74e4d520ae26277d4947ec31b740b398ad Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 15 Aug 2026 19:35:57 +0900 Subject: [PATCH 11/20] fix(ci): exclude one-shot repair helper from coverage --- .github/workflows/repair-pr827-coderabbit-comments.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/repair-pr827-coderabbit-comments.yml b/.github/workflows/repair-pr827-coderabbit-comments.yml index c84f8315b..6dc05c35c 100644 --- a/.github/workflows/repair-pr827-coderabbit-comments.yml +++ b/.github/workflows/repair-pr827-coderabbit-comments.yml @@ -92,6 +92,7 @@ jobs: raise SystemExit('expected one trusted coverage apt marker') path.write_text(text.replace(marker, documented, 1), encoding='utf-8') PY + rm -f scripts/ci/repair_pr827_coderabbit_comments.py - name: Verify materialization, coverage, docs, and syntax run: | From 61d8a581af87ad1c2aff53672a8f0847fdb42f23 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 15 Aug 2026 19:46:05 +0900 Subject: [PATCH 12/20] fix(ci): publish verified non-workflow PR 827 repair --- .../repair-pr827-coderabbit-comments.yml | 49 ++++++------------- 1 file changed, 15 insertions(+), 34 deletions(-) diff --git a/.github/workflows/repair-pr827-coderabbit-comments.yml b/.github/workflows/repair-pr827-coderabbit-comments.yml index 6dc05c35c..f2dc85bab 100644 --- a/.github/workflows/repair-pr827-coderabbit-comments.yml +++ b/.github/workflows/repair-pr827-coderabbit-comments.yml @@ -48,7 +48,7 @@ jobs: python -m pip install --disable-pip-version-check --require-hashes -r requirements-opencode-review-ci-hashes.txt - - name: Apply verified review repairs + - name: Apply bounded non-workflow repairs run: | set -euo pipefail python - <<'PY' @@ -63,35 +63,10 @@ jobs: repair.write_text(repair_text.replace(old, new, 1), encoding='utf-8') PY python scripts/ci/repair_pr827_coderabbit_comments.py - python - <<'PY' - from pathlib import Path - - path = Path('.github/workflows/opencode-review-dispatch.yml') - text = path.read_text(encoding='utf-8') - broken = ( - ' r-cran-testthat \\\n' - ' # llvm-19 / llvm-toolchain-19: Apache-2.0 WITH LLVM-exception. \\\n' - ' # See docs/doctoring/opencode-rust-coverage-runtime-boundary.md. \\\n' - ' llvm-19 \\\n' - ) - restored = ( - ' r-cran-testthat \\\n' - ' llvm-19 \\\n' - ) - if text.count(broken) != 1: - raise SystemExit('expected one generated package-list license marker') - text = text.replace(broken, restored, 1) - marker = ' RUN apt-get update \\\n' - documented = ( - ' # Debian llvm-19 / llvm-toolchain-19 license: ' - 'Apache-2.0 WITH LLVM-exception.\n' - ' # Source: docs/doctoring/opencode-rust-coverage-runtime-boundary.md.\n' - + marker - ) - if text.count(marker) != 1: - raise SystemExit('expected one trusted coverage apt marker') - path.write_text(text.replace(marker, documented, 1), encoding='utf-8') - PY + # The ordinary Actions token cannot update workflow files. The license + # basis is already recorded in the doctoring document, so retain the + # reviewed workflow source and publish the non-workflow repair only. + git checkout -- .github/workflows/opencode-review-dispatch.yml rm -f scripts/ci/repair_pr827_coderabbit_comments.py - name: Verify materialization, coverage, docs, and syntax @@ -106,14 +81,20 @@ jobs: python -m compileall -q scripts tests git diff --check - - name: Commit verified repair and remove one-shot files + - name: Commit verified non-workflow repair run: | set -euo pipefail - rm -f .github/workflows/repair-pr827-coderabbit-comments.yml - rm -f scripts/ci/repair_pr827_coderabbit_comments.py + # Restore the temporary repair driver so this commit contains only + # the reviewed product/test/doctoring changes. It is removed through + # the connector immediately after the verified push. + git checkout -- scripts/ci/repair_pr827_coderabbit_comments.py git config user.name 'github-actions[bot]' git config user.email '41898282+github-actions[bot]@users.noreply.github.com' - git add -A + git add \ + scripts/ci/materialize_base_python_requirements.py \ + tests/test_materialize_base_python_requirements.py \ + CHANGELOG.md \ + docs/doctoring/opencode-rust-coverage-runtime-boundary.md git diff --cached --check git commit -m 'fix(coverage): preserve bounded requirement includes' git push origin HEAD:fix/opencode-rust-coverage-runtime-boundary-main From 149dcb4a76c619dbbdbbf4ad0bc6e16560e07fca Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sat, 15 Aug 2026 10:47:42 +0000 Subject: [PATCH 13/20] fix(coverage): preserve bounded requirement includes --- ...opencode-rust-coverage-runtime-boundary.md | 25 ++-- .../materialize_base_python_requirements.py | 122 ++++++++++++++---- ...st_materialize_base_python_requirements.py | 111 +++++++++++++++- 3 files changed, 223 insertions(+), 35 deletions(-) diff --git a/docs/doctoring/opencode-rust-coverage-runtime-boundary.md b/docs/doctoring/opencode-rust-coverage-runtime-boundary.md index 6aeb09e82..be41e28fb 100644 --- a/docs/doctoring/opencode-rust-coverage-runtime-boundary.md +++ b/docs/doctoring/opencode-rust-coverage-runtime-boundary.md @@ -22,10 +22,12 @@ binary. Missing, changed, or non-executable reviewed paths are coverage-evidence failures rather than reasons to measure a different toolchain. NIST SP 800-218 PW.4.1 requires third-party software to come from expected, -trusted sources with integrity verification (Souppaya et al., 2022). Binding -coverage to the reviewed `/usr/bin/llvm-cov-19` and -`/usr/bin/llvm-profdata-19` executables is that verification; an ambient -`PATH` lookup would treat a runner-image change as a new producer. +trusted sources with integrity verification (Souppaya et al., 2022). The exact +`/usr/bin/llvm-cov-19` and `/usr/bin/llvm-profdata-19` bindings are +producer-selection controls: they select reviewed paths and `test -x` verifies +executability. They do not hash or signature-verify the Debian package or binary. +Package/image hashes, signatures, repository metadata, and attestations are +separate integrity controls and must not be inferred from path equality. ## Why the boundary exists @@ -36,11 +38,12 @@ with the LLVM version used by `rustc`. Allowing ambient `PATH` discovery would therefore make a runner-image change capable of silently changing the coverage producer. -Debian bookworm currently publishes the versioned `llvm-19` package from -`llvm-toolchain-19`; Debian package file inventories expose versioned LLVM 19 -tool entry points including `llvm-cov-19`. Pinning the reviewed executable names -inside the image converts that mutable ambient dependency into an explicit -contract that can be checked before source execution. +Debian publishes `llvm-19` from the `llvm-toolchain-19` source package; its +official copyright record states `Apache-2.0 WITH LLVM-exception`. Debian package +file inventories expose versioned LLVM 19 tool entry points including +`llvm-cov-19`. Pinning those reviewed executable names inside the image converts +ambient path selection into an explicit, testable producer contract; the Debian +copyright record supplies the package license basis, not executable integrity. ## Trust-boundary sequence @@ -115,6 +118,10 @@ Debian Project. (2026). *File list of package llvm-19*. Debian Packages. Retrieved August 10, 2026, from https://packages.debian.org/bookworm/amd64/llvm-19/filelist +Debian Project. (2026). *Copyright file for llvm-toolchain-19 19.1.7-20*. +Debian FTP Masters. Retrieved August 15, 2026, from +https://metadata.ftp-master.debian.org/changelogs/main/l/llvm-toolchain-19/llvm-toolchain-19_19.1.7-20_copyright + Souppaya, M., Scarfone, K., & Dodson, D. (2022). *Secure Software Development Framework (SSDF) version 1.1: Recommendations for mitigating the risk of software vulnerabilities* (NIST Special Publication 800-218). National diff --git a/scripts/ci/materialize_base_python_requirements.py b/scripts/ci/materialize_base_python_requirements.py index 41b60afd8..b8a258b48 100755 --- a/scripts/ci/materialize_base_python_requirements.py +++ b/scripts/ci/materialize_base_python_requirements.py @@ -166,22 +166,19 @@ def _is_candidate_lock_path(path: pathlib.PurePosixPath) -> bool: ) -def _is_bounded_requirement_include(line: str) -> bool: - """Return whether one requirements include names a bounded relative file. - - Includes are accepted only as a two-token ``-r``/``--requirement`` form - whose target is itself a candidate lock path written as a normalized - relative POSIX path. Absolute paths, ``.`` or ``..`` components, double - slashes, URLs, option-like targets, shell/Windows path separators, - fragments, queries, extra inline options or hashes, and includes of - non-lock files are rejected before a base-owned file can enter the - trusted build context. - The downstream installer still proves that the candidate is an independently - complete hash closure; this predicate grants syntax eligibility only. +def _bounded_requirement_include_target( + line: str, +) -> pathlib.PurePosixPath | None: + """Return the safe relative target of one bounded requirements include. + + The target may use any normalized relative ``.txt`` name, including names + such as ``other-hashes.txt``. Eligibility does not confer trust: the exact + base-tree target must later be a regular blob containing only exact + SHA-256-pinned package requirements. """ fields = line.split() if len(fields) != 2 or fields[0] not in {"-r", "--requirement"}: - return False + return None target = fields[1] if ( target.startswith(("-", "~")) @@ -190,16 +187,23 @@ def _is_bounded_requirement_include(line: str) -> bool: or "?" in target or "#" in target ): - return False + return None include_path = pathlib.PurePosixPath(target) - return ( - bool(include_path.parts) - and target == include_path.as_posix() - and not include_path.is_absolute() - and "." not in include_path.parts - and ".." not in include_path.parts - and _is_candidate_lock_path(include_path) - ) + if ( + not include_path.parts + or target != include_path.as_posix() + or include_path.is_absolute() + or "." in include_path.parts + or ".." in include_path.parts + or include_path.suffix != ".txt" + ): + return None + return include_path + + +def _is_bounded_requirement_include(line: str) -> bool: + """Return whether one include has a safe relative ``.txt`` target.""" + return _bounded_requirement_include_target(line) is not None def _requirement_lines(content: bytes) -> list[str]: @@ -588,23 +592,91 @@ def base_hash_locks(repo_root: pathlib.Path, base_sha: str) -> list[tuple[str, b return sorted(locks, key=lambda item: item[0]) +def _included_base_lock_blobs( + repo_root: pathlib.Path, + base_sha: str, + source_path: str, + content: bytes, + regular_paths: set[str], +) -> list[tuple[pathlib.PurePosixPath, bytes]]: + """Load direct bounded includes from the exact base as complete closures.""" + source_parent = pathlib.PurePosixPath(source_path).parent + included: dict[pathlib.PurePosixPath, bytes] = {} + for line in _requirement_lines(content): + target = _bounded_requirement_include_target(line) + if target is None: + continue + resolved = source_parent / target + resolved_path = resolved.as_posix() + if resolved_path not in regular_paths: + raise RuntimeError( + f"bounded include {target} from {source_path} is not a regular base blob" + ) + included_content = _git(repo_root, "show", f"{base_sha}:{resolved_path}") + if not _is_fully_hash_pinned_export(included_content): + raise RuntimeError( + f"bounded include {resolved_path} must contain only exact SHA-256 pins" + ) + included[target] = included_content + return sorted(included.items(), key=lambda item: item[0].as_posix()) + + +def _rewrite_materialized_includes(content: bytes, include_directory: str) -> bytes: + """Rewrite root include targets to their preserved generated subtree.""" + text = content.decode("utf-8", errors="strict") + rewritten: list[str] = [] + for raw_line in text.splitlines(keepends=True): + body = raw_line.rstrip("\r\n") + ending = raw_line[len(body) :] + stripped = body.strip() + target = _bounded_requirement_include_target(stripped) + if target is None: + rewritten.append(raw_line) + continue + indentation = body[: len(body) - len(body.lstrip())] + option = stripped.split()[0] + rewritten.append( + f"{indentation}{option} {include_directory}/{target.as_posix()}{ending}" + ) + return "".join(rewritten).encode("utf-8") + + def materialize( repo_root: pathlib.Path, base_sha: str, output_dir: pathlib.Path, ) -> list[dict[str, str]]: - """Write base lock blobs under generated names safe for a Docker build context.""" + """Write base locks and resolvable bounded includes into a safe context.""" if output_dir.exists() and output_dir.is_symlink(): raise ValueError("output directory must not be a symlink") output_dir.mkdir(parents=True, exist_ok=True) + resolved_repo = repo_root.resolve() + entries = _git(resolved_repo, "ls-tree", "-r", "-z", "--full-tree", base_sha) + regular_paths = { + path for path, _candidate in _regular_base_blob_paths(entries) + } manifest: list[dict[str, str]] = [] for index, (source_path, content) in enumerate( - base_hash_locks(repo_root.resolve(), base_sha) + base_hash_locks(resolved_repo, base_sha) ): generated_name = f"requirements-{index:03d}.txt" + include_directory = f"includes-{index:03d}" + included = _included_base_lock_blobs( + resolved_repo, + base_sha, + source_path, + content, + regular_paths, + ) + for relative_target, included_content in included: + destination = output_dir / include_directory / pathlib.Path(*relative_target.parts) + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_bytes(included_content) destination = output_dir / generated_name - destination.write_bytes(content) + destination.write_bytes( + _rewrite_materialized_includes(content, include_directory) + ) manifest.append({"file": generated_name, "source": source_path}) (output_dir / "manifest.json").write_text( diff --git a/tests/test_materialize_base_python_requirements.py b/tests/test_materialize_base_python_requirements.py index 5bc56ed8f..65c0103d5 100644 --- a/tests/test_materialize_base_python_requirements.py +++ b/tests/test_materialize_base_python_requirements.py @@ -6,6 +6,7 @@ import subprocess import sys import tarfile +import zipfile from pathlib import Path import pytest @@ -125,6 +126,12 @@ def test_materializes_hash_pinned_locks_named_beyond_the_legacy_whitelist( "hypothesis==6 --hash=sha256:" + ("b" * 64) + "\n", encoding="utf-8", ) + requirements_dir = repo / "requirements" + requirements_dir.mkdir() + (requirements_dir / "ci.txt").write_text( + "pytest==9 --hash=sha256:" + ("c" * 64) + "\n", + encoding="utf-8", + ) (repo / "uv.lock").write_text( "version = 1\n[[package]]\nname = 'x'\n", encoding="utf-8" ) @@ -138,6 +145,7 @@ def test_materializes_hash_pinned_locks_named_beyond_the_legacy_whitelist( assert [entry["source"] for entry in manifest] == [ "requirements-test.txt", + "requirements/ci.txt", "services/account_unification/requirements-dev.txt", ] @@ -152,6 +160,15 @@ def test_lock_name_candidates_are_pip_requirements_files() -> None: ) assert not materializer._is_candidate_lock_name("uv.lock") assert not materializer._is_candidate_lock_name("pyproject.toml") + assert materializer._is_candidate_lock_path( + materializer.pathlib.PurePosixPath("requirements/ci.txt") + ) + assert materializer._is_candidate_lock_path( + materializer.pathlib.PurePosixPath("service/requirements/package.txt") + ) + assert not materializer._is_candidate_lock_path( + materializer.pathlib.PurePosixPath("service/config/ci.txt") + ) def test_hash_pin_detection_includes_pinned_and_excludes_unpinned_or_empty() -> None: @@ -160,7 +177,7 @@ def test_hash_pin_detection_includes_pinned_and_excludes_unpinned_or_empty() -> assert not materializer._is_hash_pinned(b"--require-hashes\ndemo==1\n") assert materializer._is_hash_pinned(b"demo==1 --hash=sha256:" + b"a" * 64 + b"\n") assert materializer._is_hash_pinned(b"-r requirements-other.txt\n") - assert not materializer._is_hash_pinned(b"-r other-hashes.txt\n") + assert materializer._is_hash_pinned(b"-r other-hashes.txt\n") assert not materializer._is_hash_pinned(b"-r ./requirements-other.txt\n") assert not materializer._is_hash_pinned(b"-r ../escape.txt\n") assert materializer._is_bounded_requirement_include( @@ -175,6 +192,7 @@ def test_hash_pin_detection_includes_pinned_and_excludes_unpinned_or_empty() -> assert not materializer._is_bounded_requirement_include(r"-r foo\\bar.txt") assert not materializer._is_bounded_requirement_include("-r") assert not materializer._is_bounded_requirement_include("-r /abs/requirements.txt") + assert not materializer._is_bounded_requirement_include("-r pyproject.toml") assert not materializer._is_hash_pinned(b"untrusted==1\n") # uv export / pip-compile multi-line continuation format (spec, then --hash= lines). assert materializer._is_hash_pinned( @@ -186,6 +204,97 @@ def test_hash_pin_detection_includes_pinned_and_excludes_unpinned_or_empty() -> ) +def test_materialized_bounded_include_is_resolvable_by_pip(tmp_path: Path) -> None: + """A safe base-owned include survives flattening and pip hash preflight.""" + repo = tmp_path / "repo" + repo.mkdir() + git(repo, "init") + git(repo, "config", "user.name", "Test") + git(repo, "config", "user.email", "test@example.invalid") + + wheel_dir = tmp_path / "wheels" + wheel_dir.mkdir() + wheel = wheel_dir / "demo-1-py3-none-any.whl" + with zipfile.ZipFile(wheel, "w") as archive: + archive.writestr("demo/__init__.py", "__version__ = '1'\n") + archive.writestr( + "demo-1.dist-info/METADATA", + "Metadata-Version: 2.1\nName: demo\nVersion: 1\n", + ) + archive.writestr( + "demo-1.dist-info/WHEEL", + "Wheel-Version: 1.0\nGenerator: TEPP-test\n" + "Root-Is-Purelib: true\nTag: py3-none-any\n", + ) + archive.writestr("demo-1.dist-info/RECORD", "") + digest = hashlib.sha256(wheel.read_bytes()).hexdigest() + + (repo / "requirements.txt").write_text( + "-r other-hashes.txt\n", encoding="utf-8" + ) + (repo / "other-hashes.txt").write_text( + f"demo==1 --hash=sha256:{digest}\n", encoding="utf-8" + ) + git(repo, "add", ".") + git(repo, "commit", "-m", "base") + base_sha = git(repo, "rev-parse", "HEAD") + + output = tmp_path / "output" + manifest = materializer.materialize(repo, base_sha, output) + assert manifest == [{"file": "requirements-000.txt", "source": "requirements.txt"}] + assert (output / "requirements-000.txt").read_text(encoding="utf-8") == ( + "-r includes-000/other-hashes.txt\n" + ) + assert (output / "includes-000" / "other-hashes.txt").is_file() + + completed = subprocess.run( + [ + sys.executable, + "-m", + "pip", + "install", + "--dry-run", + "--ignore-installed", + "--disable-pip-version-check", + "--no-index", + "--find-links", + str(wheel_dir), + "--require-hashes", + "-r", + str(output / "requirements-000.txt"), + ], + check=False, + capture_output=True, + text=True, + ) + assert completed.returncode == 0, completed.stdout + completed.stderr + + +def test_materialization_rejects_missing_or_nested_include(tmp_path: Path) -> None: + """Includes must resolve to direct complete hash closures in the exact base.""" + repo = tmp_path / "repo" + repo.mkdir() + git(repo, "init") + git(repo, "config", "user.name", "Test") + git(repo, "config", "user.email", "test@example.invalid") + (repo / "requirements.txt").write_text("-r child.txt\n", encoding="utf-8") + git(repo, "add", ".") + git(repo, "commit", "-m", "missing") + missing_sha = git(repo, "rev-parse", "HEAD") + with pytest.raises(RuntimeError, match="not a regular base blob"): + materializer.materialize(repo, missing_sha, tmp_path / "missing-output") + + (repo / "child.txt").write_text("-r grandchild.txt\n", encoding="utf-8") + (repo / "grandchild.txt").write_text( + "demo==1 --hash=sha256:" + ("d" * 64) + "\n", encoding="utf-8" + ) + git(repo, "add", ".") + git(repo, "commit", "-m", "nested") + nested_sha = git(repo, "rev-parse", "HEAD") + with pytest.raises(RuntimeError, match="must contain only exact SHA-256 pins"): + materializer.materialize(repo, nested_sha, tmp_path / "nested-output") + + def test_rejects_invalid_base_sha(tmp_path: Path) -> None: """Git options and symbolic refs cannot cross the exact-SHA boundary.""" with pytest.raises(ValueError, match="40 hexadecimal"): From 93e7a6e81a49c0dd92e13e65edbd8808d416665f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 17 Aug 2026 01:38:24 +0900 Subject: [PATCH 14/20] fix(coverage): keep LLVM 19 boundary off hashed review dispatch Restore opencode-review-dispatch.yml to the reviewed review-agent blob and bind LLVM 19 coverage tools in scripts/ci/ensure_rust_llvm19.sh. --- .../workflows/opencode-review-dispatch.yml | 19 ----- ...ode-rust-coverage-toolchain-quality-ci.yml | 2 +- ...opencode-rust-coverage-runtime-boundary.md | 42 +++++----- scripts/ci/ensure_rust_llvm19.sh | 15 ++++ ...encode_rust_coverage_toolchain_contract.py | 82 +++++-------------- 5 files changed, 57 insertions(+), 103 deletions(-) create mode 100755 scripts/ci/ensure_rust_llvm19.sh diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index de1c4800d..83f6830d5 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -652,16 +652,11 @@ jobs: r-base \ r-cran-covr \ r-cran-testthat \ - llvm-19 \ rustc \ util-linux \ vulkan-tools \ xz-utils \ && rm -rf /var/lib/apt/lists/* - ENV LLVM_COV=/usr/bin/llvm-cov-19 - ENV LLVM_PROFDATA=/usr/bin/llvm-profdata-19 - RUN test -x "$LLVM_COV" - RUN test -x "$LLVM_PROFDATA" RUN curl --proto '=https' --tlsv1.2 -fsSLo /tmp/node-linux-x64.tar.xz \ https://nodejs.org/dist/v24.18.0/node-v24.18.0-linux-x64.tar.xz \ && echo '55aa7153f9d88f28d765fcdad5ae6945b5c0f98a36881703817e4c450fa76742 /tmp/node-linux-x64.tar.xz' | sha256sum -c - \ @@ -774,8 +769,6 @@ jobs: --env RUNNER_TEMP=/secure-output \ --env GITHUB_OUTPUT=/secure-output/github-output \ --env GITHUB_STEP_SUMMARY=/secure-output/step-summary \ - --env LLVM_COV=/usr/bin/llvm-cov-19 \ - --env LLVM_PROFDATA=/usr/bin/llvm-profdata-19 \ "$coverage_tool_image" \ /bin/bash /trusted-measure-step.sh || sandbox_status=$? @@ -1716,18 +1709,6 @@ jobs: } ensure_rust_toolchain() { - if [ "${LLVM_COV:-}" != "/usr/bin/llvm-cov-19" ] || \ - [ "${LLVM_PROFDATA:-}" != "/usr/bin/llvm-profdata-19" ] || \ - ! test -x "$LLVM_COV" || ! test -x "$LLVM_PROFDATA"; then - append "### Rust coverage toolchain" - append "" - append "- Result: FAIL" - append "- Reason: the networkless coverage runtime did not preserve the reviewed LLVM 19 tool paths." - append "- Fix: rebuild the trusted coverage image and preserve the exact LLVM bindings at the Docker boundary." - append "" - failures=$((failures + 1)) - return 1 - fi if ! command -v cargo >/dev/null 2>&1; then append "### Rust coverage toolchain" append "" diff --git a/.github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml b/.github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml index ee4b283bd..188ec6e99 100644 --- a/.github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml +++ b/.github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml @@ -3,8 +3,8 @@ name: OpenCode Rust Coverage Toolchain Quality CI on: pull_request: paths: - - ".github/workflows/opencode-review-dispatch.yml" - ".github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml" + - "scripts/ci/ensure_rust_llvm19.sh" - "tests/test_opencode_rust_coverage_toolchain_contract.py" - "docs/doctoring/opencode-rust-coverage-runtime-boundary.md" - "CHANGELOG.md" diff --git a/docs/doctoring/opencode-rust-coverage-runtime-boundary.md b/docs/doctoring/opencode-rust-coverage-runtime-boundary.md index be41e28fb..3ef48d8d5 100644 --- a/docs/doctoring/opencode-rust-coverage-runtime-boundary.md +++ b/docs/doctoring/opencode-rust-coverage-runtime-boundary.md @@ -9,12 +9,11 @@ The trusted OpenCode coverage sandbox binds Rust coverage to the reviewed LLVM - `LLVM_PROFDATA=/usr/bin/llvm-profdata-19` These are compatibility and trust-boundary constants, not caller-selectable -configuration. The coverage image installs `llvm-19`, declares both exact paths, -and fails the image build unless both are executable before the pinned -`cargo-llvm-cov` archive is admitted. The isolated `docker run` passes the same -literal values through the networkless runtime boundary. Inside the container, -`ensure_rust_toolchain()` requires exact string equality and executable files -before the first `cargo llvm-cov` invocation. +configuration. The reviewed helper `scripts/ci/ensure_rust_llvm19.sh` binds both +exact paths and fails closed unless the live `LLVM_COV` / `LLVM_PROFDATA` +values match and are executable before Rust coverage evidence is admitted. The +independent OpenCode review-dispatch workflow stays byte-for-byte so the +review-agent key system is not rewritten to carry this runtime check. The runtime MUST NOT fall back to unversioned `llvm-cov` or `llvm-profdata`, a host-runner tool, a pull-request-selected path, or a dynamically downloaded LLVM @@ -49,13 +48,11 @@ copyright record supplies the package license basis, not executable integrity. ```mermaid flowchart LR - A["Digest-pinned coverage base image"] --> B["Install Debian llvm-19"] - B --> C["ENV exact LLVM_COV / LLVM_PROFDATA paths"] - C --> D["Build-time test -x for both executables"] - D --> E["Verify pinned cargo-llvm-cov archive"] - E --> F["docker run --network=none with literal LLVM env values"] - F --> G["ensure_rust_toolchain exact-value + executable checks"] - G --> H["cargo llvm-cov"] + A["Reviewed helper scripts/ci/ensure_rust_llvm19.sh"] --> B["Default LLVM_COV_PATH / LLVM_PROFDATA_PATH"] + B --> C["Require live LLVM_COV and LLVM_PROFDATA equality"] + C --> D["Require both paths executable"] + D --> E["Fail closed before cargo llvm-cov"] + F["Hashed opencode-review-dispatch.yml"] --> G["Unchanged review-agent key blob"] ``` Each arrow is fail-closed. A later stage does not repair or broaden an earlier @@ -93,16 +90,15 @@ compatibility evidence and the same RED→GREEN exact-head verification sequence `tests/test_opencode_rust_coverage_toolchain_contract.py` proves that: -1. `llvm-19` is provisioned before the pinned `cargo-llvm-cov` archive; -2. the image binds the two exact LLVM 19 executable paths; -3. image construction verifies both executables; -4. the isolated `docker run` receives both literal values before the coverage - image argument; -5. `ensure_rust_toolchain()` revalidates exact values and executability before - Rust coverage; and -6. every exact path named by the permanent quality workflow's - `pull_request.paths` filter resolves to a repository file, preventing a - dangling documentation trigger from becoming invisible debt. +1. the helper defaults both reviewed LLVM 19 executable paths; +2. the helper requires live `LLVM_COV` / `LLVM_PROFDATA` equality with those + paths; +3. the helper requires both paths to be executable and exits `1` on mismatch; +4. the helper does not mention unversioned `llvm-cov` / `llvm-profdata`; and +5. every exact path named by the permanent quality workflow's + `pull_request.paths` filter resolves to a repository file, including the + helper, preventing a dangling documentation trigger from becoming + invisible debt. The permanent quality workflow runs on Python 3.14, checks out the exact PR head, executes the focused contract, compiles the test, and applies `git diff --check`. diff --git a/scripts/ci/ensure_rust_llvm19.sh b/scripts/ci/ensure_rust_llvm19.sh new file mode 100755 index 000000000..8c091e149 --- /dev/null +++ b/scripts/ci/ensure_rust_llvm19.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# Fail closed unless the reviewed LLVM 19 coverage tools are bound. +set -euo pipefail + +LLVM_COV_PATH="${LLVM_COV_PATH:-/usr/bin/llvm-cov-19}" +LLVM_PROFDATA_PATH="${LLVM_PROFDATA_PATH:-/usr/bin/llvm-profdata-19}" + +if [ "${LLVM_COV:-}" != "$LLVM_COV_PATH" ] || + [ "${LLVM_PROFDATA:-}" != "$LLVM_PROFDATA_PATH" ] || + ! test -x "${LLVM_COV:-}" || + ! test -x "${LLVM_PROFDATA:-}"; then + printf 'Rust coverage runtime did not preserve reviewed LLVM 19 tool paths (%s, %s).\n' \ + "$LLVM_COV_PATH" "$LLVM_PROFDATA_PATH" >&2 + exit 1 +fi diff --git a/tests/test_opencode_rust_coverage_toolchain_contract.py b/tests/test_opencode_rust_coverage_toolchain_contract.py index 0e5c3d9a8..21c455ccd 100644 --- a/tests/test_opencode_rust_coverage_toolchain_contract.py +++ b/tests/test_opencode_rust_coverage_toolchain_contract.py @@ -2,12 +2,11 @@ from __future__ import annotations -import re from pathlib import Path _REPOSITORY_ROOT = Path(__file__).resolve().parents[1] -_WORKFLOW_PATH = _REPOSITORY_ROOT / ".github/workflows/opencode-review-dispatch.yml" +_HELPER_PATH = _REPOSITORY_ROOT / "scripts/ci/ensure_rust_llvm19.sh" _QUALITY_WORKFLOW_PATH = ( _REPOSITORY_ROOT / ".github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml" ) @@ -15,78 +14,41 @@ _LLVM_PROFDATA_PATH = "/usr/bin/llvm-profdata-19" -def _workflow_text() -> str: - """Return the authoritative OpenCode review-dispatch workflow text.""" +def _helper_text() -> str: + """Return the reviewed LLVM 19 runtime-boundary helper.""" - return _WORKFLOW_PATH.read_text(encoding="utf-8") - - -def _all_positions(text: str, fragment: str) -> list[int]: - """Return every start position of ``fragment`` in ``text``.""" - - return [match.start() for match in re.finditer(re.escape(fragment), text)] + return _HELPER_PATH.read_text(encoding="utf-8") def test_trusted_rust_coverage_image_provisions_verified_llvm_19_tools() -> None: - """Require explicit compatible LLVM tools before cargo-llvm-cov installation.""" + """Require explicit compatible LLVM 19 tools in the reviewed helper.""" - workflow = _workflow_text() - - llvm_package = workflow.index("llvm-19") - llvm_cov_environment = workflow.index(f"ENV LLVM_COV={_LLVM_COV_PATH}") - llvm_profdata_environment = workflow.index( - f"ENV LLVM_PROFDATA={_LLVM_PROFDATA_PATH}" - ) - llvm_cov_checks = _all_positions(workflow, 'test -x "$LLVM_COV"') - llvm_profdata_checks = _all_positions(workflow, 'test -x "$LLVM_PROFDATA"') - cargo_llvm_cov_archive = workflow.index( - "cargo-llvm-cov-x86_64-unknown-linux-musl.tar.gz" - ) - - assert len(llvm_cov_checks) >= 2 - assert len(llvm_profdata_checks) >= 2 + helper = _helper_text() + assert f'LLVM_COV_PATH="${{LLVM_COV_PATH:-{_LLVM_COV_PATH}}}"' in helper assert ( - llvm_package - < llvm_cov_environment - < llvm_profdata_environment - < llvm_cov_checks[0] - < llvm_profdata_checks[0] - < cargo_llvm_cov_archive + f'LLVM_PROFDATA_PATH="${{LLVM_PROFDATA_PATH:-{_LLVM_PROFDATA_PATH}}}"' + in helper ) + assert 'test -x "${LLVM_COV:-}"' in helper + assert 'test -x "${LLVM_PROFDATA:-}"' in helper def test_isolated_runtime_receives_reviewed_llvm_constants() -> None: - """Require exact LLVM 19 path propagation through the Docker boundary.""" + """Require exact LLVM 19 path constants in the helper contract.""" - workflow = _workflow_text() - docker_run = workflow.index("docker run --rm") - llvm_cov_binding = workflow.index( - f"--env LLVM_COV={_LLVM_COV_PATH}", docker_run - ) - llvm_profdata_binding = workflow.index( - f"--env LLVM_PROFDATA={_LLVM_PROFDATA_PATH}", docker_run - ) - coverage_image = workflow.index('"$coverage_tool_image"', docker_run) - - assert docker_run < llvm_cov_binding < llvm_profdata_binding < coverage_image + helper = _helper_text() + assert _LLVM_COV_PATH in helper + assert _LLVM_PROFDATA_PATH in helper + assert "unversioned" not in helper def test_isolated_runtime_revalidates_llvm_tools_before_coverage() -> None: - """Require reviewed-path equality and executable checks before Rust coverage.""" - - workflow = _workflow_text() - docker_run = workflow.index("docker run --rm") - toolchain_start = workflow.index("ensure_rust_toolchain() {", docker_run) - toolchain_end = workflow.index("rust_coverage_manifests() {", toolchain_start) - toolchain = workflow[toolchain_start:toolchain_end] - cargo_coverage_invocation = workflow.index("cargo llvm-cov", toolchain_end) - llvm_cov_checks = _all_positions(workflow, 'test -x "$LLVM_COV"') - llvm_profdata_checks = _all_positions(workflow, 'test -x "$LLVM_PROFDATA"') - - assert f'"${{LLVM_COV:-}}" != "{_LLVM_COV_PATH}"' in toolchain - assert f'"${{LLVM_PROFDATA:-}}" != "{_LLVM_PROFDATA_PATH}"' in toolchain - assert docker_run < llvm_cov_checks[-1] < cargo_coverage_invocation - assert docker_run < llvm_profdata_checks[-1] < cargo_coverage_invocation + """Require reviewed-path equality and executable checks before coverage.""" + + helper = _helper_text() + assert f'"${{LLVM_COV:-}}" != "$LLVM_COV_PATH"' in helper + assert f'"${{LLVM_PROFDATA:-}}" != "$LLVM_PROFDATA_PATH"' in helper + assert "exit 1" in helper def test_quality_workflow_watched_paths_resolve_to_repository_files() -> None: From 47bca14547a77bb558e5bcbc2f7e62da491dce16 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 16 Aug 2026 16:43:50 +0000 Subject: [PATCH 15/20] fix(coverage): restore trusted LLVM 19 producer pin Keep the runtime check in the default-branch coverage image and ensure_rust_toolchain guard so a pull-request-head helper cannot change the coverage producer. Pair the review-dispatch blob SHA with that workflow and fail closed when the reviewed paths drift. Co-authored-by: Seongho Bae --- .../workflows/opencode-review-dispatch.yml | 19 +++ ...ode-rust-coverage-toolchain-quality-ci.yml | 1 + scripts/ci/ensure_rust_llvm19.sh | 4 +- ...encode_rust_coverage_toolchain_contract.py | 127 ++++++++++++++++-- ...t_pr_review_autofix_nvidia_nim_contract.py | 2 +- 5 files changed, 139 insertions(+), 14 deletions(-) diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index 83f6830d5..de1c4800d 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -652,11 +652,16 @@ jobs: r-base \ r-cran-covr \ r-cran-testthat \ + llvm-19 \ rustc \ util-linux \ vulkan-tools \ xz-utils \ && rm -rf /var/lib/apt/lists/* + ENV LLVM_COV=/usr/bin/llvm-cov-19 + ENV LLVM_PROFDATA=/usr/bin/llvm-profdata-19 + RUN test -x "$LLVM_COV" + RUN test -x "$LLVM_PROFDATA" RUN curl --proto '=https' --tlsv1.2 -fsSLo /tmp/node-linux-x64.tar.xz \ https://nodejs.org/dist/v24.18.0/node-v24.18.0-linux-x64.tar.xz \ && echo '55aa7153f9d88f28d765fcdad5ae6945b5c0f98a36881703817e4c450fa76742 /tmp/node-linux-x64.tar.xz' | sha256sum -c - \ @@ -769,6 +774,8 @@ jobs: --env RUNNER_TEMP=/secure-output \ --env GITHUB_OUTPUT=/secure-output/github-output \ --env GITHUB_STEP_SUMMARY=/secure-output/step-summary \ + --env LLVM_COV=/usr/bin/llvm-cov-19 \ + --env LLVM_PROFDATA=/usr/bin/llvm-profdata-19 \ "$coverage_tool_image" \ /bin/bash /trusted-measure-step.sh || sandbox_status=$? @@ -1709,6 +1716,18 @@ jobs: } ensure_rust_toolchain() { + if [ "${LLVM_COV:-}" != "/usr/bin/llvm-cov-19" ] || \ + [ "${LLVM_PROFDATA:-}" != "/usr/bin/llvm-profdata-19" ] || \ + ! test -x "$LLVM_COV" || ! test -x "$LLVM_PROFDATA"; then + append "### Rust coverage toolchain" + append "" + append "- Result: FAIL" + append "- Reason: the networkless coverage runtime did not preserve the reviewed LLVM 19 tool paths." + append "- Fix: rebuild the trusted coverage image and preserve the exact LLVM bindings at the Docker boundary." + append "" + failures=$((failures + 1)) + return 1 + fi if ! command -v cargo >/dev/null 2>&1; then append "### Rust coverage toolchain" append "" diff --git a/.github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml b/.github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml index 188ec6e99..d860a32ea 100644 --- a/.github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml +++ b/.github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml @@ -3,6 +3,7 @@ name: OpenCode Rust Coverage Toolchain Quality CI on: pull_request: paths: + - ".github/workflows/opencode-review-dispatch.yml" - ".github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml" - "scripts/ci/ensure_rust_llvm19.sh" - "tests/test_opencode_rust_coverage_toolchain_contract.py" diff --git a/scripts/ci/ensure_rust_llvm19.sh b/scripts/ci/ensure_rust_llvm19.sh index 8c091e149..fb85d4aa9 100755 --- a/scripts/ci/ensure_rust_llvm19.sh +++ b/scripts/ci/ensure_rust_llvm19.sh @@ -2,8 +2,8 @@ # Fail closed unless the reviewed LLVM 19 coverage tools are bound. set -euo pipefail -LLVM_COV_PATH="${LLVM_COV_PATH:-/usr/bin/llvm-cov-19}" -LLVM_PROFDATA_PATH="${LLVM_PROFDATA_PATH:-/usr/bin/llvm-profdata-19}" +LLVM_COV_PATH="/usr/bin/llvm-cov-19" +LLVM_PROFDATA_PATH="/usr/bin/llvm-profdata-19" if [ "${LLVM_COV:-}" != "$LLVM_COV_PATH" ] || [ "${LLVM_PROFDATA:-}" != "$LLVM_PROFDATA_PATH" ] || diff --git a/tests/test_opencode_rust_coverage_toolchain_contract.py b/tests/test_opencode_rust_coverage_toolchain_contract.py index 21c455ccd..a583439b6 100644 --- a/tests/test_opencode_rust_coverage_toolchain_contract.py +++ b/tests/test_opencode_rust_coverage_toolchain_contract.py @@ -2,11 +2,19 @@ from __future__ import annotations +import os +import stat +import subprocess from pathlib import Path +import pytest + _REPOSITORY_ROOT = Path(__file__).resolve().parents[1] _HELPER_PATH = _REPOSITORY_ROOT / "scripts/ci/ensure_rust_llvm19.sh" +_DISPATCH_WORKFLOW_PATH = ( + _REPOSITORY_ROOT / ".github/workflows/opencode-review-dispatch.yml" +) _QUALITY_WORKFLOW_PATH = ( _REPOSITORY_ROOT / ".github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml" ) @@ -20,37 +28,77 @@ def _helper_text() -> str: return _HELPER_PATH.read_text(encoding="utf-8") -def test_trusted_rust_coverage_image_provisions_verified_llvm_19_tools() -> None: - """Require explicit compatible LLVM 19 tools in the reviewed helper.""" +def _dispatch_text() -> str: + """Return the trusted coverage-image and sandbox workflow.""" - helper = _helper_text() - assert f'LLVM_COV_PATH="${{LLVM_COV_PATH:-{_LLVM_COV_PATH}}}"' in helper - assert ( - f'LLVM_PROFDATA_PATH="${{LLVM_PROFDATA_PATH:-{_LLVM_PROFDATA_PATH}}}"' - in helper + return _DISPATCH_WORKFLOW_PATH.read_text(encoding="utf-8") + + +def _run_helper( + env: dict[str, str], +) -> subprocess.CompletedProcess[str]: + """Execute the helper with a caller-supplied environment.""" + + return subprocess.run( + ["bash", str(_HELPER_PATH)], + check=False, + capture_output=True, + text=True, + env=env, ) - assert 'test -x "${LLVM_COV:-}"' in helper - assert 'test -x "${LLVM_PROFDATA:-}"' in helper + + +def test_trusted_coverage_image_provisions_verified_llvm_19_tools() -> None: + """Require Debian llvm-19 and build-time executability in trusted source.""" + + dispatch = _dispatch_text() + assert " llvm-19 \\\n" in dispatch + assert f"ENV LLVM_COV={_LLVM_COV_PATH}\n" in dispatch + assert f"ENV LLVM_PROFDATA={_LLVM_PROFDATA_PATH}\n" in dispatch + assert 'RUN test -x "$LLVM_COV"\n' in dispatch + assert 'RUN test -x "$LLVM_PROFDATA"\n' in dispatch def test_isolated_runtime_receives_reviewed_llvm_constants() -> None: - """Require exact LLVM 19 path constants in the helper contract.""" + """Require exact LLVM 19 path constants at the Docker sandbox boundary.""" + dispatch = _dispatch_text() helper = _helper_text() + assert f" --env LLVM_COV={_LLVM_COV_PATH} \\\n" in dispatch + assert f" --env LLVM_PROFDATA={_LLVM_PROFDATA_PATH} \\\n" in dispatch assert _LLVM_COV_PATH in helper assert _LLVM_PROFDATA_PATH in helper assert "unversioned" not in helper + assert "${LLVM_COV_PATH:-" not in helper + assert "${LLVM_PROFDATA_PATH:-" not in helper def test_isolated_runtime_revalidates_llvm_tools_before_coverage() -> None: - """Require reviewed-path equality and executable checks before coverage.""" + """Require the trusted toolchain guard to fail closed before cargo llvm-cov.""" + dispatch = _dispatch_text() helper = _helper_text() + assert f'[ "${{LLVM_COV:-}}" != "{_LLVM_COV_PATH}" ]' in dispatch + assert f'[ "${{LLVM_PROFDATA:-}}" != "{_LLVM_PROFDATA_PATH}" ]' in dispatch + assert 'test -x "$LLVM_COV"' in dispatch + assert 'test -x "$LLVM_PROFDATA"' in dispatch + assert "networkless coverage runtime did not preserve the reviewed LLVM 19" in dispatch + assert f'LLVM_COV_PATH="{_LLVM_COV_PATH}"' in helper + assert f'LLVM_PROFDATA_PATH="{_LLVM_PROFDATA_PATH}"' in helper assert f'"${{LLVM_COV:-}}" != "$LLVM_COV_PATH"' in helper assert f'"${{LLVM_PROFDATA:-}}" != "$LLVM_PROFDATA_PATH"' in helper assert "exit 1" in helper +def test_quality_workflow_watches_the_trusted_dispatch_workflow() -> None: + """Guard drift in the hashed review-dispatch blob must retrigger this contract.""" + + quality_workflow = _QUALITY_WORKFLOW_PATH.read_text(encoding="utf-8") + assert ( + ' - ".github/workflows/opencode-review-dispatch.yml"\n' in quality_workflow + ) + + def test_quality_workflow_watched_paths_resolve_to_repository_files() -> None: """Every exact-path trigger in the permanent quality workflow must exist.""" @@ -65,5 +113,62 @@ def test_quality_workflow_watched_paths_resolve_to_repository_files() -> None: ] assert watched_paths + assert ".github/workflows/opencode-review-dispatch.yml" in watched_paths for relative_path in watched_paths: assert (_REPOSITORY_ROOT / relative_path).is_file(), relative_path + + +def test_helper_fails_closed_when_reviewed_paths_are_unbound() -> None: + """A coverage runtime without the reviewed LLVM env cannot produce evidence.""" + + result = _run_helper({"PATH": os.environ.get("PATH", "/usr/bin")}) + + assert result.returncode == 1 + assert _LLVM_COV_PATH in result.stderr + assert _LLVM_PROFDATA_PATH in result.stderr + + +def test_helper_fails_closed_when_caller_overrides_the_reviewed_paths( + tmp_path: Path, +) -> None: + """Caller-selected LLVM_COV_PATH values cannot retarget the reviewed tools.""" + + decoy = tmp_path / "llvm-cov-decoy" + decoy.write_text("#!/bin/sh\nexit 0\n", encoding="utf-8") + decoy.chmod(decoy.stat().st_mode | stat.S_IXUSR) + result = _run_helper( + { + "PATH": os.environ.get("PATH", "/usr/bin"), + "LLVM_COV": str(decoy), + "LLVM_PROFDATA": str(decoy), + "LLVM_COV_PATH": str(decoy), + "LLVM_PROFDATA_PATH": str(decoy), + } + ) + + assert result.returncode == 1 + assert _LLVM_COV_PATH in result.stderr + + +@pytest.mark.skipif( + not Path(_LLVM_COV_PATH).is_file() or not Path(_LLVM_PROFDATA_PATH).is_file(), + reason="reviewed LLVM 19 tools are not installed on this host", +) +def test_helper_admits_the_reviewed_llvm_19_tools_when_present() -> None: + """The helper accepts only the reviewed executable paths when they exist.""" + + if not os.access(_LLVM_COV_PATH, os.X_OK) or not os.access( + _LLVM_PROFDATA_PATH, os.X_OK + ): + pytest.skip("reviewed LLVM 19 tools are not executable on this host") + + result = _run_helper( + { + "PATH": os.environ.get("PATH", "/usr/bin"), + "LLVM_COV": _LLVM_COV_PATH, + "LLVM_PROFDATA": _LLVM_PROFDATA_PATH, + } + ) + + assert result.returncode == 0 + assert result.stderr == "" diff --git a/tests/test_pr_review_autofix_nvidia_nim_contract.py b/tests/test_pr_review_autofix_nvidia_nim_contract.py index 1bbd98750..335bfa13d 100644 --- a/tests/test_pr_review_autofix_nvidia_nim_contract.py +++ b/tests/test_pr_review_autofix_nvidia_nim_contract.py @@ -20,7 +20,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 = "83f6830d5c21a324b4dbcd4e5c21a07968994b81" +REVIEW_DISPATCH_BLOB_SHA = "de1c4800d362bda4a90c31a0c8e39687a782afb4" def _workflow_text(path: Path) -> str: From 09985adfda6d58538413f870c657f5e974f60769 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 16 Aug 2026 16:48:49 +0000 Subject: [PATCH 16/20] fix(coverage): fail closed when review-dispatch blob SHA drifts The LLVM 19 quality CI already watches opencode-review-dispatch.yml, but the hourly NVIDIA NIM gate that owns REVIEW_DISPATCH_BLOB_SHA does not. Pair the blob pin in the workflow that retriggers on a producer rewrite so a later trusted-image change cannot leave the independent review-dispatch identity stale. Co-authored-by: Seongho Bae --- ...ode-rust-coverage-toolchain-quality-ci.yml | 1 + ...encode_rust_coverage_toolchain_contract.py | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/.github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml b/.github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml index d860a32ea..5e3d6c425 100644 --- a/.github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml +++ b/.github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml @@ -7,6 +7,7 @@ on: - ".github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml" - "scripts/ci/ensure_rust_llvm19.sh" - "tests/test_opencode_rust_coverage_toolchain_contract.py" + - "tests/test_pr_review_autofix_nvidia_nim_contract.py" - "docs/doctoring/opencode-rust-coverage-runtime-boundary.md" - "CHANGELOG.md" diff --git a/tests/test_opencode_rust_coverage_toolchain_contract.py b/tests/test_opencode_rust_coverage_toolchain_contract.py index a583439b6..001a6aba5 100644 --- a/tests/test_opencode_rust_coverage_toolchain_contract.py +++ b/tests/test_opencode_rust_coverage_toolchain_contract.py @@ -3,6 +3,7 @@ from __future__ import annotations import os +import re import stat import subprocess from pathlib import Path @@ -18,8 +19,15 @@ _QUALITY_WORKFLOW_PATH = ( _REPOSITORY_ROOT / ".github/workflows/opencode-rust-coverage-toolchain-quality-ci.yml" ) +_NIM_CONTRACT_PATH = ( + _REPOSITORY_ROOT / "tests/test_pr_review_autofix_nvidia_nim_contract.py" +) _LLVM_COV_PATH = "/usr/bin/llvm-cov-19" _LLVM_PROFDATA_PATH = "/usr/bin/llvm-profdata-19" +_BLOB_SHA_PATTERN = re.compile( + r'^REVIEW_DISPATCH_BLOB_SHA = "([0-9a-f]{40})"$', + re.MULTILINE, +) def _helper_text() -> str: @@ -97,6 +105,25 @@ def test_quality_workflow_watches_the_trusted_dispatch_workflow() -> None: assert ( ' - ".github/workflows/opencode-review-dispatch.yml"\n' in quality_workflow ) + assert ( + ' - "tests/test_pr_review_autofix_nvidia_nim_contract.py"\n' + in quality_workflow + ) + + +def test_review_dispatch_blob_sha_stays_paired_with_trusted_workflow() -> None: + """A dispatch.yml rewrite must update the independent review-dispatch blob pin.""" + + nim_contract = _NIM_CONTRACT_PATH.read_text(encoding="utf-8") + match = _BLOB_SHA_PATTERN.search(nim_contract) + assert match is not None + hashed = subprocess.run( + ["git", "hash-object", str(_DISPATCH_WORKFLOW_PATH)], + check=True, + capture_output=True, + text=True, + ) + assert hashed.stdout.strip() == match.group(1) def test_quality_workflow_watched_paths_resolve_to_repository_files() -> None: @@ -114,6 +141,7 @@ def test_quality_workflow_watched_paths_resolve_to_repository_files() -> None: assert watched_paths assert ".github/workflows/opencode-review-dispatch.yml" in watched_paths + assert "tests/test_pr_review_autofix_nvidia_nim_contract.py" in watched_paths for relative_path in watched_paths: assert (_REPOSITORY_ROOT / relative_path).is_file(), relative_path From 3f528c6799c53b904d870ecfd9681ad4806fadc4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 00:11:52 +0900 Subject: [PATCH 17/20] fix(coverage): preserve bounded requirement includes --- .../trusted-uv-flat-include-isolation.md | 66 +++++++++---------- .../materialize_base_python_requirements.py | 2 +- .../test_uv_flat_lock_publication_boundary.py | 10 +-- 3 files changed, 39 insertions(+), 39 deletions(-) diff --git a/docs/doctoring/trusted-uv-flat-include-isolation.md b/docs/doctoring/trusted-uv-flat-include-isolation.md index 1f5178aae..aa827d85f 100644 --- a/docs/doctoring/trusted-uv-flat-include-isolation.md +++ b/docs/doctoring/trusted-uv-flat-include-isolation.md @@ -1,44 +1,42 @@ -# Trusted uv flat-include isolation +# Trusted uv bounded-include materialization ## Status -Accepted on 2026-08-18 for generated base Python lock publication. +Accepted on 2026-08-20 for generated base Python lock publication. ## Buyer-facing failure -The central coverage lane renames every selected source lock to a generated flat -name such as `requirements-000.txt`. A source requirements file containing a -relative `-r` or `--requirement` directive is valid pip syntax, but pip resolves -the referenced path relative to the generated output location. Publishing only -the referrer can therefore fail a downstream repository before its own tests, -branch coverage, or docstring evidence executes. +The central coverage lane renames selected source locks to generated names such +as `requirements-000.txt`. A source requirements file containing a relative +`-r` or `--requirement` directive must keep that source-directory relationship; +publishing only the referrer can otherwise fail a downstream repository before +its own tests, branch coverage, or docstring evidence executes. ## Root cause and decision -The previous implementation conflated two authority boundaries: +The materializer separates two authority boundaries: - `_is_hash_pinned` answers whether a source file uses bounded requirements syntax, including a normalized relative include; and -- `base_hash_locks` decides whether one source blob can be copied independently - under a generated flat name. - -A bounded relative include may pass the first question while failing the second. -The materializer now keeps bounded-include syntax diagnostics unchanged but uses -`_is_flat_materializable_lock` for publication. That predicate admits only a -non-empty, standalone closure whose logical requirement lines are exact `==` -pins carrying complete SHA-256 hashes. `base_hash_locks` also uses the existing -path-aware candidate predicate, so independently complete direct `.txt` children -such as `requirements/ci.txt` and `service/requirements/package.txt` remain -eligible. +- `base_hash_locks` inventories those exact-base referrers and complete included + blobs for graph-aware publication. + +A bounded include is resolved relative to its exact base-tree parent, required to +be a regular blob containing only complete SHA-256 package pins, and written +under a preserved generated include directory. The root lock is rewritten to +that generated relative path. Missing targets, nested includes, unsafe path +components, and non-pinned leaves fail closed. Path-aware candidate discovery +continues to include direct `.txt` children such as `requirements/ci.txt` and +`service/requirements/package.txt`. ## Security and ownership boundary No URL, proxy, redirect, package index, caller-controlled header, output path, review authority, credential, or repository write scope is expanded. The fixed -GitHub Releases uv download and redirect boundary is unchanged. Relative include -publication remains fail-closed until a separately reviewed implementation can -reconstruct the complete immutable include graph, preserve source-directory -identity, rewrite every edge, and prove the resulting closure. +GitHub Releases uv download and redirect boundary is unchanged. Include edges +are read only from the exact validated base revision, remain relative only inside +the generated include subtree, and are never followed beyond one direct +hash-pinned leaf. This is a central `.github` materialization correction. Product repositories, including BandScope, retain ownership of their own requirements, tests, and @@ -49,26 +47,26 @@ to work around a generated-path defect. The regression suite proves all of the following: -1. both `-r` and `--requirement` referrers are excluded from flat publication; -2. an independently complete referenced lock remains eligible; +1. both `-r` and `--requirement` referrers are rewritten to preserved generated + include paths; +2. missing, unsafe, nested, or non-pinned included locks fail closed; 3. complete direct `.txt` children of a directory named `requirements` are discovered; and -4. empty, directive-only, standalone exact-pin, and include-only inputs exercise - both branches of the publication predicate. +4. empty, directive-only, standalone exact-pin, and bounded-include inputs + exercise both branches of the publication predicate. Merge requires the focused trusted-uv suite, complete central tests, production statement and branch coverage at 100%, complete production docstrings, Python 3.10 and current-stable compilation, exact-head security checks, and ordinary protected-branch review. A downstream repository using nested requirements -should publish one standalone hash-locked closure or wait for a graph-aware -materializer; operators must not manually copy or rename an unresolved include. +should keep each included leaf as an exact SHA-256-pinned regular base blob; +operators must not manually copy or rename an unresolved include. ## Rollback -Do not restore relative include publication. A rollback would reintroduce a -source-relative edge into a namespace that no longer preserves source location. -Restore only after a graph-aware implementation has equivalent RED fixtures, -immutable edge rewriting, closure verification, and the same security gates. +Do not weaken the exact-base path, leaf-pin, or direct-include checks. Any future +rollback must retain equivalent missing-target, unsafe-path, nested-include, and +immutable edge-rewriting fixtures with the same security gates. ## APA 7th references diff --git a/scripts/ci/materialize_base_python_requirements.py b/scripts/ci/materialize_base_python_requirements.py index b8a258b48..54f71be71 100755 --- a/scripts/ci/materialize_base_python_requirements.py +++ b/scripts/ci/materialize_base_python_requirements.py @@ -581,7 +581,7 @@ def base_hash_locks(repo_root: pathlib.Path, base_sha: str) -> list[tuple[str, b for path, candidate in regular_blobs: if _is_candidate_lock_path(candidate): content = _git(repo_root, "show", f"{base_sha}:{path}") - if _is_flat_materializable_lock(content): + if _is_hash_pinned(content): locks.append((path, content)) elif candidate.name == "uv.lock": if _uv_pyproject_path(path) not in regular_paths: diff --git a/tests/test_uv_flat_lock_publication_boundary.py b/tests/test_uv_flat_lock_publication_boundary.py index 6ef1ac2f0..8345c5745 100644 --- a/tests/test_uv_flat_lock_publication_boundary.py +++ b/tests/test_uv_flat_lock_publication_boundary.py @@ -34,12 +34,12 @@ def test_flat_materializable_lock_requires_a_standalone_exact_closure( @pytest.mark.parametrize("directive", ["-r", "--requirement"]) -def test_flat_publication_excludes_relative_include_referrers( +def test_materialization_preserves_relative_include_referrers( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, directive: str, ) -> None: - """A generated flat name cannot preserve a source-relative include edge.""" + """A bounded source-relative include is retained for graph-aware materialization.""" tree = ( b"100644 blob " + (b"0" * 40) @@ -62,7 +62,8 @@ def fake_git(_repo_root: Path, *args: str) -> bytes: monkeypatch.setattr(materializer, "_git", fake_git) assert materializer.base_hash_locks(tmp_path, "a" * 40) == [ - ("requirements-other.txt", target_lock) + ("requirements-other.txt", target_lock), + ("requirements.txt", f"{directive} requirements-other.txt\n".encode()), ] @@ -70,7 +71,7 @@ def test_flat_publication_discovers_standalone_requirements_directory_locks( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, ) -> None: - """Path-aware discovery keeps complete direct requirements-directory locks.""" + """Path-aware discovery keeps standalone and bounded include locks.""" tree = ( b"100644 blob " + (b"0" * 40) @@ -101,6 +102,7 @@ def fake_git(_repo_root: Path, *args: str) -> bytes: monkeypatch.setattr(materializer, "_git", fake_git) assert materializer.base_hash_locks(tmp_path, "a" * 40) == [ + ("requirements.txt", b"-r requirements/ci.txt\n"), ("requirements/ci.txt", ci_lock), ("service/requirements/package.txt", service_lock), ] From cbda28b701a2b6067c6d9e14cbb049307e7f0d94 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 20 Aug 2026 21:17:32 +0900 Subject: [PATCH 18/20] test(coverage): execute bounded repair driver --- .../ci/repair_pr827_coderabbit_comments.py | 8 +- ...st_materialize_base_python_requirements.py | 87 +++++++++++++++++++ 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/scripts/ci/repair_pr827_coderabbit_comments.py b/scripts/ci/repair_pr827_coderabbit_comments.py index 267b45f02..be130e362 100644 --- a/scripts/ci/repair_pr827_coderabbit_comments.py +++ b/scripts/ci/repair_pr827_coderabbit_comments.py @@ -7,20 +7,24 @@ def replace_once(path: str, old: str, new: str) -> None: - """Replace one exact fragment and fail closed on drift.""" + """Replace one exact fragment and accept an already-applied repair.""" file_path = Path(path) text = file_path.read_text(encoding="utf-8") count = text.count(old) + if count == 0 and text.count(new) == 1: + return if count != 1: raise SystemExit(f"{path}: expected one replacement marker, found {count}") file_path.write_text(text.replace(old, new, 1), encoding="utf-8") def replace_between(path: str, start: str, end: str, replacement: str) -> None: - """Replace a uniquely delimited source section.""" + """Replace a uniquely delimited source section idempotently.""" file_path = Path(path) text = file_path.read_text(encoding="utf-8") start_index = text.find(start) + if start_index < 0 and text.count(replacement) == 1: + return if start_index < 0 or text.find(start, start_index + 1) >= 0: raise SystemExit(f"{path}: start marker missing or ambiguous") end_index = text.find(end, start_index) diff --git a/tests/test_materialize_base_python_requirements.py b/tests/test_materialize_base_python_requirements.py index 65c0103d5..bd7ec3710 100644 --- a/tests/test_materialize_base_python_requirements.py +++ b/tests/test_materialize_base_python_requirements.py @@ -1,8 +1,10 @@ from __future__ import annotations +import ast import hashlib import io import runpy +import shutil import subprocess import sys import tarfile @@ -295,6 +297,91 @@ def test_materialization_rejects_missing_or_nested_include(tmp_path: Path) -> No materializer.materialize(repo, nested_sha, tmp_path / "nested-output") +def test_bounded_repair_driver_runs_against_a_staged_fixture( + tmp_path: Path, monkeypatch: pytest.MonkeyPatch +) -> None: + """The one-shot repair driver applies every guarded edit in isolation.""" + repository_root = Path(__file__).parents[1] + relative_files = ( + "scripts/ci/repair_pr827_coderabbit_comments.py", + "scripts/ci/materialize_base_python_requirements.py", + "tests/test_materialize_base_python_requirements.py", + "docs/doctoring/opencode-rust-coverage-runtime-boundary.md", + ".github/workflows/opencode-review-dispatch.yml", + "CHANGELOG.md", + ) + for relative_file in relative_files: + source = repository_root / relative_file + destination = tmp_path / relative_file + destination.parent.mkdir(parents=True, exist_ok=True) + shutil.copy2(source, destination) + + changelog = tmp_path / "CHANGELOG.md" + changelog.write_text( + changelog.read_text(encoding="utf-8").replace( + "## [Unreleased]\n", + "## [Unreleased]\n\n" + "- Materialized base Python locks only when every package line is an exact " + "SHA-256 pin or a bounded relative `-r`/`--requirement` include. A lone " + "`--require-hashes` directive, a dotted include such as `./lock.txt`, or " + "`-r other-hashes.txt` no longer enters the trusted build context.\n", + 1, + ), + encoding="utf-8", + ) + + monkeypatch.chdir(tmp_path) + runpy.run_path( + str(repository_root / "scripts/ci/repair_pr827_coderabbit_comments.py"), + run_name="__main__", + ) + + materializer_source = ( + tmp_path / "scripts/ci/materialize_base_python_requirements.py" + ).read_text(encoding="utf-8") + assert "def _bounded_requirement_include_target(" in materializer_source + assert "def _included_base_lock_blobs(" in materializer_source + assert "includes-000/" in ( + tmp_path / "tests/test_materialize_base_python_requirements.py" + ).read_text(encoding="utf-8") + assert "Apache-2.0 WITH LLVM-exception" in ( + tmp_path / "docs/doctoring/opencode-rust-coverage-runtime-boundary.md" + ).read_text(encoding="utf-8") + + script_path = repository_root / "scripts/ci/repair_pr827_coderabbit_comments.py" + tree = ast.parse(script_path.read_text(encoding="utf-8"), filename=str(script_path)) + definitions = [node for node in tree.body if isinstance(node, ast.FunctionDef)] + namespace: dict[str, object] = {"Path": Path} + exec( + compile( + ast.fix_missing_locations(ast.Module(body=definitions, type_ignores=[])), + str(script_path), + "exec", + ), + namespace, + ) + replace_once = namespace["replace_once"] + replace_between = namespace["replace_between"] + once_file = tmp_path / "once.txt" + once_file.write_text("old", encoding="utf-8") + replace_once(str(once_file), "old", "new") # type: ignore[operator] + assert once_file.read_text(encoding="utf-8") == "new" + with pytest.raises(SystemExit, match="expected one replacement marker"): + replace_once(str(once_file), "missing", "other") # type: ignore[operator] + + between_file = tmp_path / "between.txt" + between_file.write_text("START old END", encoding="utf-8") + replace_between(str(between_file), "START", "END", "START new ") # type: ignore[operator] + assert between_file.read_text(encoding="utf-8") == "START new END" + replace_between(str(between_file), "MISSING", "END", "START new ") # type: ignore[operator] + between_file.write_text("START old START END", encoding="utf-8") + with pytest.raises(SystemExit, match="start marker missing or ambiguous"): + replace_between(str(between_file), "START", "END", "replacement") # type: ignore[operator] + between_file.write_text("START old", encoding="utf-8") + with pytest.raises(SystemExit, match="end marker missing"): + replace_between(str(between_file), "START", "END", "replacement") # type: ignore[operator] + + def test_rejects_invalid_base_sha(tmp_path: Path) -> None: """Git options and symbolic refs cannot cross the exact-SHA boundary.""" with pytest.raises(ValueError, match="40 hexadecimal"): From 45933aaeab715e8971239eeedf1a2184ed9077e8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 14:12:10 +0900 Subject: [PATCH 19/20] fix(coverage): tolerate duplicate changelog repair markers --- scripts/ci/repair_pr827_coderabbit_comments.py | 13 +++++++++---- tests/test_materialize_base_python_requirements.py | 6 ++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/scripts/ci/repair_pr827_coderabbit_comments.py b/scripts/ci/repair_pr827_coderabbit_comments.py index be130e362..f639a4639 100644 --- a/scripts/ci/repair_pr827_coderabbit_comments.py +++ b/scripts/ci/repair_pr827_coderabbit_comments.py @@ -6,14 +6,18 @@ from pathlib import Path -def replace_once(path: str, old: str, new: str) -> None: - """Replace one exact fragment and accept an already-applied repair.""" +def replace_once( + path: str, old: str, new: str, *, allow_repeated: bool = False +) -> None: + """Replace one exact fragment, optionally tolerating repeated history markers.""" file_path = Path(path) text = file_path.read_text(encoding="utf-8") count = text.count(old) - if count == 0 and text.count(new) == 1: + if count == 0 and ( + text.count(new) == 1 or (allow_repeated and text.count(new) > 0) + ): return - if count != 1: + if count == 0 or (count != 1 and not allow_repeated): raise SystemExit(f"{path}: expected one replacement marker, found {count}") file_path.write_text(text.replace(old, new, 1), encoding="utf-8") @@ -351,6 +355,7 @@ def test_materialization_rejects_missing_or_nested_include(tmp_path: Path) -> No "CHANGELOG.md", "- Materialized base Python locks only when every package line is an exact SHA-256 pin or a bounded relative `-r`/`--requirement` include. A lone `--require-hashes` directive, a dotted include such as `./lock.txt`, or `-r other-hashes.txt` no longer enters the trusted build context.\n", "- Materialized base Python locks only when every package line is an exact SHA-256 pin or a bounded relative `-r`/`--requirement` include. Includes such as `-r other-hashes.txt` remain allowed when the exact base-tree target is a regular, complete SHA-256-pinned closure; a lone `--require-hashes` directive, dotted `./lock.txt`, traversal, absolute, URL, and option-like targets fail closed.\n", + allow_repeated=True, ) replace_once( diff --git a/tests/test_materialize_base_python_requirements.py b/tests/test_materialize_base_python_requirements.py index bd7ec3710..83c437459 100644 --- a/tests/test_materialize_base_python_requirements.py +++ b/tests/test_materialize_base_python_requirements.py @@ -368,6 +368,12 @@ def test_bounded_repair_driver_runs_against_a_staged_fixture( assert once_file.read_text(encoding="utf-8") == "new" with pytest.raises(SystemExit, match="expected one replacement marker"): replace_once(str(once_file), "missing", "other") # type: ignore[operator] + repeated_file = tmp_path / "repeated.txt" + repeated_file.write_text("oldold", encoding="utf-8") + replace_once( # type: ignore[operator] + str(repeated_file), "old", "new", allow_repeated=True + ) + assert repeated_file.read_text(encoding="utf-8") == "newold" between_file = tmp_path / "between.txt" between_file.write_text("START old END", encoding="utf-8") From 69a5a411fc85fb6b26f79d580271e4c7502aeebb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 21 Aug 2026 14:36:32 +0900 Subject: [PATCH 20/20] fix(coverage): make repair driver rerun-safe --- .../repair-pr827-coderabbit-comments.yml | 7 +- ...opencode-rust-coverage-runtime-boundary.md | 22 ++-- .../materialize_base_python_requirements.py | 13 +- .../ci/repair_pr827_coderabbit_comments.py | 115 +++++++++++++++--- ...st_materialize_base_python_requirements.py | 24 +++- ...encode_rust_coverage_toolchain_contract.py | 14 ++- 6 files changed, 159 insertions(+), 36 deletions(-) diff --git a/.github/workflows/repair-pr827-coderabbit-comments.yml b/.github/workflows/repair-pr827-coderabbit-comments.yml index f2dc85bab..7221c4565 100644 --- a/.github/workflows/repair-pr827-coderabbit-comments.yml +++ b/.github/workflows/repair-pr827-coderabbit-comments.yml @@ -16,7 +16,8 @@ jobs: if: >- github.event.pull_request.number == 827 && github.event.pull_request.head.repo.full_name == github.repository && - github.event.pull_request.head.ref == 'fix/opencode-rust-coverage-runtime-boundary-main' + github.event.pull_request.head.ref == 'fix/opencode-rust-coverage-runtime-boundary-main' && + github.event.pull_request.head.user.login != 'github-actions[bot]' runs-on: ubuntu-24.04 timeout-minutes: 45 permissions: @@ -96,5 +97,9 @@ jobs: CHANGELOG.md \ docs/doctoring/opencode-rust-coverage-runtime-boundary.md git diff --cached --check + if git diff --cached --quiet; then + echo 'No non-workflow repair changes remain; the rerun is complete.' + exit 0 + fi git commit -m 'fix(coverage): preserve bounded requirement includes' git push origin HEAD:fix/opencode-rust-coverage-runtime-boundary-main diff --git a/docs/doctoring/opencode-rust-coverage-runtime-boundary.md b/docs/doctoring/opencode-rust-coverage-runtime-boundary.md index 3ef48d8d5..8adc68d0e 100644 --- a/docs/doctoring/opencode-rust-coverage-runtime-boundary.md +++ b/docs/doctoring/opencode-rust-coverage-runtime-boundary.md @@ -9,24 +9,28 @@ The trusted OpenCode coverage sandbox binds Rust coverage to the reviewed LLVM - `LLVM_PROFDATA=/usr/bin/llvm-profdata-19` These are compatibility and trust-boundary constants, not caller-selectable -configuration. The reviewed helper `scripts/ci/ensure_rust_llvm19.sh` binds both -exact paths and fails closed unless the live `LLVM_COV` / `LLVM_PROFDATA` +configuration. The reviewed helper `scripts/ci/ensure_rust_llvm19.sh` validates +both exact paths and fails closed unless the live `LLVM_COV` / `LLVM_PROFDATA` values match and are executable before Rust coverage evidence is admitted. The -independent OpenCode review-dispatch workflow stays byte-for-byte so the -review-agent key system is not rewritten to carry this runtime check. +actual environment binding is owned by +`.github/workflows/opencode-review-dispatch.yml`, through its Dockerfile `ENV` +declarations and the isolated container's `docker run --env` arguments. If that +workflow changes, its `REVIEW_DISPATCH_BLOB_SHA` pin must change with it; this +does not rewrite the review-agent key system. The runtime MUST NOT fall back to unversioned `llvm-cov` or `llvm-profdata`, a host-runner tool, a pull-request-selected path, or a dynamically downloaded LLVM binary. Missing, changed, or non-executable reviewed paths are coverage-evidence failures rather than reasons to measure a different toolchain. -NIST SP 800-218 PW.4.1 requires third-party software to come from expected, -trusted sources with integrity verification (Souppaya et al., 2022). The exact +NIST SP 800-218 PW.4.1 covers acquiring and maintaining third-party software +from expected, trusted sources and reviewing its provenance (Souppaya et al., +2022). PW.4.4 covers verifying the integrity of acquired components. The exact `/usr/bin/llvm-cov-19` and `/usr/bin/llvm-profdata-19` bindings are producer-selection controls: they select reviewed paths and `test -x` verifies -executability. They do not hash or signature-verify the Debian package or binary. -Package/image hashes, signatures, repository metadata, and attestations are -separate integrity controls and must not be inferred from path equality. +executability. They do not hash or signature-verify the Debian package or binary; +package/image hashes, signatures, repository metadata, and attestations remain +separate PW.4.4 integrity controls and must not be inferred from path equality. ## Why the boundary exists diff --git a/scripts/ci/materialize_base_python_requirements.py b/scripts/ci/materialize_base_python_requirements.py index 54f71be71..314668438 100755 --- a/scripts/ci/materialize_base_python_requirements.py +++ b/scripts/ci/materialize_base_python_requirements.py @@ -613,7 +613,7 @@ def _included_base_lock_blobs( f"bounded include {target} from {source_path} is not a regular base blob" ) included_content = _git(repo_root, "show", f"{base_sha}:{resolved_path}") - if not _is_fully_hash_pinned_export(included_content): + if not _is_flat_materializable_lock(included_content): raise RuntimeError( f"bounded include {resolved_path} must contain only exact SHA-256 pins" ) @@ -621,9 +621,14 @@ def _included_base_lock_blobs( return sorted(included.items(), key=lambda item: item[0].as_posix()) -def _rewrite_materialized_includes(content: bytes, include_directory: str) -> bytes: +def _rewrite_materialized_includes( + content: bytes, include_directory: str, source_path: str = "" +) -> bytes: """Rewrite root include targets to their preserved generated subtree.""" - text = content.decode("utf-8", errors="strict") + try: + text = content.decode("utf-8", errors="strict") + except UnicodeDecodeError as exc: + raise RuntimeError(f"base lock {source_path} is not valid UTF-8") from exc rewritten: list[str] = [] for raw_line in text.splitlines(keepends=True): body = raw_line.rstrip("\r\n") @@ -675,7 +680,7 @@ def materialize( destination.write_bytes(included_content) destination = output_dir / generated_name destination.write_bytes( - _rewrite_materialized_includes(content, include_directory) + _rewrite_materialized_includes(content, include_directory, source_path) ) manifest.append({"file": generated_name, "source": source_path}) diff --git a/scripts/ci/repair_pr827_coderabbit_comments.py b/scripts/ci/repair_pr827_coderabbit_comments.py index f639a4639..8b1df14cb 100644 --- a/scripts/ci/repair_pr827_coderabbit_comments.py +++ b/scripts/ci/repair_pr827_coderabbit_comments.py @@ -22,6 +22,30 @@ def replace_once( file_path.write_text(text.replace(old, new, 1), encoding="utf-8") +def insert_before(path: str, anchor: str, addition: str) -> None: + """Insert an addition before one unique anchor, at most once.""" + file_path = Path(path) + text = file_path.read_text(encoding="utf-8") + if addition in text: + return + count = text.count(anchor) + if count != 1: + raise SystemExit(f"{path}: expected one insertion anchor, found {count}") + file_path.write_text(text.replace(anchor, addition + anchor, 1), encoding="utf-8") + + +def insert_after(path: str, anchor: str, addition: str) -> None: + """Insert an addition after one unique anchor, at most once.""" + file_path = Path(path) + text = file_path.read_text(encoding="utf-8") + if addition in text: + return + count = text.count(anchor) + if count != 1: + raise SystemExit(f"{path}: expected one insertion anchor, found {count}") + file_path.write_text(text.replace(anchor, anchor + addition, 1), encoding="utf-8") + + def replace_between(path: str, start: str, end: str, replacement: str) -> None: """Replace a uniquely delimited source section idempotently.""" file_path = Path(path) @@ -118,7 +142,7 @@ def _is_bounded_requirement_include(line: str) -> bool: f"bounded include {target} from {source_path} is not a regular base blob" ) included_content = _git(repo_root, "show", f"{base_sha}:{resolved_path}") - if not _is_fully_hash_pinned_export(included_content): + if not _is_flat_materializable_lock(included_content): raise RuntimeError( f"bounded include {resolved_path} must contain only exact SHA-256 pins" ) @@ -126,9 +150,14 @@ def _is_bounded_requirement_include(line: str) -> bool: return sorted(included.items(), key=lambda item: item[0].as_posix()) -def _rewrite_materialized_includes(content: bytes, include_directory: str) -> bytes: +def _rewrite_materialized_includes( + content: bytes, include_directory: str, source_path: str = "" +) -> bytes: """Rewrite root include targets to their preserved generated subtree.""" - text = content.decode("utf-8", errors="strict") + try: + text = content.decode("utf-8", errors="strict") + except UnicodeDecodeError as exc: + raise RuntimeError(f"base lock {source_path} is not valid UTF-8") from exc rewritten: list[str] = [] for raw_line in text.splitlines(keepends=True): body = raw_line.rstrip("\\r\\n") @@ -147,7 +176,7 @@ def _rewrite_materialized_includes(content: bytes, include_directory: str) -> by ''' -replace_once(SCRIPT, "def materialize(\n", helpers + "def materialize(\n") +insert_before(SCRIPT, "def materialize(\n", helpers) replace_between( SCRIPT, @@ -187,7 +216,7 @@ def _rewrite_materialized_includes(content: bytes, include_directory: str) -> by destination.write_bytes(included_content) destination = output_dir / generated_name destination.write_bytes( - _rewrite_materialized_includes(content, include_directory) + _rewrite_materialized_includes(content, include_directory, source_path) ) manifest.append({"file": generated_name, "source": source_path}) @@ -205,16 +234,15 @@ def _rewrite_materialized_includes(content: bytes, include_directory: str) -> by ''', ) -replace_once(TEST, "import tarfile\n", "import tarfile\nimport zipfile\n") +insert_before(TEST, "from pathlib import Path\n", "import zipfile\n") replace_once( TEST, ' assert not materializer._is_hash_pinned(b"-r other-hashes.txt\\n")\n', ' assert materializer._is_hash_pinned(b"-r other-hashes.txt\\n")\n', ) -replace_once( +insert_after( TEST, ' assert not materializer._is_candidate_lock_name("pyproject.toml")\n', - ' assert not materializer._is_candidate_lock_name("pyproject.toml")\n' ' assert materializer._is_candidate_lock_path(\n' ' materializer.pathlib.PurePosixPath("requirements/ci.txt")\n' ' )\n' @@ -225,16 +253,12 @@ def _rewrite_materialized_includes(content: bytes, include_directory: str) -> by ' materializer.pathlib.PurePosixPath("service/config/ci.txt")\n' ' )\n', ) -replace_once( +insert_after( TEST, ' (repo / "requirements-test.txt").write_text(\n' ' "hypothesis==6 --hash=sha256:" + ("b" * 64) + "\\n",\n' ' encoding="utf-8",\n' ' )\n', - ' (repo / "requirements-test.txt").write_text(\n' - ' "hypothesis==6 --hash=sha256:" + ("b" * 64) + "\\n",\n' - ' encoding="utf-8",\n' - ' )\n' ' requirements_dir = repo / "requirements"\n' ' requirements_dir.mkdir()\n' ' (requirements_dir / "ci.txt").write_text(\n' @@ -250,6 +274,27 @@ def _rewrite_materialized_includes(content: bytes, include_directory: str) -> by ' "requirements/ci.txt",\n' ' "services/account_unification/requirements-dev.txt",\n', ) +insert_before( + TEST, + ' between_file.write_text("START old", encoding="utf-8")\n', + ''' before_file = tmp_path / "before.txt" + before_file.write_text("ANCHOR", encoding="utf-8") + insert_before = namespace["insert_before"] + insert_before(str(before_file), "ANCHOR", "PREFIX ") # type: ignore[operator] + assert before_file.read_text(encoding="utf-8") == "PREFIX ANCHOR" + insert_before(str(before_file), "ANCHOR", "PREFIX ") # type: ignore[operator] + with pytest.raises(SystemExit, match="expected one insertion anchor"): + insert_before(str(before_file), "MISSING", "OTHER ") # type: ignore[operator] + after_file = tmp_path / "after.txt" + after_file.write_text("ANCHOR", encoding="utf-8") + insert_after = namespace["insert_after"] + insert_after(str(after_file), "ANCHOR", " SUFFIX") # type: ignore[operator] + assert after_file.read_text(encoding="utf-8") == "ANCHOR SUFFIX" + insert_after(str(after_file), "ANCHOR", " SUFFIX") # type: ignore[operator] + with pytest.raises(SystemExit, match="expected one insertion anchor"): + insert_after(str(after_file), "MISSING", " OTHER") # type: ignore[operator] +''', +) integration_test = '''def test_materialized_bounded_include_is_resolvable_by_pip(tmp_path: Path) -> None: """A safe base-owned include survives flattening and pip hash preflight.""" @@ -280,7 +325,7 @@ def _rewrite_materialized_includes(content: bytes, include_directory: str) -> by "-r other-hashes.txt\\n", encoding="utf-8" ) (repo / "other-hashes.txt").write_text( - f"demo==1 --hash=sha256:{digest}\\n", encoding="utf-8" + f"--require-hashes\\ndemo==1 --hash=sha256:{digest}\\n", encoding="utf-8" ) git(repo, "add", ".") git(repo, "commit", "-m", "base") @@ -341,15 +386,43 @@ def test_materialization_rejects_missing_or_nested_include(tmp_path: Path) -> No with pytest.raises(RuntimeError, match="must contain only exact SHA-256 pins"): materializer.materialize(repo, nested_sha, tmp_path / "nested-output") + with pytest.raises(RuntimeError, match="base lock requirements.txt is not valid UTF-8"): + materializer._rewrite_materialized_includes( + b"\\xff", "includes-000", "requirements.txt" + ) + ''' -replace_once(TEST, "def test_rejects_invalid_base_sha", integration_test + "def test_rejects_invalid_base_sha") +insert_before(TEST, "def test_rejects_invalid_base_sha", integration_test) replace_once( TEST, ' assert not materializer._is_bounded_requirement_include("-r /abs/requirements.txt")\n', ' assert not materializer._is_bounded_requirement_include("-r /abs/requirements.txt")\n' ' assert not materializer._is_bounded_requirement_include("-r pyproject.toml")\n', ) +replace_once( + "tests/test_opencode_rust_coverage_toolchain_contract.py", + """ assert f'"${{LLVM_COV:-}}" != "$LLVM_COV_PATH"' in helper + assert f'"${{LLVM_PROFDATA:-}}" != "$LLVM_PROFDATA_PATH"' in helper +""", + """ assert '"${LLVM_COV:-}" != "$LLVM_COV_PATH"' in helper + assert '"${LLVM_PROFDATA:-}" != "$LLVM_PROFDATA_PATH"' in helper +""", +) +insert_after( + "tests/test_opencode_rust_coverage_toolchain_contract.py", + " for relative_path in watched_paths:\n" + " assert (_REPOSITORY_ROOT / relative_path).is_file(), relative_path\n", + """ doctoring = ( + _REPOSITORY_ROOT + / "docs/doctoring/opencode-rust-coverage-runtime-boundary.md" + ).read_text(encoding="utf-8") + assert "/usr/bin/llvm-cov-19" in doctoring + assert "/usr/bin/llvm-profdata-19" in doctoring + assert "unversioned `llvm-cov`" in doctoring + assert "fails closed" in doctoring +""", +) replace_once( "CHANGELOG.md", @@ -358,10 +431,15 @@ def test_materialization_rejects_missing_or_nested_include(tmp_path: Path) -> No allow_repeated=True, ) +replace_once( + DOC, + "These are compatibility and trust-boundary constants, not caller-selectable\nconfiguration. The reviewed helper `scripts/ci/ensure_rust_llvm19.sh` binds both\nexact paths and fails closed unless the live `LLVM_COV` / `LLVM_PROFDATA`\nvalues match and are executable before Rust coverage evidence is admitted. The\nindependent OpenCode review-dispatch workflow stays byte-for-byte so the\nreview-agent key system is not rewritten to carry this runtime check.\n", + "These are compatibility and trust-boundary constants, not caller-selectable\nconfiguration. The reviewed helper `scripts/ci/ensure_rust_llvm19.sh` validates\nboth exact paths and fails closed unless the live `LLVM_COV` / `LLVM_PROFDATA`\nvalues match and are executable before Rust coverage evidence is admitted. The\nactual environment binding is owned by\n`.github/workflows/opencode-review-dispatch.yml`, through its Dockerfile `ENV`\ndeclarations and the isolated container's `docker run --env` arguments. If that\nworkflow changes, its `REVIEW_DISPATCH_BLOB_SHA` pin must change with it; this\ndoes not rewrite the review-agent key system.\n", +) replace_once( DOC, "NIST SP 800-218 PW.4.1 requires third-party software to come from expected,\ntrusted sources with integrity verification (Souppaya et al., 2022). Binding\ncoverage to the reviewed `/usr/bin/llvm-cov-19` and\n`/usr/bin/llvm-profdata-19` executables is that verification; an ambient\n`PATH` lookup would treat a runner-image change as a new producer.\n", - "NIST SP 800-218 PW.4.1 requires third-party software to come from expected,\ntrusted sources with integrity verification (Souppaya et al., 2022). The exact\n`/usr/bin/llvm-cov-19` and `/usr/bin/llvm-profdata-19` bindings are\nproducer-selection controls: they select reviewed paths and `test -x` verifies\nexecutability. They do not hash or signature-verify the Debian package or binary.\nPackage/image hashes, signatures, repository metadata, and attestations are\nseparate integrity controls and must not be inferred from path equality.\n", + "NIST SP 800-218 PW.4.1 covers acquiring and maintaining third-party software\nfrom expected, trusted sources and reviewing its provenance (Souppaya et al.,\n2022). PW.4.4 covers verifying the integrity of acquired components. The exact\n`/usr/bin/llvm-cov-19` and `/usr/bin/llvm-profdata-19` bindings are\nproducer-selection controls: they select reviewed paths and `test -x` verifies\nexecutability. They do not hash or signature-verify the Debian package or binary;\npackage/image hashes, signatures, repository metadata, and attestations remain\nseparate PW.4.4 integrity controls and must not be inferred from path equality.\n", ) replace_once( DOC, @@ -376,6 +454,7 @@ def test_materialization_rejects_missing_or_nested_include(tmp_path: Path) -> No replace_once( WORKFLOW, - " r-cran-testthat \\\n llvm-19 \\\n", - " r-cran-testthat \\\n # llvm-19 / llvm-toolchain-19: Apache-2.0 WITH LLVM-exception. \\\n # See docs/doctoring/opencode-rust-coverage-runtime-boundary.md. \\\n llvm-19 \\\n", + " RUN apt-get update \\\n && apt-get install --no-install-recommends -y \\\n", + " # llvm-19 / llvm-toolchain-19: Apache-2.0 WITH LLVM-exception. See docs/doctoring/opencode-rust-coverage-runtime-boundary.md.\n" + " RUN apt-get update \\\n && apt-get install --no-install-recommends -y \\\n", ) diff --git a/tests/test_materialize_base_python_requirements.py b/tests/test_materialize_base_python_requirements.py index 83c437459..6073dbdc1 100644 --- a/tests/test_materialize_base_python_requirements.py +++ b/tests/test_materialize_base_python_requirements.py @@ -235,7 +235,7 @@ def test_materialized_bounded_include_is_resolvable_by_pip(tmp_path: Path) -> No "-r other-hashes.txt\n", encoding="utf-8" ) (repo / "other-hashes.txt").write_text( - f"demo==1 --hash=sha256:{digest}\n", encoding="utf-8" + f"--require-hashes\ndemo==1 --hash=sha256:{digest}\n", encoding="utf-8" ) git(repo, "add", ".") git(repo, "commit", "-m", "base") @@ -296,6 +296,11 @@ def test_materialization_rejects_missing_or_nested_include(tmp_path: Path) -> No with pytest.raises(RuntimeError, match="must contain only exact SHA-256 pins"): materializer.materialize(repo, nested_sha, tmp_path / "nested-output") + with pytest.raises(RuntimeError, match="base lock requirements.txt is not valid UTF-8"): + materializer._rewrite_materialized_includes( + b"\xff", "includes-000", "requirements.txt" + ) + def test_bounded_repair_driver_runs_against_a_staged_fixture( tmp_path: Path, monkeypatch: pytest.MonkeyPatch @@ -306,6 +311,7 @@ def test_bounded_repair_driver_runs_against_a_staged_fixture( "scripts/ci/repair_pr827_coderabbit_comments.py", "scripts/ci/materialize_base_python_requirements.py", "tests/test_materialize_base_python_requirements.py", + "tests/test_opencode_rust_coverage_toolchain_contract.py", "docs/doctoring/opencode-rust-coverage-runtime-boundary.md", ".github/workflows/opencode-review-dispatch.yml", "CHANGELOG.md", @@ -380,6 +386,22 @@ def test_bounded_repair_driver_runs_against_a_staged_fixture( replace_between(str(between_file), "START", "END", "START new ") # type: ignore[operator] assert between_file.read_text(encoding="utf-8") == "START new END" replace_between(str(between_file), "MISSING", "END", "START new ") # type: ignore[operator] + before_file = tmp_path / "before.txt" + before_file.write_text("ANCHOR", encoding="utf-8") + insert_before = namespace["insert_before"] + insert_before(str(before_file), "ANCHOR", "PREFIX ") # type: ignore[operator] + assert before_file.read_text(encoding="utf-8") == "PREFIX ANCHOR" + insert_before(str(before_file), "ANCHOR", "PREFIX ") # type: ignore[operator] + with pytest.raises(SystemExit, match="expected one insertion anchor"): + insert_before(str(before_file), "MISSING", "OTHER ") # type: ignore[operator] + after_file = tmp_path / "after.txt" + after_file.write_text("ANCHOR", encoding="utf-8") + insert_after = namespace["insert_after"] + insert_after(str(after_file), "ANCHOR", " SUFFIX") # type: ignore[operator] + assert after_file.read_text(encoding="utf-8") == "ANCHOR SUFFIX" + insert_after(str(after_file), "ANCHOR", " SUFFIX") # type: ignore[operator] + with pytest.raises(SystemExit, match="expected one insertion anchor"): + insert_after(str(after_file), "MISSING", " OTHER") # type: ignore[operator] between_file.write_text("START old START END", encoding="utf-8") with pytest.raises(SystemExit, match="start marker missing or ambiguous"): replace_between(str(between_file), "START", "END", "replacement") # type: ignore[operator] diff --git a/tests/test_opencode_rust_coverage_toolchain_contract.py b/tests/test_opencode_rust_coverage_toolchain_contract.py index 001a6aba5..b1fd4a124 100644 --- a/tests/test_opencode_rust_coverage_toolchain_contract.py +++ b/tests/test_opencode_rust_coverage_toolchain_contract.py @@ -93,8 +93,8 @@ def test_isolated_runtime_revalidates_llvm_tools_before_coverage() -> None: assert "networkless coverage runtime did not preserve the reviewed LLVM 19" in dispatch assert f'LLVM_COV_PATH="{_LLVM_COV_PATH}"' in helper assert f'LLVM_PROFDATA_PATH="{_LLVM_PROFDATA_PATH}"' in helper - assert f'"${{LLVM_COV:-}}" != "$LLVM_COV_PATH"' in helper - assert f'"${{LLVM_PROFDATA:-}}" != "$LLVM_PROFDATA_PATH"' in helper + assert '"${LLVM_COV:-}" != "$LLVM_COV_PATH"' in helper + assert '"${LLVM_PROFDATA:-}" != "$LLVM_PROFDATA_PATH"' in helper assert "exit 1" in helper @@ -127,7 +127,7 @@ def test_review_dispatch_blob_sha_stays_paired_with_trusted_workflow() -> None: def test_quality_workflow_watched_paths_resolve_to_repository_files() -> None: - """Every exact-path trigger in the permanent quality workflow must exist.""" + """Every watched path and its documented runtime contract must exist.""" quality_workflow = _QUALITY_WORKFLOW_PATH.read_text(encoding="utf-8") watched_section = quality_workflow.split(" paths:\n", 1)[1].split( @@ -144,6 +144,14 @@ def test_quality_workflow_watched_paths_resolve_to_repository_files() -> None: assert "tests/test_pr_review_autofix_nvidia_nim_contract.py" in watched_paths for relative_path in watched_paths: assert (_REPOSITORY_ROOT / relative_path).is_file(), relative_path + doctoring = ( + _REPOSITORY_ROOT + / "docs/doctoring/opencode-rust-coverage-runtime-boundary.md" + ).read_text(encoding="utf-8") + assert "/usr/bin/llvm-cov-19" in doctoring + assert "/usr/bin/llvm-profdata-19" in doctoring + assert "unversioned `llvm-cov`" in doctoring + assert "fails closed" in doctoring def test_helper_fails_closed_when_reviewed_paths_are_unbound() -> None: