diff --git a/CHANGELOG.d/20260920-strix-trusted-binder-runtime-fixture.md b/CHANGELOG.d/20260920-strix-trusted-binder-runtime-fixture.md new file mode 100644 index 0000000000..bc10810558 --- /dev/null +++ b/CHANGELOG.d/20260920-strix-trusted-binder-runtime-fixture.md @@ -0,0 +1,11 @@ +### Strix keeps trusted evidence binding outside consumer workspaces + +- The executable Strix harness now pins the trusted binder owner: the gate must + resolve `strix_evidence_binding.py` beside its trusted source, never from the + consumer `STRIX_REPO_ROOT`, and the consumer fixture fails if it carries the + binder. +- The commercial-readiness receipt contract now compares the complete parsed + harden-runner endpoint set instead of treating an expected hostname as a URL + substring. This closes the exact CodeQL + `py/incomplete-url-substring-sanitization` finding without suppressing it or + widening egress. diff --git a/CHANGELOG.md b/CHANGELOG.md index d90fa0c899..be6ac3dcc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### Strix supplies bounded Job Analysis authority context from the trusted base + +- ContextualWisdomLab/orgmetra#63 changes `packages/hris-kernel/src/orgmetra_hris_kernel/job_analysis.py`, but the Strix scan workspace previously omitted the unchanged authorization, HTTP, snapshot, and persistence collaborators that establish its resource-ownership boundary. That incomplete context produced a false HIGH IDOR finding even though the product reconstructs owner scope and authorizes resource fields before port access. A source-first executable fixture now requires the changed PR-head module, exactly five unchanged Job Analysis authority files from the authenticated trusted base, and exclusion of an unrelated administration file. RED `1fd22f4e` failed because `auth.py` was absent; the gate now recognizes only the normalized Job Analysis trigger and adds the five fixed context paths through the existing trusted-base materialization boundary. Follow-up `5ee6c876da508e45d284517a3812d52e053e3728` closes a fail-open edge in that contract: if any of the five required trusted-base files is absent while the Job Analysis trigger changed, the gate exits before invoking Strix. The same commit pins pytest-asyncio's fixture loop scope to `function`, eliminating the suite's configuration deprecation without changing any async fixture behavior. No consumer source, provider/model policy, severity gate, timeout, or write authority changes. + ### Intel macOS native archives are bound to x86_64 bytes - The release prescreener now requires every native member in an Intel macOS diff --git a/docs/product-technical-gap-baseline.md b/docs/product-technical-gap-baseline.md index 6e5f1c549a..39a893e2ed 100644 --- a/docs/product-technical-gap-baseline.md +++ b/docs/product-technical-gap-baseline.md @@ -3438,6 +3438,102 @@ alone -- it is a documented multi-PR hot-file collision zone. Contract: **Evidence / remaining condition.** The standalone fixture mechanism was executed locally against Python stdlib and produced one canonical request followed by terminal HTTP 302 for every hostile target. This is mechanism evidence, not repository acceptance. Final authority requires focused/full exact-tree GREEN, fresh exact-head Security/SAST/Python Security/CodeQL/runtime-quality checks, no unresolved actionable review, ordinary protected-main integration, and downstream consumer validation. No scanner suppression, redirect allowlist widening, provider fallback, workflow gate weakening, or credential-boundary change is included. +## 2026-09-20 Strix trusted-binder consumer-isolation gap + +**Status:** Proposed on `ContextualWisdomLab/.github#2291`; exact-head hosted +checks, independent review, and protected-main integration remain required. + +**Context Map / owner.** The central `.github` CI bounded context owns +`strix_quick_gate.sh`, its evidence binder, and the executable gate harness. +Consumer repositories supply only the scan workspace through +`STRIX_REPO_ROOT`; they do not copy or own the binder. + +**Gap / root cause.** The production gate incorrectly resolved the trusted +binder from the consumer root. The first repair correctly moved that lookup to +`SCRIPT_DIR`, but its test harness copied only the gate and model helper into +the isolated fixture. The current PR head therefore still reproduced the same +missing-binder exit in the `success` scenario. Three assertions in that harness +also described the removed standalone `coverage-source-tree` job after its +responsibility moved into `validate-pr-metadata`. + +**Action / evidence.** The production gate resolves +`strix_evidence_binding.py` beside its trusted source. RED `191bd630` +requires the generic executable consumer fixture to contain no binder. GREEN +`ef1a8667` materializes the gate, model helper, and binder under a separate +`trusted-source/scripts/ci` directory, passes only the binder-free consumer +workspace through `STRIX_REPO_ROOT`, and invokes the trusted gate by its +absolute path. This makes the core executable fixture reproduce the production +owner boundary instead of proving a co-located copy. The full exact-tree Strix +harness and hosted checks remain the release authority; no provider, model, +timeout, severity, or consumer ownership boundary changes. + +**2026-09-26 exact-head RCA / owner integration.** Exact Python-security job +`107750961662` on head `1794626af3473ef23b9c2e678c3f06fd6c11636f` +found AnyIO 4.14.0's CVE-2026-63374, CVE-2026-64847, and CVE-2026-63349 in +`requirements-strix-ci-hashes.txt`; this branch had not adopted the central +source-to-hash AnyIO 4.14.2 repair from `ContextualWisdomLab/.github#2385`. +Exact CodeQL dispatch run `36204821293`, Python job `108319933572`, separately +produced one Medium+ SARIF result: +`py/incomplete-url-substring-sanitization` at +`tests/test_organization_commercial_readiness_loop_receipt_contract.py:60`. +The receipt test parsed the complete YAML endpoint block but then expressed the +expected receiver hostname through a subset/membership-style assertion that +CodeQL correctly rejects on URL-security surfaces. The ordinary two-parent +owner integration adopts #2385's AnyIO contract; the test now compares the +complete seven-entry endpoint set exactly. This strengthens the egress oracle: +an unexpected endpoint fails rather than being tolerated. No CodeQL query, +severity, SARIF gate, dependency audit, or endpoint allowlist is suppressed or +widened. Fresh exact-head hosted Python Security and CodeQL remain mandatory. + +**2026-09-27 Job Analysis bounded-context repair.** ContextualWisdomLab/orgmetra#63 exact head +`d88800a5ca3ca15df332e8def5e25064c46e4005` changes the HRIS-kernel Job +Analysis aggregate module, while the trusted scan workspace previously omitted +the unchanged product-owned authority context that explains its ownership +checks. Strix consequently reported a HIGH IDOR finding against an incomplete +workspace even though the Job Analysis API reconstructs the canonical owner and +authorizes resource fields before snapshot or PostgreSQL port access. Source- +first RED `1fd22f4e1e86d0ebfe5dab932697e95593c9ad10` adds an executable +pull-request-target fixture whose fake scanner refuses to run unless the changed +PR-head `job_analysis.py` is accompanied by exactly the five fixed trusted-base +collaborators (`auth.py`, `authorization.py`, `http.py`, `postgres.py`, and +`snapshot.py`); it also proves an unrelated administration module is excluded. +The minimal GREEN recognizes only that normalized trigger and emits those five +paths through the existing trusted-base context materializer. This is a bounded +CI-context repair, not a transfer of product domain truth: no Orgmetra source, +authorization order, persistence boundary, model/provider policy, severity, +timeout, or write capability changes. Exact-head hosted Strix acceptance, +independent review, ordinary protected-main integration, and a fresh +ContextualWisdomLab/orgmetra#63 consumer run remain mandatory before the +false-positive gap is complete. + +**2026-09-27 required-context fail-closed follow-up.** Review of PR #2291 at +head `b90d873e67860944308d5cef919a1f95243ef98f` found that the five paths above +were selected but not required: the shared trusted-context copier treated a +missing base path as an optional success. A Job Analysis scan could therefore +reach Strix without the authority evidence the mapping promises. RED removed +`auth.py` from the authenticated base, changed only the Job Analysis kernel, +and observed exit 1 after one fake-Strix invocation. Exact source commit +`5ee6c876da508e45d284517a3812d52e053e3728` makes those five paths mandatory +only when that kernel trigger is in the authenticated changed-file inventory; +missing context now exits 2 before Strix, while unrelated mapping families keep +their prior optional-file behavior. The full Strix shell harness passes, and +the full Python suite passes with DeprecationWarning promoted to an error +(`3388 passed, 28 skipped, 40 subtests`). Hosted exact-head checks, qualifying +independent review, ordinary protected-main integration, and a fresh +ContextualWisdomLab/orgmetra#63 consumer run remain mandatory. + +**2026-09-29 protected-main integration.** The branch had fallen 260 commits +behind `main` and was `CONFLICTING`. An ordinary two-parent merge adopts the +trusted-binder lookup, separated trusted fixture runtime, and OpenCode +assertions that `main` had already absorbed; this PR now owns only the +binder-owner harness assertions, the consumer-fixture binder guard, the Job +Analysis authority mapping and its fail-closed copier, the exact receipt +endpoint set, and the explicit pytest-asyncio loop scope. The duplicate +fixture helper produced by the automatic merge was removed in favour of +`main`'s definition. Exact-tree evidence: full Strix harness PASS, filtered Job +Analysis case PASS, and the full Python suite 5,095 passed and 5 skipped. +Hosted exact-head checks and qualifying independent review remain mandatory. + ## 2026-09-27 exact release distribution/scope evidence coverage **Status:** Proposed on `ContextualWisdomLab/.github#2400`; the current diff --git a/pyproject.toml b/pyproject.toml index ff6353c164..9e436ff212 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,7 @@ dev = [ [tool.pytest.ini_options] pythonpath = ["."] +asyncio_default_fixture_loop_scope = "function" [tool.coverage.run] branch = true diff --git a/scripts/ci/strix_quick_gate.sh b/scripts/ci/strix_quick_gate.sh index ccf08f48eb..efb9f65e08 100755 --- a/scripts/ci/strix_quick_gate.sh +++ b/scripts/ci/strix_quick_gate.sh @@ -1384,6 +1384,7 @@ pull_request_scope_context_files() { local needs_backend_app_python=0 local needs_contextual_orchestrator_python=0 local needs_frontend_email_api_context=0 + local needs_orgmetra_job_analysis_authority_context=0 local needs_deployment_context=0 local changed_file normalized_changed_file for changed_file in "$@"; do @@ -1400,6 +1401,9 @@ pull_request_scope_context_files() { contextual_orchestrator/*.py) needs_contextual_orchestrator_python=1 ;; + packages/hris-kernel/src/orgmetra_hris_kernel/job_analysis.py) + needs_orgmetra_job_analysis_authority_context=1 + ;; # The app shell, email components, threading URL builder, and API client can # shape frontend email retrieval flows; include backend auth context with them. frontend/src/components/EmailDetail.tsx | frontend/src/components/EmailList.tsx | frontend/src/app/page.tsx | frontend/src/lib/api-client.ts | frontend/src/lib/email-threading.ts) @@ -1549,6 +1553,16 @@ backend/services/threading_service.py EOF fi + if [ "$needs_orgmetra_job_analysis_authority_context" -eq 1 ]; then + cat <<'EOF' +services/job-analysis-api/src/orgmetra_job_analysis_api/auth.py +services/job-analysis-api/src/orgmetra_job_analysis_api/authorization.py +services/job-analysis-api/src/orgmetra_job_analysis_api/http.py +services/job-analysis-api/src/orgmetra_job_analysis_api/postgres.py +services/job-analysis-api/src/orgmetra_job_analysis_api/snapshot.py +EOF + fi + if [ "$needs_deployment_context" -eq 1 ]; then cat <<'EOF' Dockerfile @@ -1688,6 +1702,26 @@ PY esac local src_path="$REPO_ROOT/$relative_path" if [ ! -e "$src_path" ]; then + case "$relative_path" in + services/job-analysis-api/src/orgmetra_job_analysis_api/auth.py | \ + services/job-analysis-api/src/orgmetra_job_analysis_api/authorization.py | \ + services/job-analysis-api/src/orgmetra_job_analysis_api/http.py | \ + services/job-analysis-api/src/orgmetra_job_analysis_api/postgres.py | \ + services/job-analysis-api/src/orgmetra_job_analysis_api/snapshot.py) + local job_analysis_change_rc=0 + changed_file_list_contains \ + "packages/hris-kernel/src/orgmetra_hris_kernel/job_analysis.py" || job_analysis_change_rc=$? + case "$job_analysis_change_rc" in + 0) + echo "ERROR: required Job Analysis trusted context file is unavailable: $context_file" >&2 + return 2 + ;; + 2) + return 2 + ;; + esac + ;; + esac return 0 fi if [ ! -f "$src_path" ] || [ -L "$src_path" ]; then diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 80c4832243..b9852863d8 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -503,6 +503,8 @@ assert_changed_file_membership_uses_cached_normalized_paths() { assert_strix_evidence_binding_contract() { assert_file_contains "$GATE_SCRIPT" "sanitize_remediation_evidence_claims" "strix gate sanitizes false already-applied remediation claims" assert_file_contains "$GATE_SCRIPT" 'scripts/ci/strix_evidence_binding.py' "strix gate binds remediation evidence through the tested Python binder" + assert_file_contains "$GATE_SCRIPT" 'local binder="$SCRIPT_DIR/strix_evidence_binding.py"' "strix gate resolves its trusted evidence binder from the central script directory" + assert_file_not_contains "$GATE_SCRIPT" 'local binder="$REPO_ROOT/scripts/ci/strix_evidence_binding.py"' "strix gate never resolves the trusted binder from the consumer repository root" assert_file_contains "$GATE_SCRIPT" "evidence_scope=pr_delta" "strix gate labels PR-delta findings with authenticated provenance" assert_file_contains "$GATE_SCRIPT" "evidence_scope=repository_baseline" "strix gate labels unchanged-path findings as repository_baseline" assert_file_contains "$REPO_ROOT/scripts/ci/strix_evidence_binding.py" 'PR_DELTA = "pr_delta"' "strix evidence binder defines pr_delta scope" @@ -3312,6 +3314,9 @@ run_gate_case() { cp "$GATE_SCRIPT" "$repo_root_dir/scripts/ci/strix_quick_gate.sh" cp "$REPO_ROOT/scripts/ci/strix_model_utils.sh" "$repo_root_dir/scripts/ci/strix_model_utils.sh" fi + if [ -e "$repo_root_dir/scripts/ci/strix_evidence_binding.py" ]; then + record_failure "scenario=$scenario consumer fixture must not own the trusted evidence binder" + fi local fake_strix="$bin_dir/strix" local path_hijack_log="$tmp_dir/path-hijack.log" cat >"$untrusted_bin_dir/strix" <<'EOF' @@ -6944,6 +6949,9 @@ run_filtered_gate_case_if_requested() { "1" \ "Container build manifest changed; materialized full PR-head blob scope" ;; + pull-request-target-job-analysis-authority-context) + run_pull_request_target_job_analysis_authority_context_scope_case + ;; repository-dispatch-pr-scope-uses-head-blob) run_pull_request_target_head_scope_case \ "repository-dispatch-pr-scope-uses-head-blob" \ @@ -8034,6 +8042,196 @@ EOF rm -rf "$tmp_dir" } +run_pull_request_target_job_analysis_authority_context_scope_case() { + local changed_file="packages/hris-kernel/src/orgmetra_hris_kernel/job_analysis.py" + local case_name="pull-request-target-job-analysis-authority-context" + local tmp_dir + tmp_dir="$(mktemp -d)" + local bin_dir="$tmp_dir/bin" + local repo_root_dir="$tmp_dir/repo" + mkdir -p "$bin_dir" "$repo_root_dir/scripts/ci" + local trusted_script_dir="$tmp_dir/trusted-source/scripts/ci" + materialize_trusted_gate_fixture "$trusted_script_dir" + + local context_files=( + "services/job-analysis-api/src/orgmetra_job_analysis_api/auth.py" + "services/job-analysis-api/src/orgmetra_job_analysis_api/authorization.py" + "services/job-analysis-api/src/orgmetra_job_analysis_api/http.py" + "services/job-analysis-api/src/orgmetra_job_analysis_api/postgres.py" + "services/job-analysis-api/src/orgmetra_job_analysis_api/snapshot.py" + ) + local context_files_text + context_files_text="$(printf '%s\n' "${context_files[@]}")" + local fake_strix="$bin_dir/strix" + local output_log="$tmp_dir/output.log" + local strix_llm_file="$tmp_dir/strix_llm.txt" + local llm_api_key_file="$tmp_dir/llm_api_key.txt" + + cat >"$fake_strix" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail + +if [ -n "${FAKE_STRIX_CALLS_FILE:-}" ]; then + printf 'call\n' >>"$FAKE_STRIX_CALLS_FILE" +fi + +target_path="" +while [ "$#" -gt 0 ]; do + if [ "$1" = "-t" ] && [ "$#" -ge 2 ]; then + target_path="$2" + break + fi + shift +done + +changed_file="$target_path/${FAKE_STRIX_EXPECTED_CHANGED_FILE:?}" +if ! grep -Fq -- 'HEAD_JOB_ANALYSIS_KERNEL_SHOULD_BE_SCANNED' "$changed_file"; then + echo "Error: Job Analysis kernel PR-head content was not scanned" >&2 + exit 93 +fi + +while IFS= read -r context_file; do + [ -n "$context_file" ] || continue + context_path="$target_path/$context_file" + if [ ! -f "$context_path" ]; then + echo "Error: Job Analysis authorization context missing: $context_file" >&2 + exit 94 + fi + if ! grep -Fqx -- "BASE_JOB_ANALYSIS_AUTHORITY_CONTEXT:$context_file" "$context_path"; then + echo "Error: Job Analysis context did not use trusted base content: $context_file" >&2 + exit 95 + fi + if grep -Fq -- "HEAD_JOB_ANALYSIS_CONTEXT_SHOULD_NOT_BE_SCANNED:$context_file" "$context_path"; then + echo "Error: unchanged Job Analysis context leaked PR-head content: $context_file" >&2 + exit 96 + fi +done <<<"${FAKE_STRIX_EXPECTED_CONTEXT_FILES:?}" + +if [ -e "$target_path/services/job-analysis-api/src/orgmetra_job_analysis_api/unrelated_admin.py" ]; then + echo "Error: unrelated service source leaked into bounded Job Analysis scope" >&2 + exit 97 +fi + +echo "scan ok with trusted Job Analysis authorization and persistence context" +EOF + chmod +x "$fake_strix" + printf '%s' 'gemini/test-model' >"$strix_llm_file" + printf '%s' 'dummy' >"$llm_api_key_file" + + ( + cd "$repo_root_dir" + git init -q + git config user.name 'Strix Test' + git config user.email 'strix-test@example.invalid' + local context_file + for context_file in "${context_files[@]}"; do + mkdir -p "$(dirname -- "$context_file")" + printf 'BASE_JOB_ANALYSIS_AUTHORITY_CONTEXT:%s\n' "$context_file" >"$context_file" + done + mkdir -p "$(dirname -- "$changed_file")" \ + services/job-analysis-api/src/orgmetra_job_analysis_api + printf '%s\n' 'BASE_JOB_ANALYSIS_KERNEL_SHOULD_NOT_BE_SCANNED' >"$changed_file" + printf '%s\n' 'UNRELATED_SERVICE_SHOULD_NOT_BE_SCANNED' \ + >services/job-analysis-api/src/orgmetra_job_analysis_api/unrelated_admin.py + git add . + git commit -qm 'base commit' + ) + local base_sha + base_sha="$(git -C "$repo_root_dir" rev-parse HEAD)" + ( + cd "$repo_root_dir" + local context_file + for context_file in "${context_files[@]}"; do + printf 'HEAD_JOB_ANALYSIS_CONTEXT_SHOULD_NOT_BE_SCANNED:%s\n' "$context_file" >"$context_file" + done + printf '%s\n' 'HEAD_JOB_ANALYSIS_KERNEL_SHOULD_BE_SCANNED' >"$changed_file" + git add . + git commit -qm 'head commit' + ) + local head_sha + head_sha="$(git -C "$repo_root_dir" rev-parse HEAD)" + git -C "$repo_root_dir" checkout -q "$base_sha" + + set +e + ( + cd "$repo_root_dir" + env -u GITHUB_EVENT_PATH \ + PATH="$bin_dir:$PATH" \ + STRIX_EXECUTABLE_PATH="$bin_dir/strix" \ + STRIX_INPUT_FILE_ROOT="$tmp_dir" \ + GITHUB_EVENT_NAME="pull_request_target" \ + PR_BASE_SHA="$base_sha" \ + PR_HEAD_SHA="$head_sha" \ + STRIX_TEST_CHANGED_FILES_OVERRIDE="$changed_file" \ + STRIX_DISABLE_PR_SCOPING="0" \ + FAKE_STRIX_EXPECTED_CHANGED_FILE="$changed_file" \ + FAKE_STRIX_EXPECTED_CONTEXT_FILES="$context_files_text" \ + STRIX_LLM_FILE="$strix_llm_file" \ + LLM_API_KEY_FILE="$llm_api_key_file" \ + STRIX_TARGET_PATH="." \ + STRIX_REPORTS_DIR="$repo_root_dir/strix_runs" \ + STRIX_REPO_ROOT="$repo_root_dir" bash "$trusted_script_dir/strix_quick_gate.sh" >"$output_log" 2>&1 + ) + local rc=$? + set -e + + assert_equals "0" "$rc" "case=$case_name exit code" + assert_file_contains "$output_log" \ + "scan ok with trusted Job Analysis authorization and persistence context" \ + "case=$case_name output" + + local missing_context_file="${context_files[0]}" + local missing_output_log="$tmp_dir/missing-context-output.log" + local strix_calls_file="$tmp_dir/strix-calls.log" + git -C "$repo_root_dir" checkout -q "$base_sha" + git -C "$repo_root_dir" rm -q -- "$missing_context_file" + git -C "$repo_root_dir" commit -qm 'base without required Job Analysis auth context' + local missing_context_base_sha + missing_context_base_sha="$(git -C "$repo_root_dir" rev-parse HEAD)" + printf '%s\n' 'HEAD_JOB_ANALYSIS_KERNEL_SHOULD_BE_SCANNED' >"$repo_root_dir/$changed_file" + git -C "$repo_root_dir" add "$changed_file" + git -C "$repo_root_dir" commit -qm 'head changes Job Analysis kernel' + local missing_context_head_sha + missing_context_head_sha="$(git -C "$repo_root_dir" rev-parse HEAD)" + git -C "$repo_root_dir" checkout -q "$missing_context_base_sha" + + set +e + ( + cd "$repo_root_dir" + env -u GITHUB_EVENT_PATH \ + PATH="$bin_dir:$PATH" \ + STRIX_EXECUTABLE_PATH="$bin_dir/strix" \ + STRIX_INPUT_FILE_ROOT="$tmp_dir" \ + GITHUB_EVENT_NAME="pull_request_target" \ + PR_BASE_SHA="$missing_context_base_sha" \ + PR_HEAD_SHA="$missing_context_head_sha" \ + STRIX_TEST_CHANGED_FILES_OVERRIDE="$changed_file" \ + STRIX_DISABLE_PR_SCOPING="0" \ + FAKE_STRIX_CALLS_FILE="$strix_calls_file" \ + FAKE_STRIX_EXPECTED_CHANGED_FILE="$changed_file" \ + FAKE_STRIX_EXPECTED_CONTEXT_FILES="$context_files_text" \ + STRIX_LLM_FILE="$strix_llm_file" \ + LLM_API_KEY_FILE="$llm_api_key_file" \ + STRIX_TARGET_PATH="." \ + STRIX_REPORTS_DIR="$repo_root_dir/strix_runs" \ + STRIX_REPO_ROOT="$repo_root_dir" bash "$trusted_script_dir/strix_quick_gate.sh" >"$missing_output_log" 2>&1 + ) + local missing_context_rc=$? + set -e + + assert_equals "2" "$missing_context_rc" "case=$case_name missing required context exits closed" + assert_file_contains "$missing_output_log" \ + "required Job Analysis trusted context file is unavailable: $missing_context_file" \ + "case=$case_name missing required context output" + local strix_call_count=0 + if [ -f "$strix_calls_file" ]; then + strix_call_count="$(wc -l <"$strix_calls_file" | tr -d '[:space:]')" + fi + assert_equals "0" "$strix_call_count" "case=$case_name missing required context must not invoke Strix" + + rm -rf "$tmp_dir" +} + run_pull_request_target_shallow_head_merge_base_fallback_case() { local tmp_dir tmp_dir="$(mktemp -d)" @@ -9839,6 +10037,8 @@ run_pull_request_target_frontend_email_context_scope_case \ run_pull_request_target_frontend_email_context_scope_case \ "frontend/src/lib/email-threading.ts" +run_pull_request_target_job_analysis_authority_context_scope_case + run_pull_request_target_aborts_on_pr_head_blob_failure_case \ "pull-request-target-added-file-pr-head-blob-read-failure" \ "src/new_module.py" \ diff --git a/tests/test_organization_commercial_readiness_loop_receipt_contract.py b/tests/test_organization_commercial_readiness_loop_receipt_contract.py index 56225ff2d1..a4c34a6646 100644 --- a/tests/test_organization_commercial_readiness_loop_receipt_contract.py +++ b/tests/test_organization_commercial_readiness_loop_receipt_contract.py @@ -56,9 +56,14 @@ def test_json_receipt_is_retained_as_an_immutable_short_lived_artifact() -> None assert "if-no-files-found: error" in source assert "retention-days: 3" in source endpoints = _harden_runner_allowed_endpoints(source) - assert { + assert endpoints == { + "api.github.com:443", + "api.opencode.ai:443", + "github.com:443", + "objects.githubusercontent.com:443", + "release-assets.githubusercontent.com:443", "results-receiver.actions.githubusercontent.com:443", "*.actions.githubusercontent.com:443", "*.blob.core.windows.net:443", - }.issubset(endpoints) + } assert "- name: Checkout exact trusted coordinator source" not in endpoints diff --git a/tests/test_product_technical_gap_baseline_repository_identity_contract.py b/tests/test_product_technical_gap_baseline_repository_identity_contract.py index 2acdc2657e..883d65fdd0 100644 --- a/tests/test_product_technical_gap_baseline_repository_identity_contract.py +++ b/tests/test_product_technical_gap_baseline_repository_identity_contract.py @@ -1,12 +1,14 @@ """Regression contract for owner-qualified cross-repository evidence identities.""" from pathlib import Path +import re import unittest BASELINE_PATH = ( Path(__file__).resolve().parents[1] / "docs" / "product-technical-gap-baseline.md" ) +CHANGELOG_PATH = Path(__file__).resolve().parents[1] / "CHANGELOG.md" class ProductTechnicalGapBaselineRepositoryIdentityContractTests(unittest.TestCase): @@ -33,6 +35,19 @@ def test_control_opencode_evidence_uses_owner_qualified_repository_identities(se with self.subTest(token=token): self.assertIn(token, baseline) + def test_orgmetra_evidence_uses_owner_qualified_issue_identity(self) -> None: + """Keep the Job Analysis evidence linked to its owning repository.""" + baseline = BASELINE_PATH.read_text(encoding="utf-8") + changelog = CHANGELOG_PATH.read_text(encoding="utf-8") + + self.assertNotIn("Orgmetra #63", changelog) + self.assertNotIn("Orgmetra #63", baseline) + self.assertIsNone(re.search(r"Orgmetra\s+#63 consumer run", baseline)) + self.assertIn("ContextualWisdomLab/orgmetra#63", changelog) + self.assertGreaterEqual( + baseline.count("ContextualWisdomLab/orgmetra#63"), 3 + ) + if __name__ == "__main__": unittest.main()