diff --git a/.github/workflows/opencode-review-dispatch.yml b/.github/workflows/opencode-review-dispatch.yml index 83f6830d5..ed97ed75c 100644 --- a/.github/workflows/opencode-review-dispatch.yml +++ b/.github/workflows/opencode-review-dispatch.yml @@ -7851,8 +7851,35 @@ jobs: exit 1 fi if [ "${GH_REPOSITORY:-}" != "${GITHUB_REPOSITORY:-}" ] && - [ "${OPENCODE_STATUS_TOKEN_SOURCE:-}" = "github-token" ]; then - echo "::notice::OpenCode repository_dispatch status publication is unavailable because only the same-repository github.token can access cross-repository target ${GH_REPOSITORY}. The exact-head formal review remains authoritative; configure PR_REVIEW_MERGE_TOKEN or OPENCODE_APPROVE_TOKEN to publish the optional commit status." + { [ "${OPENCODE_STATUS_TOKEN_SOURCE:-}" = "github-token" ] || + [ "${OPENCODE_STATUS_TOKEN_SOURCE:-}" = "opencode-app" ]; }; then + formal_review_file="$(mktemp)" + cleanup_formal_review_evidence() { + rm -f "$formal_review_file" + } + trap cleanup_formal_review_evidence EXIT + if ! gh api "repos/${GH_REPOSITORY}/pulls/${PR_NUMBER}/reviews" --paginate --slurp | + jq 'flatten' >"$formal_review_file"; then + echo "::error::OpenCode repository_dispatch status publication could not prove an exact-head formal OpenCode review before skipping unavailable cross-repository commit-status publication." + exit 1 + fi + if ! jq -e --arg head "$PR_HEAD_SHA" ' + any(.[]; + (.commit_id // "") == $head + and ( + (.user.login // "") == "opencode-agent[bot]" + or (.user.login // "") == "opencode-agent" + ) + and ( + ((.state // "") | ascii_upcase) == "APPROVED" + or ((.state // "") | ascii_upcase) == "CHANGES_REQUESTED" + ) + ) + ' "$formal_review_file" >/dev/null; then + echo "::error::OpenCode repository_dispatch status publication could not prove an exact-head formal OpenCode review; refusing to hide a cross-repository review-evidence gap." + exit 1 + fi + echo "::notice::OpenCode repository_dispatch status publication is unavailable because only the same-repository github.token can access cross-repository target ${GH_REPOSITORY}, and the OpenCode App token has no cross-repository commit-status permission in this deployment. A verified exact-head formal review remains authoritative; configure PR_REVIEW_MERGE_TOKEN or OPENCODE_APPROVE_TOKEN to publish the optional commit status." exit 0 fi diff --git a/.github/workflows/opencode-review.yml b/.github/workflows/opencode-review.yml index 7f1ad6d00..80ed61440 100644 --- a/.github/workflows/opencode-review.yml +++ b/.github/workflows/opencode-review.yml @@ -33,7 +33,6 @@ jobs: coverage-source-tree: name: coverage-source-tree - needs: [required-workflow-bootstrap] runs-on: ubuntu-latest steps: - run: >- @@ -42,7 +41,6 @@ jobs: coverage-evidence: name: coverage-evidence - needs: [coverage-source-tree] runs-on: ubuntu-latest steps: - run: >- @@ -51,7 +49,6 @@ jobs: opencode-review-target: name: opencode-review - needs: [coverage-evidence] runs-on: ubuntu-latest steps: - run: >- diff --git a/.github/workflows/pr-review-merge-scheduler.yml b/.github/workflows/pr-review-merge-scheduler.yml index 8e1157060..5882b4fd4 100644 --- a/.github/workflows/pr-review-merge-scheduler.yml +++ b/.github/workflows/pr-review-merge-scheduler.yml @@ -99,7 +99,10 @@ concurrency: github.event_name == 'repository_dispatch' && github.event.client_payload.pr_number != '' && format('pr-{0}', github.event.client_payload.pr_number) || github.event_name == 'repository_dispatch' && github.run_id || github.ref }} - cancel-in-progress: ${{ github.event_name == 'pull_request_target' || github.event_name == 'pull_request_review' || github.event_name == 'repository_dispatch' }} + # scan-pr-queue is a required check. Cancelling an in-flight same-head run + # leaves a CANCELLED required conclusion that stays in the rollup after a + # later success, so the PR looks failed with zero failed jobs. + cancel-in-progress: false # Scorecard Token-Permissions (alert #9): declare a least-privilege default at # the workflow level. The scan-pr-queue job that actually needs write access diff --git a/.github/workflows/strix.yml b/.github/workflows/strix.yml index 03ec23257..c3884b01d 100644 --- a/.github/workflows/strix.yml +++ b/.github/workflows/strix.yml @@ -839,9 +839,10 @@ jobs: # exit 1 both for genuine blocking vulnerabilities AND for # LLM-backend-unavailable outcomes (GitHub Models "Too many requests" # rate limits, OpenAI quota starvation, 413 tokens_limit_reached - # token-cap, connection/warm-up failures) that could not complete a scan. A backend outage is CI - # infrastructure noise, not a security finding, so it must not fail - # the required check and block merges. + # token-cap, connection/warm-up failures) that could not complete a + # scan. Incomplete provider evidence is not a clean security result, + # so the required check stays failed until a later scheduler pass or + # explicit rerun obtains complete evidence. strix_run_log="$RUNNER_TEMP/strix_gate_console.log" strix_rc=0 set +e @@ -854,28 +855,21 @@ jobs: fi # Preserve configuration failures (exit 2) and any unexpected exit - # code as hard failures — only the scan-failure code (1) can be an - # infrastructure/backend-unavailability outcome. + # code as hard failures. A scan-failure code (1), including provider + # or backend unavailability, is incomplete security evidence and + # remains a hard failure for the required check. if [ "$strix_rc" -ne 1 ]; then exit "$strix_rc" fi # Recognized signals that the LLM backend was unavailable / starved. backend_unavailable_signal='RateLimitError|Too many requests\. For more on scraping GitHub|exceeded your current quota|insufficient_quota|billing details|"status"[[:space:]]*:[[:space:]]*"RESOURCE_EXHAUSTED"|tokens_limit_reached|Request body too large|Max size:[[:space:]]*[0-9]+[[:space:]]+tokens|Error code:[[:space:]]*413|LLM CONNECTION FAILED|Could not establish connection to the language model|LLM warm-up failed|Configured model and fallback models were unavailable|Configured Vertex model and fallback models were unavailable|emitted provider infrastructure or failure-signal output|before provider infrastructure failure|litellm(\.exceptions)?\.NotFoundError[^[:cntrl:]]*Nvidia_nimException[^[:cntrl:]]*Error code:[[:space:]]*404' - # Any evidence that a vulnerability was actually reported. Its presence - # forces a hard failure so real findings are NEVER downgraded. Keep the - # severity branch anchored away from identifiers so environment lines - # such as STRIX_FAIL_ON_MIN_SEVERITY do not look like findings. - reported_vulnerability_signal='Vulnerabilities[[:space:]]+[1-9]|(^|[^A-Za-z0-9_])severity[[:space:]]*:' - - # Neutral skip only when ALL hold: a backend-unavailability signal is - # present and no vulnerability was reported anywhere. This preserves - # real security gating while keeping uncontrollable provider outages - # from blocking current-head merge progress. - if grep -Eiq "$backend_unavailable_signal" "$strix_run_log" \ - && ! grep -Eiq "$reported_vulnerability_signal" "$strix_run_log"; then - echo "::warning title=Strix backend unavailable::Strix could not complete because its LLM backend was unavailable (rate limit / token cap / connection or warm-up failure) before producing a vulnerability report. Treating as a neutral skip so an infrastructure outage does not block merges; genuine findings still fail the check. See the strix-reports artifact and the run log." - exit 0 + # Provider/backend failures are not a clean scan. Keep the required + # check red and let the scheduler or an explicit rerun recover after + # provider capacity returns; never convert missing evidence to a pass. + if grep -Eiq "$backend_unavailable_signal" "$strix_run_log"; then + echo "::error title=Strix backend unavailable::Strix could not complete because its LLM backend was unavailable before producing complete security evidence. The required check remains failed; rerun after provider capacity recovers. See the strix-reports artifact and the run log." + exit "$strix_rc" fi echo "Strix reported security findings or failed for a non-backend reason; failing the required check (gate exit ${strix_rc})." >&2 diff --git a/AGENTS.md b/AGENTS.md index 16f0981c0..908f51d8f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,5 +3,5 @@ > **Agents: read the master context FIRST.** Before any work, read [`docs/CWL-MASTER-CONTEXT.md`](docs/CWL-MASTER-CONTEXT.md) (mission · naruon-as-platform + inter-component UML · cross-cutting disciplines · conventions · roadmap · current state), the live **GitHub Project #1** (work/roadmap source of truth), the full spec **ContextualWisdomLab/naruon#974**, and operate the Project per [`docs/agent-github-project-protocol.md`](docs/agent-github-project-protocol.md). The repo/Project — not any private agent memory — is the source of truth. -Materialize accepts only exact SHA-256 pins or a bounded relative `-r` include (no `.`/`..`); a lone `--require-hashes` directive is not trust evidence. See [`docs/doctoring/hourly-nvidia-nim-autofix.md`](docs/doctoring/hourly-nvidia-nim-autofix.md). -Conflict-scope roots fail closed when the immediate parent directory is a symbolic link. +Materialize accepts only exact SHA-256 pins or a bounded relative `-r` include (no `.`/`..`); a lone `--require-hashes` directive is not trust evidence. See [`docs/doctoring/cross-repo-opencode-evidence.md`](docs/doctoring/cross-repo-opencode-evidence.md). +Conflict-scope roots fail closed when the immediate parent directory is a symbolic link. See [`docs/doctoring/hourly-nvidia-nim-autofix.md`](docs/doctoring/hourly-nvidia-nim-autofix.md). diff --git a/CHANGELOG.md b/CHANGELOG.md index 1de9130a5..923d67847 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,8 @@ Semantic Versioning where the repository publishes a release. ### Fixed - 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. +- Stopped cancelling in-flight required `scan-pr-queue` runs and stopped serializing the OpenCode required-workflow stub jobs, so a later same-head success is not hidden behind a cancelled or queued required check. +- Sent a static User-Agent on the pinned trusted-uv archive request, required an exact-head formal OpenCode review before skipping unavailable cross-repository commit-status publication, and kept Strix failed when provider evidence is incomplete instead of neutralizing an outage into a pass. - Refused a conflict-scope repository root whose immediate parent is a symbolic link, so a swapped parent cannot redirect the canonical worktree after the last-component check (CWE-367). - Bounded the Strix quality self-test's deterministic timeout fixtures to 3-second process and 5-second fake-sleep budgets so exact-head policy evidence completes inside the existing job limit without changing production Strix scanner timeouts, providers, credentials, or review semantics. - Allowed commas and ASCII parentheses in the bounded Strix changed-file path policy so legal tracked Packrat fixtures can receive exact-head security analysis, while rejecting raw `..` components before normalization and keeping controls, backslashes, whitespace ambiguity, and shell punctuation fail-closed. diff --git a/docs/doctoring/base-requirements-lock-discovery.md b/docs/doctoring/base-requirements-lock-discovery.md new file mode 100644 index 000000000..66e7efc9e --- /dev/null +++ b/docs/doctoring/base-requirements-lock-discovery.md @@ -0,0 +1,59 @@ +# Base requirements lock discovery contract + +## Purpose + +This doctoring record defines how the central review and coverage workflows discover hash-pinned Python requirement locks from an authenticated pull-request base commit. It records the nested-path regression repaired in pull request #939 and preserves the security boundary already developed in pull request #785. + +## Incident + +The materializer intentionally recognizes two candidate forms: + +- conventional file names such as `requirements.txt`, `requirements-dev.txt`, and `requirements.lock`; and +- direct `.txt` children of any directory named `requirements`, such as `requirements/ci.txt` and `service/requirements/package.txt`. + +The path predicate implemented both forms, but `base_hash_locks()` still called the basename-only predicate. As a result, a direct child such as `requirements/ci.txt` was rejected before its authenticated base blob and hash-pinned content could be evaluated. The implementation advertised path-aware eligibility while the collector enforced only legacy basename eligibility. + +The repair changes the collector to call `_is_candidate_lock_path(candidate)` with the already parsed `PurePosixPath`. It does not broaden the accepted Git object types or relax content validation. + +## Trust boundary + +A candidate enters the generated build context only when every applicable condition holds: + +1. The base revision is an exact 40-character hexadecimal commit SHA. +2. `git ls-tree` reports a regular `100...` blob in that exact base tree. +3. The repository-relative path is non-absolute and contains no `..` component. +4. The path is either a conventional requirements lock name or a direct `.txt` child of a directory named `requirements`. +5. Every substantive requirement is an exact `==` pin with complete SHA-256 hashes, or a separately bounded relative requirements include. +6. Symlinks, gitlinks, malformed tree entries, unpinned files, unsafe includes, and pull-request-only content remain excluded. +7. `uv.lock` follows its separate trusted export path and still requires the corresponding base-owned `pyproject.toml`. + +Path eligibility is candidate discovery, not dependency trust. The existing hash, include, export, and downstream closure checks remain authoritative. + +## Test-first evidence + +Temporary repair workflow run `31787913977` executed the following sequence on head `912313ff92cdcee6f240e9584f79ca37615ee5a2`: + +1. Created a temporary Git repository containing hash-pinned `requirements/ci.txt` and `service/requirements/package.txt` blobs. +2. Confirmed the regression test failed before the implementation change because neither path was collected. +3. Replaced the basename-only collector predicate with the repository-relative path predicate. +4. Confirmed both paths were returned in deterministic repository order. +5. Compiled the implementation and regression test and ran `git diff --check`. +6. Deleted the temporary writer workflow before committing the production change. + +An earlier repair attempt failed before exercising the assertion because direct script execution omitted the repository root from `sys.path`. The corrected workflow ran both RED and GREEN phases with the same explicit `PYTHONPATH=.` environment, so the observed transition is attributable to the collector change rather than import setup. + +## Permanent regression command + +```bash +PYTHONPATH=. python3 tests/test_materialize_base_python_requirement_paths.py +python3 -m compileall -q \ + scripts/ci/materialize_base_python_requirements.py \ + tests/test_materialize_base_python_requirement_paths.py +git diff --check +``` + +The repository quality workflow must also run the full materializer and Strix regression suites on the exact pull-request head. Focused repair evidence cannot replace protected-branch checks, semantic review, or required independent approvals. + +## Change-management rule + +Future changes to candidate naming, path parsing, Git tree filtering, requirement includes, `uv.lock` export, or materialized manifests must update the path-discovery tests and the broader materializer suite together. A path predicate and its collector call site must not evolve independently. diff --git a/docs/doctoring/cross-repo-opencode-evidence.md b/docs/doctoring/cross-repo-opencode-evidence.md new file mode 100644 index 000000000..ff9b7f1d3 --- /dev/null +++ b/docs/doctoring/cross-repo-opencode-evidence.md @@ -0,0 +1,33 @@ +# Cross-repository OpenCode evidence + +## Incident and buyer impact + +Materialize accepts only exact SHA-256 pins or a bounded relative `-r` +include; a lone `--require-hashes` line is not lock evidence. + +Sibling-repo reviews (for example `ContextualWisdomLab/naruon#1317`) lost +coverage-evidence because `releases.astral.sh` rejected the default Python +User-Agent, and the OpenCode App token could not publish a commit status +across repositories. A later Strix provider outage was also converted into +a green required check, so incomplete security evidence looked like a pass. + +## Decision + +1. Send a static `User-Agent: cwl-trusted-uv-materializer/1` on the fixed + Astral HTTPS URL. The URL, no-redirect opener, size bound, checksum, and + executable version checks stay unchanged. +2. Before skipping cross-repository status publication, prove an exact-head + formal OpenCode review (`APPROVED` or `CHANGES_REQUESTED`). Missing proof + fails closed. +3. Keep Strix red when the backend is unavailable. Incomplete provider + evidence is not a clean scan. + +## References + +Fielding, R., Nottingham, M., & Reschke, J. (Eds.). (2022). *HTTP semantics* +(RFC 9110). Internet Engineering Task Force. +https://doi.org/10.17487/RFC9110 + +National Institute of Standards and Technology. (2020). *Security and privacy +controls for information systems and organizations* (NIST Special Publication +800-53 Rev. 5). https://doi.org/10.6028/NIST.SP.800-53r5 diff --git a/docs/doctoring/hourly-nvidia-nim-autofix.md b/docs/doctoring/hourly-nvidia-nim-autofix.md index 6b05c6bd6..ab37dfde0 100644 --- a/docs/doctoring/hourly-nvidia-nim-autofix.md +++ b/docs/doctoring/hourly-nvidia-nim-autofix.md @@ -226,10 +226,14 @@ and repository policy. ## Independent review-agent boundary -`.github/workflows/opencode-review-dispatch.yml` is not modified by this slice. -The regression contract pins that workflow's Git blob SHA byte-for-byte rather -than inferring independence from provider-name strings. The existing reviewer -retains its own separately reviewed identity, model pool, and credential chain. +`.github/workflows/opencode-review-dispatch.yml` is not modified by the hourly +NVIDIA NIM autofix slice. Pull request #939 later required an exact-head formal +OpenCode review before skipping unavailable cross-repository commit-status +publication; that change does not share autofix credentials or give the +write-capable worker review authority. The regression contract pins the current +reviewed Git blob SHA byte-for-byte rather than inferring independence from +provider-name strings. The existing reviewer retains its own separately reviewed +identity, model pool, and credential chain. This is a control separation, not naming convention. Review produces a verdict that may gate merge; autofix proposes branch changes. Their credentials, diff --git a/docs/doctoring/strix-bounded-pr-scope.md b/docs/doctoring/strix-bounded-pr-scope.md new file mode 100644 index 000000000..6b2755879 --- /dev/null +++ b/docs/doctoring/strix-bounded-pr-scope.md @@ -0,0 +1,62 @@ +# Strix bounded pull-request scope and CI recursion contract + +## Purpose + +This doctoring record defines the trusted boundary used when Strix reviews a bounded set of pull-request changes from an organization-required workflow. It also records the GitHub Actions recursion behavior encountered while repairing the boundary so that future maintainers do not misclassify infrastructure state as a target-code defect. + +## Incident chain + +A downstream OpenCode review dispatch for `ContextualWisdomLab/pg-llm-batch#190` failed while the central workflow materialized its trusted `uv` executable. The trusted download retained a fixed Astral release URL, a no-proxy/no-redirect opener, a bounded response read, SHA-256 verification, and executable-version verification, but the request did not identify the organization client. Pull request #939 adds a fixed `User-Agent` and regression coverage without weakening those trust checks. + +During verification of the central repair, Strix received an intentionally bounded pull-request target. The GitHub Actions runner created that target below a host temporary directory, while the Strix sandbox mounted the same files below `/workspace/`. The original host path was intentionally absent inside the sandbox. Treating that absence as a missing-code vulnerability was therefore a scanner-orientation error, not a finding in the pull-request content. + +Repair workflow run `31784776654` established the regression test first, applied the trusted static scope guidance, ran shell syntax validation, ran the focused Python contract, and completed the full `scripts/ci/test_strix_quick_gate.sh` harness before committing the production change. Both temporary repair workflows were removed by the verified commit. + +## Trusted scope contract + +The following invariants apply: + +1. `pull_request_target` executes the protected-base workflow and trusted gate implementation. Pull-request content is materialized as data in a separate bounded directory; it is not executed with privileged credentials. +2. A target created under the runner host temporary directory may be mounted at `/workspace/` inside the Strix sandbox. Absence of the original host pathname inside the sandbox is expected. +3. For the internal bounded pull-request scope only, the trusted gate supplies a static instruction explaining the mount contract and directing Strix to inspect the files present in the current working directory. +4. No repository input, dispatch payload, pull-request field, environment override, or caller-supplied instruction is forwarded to the security model. The instruction is selected only when `TARGET_PATH_IS_INTERNAL_PR_SCOPE=1` was set by trusted scope materialization. +5. The bounded directory is the complete authorized target for the changed-path scan. Strix must continue to report actionable vulnerabilities in the workflow, shell, Python, configuration, and other eligible files that are actually present. +6. Scope orientation must not suppress provider failures, malformed reports, integrity failures, missing authorized files, or vulnerabilities in present content. Those conditions remain fail-closed. + +## GitHub Actions recursion behavior + +The verified repair commit was pushed by a workflow using the repository `GITHUB_TOKEN`. GitHub created the resulting pull-request workflow runs in an approval-required state and reported `action_required` without jobs. This is GitHub's recursion protection rather than test execution evidence. A maintainer-authenticated commit or explicit workflow approval is required before exact-head CI can run normally. + +This repository must not replace the recursion protection with a broadly privileged token merely to make a self-repair workflow recursively trigger CI. Temporary repair workflows must remain narrowly scoped, use least-privilege `contents: write`, verify that the remote branch has not advanced, run the full regression harness before pushing, and delete themselves from the resulting production commit. + +## Regression evidence + +The minimum local or CI evidence for this boundary is: + +```bash +bash -n scripts/ci/strix_quick_gate.sh +python3 -m unittest discover \ + --start-directory tests \ + --pattern 'test_strix_internal_scope_instruction_contract.py' \ + --verbose +bash scripts/ci/test_strix_quick_gate.sh +``` + +The exact pull-request head must additionally complete the trusted-uv materializer quality workflow, Strix changed-path quality workflow, repository security workflows, required OpenCode review, required Strix scan, and all protected-branch review requirements. A previous-head repair run, an approval-required run with no jobs, or a downstream repository's successful leaf checks cannot substitute for current-head central evidence. + +## Operational recovery sequence + +1. Confirm the downstream source head and reproduce the central failure against that exact SHA. +2. Repair the central trusted implementation; do not add unrelated downstream source changes. +3. Add a regression contract that fails before the central repair and passes after it. +4. Verify trusted URL, redirect, proxy, size, checksum, executable-version, and credential boundaries remain intact. +5. Run the full Strix gate harness before committing a scope-orientation change. +6. Remove temporary repair automation from the production diff. +7. Obtain exact-head central CI and independent approvals without dismissing reviews or bypassing branch protection. +8. Merge the central repair normally, then rerun the downstream review on the unchanged downstream head so the infrastructure-derived review is superseded through the standard review path. + +## References + +GitHub. (n.d.). *GITHUB_TOKEN*. GitHub Docs. Retrieved August 14, 2026, from https://docs.github.com/en/actions/concepts/security/github_token + +GitHub. (n.d.). *Securely using pull_request_target*. GitHub Docs. Retrieved August 14, 2026, from https://docs.github.com/en/actions/reference/security/securely-using-pull_request_target diff --git a/scripts/ci/materialize_base_python_requirements.py b/scripts/ci/materialize_base_python_requirements.py index 7a9c204b8..dc69e7fe6 100755 --- a/scripts/ci/materialize_base_python_requirements.py +++ b/scripts/ci/materialize_base_python_requirements.py @@ -42,6 +42,7 @@ "https://releases.astral.sh/github/uv/releases/download/0.12.1/" "uv-x86_64-unknown-linux-gnu.tar.gz" ) +TRUSTED_UV_DOWNLOAD_USER_AGENT = "cwl-trusted-uv-materializer/1" TRUSTED_UV_ARCHIVE_SHA256 = ( "90b2f223fb69d19db49e117da601f64978593417988530aa733d456141b4bcbb" ) @@ -87,7 +88,6 @@ def _is_candidate_lock_name(name: str) -> bool: ) - def _is_candidate_lock_path(path: pathlib.PurePosixPath) -> bool: """Return whether one safe tracked path can name a pip requirements lock. @@ -222,12 +222,16 @@ def _download_trusted_uv_archive() -> bytes: """Download the fixed uv release archive through one HTTPS trust boundary.""" _install_trusted_uv_url_opener() try: - # Keep the audited URL literal at the network sink so static analysis can - # prove that neither user data nor repository content selects a scheme, - # host, path, query, fragment, method, or request header. - with urllib.request.urlopen( # nosemgrep: python.lang.security.audit.dynamic-urllib-use-detected.dynamic-urllib-use-detected # nosec B310 + # Keep the audited URL literal and static request header in this trusted + # function so neither user data nor repository content selects the + # scheme, host, path, query, fragment, method, or request header. + request = urllib.request.Request( "https://releases.astral.sh/github/uv/releases/download/0.12.1/" "uv-x86_64-unknown-linux-gnu.tar.gz", + headers={"User-Agent": TRUSTED_UV_DOWNLOAD_USER_AGENT}, + ) + with urllib.request.urlopen( # nosemgrep: python.lang.security.audit.dynamic-urllib-use-detected.dynamic-urllib-use-detected # nosec B310 + request, timeout=TRUSTED_UV_DOWNLOAD_TIMEOUT_SECONDS, ) as response: final_url = urllib.parse.urlparse(response.geturl()) @@ -512,7 +516,7 @@ def base_hash_locks(repo_root: pathlib.Path, base_sha: str) -> list[tuple[str, b regular_paths = {path for path, _candidate in regular_blobs} locks: list[tuple[str, bytes]] = [] for path, candidate in regular_blobs: - if _is_candidate_lock_name(candidate.name): + if _is_candidate_lock_path(candidate): content = _git(repo_root, "show", f"{base_sha}:{path}") if _is_hash_pinned(content): locks.append((path, content)) @@ -585,4 +589,4 @@ def main(argv: list[str] | None = None) -> int: if __name__ == "__main__": - raise SystemExit(main()) \ No newline at end of file + raise SystemExit(main()) diff --git a/scripts/ci/strix_quick_gate.sh b/scripts/ci/strix_quick_gate.sh index 0f37f3460..cde2472f0 100755 --- a/scripts/ci/strix_quick_gate.sh +++ b/scripts/ci/strix_quick_gate.sh @@ -68,6 +68,7 @@ NORMALIZED_CHANGED_FILES=() PULL_REQUEST_SCOPE_DIRS=() LAST_PULL_REQUEST_SCOPE_DIR="" TARGET_PATH_IS_INTERNAL_PR_SCOPE=0 +INTERNAL_PR_SCOPE_INSTRUCTION="This target is a deliberately bounded pull-request changed-file scope mounted by Strix under /workspace/. The original GitHub Actions runner host path is intentionally absent inside the sandbox, and that absence is not a vulnerability. Treat the files in the current working directory as the complete authorized target for this quick changed-path scan. Inspect the available workflow, shell, Python, and configuration files for actionable content vulnerabilities. Do not report the missing host path or intentional scope bounding as a target-code vulnerability." resolve_trusted_input_file() { local label="$1" @@ -2328,6 +2329,7 @@ run_strix_once() { local rc local llm_api_base_value local child_model + local child_instruction="" local resolved_target_path local timeout_seconds="$STRIX_PROCESS_TIMEOUT_SECONDS" local total_budget_limited_timeout=0 @@ -2354,6 +2356,9 @@ run_strix_once() { if ! resolved_target_path="$(resolve_current_target_path "$TARGET_PATH")"; then return 1 fi + if [ "$TARGET_PATH_IS_INTERNAL_PR_SCOPE" -eq 1 ]; then + child_instruction="$INTERNAL_PR_SCOPE_INSTRUCTION" + fi local start_epoch start_epoch="$(date +%s)" local child_llm_api_key="" @@ -2375,6 +2380,7 @@ run_strix_once() { STRIX_CHILD_EXECUTABLE_ROOT="$STRIX_EXECUTABLE_ROOT" \ STRIX_CHILD_EXECUTABLE_SHA256="$STRIX_EXECUTABLE_SHA256" \ STRIX_CHILD_REQUIRE_EXECUTABLE_INTEGRITY="${IS_PR_EVIDENCE_RUN:-false}" \ + STRIX_CHILD_INSTRUCTION="$child_instruction" \ python3 - "$timeout_seconds" "$resolved_target_path" "$SCAN_MODE" "$STRIX_LOG" <<'PY' import hashlib import hmac @@ -2529,6 +2535,9 @@ if any(ch in str(target_cwd) for ch in ("\x00", "\n", "\r")): raise SystemExit(2) command = [resolved_strix_bin, "-n", "-t", ".", "--scan-mode", scan_mode] +instruction = os.environ.get("STRIX_CHILD_INSTRUCTION", "").strip() +if instruction: + command.extend(["--instruction", instruction]) try: process = subprocess.Popen( diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 7343c06ac..a35066687 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -1506,7 +1506,7 @@ assert_pr_review_merge_scheduler_uses_github_actions_bot_token() { assert_file_contains "$workflow_file" "github.event_name == 'workflow_run' && github.event.workflow_run.pull_requests[0].number && format('pr-{0}', github.event.workflow_run.pull_requests[0].number)" "scheduler scopes workflow_run concurrency to the completed review PR" assert_file_contains "$workflow_file" "github.event_name == 'schedule' && format('schedule-{0}', github.event.schedule)" "scheduler isolates the 15-minute organization sweep from the separate 30-minute scheduled scan" assert_file_contains "$workflow_file" "github.event_name == 'repository_dispatch' && github.run_id" "scheduler keeps manual queue scans isolated per run" - assert_file_contains "$workflow_file" "cancel-in-progress: \${{ github.event_name == 'pull_request_target' || github.event_name == 'pull_request_review' || github.event_name == 'repository_dispatch' }}" "scheduler cancels stale PR/review/manual queue scans instead of accumulating merge/update attempts" + assert_file_contains "$workflow_file" "cancel-in-progress: false" "scheduler does not cancel in-progress required scan-pr-queue runs" assert_file_contains "$workflow_file" "timeout-minutes: 60" "organization sweep has enough headroom to finish the complete repository walk" assert_file_contains "$workflow_file" "ORG_SWEEP_TRIGGER_REVIEWS: \${{ github.event_name == 'schedule' ||" "scheduled organization sweeps retry missing current-head OpenCode reviews" assert_file_contains "$workflow_file" "ORG_SWEEP_ENABLE_AUTO_MERGE: \${{ github.event_name == 'schedule' ||" "scheduled organization sweeps merge approved current heads" diff --git a/tests/test_materialize_base_python_requirement_paths.py b/tests/test_materialize_base_python_requirement_paths.py new file mode 100644 index 000000000..fbf0e5633 --- /dev/null +++ b/tests/test_materialize_base_python_requirement_paths.py @@ -0,0 +1,62 @@ +"""Regression contracts for repository-relative requirements lock discovery.""" + +from __future__ import annotations + +import subprocess +import tempfile +import unittest +from pathlib import Path + +from scripts.ci import materialize_base_python_requirements as materializer + + +def _git(repo: Path, *args: str) -> str: + """Run one deterministic Git command in the fixture repository.""" + return subprocess.run( + ["git", "-C", str(repo), *args], + check=True, + capture_output=True, + text=True, + ).stdout.strip() + + +class BaseHashLockPathTests(unittest.TestCase): + """Protect direct ``requirements`` directory child discovery.""" + + def test_collects_hash_locks_under_any_requirements_directory(self) -> None: + """Use the complete repository-relative path, not only the basename.""" + with tempfile.TemporaryDirectory() as temporary_directory: + repo = Path(temporary_directory) + _git(repo, "init") + _git(repo, "config", "user.name", "Test") + _git(repo, "config", "user.email", "test@example.invalid") + + root_requirements = repo / "requirements" + nested_requirements = repo / "service" / "requirements" + root_requirements.mkdir() + nested_requirements.mkdir(parents=True) + (root_requirements / "ci.txt").write_text( + "ci-demo==1 --hash=sha256:" + ("a" * 64) + "\n", + encoding="utf-8", + ) + (nested_requirements / "package.txt").write_text( + "service-demo==1 --hash=sha256:" + ("b" * 64) + "\n", + encoding="utf-8", + ) + _git(repo, "add", ".") + _git(repo, "commit", "-m", "base") + base_sha = _git(repo, "rev-parse", "HEAD") + + locks = materializer.base_hash_locks(repo, base_sha) + + self.assertEqual( + [path for path, _content in locks], + [ + "requirements/ci.txt", + "service/requirements/package.txt", + ], + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_opencode_agent_contract.py b/tests/test_opencode_agent_contract.py index 5316b67d9..bea2e0cc5 100644 --- a/tests/test_opencode_agent_contract.py +++ b/tests/test_opencode_agent_contract.py @@ -1994,10 +1994,7 @@ def test_merge_scheduler_uses_escalating_mutation_credentials(): assert 'check_delay="$((check_attempt * 2))"' in workflow assert "steps.review_followup.outputs.proceed != 'false'" in workflow assert "The scheduled organization sweep remains authoritative." in workflow - assert ( - "github.event_name == 'pull_request_review' || " - "github.event_name == 'repository_dispatch'" in workflow - ) + assert "cancel-in-progress: false" in workflow def test_opencode_runs_merge_scheduler_after_review_without_repo_local_dispatch(): @@ -2025,6 +2022,7 @@ def test_opencode_runs_merge_scheduler_after_review_without_repo_local_dispatch( ) in status_step assert "OPENCODE_STATUS_TOKEN_SOURCE" in status_step assert "steps.opencode_app_token.outputs.available == 'true' && 'opencode-app'" in status_step + assert '[ "${OPENCODE_STATUS_TOKEN_SOURCE:-}" = "opencode-app" ]' in status_step assert "OPENCODE_CHANGED_FILES_FILE" in status_step assert "OPENCODE_ARTIFACT_MANIFEST_SHA256" in status_step assert "OPENCODE_SOURCE_WORKDIR" in status_step @@ -2038,10 +2036,17 @@ def test_opencode_runs_merge_scheduler_after_review_without_repo_local_dispatch( assert "exit 1" in status_step cross_repository_guard = status_step.split( 'if [ "${GH_REPOSITORY:-}" != "${GITHUB_REPOSITORY:-}" ]', 1 - )[1].split("\n fi", 1)[0] + )[1].split("\n\n state=", 1)[0] assert "exact-head formal review remains authoritative" in cross_repository_guard + assert 'formal_review_file="$(mktemp)"' in cross_repository_guard + assert 'gh api "repos/${GH_REPOSITORY}/pulls/${PR_NUMBER}/reviews"' in cross_repository_guard + assert '(.commit_id // "") == $head' in cross_repository_guard + assert 'opencode-agent[bot]' in cross_repository_guard + assert "APPROVED" in cross_repository_guard + assert "CHANGES_REQUESTED" in cross_repository_guard + assert "could not prove an exact-head formal OpenCode review" in cross_repository_guard assert "exit 0" in cross_repository_guard - assert "exit 1" not in cross_repository_guard + assert "exit 1" in cross_repository_guard assert "using %s token" in status_step assert "scripts/ci/opencode_dispatch_status.py" in status_step assert "COVERAGE_EVIDENCE_RESULT" in status_step diff --git a/tests/test_pr_review_autofix_nvidia_nim_contract.py b/tests/test_pr_review_autofix_nvidia_nim_contract.py index 1bbd98750..3d3d4c449 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 = "ed97ed75cb00c661240e9ed7251c45ce28c333d4" def _workflow_text(path: Path) -> str: @@ -157,7 +157,7 @@ def test_missing_nvidia_nim_secret_fails_closed_before_model_execution() -> None def test_independent_review_agent_key_system_is_unchanged() -> None: - """Pin the existing read-only reviewer workflow byte-for-byte.""" + """Pin the read-only reviewer workflow, including formal-review proof.""" result = subprocess.run( ["git", "hash-object", str(REVIEW_DISPATCH_WORKFLOW)], check=True, diff --git a/tests/test_required_workflow_queue_contract.py b/tests/test_required_workflow_queue_contract.py index 233c08584..e42049085 100644 --- a/tests/test_required_workflow_queue_contract.py +++ b/tests/test_required_workflow_queue_contract.py @@ -296,6 +296,28 @@ def test_close_empty_pr_metadata_lookup_retries_and_fails_open() -> None: assert "exit 0" in workflow +def test_merge_scheduler_does_not_cancel_required_queue_scans() -> None: + """A cancelled required scan-pr-queue stays red after later same-head success.""" + workflow = workflow_text("pr-review-merge-scheduler.yml") + concurrency = workflow.split("concurrency:", 1)[1].split("permissions:", 1)[0] + + assert "cancel-in-progress: false" in concurrency + assert "cancel-in-progress: true" not in concurrency + assert "leaves a CANCELLED required conclusion" in concurrency + + +def test_opencode_bootstrap_required_checks_do_not_serialize_runner_waits() -> None: + """Required stub names must not wait on each other for a runner.""" + bootstrap = workflow_text("opencode-review.yml") + jobs = bootstrap.split("jobs:\n", 1)[1] + + assert " required-workflow-bootstrap:\n" in jobs + assert " coverage-source-tree:\n" in jobs + assert " coverage-evidence:\n" in jobs + assert " opencode-review-target:\n" in jobs + assert " needs:" not in jobs + + def test_cancelled_review_workflow_runs_do_not_spawn_more_queue_work() -> None: for filename in ("noema-review.yml", "pr-review-merge-scheduler.yml"): workflow = workflow_text(filename) @@ -1079,7 +1101,7 @@ def test_optional_strix_workflow_absence_is_logged_without_failing_lookup() -> N assert 'if target_workflow_available "strix.yml"; then' in failed_check_evidence -def test_strix_provider_outage_without_findings_is_neutralized() -> None: +def test_strix_provider_outage_without_findings_fails_closed() -> None: workflow = workflow_text("strix.yml") assert "RateLimitError|Too many requests" in workflow @@ -1087,12 +1109,16 @@ def test_strix_provider_outage_without_findings_is_neutralized() -> None: assert "billing details" in workflow assert "LLM warm-up failed" in workflow assert "zero_vulnerabilities_signal" not in workflow - assert "(^|[^A-Za-z0-9_])severity[[:space:]]*:" in workflow assert "STRIX_FAIL_ON_MIN_SEVERITY: MEDIUM" in workflow - assert "before producing a vulnerability report" in workflow - assert "genuine findings still fail the check" in workflow + assert "before producing complete security evidence" in workflow + assert "The required check remains failed" in workflow + assert "rerun after provider capacity recovers" in workflow + assert "Incomplete provider evidence is not a clean security result" in workflow + assert "backend outage is CI infrastructure noise" not in workflow + assert "reported_vulnerability_signal" not in workflow assert ( - '&& ! grep -Eiq "$reported_vulnerability_signal" "$strix_run_log"' in workflow + 'if grep -Eiq "$backend_unavailable_signal" "$strix_run_log"; then' + in workflow ) diff --git a/tests/test_strix_internal_scope_instruction_contract.py b/tests/test_strix_internal_scope_instruction_contract.py new file mode 100644 index 000000000..84c1dac99 --- /dev/null +++ b/tests/test_strix_internal_scope_instruction_contract.py @@ -0,0 +1,60 @@ +"""Protect Strix's interpretation of bounded pull-request scan targets.""" + +from pathlib import Path +import unittest + + +SCRIPT_PATH = Path("scripts/ci/strix_quick_gate.sh") + + +class InternalScopeInstructionContractTests(unittest.TestCase): + """Keep static sandbox guidance scoped to trusted PR materialization.""" + + @classmethod + def setUpClass(cls) -> None: + """Load the gate implementation once for contract assertions.""" + cls.script = SCRIPT_PATH.read_text(encoding="utf-8") + + def test_guidance_explains_the_sandbox_mount_contract(self) -> None: + """Tell Strix why the runner host path is absent without hiding code.""" + self.assertIn( + "deliberately bounded pull-request changed-file scope", + self.script, + ) + self.assertIn("/workspace/", self.script) + self.assertIn("host path is intentionally absent", self.script) + self.assertIn("complete authorized target", self.script) + self.assertIn("actionable content vulnerabilities", self.script) + + def test_guidance_is_only_selected_for_internal_pr_scope(self) -> None: + """Never relay caller-controlled instructions to the security agent.""" + expected = ( + 'if [ "$TARGET_PATH_IS_INTERNAL_PR_SCOPE" -eq 1 ]; then\n' + '\t\tchild_instruction="$INTERNAL_PR_SCOPE_INSTRUCTION"\n' + '\tfi' + ) + self.assertIn(expected, self.script) + self.assertIn('local child_instruction=""', self.script) + self.assertNotIn( + 'STRIX_CHILD_INSTRUCTION="${STRIX_INSTRUCTION', + self.script, + ) + + def test_child_process_receives_the_static_cli_instruction(self) -> None: + """Forward the trusted guidance through the stripped child environment.""" + self.assertIn( + 'STRIX_CHILD_INSTRUCTION="$child_instruction"', + self.script, + ) + self.assertIn( + 'instruction = os.environ.get("STRIX_CHILD_INSTRUCTION", "").strip()', + self.script, + ) + self.assertIn( + 'command.extend(["--instruction", instruction])', + self.script, + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/test_strix_nvidia_nim_not_found_fallback.py b/tests/test_strix_nvidia_nim_not_found_fallback.py index a48f3092d..310acb4a5 100644 --- a/tests/test_strix_nvidia_nim_not_found_fallback.py +++ b/tests/test_strix_nvidia_nim_not_found_fallback.py @@ -85,18 +85,14 @@ def _workflow_signal_pattern(workflow: str, variable_name: str) -> str: return match.group(1) -def _workflow_neutralizes(log_text: str) -> bool: - """Execute the outer workflow's backend-neutralization condition.""" +def _workflow_detects_backend_unavailability(log_text: str) -> bool: + """Execute the outer workflow's backend-unavailability classifier.""" workflow = STRIX_WORKFLOW.read_text(encoding="utf-8") backend_pattern = _workflow_signal_pattern( workflow, "backend_unavailable_signal", ) - vulnerability_pattern = _workflow_signal_pattern( - workflow, - "reported_vulnerability_signal", - ) with tempfile.TemporaryDirectory(prefix="strix-workflow-404-") as temp_dir: log_path = Path(temp_dir) / "strix.log" log_path.write_text(log_text, encoding="utf-8") @@ -106,17 +102,9 @@ def _workflow_neutralizes(log_text: str) -> bool: capture_output=True, text=True, ) - vulnerability = subprocess.run( - ["grep", "-Eiq", vulnerability_pattern, str(log_path)], - check=False, - capture_output=True, - text=True, - ) if backend.returncode not in {0, 1}: raise AssertionError(backend.stderr) - if vulnerability.returncode not in {0, 1}: - raise AssertionError(vulnerability.stderr) - return backend.returncode == 0 and vulnerability.returncode == 1 + return backend.returncode == 0 class StrixNvidiaNotFoundFallbackTests(unittest.TestCase): @@ -200,15 +188,15 @@ def test_workflow_uses_available_free_first_nvidia_plan(self) -> None: self.assertNotIn(RETIRED_PRIMARY_MODEL, default_gate) def test_outer_workflow_requires_litellm_context_for_nvidia_404(self) -> None: - """Reject provider-like target text in the outer neutralization gate.""" + """Reject provider-like target text in the outer failure classifier.""" self.assertFalse( - _workflow_neutralizes( + _workflow_detects_backend_unavailability( "source literal: Nvidia_nimException Error code: 404\n" ) ) self.assertTrue( - _workflow_neutralizes( + _workflow_detects_backend_unavailability( "litellm.exceptions.NotFoundError: Nvidia_nimException - " "Error code: 404\nVulnerabilities 0\n" ) @@ -218,7 +206,7 @@ def test_outer_workflow_rejects_cross_line_signal_assembly(self) -> None: """Require exception, provider, and 404 evidence on one physical line.""" self.assertFalse( - _workflow_neutralizes( + _workflow_detects_backend_unavailability( "litellm.exceptions.NotFoundError: provider unavailable\n" "Nvidia_nimException Error code: 404\n" ) @@ -228,33 +216,39 @@ def test_outer_workflow_rejects_nvidia_404_without_litellm_context(self) -> None """Require LiteLLM NotFoundError context, not just NVIDIA + 404.""" self.assertFalse( - _workflow_neutralizes( + _workflow_detects_backend_unavailability( "Nvidia_nimException Error code: 404\nVulnerabilities 0\n" ) ) - def test_outer_workflow_never_neutralizes_reported_vulnerabilities(self) -> None: - """Keep a real vulnerability signal blocking despite provider failure.""" + def test_outer_workflow_detects_provider_failure_even_with_reported_findings( + self, + ) -> None: + """Keep provider failure evidence visible even beside a finding.""" - self.assertFalse( - _workflow_neutralizes( + self.assertTrue( + _workflow_detects_backend_unavailability( "litellm.exceptions.NotFoundError: Nvidia_nimException - " "Error code: 404\nVulnerabilities 1\n" ) ) - def test_workflow_neutralizes_only_nvidia_404_without_findings(self) -> None: - """Retain the static fail-closed vulnerability evidence contract.""" + def test_workflow_fails_closed_on_provider_unavailability(self) -> None: + """Never turn incomplete provider evidence into a successful check.""" workflow = STRIX_WORKFLOW.read_text(encoding="utf-8") self.assertIn("Nvidia_nimException", workflow) self.assertIn("Error code:[[:space:]]*404", workflow) - self.assertIn("reported_vulnerability_signal", workflow) - self.assertIn("Vulnerabilities[[:space:]]+[1-9]", workflow) self.assertIn( - '! grep -Eiq "$reported_vulnerability_signal"', + 'echo "::error title=Strix backend unavailable::', workflow, ) + self.assertNotIn("reported_vulnerability_signal", workflow) + self.assertNotIn("Treating as a neutral skip", workflow) + failure_block = workflow.split( + "backend_unavailable_signal=", maxsplit=1 + )[1].split("- name: Collect Strix reports", maxsplit=1)[0] + self.assertNotIn("exit 0", failure_block) if __name__ == "__main__": diff --git a/tests/test_trusted_uv_download_contract.py b/tests/test_trusted_uv_download_contract.py index 02f3c5961..afc5eae8a 100644 --- a/tests/test_trusted_uv_download_contract.py +++ b/tests/test_trusted_uv_download_contract.py @@ -53,25 +53,16 @@ def _urlopen_calls() -> list[ast.Call]: ] -def test_urlopen_receives_one_literal_https_release_url() -> None: - """Static analysis can prove repository or user data never selects the URL.""" +def test_urlopen_receives_one_static_release_request() -> None: + """Static analysis can prove repository data never selects the request.""" calls = _urlopen_calls() assert len(calls) == 1 assert len(calls[0].args) == 1 - url_argument = calls[0].args[0] - assert isinstance(url_argument, ast.Constant) - assert isinstance(url_argument.value, str) - assert url_argument.value == _EXPECTED_URL - - -def test_literal_network_sink_matches_the_documented_release_constant() -> None: - """The scanner-friendly sink literal cannot drift from the release identity.""" - assert _assigned_literal("TRUSTED_UV_ARCHIVE_URL") == _EXPECTED_URL - + request_argument = calls[0].args[0] + assert isinstance(request_argument, ast.Name) + assert request_argument.id == "request" -def test_downloader_never_constructs_a_dynamic_request_object() -> None: - """The audited downloader cannot hide a dynamic URL inside ``Request``.""" request_calls = [ node for node in ast.walk(_download_function()) @@ -79,8 +70,32 @@ def test_downloader_never_constructs_a_dynamic_request_object() -> None: and isinstance(node.func, ast.Attribute) and node.func.attr == "Request" ] + assert len(request_calls) == 1 + assert len(request_calls[0].args) == 1 + url_argument = request_calls[0].args[0] + assert isinstance(url_argument, ast.Constant) + assert isinstance(url_argument.value, str) + assert url_argument.value == _EXPECTED_URL + + headers = next( + keyword.value + for keyword in request_calls[0].keywords + if keyword.arg == "headers" + ) + assert isinstance(headers, ast.Dict) + assert len(headers.keys) == 1 + assert isinstance(headers.keys[0], ast.Constant) + assert headers.keys[0].value == "User-Agent" + assert isinstance(headers.values[0], ast.Name) + assert headers.values[0].id == "TRUSTED_UV_DOWNLOAD_USER_AGENT" + assert _assigned_literal("TRUSTED_UV_DOWNLOAD_USER_AGENT") == ( + "cwl-trusted-uv-materializer/1" + ) - assert request_calls == [] + +def test_literal_network_sink_matches_the_documented_release_constant() -> None: + """The scanner-friendly sink literal cannot drift from the release identity.""" + assert _assigned_literal("TRUSTED_UV_ARCHIVE_URL") == _EXPECTED_URL def test_literal_urlopen_sink_has_one_scoped_semgrep_suppression() -> None: