diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 000000000..d64a5d069 --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,94 @@ +# Architecture — ContextualWisdomLab `.github` + +This repository is the organization control plane. It is not naruon and it +does not own product data. Sibling products remain standalone modules; this +repo publishes org profile assets, reusable required workflows, and the +review/merge schedulers those products consume. + +## System context + +```mermaid +flowchart LR + Buyer["Commercial buyer / reviewer"] + Agents["Agents on AGENTS.md"] + Project["GitHub Project #1"] + Hub["This repo: org .github"] + Products["Owned products
naruon · orchestrator · engines"] + Runner["Required workflows in each repo context"] + + Buyer --> Hub + Agents --> Project + Agents --> Hub + Project --> Hub + Hub --> Runner + Runner --> Products + Products -->|"standalone or as module"| Buyer +``` + +## Trusted uv origin gate + +```mermaid +flowchart TD + URL["Literal HTTPS releases.astral.sh URL"] + Fetch["urlopen with empty proxy map"] + Origin{"scheme=https, host=releases.astral.sh, port absent or 443?"} + Hash{"Pinned SHA-256 and member bounds?"} + Accept["Install verified uv exporter"] + Reject["Fail closed"] + + URL --> Fetch + Fetch --> Origin + Origin -->|"no"| Reject + Origin -->|"yes"| Hash + Hash -->|"no"| Reject + Hash -->|"yes"| Accept +``` + +CWE-346 requires origin validation to stay a single helper. Repository +content cannot select scheme, host, path, query, fragment, or port. + +## Control-plane data flow + +```mermaid +sequenceDiagram + participant PR as Pull request + participant RW as Required workflows + participant OC as OpenCode reviewer + participant SV as sandboxed_verify / web E2E + participant MS as Merge scheduler + + PR->>RW: pull_request_target on trusted base + RW->>OC: bounded evidence + NVIDIA NIM / OpenCode + OC->>SV: PoC command in isolated copy + SV-->>OC: redacted stdout/stderr + command metadata + OC-->>PR: APPROVE or request changes + MS->>PR: merge only on current-head approval + green checks +``` + +## Trust boundaries + +- Required review workflows execute **base-branch** scripts. A PR that edits + those workflows cannot widen its own `pull_request_target` token. +- Reviewer agents stay `edit: deny`. They judge; they do not implement. +- Sandbox helpers copy the workspace, drop secret environment values unless + explicitly allowlisted by **name**, and run subprocesses with `shell=False`. +- Logs and review receipts redact credential shapes (tokens, bearer values, + known provider prefixes). They do not mask operational PII that the + control plane must process. +- LLM and scheduled agents bind `NVIDIA_NIM_API_KEY` (env may be + `NVIDIA_API_KEY`). They never use `COPILOT_GITHUB_TOKEN`. +- Rust remains the psychometric arithmetic owner. + +## Quality gates + +`scripts/ci/` ships with 100% statement/branch coverage and 100% docstrings. +CI installs Python tools only with `pip install --require-hashes`. + +## Related durable documents + +- [`docs/CWL-MASTER-CONTEXT.md`](docs/CWL-MASTER-CONTEXT.md) — mission and + ecosystem. +- [`PR_GOVERNANCE_AUDIT.md`](PR_GOVERNANCE_AUDIT.md) — live review/merge + contract. +- [`docs/doctoring/trusted-uv-lock-materialization.md`](docs/doctoring/trusted-uv-lock-materialization.md) + — current increment's origin-validation decision and APA 7th citations. diff --git a/CHANGELOG.md b/CHANGELOG.md index bf30091dd..7507eb492 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ Semantic Versioning where the repository publishes a release. ### Added - Added a trusted pull-request comment router for `@cwl-noema-review` and review-only `@opencode-agent` dispatches, with an organization sweep, exact-head receipts, repository allowlisting, fixed runners, immutable checkout pins, and a permanent 100% statement/branch/docstring quality gate. -- Added exact-base `uv.lock` materialization that reconstructs standalone nested projects with a checksum-pinned official `uv` exporter, isolated frozen/offline execution, strict exact-pin and SHA-256 output validation, and complete Python 3.10/3.14 quality evidence. +- Added exact-base `uv.lock` materialization that reconstructs standalone nested projects with a checksum-pinned official `uv` exporter, isolated frozen/offline execution, strict exact-pin and SHA-256 output validation, and complete Python 3.10/3.14 quality evidence. The decision record now cites CWE-346 so origin validation stays a single helper that cannot accept a non-HTTPS, non-`releases.astral.sh`, or nondefault-port final URL. +- Recorded the org control-plane architecture, including the trusted-uv origin gate, so agents reconstruct the download trust boundary from the repo instead of private memory. ### Fixed diff --git a/CLAUDE.md b/CLAUDE.md index 1c7bdb2f6..6c74c378b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -64,7 +64,9 @@ Details: `README.md` and `PR_GOVERNANCE_AUDIT.md`. - `fuzz/` + `.clusterfuzzlite/` — Atheris fuzz targets for the review-output normalizer and the ClusterFuzzLite discovery marker. - `docs/` — master context, Project protocol, `org-required-workflow-rollout.md`, - `scorecard-governance.md`, SBOM inventory. + `scorecard-governance.md`, SBOM inventory. Doctoring records live under + `docs/doctoring/`. [`ARCHITECTURE.md`](ARCHITECTURE.md) is the control-plane + diagram for trusted-uv origin validation and merge trust boundaries. - `.jules/` — recorded performance (`bolt.md`) and security (`sentinel.md`) learnings from past work on `scripts/ci/`; worth scanning before optimizing or hardening those scripts. diff --git a/docs/doctoring/trusted-uv-lock-materialization.md b/docs/doctoring/trusted-uv-lock-materialization.md index 8f78759ca..aa2e83391 100644 --- a/docs/doctoring/trusted-uv-lock-materialization.md +++ b/docs/doctoring/trusted-uv-lock-materialization.md @@ -21,7 +21,9 @@ The implementation therefore: 4. downloads one fixed official Astral `uv` archive from a literal HTTPS URL and accepts a response only when its parsed origin remains HTTPS, `releases.astral.sh`, and the absent or explicit default port 443; malformed - or nondefault ports fail closed; + or nondefault ports fail closed. CWE-346 requires that origin check to live + in one helper so a later download path cannot skip scheme, host, or port + validation (MITRE, 2026); 5. verifies the bounded archive with a pinned SHA-256 digest before extraction; 6. accepts only the expected regular-file tar member within explicit size bounds; 7. writes the executable with mode `0755` and verifies that it reports the exact @@ -184,6 +186,9 @@ Berners-Lee, T., Fielding, R., & Masinter, L. (2005). *Uniform Resource Identifi (URI): Generic syntax* (STD 66; RFC 3986). Internet Engineering Task Force. https://doi.org/10.17487/RFC3986 +MITRE. (2026). *CWE-346: Origin validation error*. +https://cwe.mitre.org/data/definitions/346.html + GitHub. (n.d.). *actions/checkout*. GitHub. Retrieved August 5, 2026, from https://github.com/actions/checkout diff --git a/scripts/ci/materialize_base_python_requirements.py b/scripts/ci/materialize_base_python_requirements.py index 98cdad459..dfe9ad436 100755 --- a/scripts/ci/materialize_base_python_requirements.py +++ b/scripts/ci/materialize_base_python_requirements.py @@ -165,6 +165,26 @@ def _git(repo_root: pathlib.Path, *args: str) -> bytes: return completed.stdout +def _verify_trusted_uv_origin(url: str) -> None: + """Verify the final response URL remains within the trusted HTTPS origin.""" + final_url = urllib.parse.urlparse(url) + try: + final_port = final_url.port + except ValueError as exc: + raise RuntimeError( + "trusted uv archive redirected outside the fixed " + "releases.astral.sh HTTPS origin" + ) from exc + if ( + (final_url.scheme, final_url.hostname) + != ("https", "releases.astral.sh") + or final_port not in (None, 443) + ): + raise RuntimeError( + "trusted uv archive redirected outside the fixed " + "releases.astral.sh HTTPS origin" + ) + def _download_trusted_uv_archive() -> bytes: """Download the fixed uv release archive through one HTTPS trust boundary.""" _install_trusted_uv_url_opener() @@ -177,23 +197,7 @@ def _download_trusted_uv_archive() -> bytes: "uv-x86_64-unknown-linux-gnu.tar.gz", timeout=TRUSTED_UV_DOWNLOAD_TIMEOUT_SECONDS, ) as response: - final_url = urllib.parse.urlparse(response.geturl()) - try: - final_port = final_url.port - except ValueError as exc: - raise RuntimeError( - "trusted uv archive redirected outside the fixed " - "releases.astral.sh HTTPS origin" - ) from exc - if ( - (final_url.scheme, final_url.hostname) - != ("https", "releases.astral.sh") - or final_port not in (None, 443) - ): - raise RuntimeError( - "trusted uv archive redirected outside the fixed " - "releases.astral.sh HTTPS origin" - ) + _verify_trusted_uv_origin(response.geturl()) payload = bytearray() while len(payload) <= TRUSTED_UV_DOWNLOAD_MAX_BYTES: chunk = response.read( diff --git a/scripts/ci/test_strix_quick_gate.sh b/scripts/ci/test_strix_quick_gate.sh index 7343c06ac..9446fa048 100755 --- a/scripts/ci/test_strix_quick_gate.sh +++ b/scripts/ci/test_strix_quick_gate.sh @@ -5118,7 +5118,7 @@ EOS ;; esac EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" cat >"$fake_gh" <<'EOF' #!/usr/bin/env bash @@ -6337,7 +6337,7 @@ else fi echo "scan ok with PR head content" EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'gemini/test-model' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" @@ -6460,7 +6460,7 @@ vertex_ai/fallback-one) ;; esac EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'vertex_ai/stale-source-primary' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" @@ -6581,7 +6581,7 @@ if [ -e "$context_file" ]; then fi echo "scan ok with bounded PR head backend context" EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'gemini/test-model' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" @@ -6719,7 +6719,7 @@ fi echo "Error: unexpected changed context scan attempt $attempt" >&2 exit 71 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'gemini/test-model' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" @@ -6924,7 +6924,7 @@ fi echo "scan ok with non-email backend scope" EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'gemini/test-model' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" @@ -7150,7 +7150,7 @@ fi echo "scan ok with frontend email trusted backend authorization context" EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'gemini/test-model' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" @@ -7239,7 +7239,7 @@ set -euo pipefail echo "scan ok" exit 0 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'gemini/test-model' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" @@ -7390,7 +7390,7 @@ printf 'called\n' >> "${FAKE_STRIX_CALL_LOG:?}" echo "Error: Strix should not run after a PR-head blob failure" >&2 exit 64 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'gemini/test-model' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" @@ -7480,7 +7480,7 @@ printf 'called\n' >> "${FAKE_STRIX_CALL_LOG:?}" echo "Error: Strix should not run after invalid pull request SHA metadata" >&2 exit 67 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'gemini/test-model' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" @@ -7573,7 +7573,7 @@ printf 'called\n' >> "${FAKE_STRIX_CALL_LOG:?}" echo "Error: Strix should not run after an irregular PR-head entry" >&2 exit 66 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'gemini/test-model' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" @@ -7654,7 +7654,7 @@ set -euo pipefail printf 'called\n' >> "${FAKE_STRIX_CALL_LOG:?}" exit 66 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'gemini/test-model' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" @@ -7758,7 +7758,7 @@ if [ -e "$target_path/vendor/newsdom-api" ]; then fi echo "scan ok with PR head content" EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'gemini/test-model' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" @@ -7853,7 +7853,7 @@ printf 'called\n' >> "${FAKE_STRIX_CALL_LOG:?}" echo "Error: Strix should not run for unsafe changed paths" >&2 exit 65 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'gemini/test-model' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" cat >"$event_payload_file" <<'EOF' @@ -7945,7 +7945,7 @@ child_pid=$! printf '%s' "$child_pid" > "${FAKE_STRIX_CHILD_PID_FILE:?}" sleep "${FAKE_STRIX_TIMEOUT_SLEEP_SECONDS:?}" EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'vertex_ai/timeout-cleanup-primary' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" @@ -8024,7 +8024,7 @@ printf 'called\n' >"${FAKE_STRIX_CALL_LOG:?}" echo "vertex scan ok without external LLM_API_BASE" exit 0 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'vertex_ai/gemini-2.5-pro' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" printf '%s' 'https://example.invalid/generateContent' >"$llm_api_base_file" @@ -8077,7 +8077,7 @@ set -euo pipefail echo "1" >> "${FAKE_STRIX_CALL_COUNT_FILE:?}" sleep 30 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'vertex_ai/total-timeout-primary' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" @@ -8148,7 +8148,7 @@ set -euo pipefail echo "1" >> "${STRIX_CALL_COUNT_FILE:?}" exit 0 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" if [ -n "$strix_llm" ]; then printf '%s' "$strix_llm" >"$strix_llm_file" fi @@ -8197,7 +8197,7 @@ set -euo pipefail echo "1" >> "${STRIX_CALL_COUNT_FILE:?}" exit 0 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf 'openai-direct/gpt-5.4 $(touch %s)' "$marker_file" >"$strix_llm_file" printf '%s' 'dummy-key' >"$llm_api_key_file" @@ -8252,7 +8252,7 @@ if [ "${LLM_API_KEY_FILE+x}" = "x" ]; then fi exit 0 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' "vertex_ai/ready-primary" >"$strix_llm_file" set +e @@ -8302,7 +8302,7 @@ if [ "${LLM_API_KEY_FILE+x}" = "x" ]; then fi exit 0 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' "vertex_ai/ready-primary" >"$strix_llm_file" printf '%s' "openai-key-should-not-reach-vertex" >"$llm_api_key_file" @@ -8345,7 +8345,7 @@ set -euo pipefail echo "unexpected strix execution" >&2 exit 99 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'vertex_ai/ready-primary' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" @@ -8398,7 +8398,7 @@ set -euo pipefail printf 'called\n' >"${FAKE_STRIX_CALL_LOG:?}" exit 0 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'openai/gpt-4o-mini' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" printf '%s' 'https://example.invalid/generateContent' >"$llm_api_base_file" @@ -8455,7 +8455,7 @@ set -euo pipefail printf 'called\n' >"${FAKE_STRIX_CALL_LOG:?}" exit 0 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'openai/gpt-4o-mini' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" printf '%s' 'https://example.invalid/generateContent' >"$llm_api_base_file" @@ -8514,7 +8514,7 @@ set -euo pipefail printf 'called\n' >"${FAKE_STRIX_CALL_LOG:?}" exit 0 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'openai/gpt-4o-mini' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" printf '%s' 'https://example.invalid/generateContent' >"$llm_api_base_file" @@ -8584,7 +8584,7 @@ set -euo pipefail printf 'called\n' >"${FAKE_STRIX_CALL_LOG:?}" exit 0 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'openai/gpt-4o-mini' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" printf '%s' 'https://example.invalid/generateContent' >"$llm_api_base_file" @@ -8643,7 +8643,7 @@ set -euo pipefail echo "Error: transport timeout" exit 1 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'openai/gpt-4o-mini' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" printf '%s' 'https://example.invalid/generateContent' >"$llm_api_base_file" @@ -8699,7 +8699,7 @@ set -euo pipefail echo "Error: transport timeout" exit 1 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'openai/gpt-4o-mini' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" printf '%s' 'https://example.invalid/generateContent' >"$llm_api_base_file" @@ -8749,7 +8749,7 @@ set -euo pipefail printf '%s\n' called >>"${FAKE_STRIX_CALL_LOG:?}" exit 0 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'openai/gpt-4o-mini' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" printf '%s' 'https://example.invalid/generateContent' >"$llm_api_base_file" @@ -8802,7 +8802,7 @@ run_absolute_outside_target_path_case() { printf 'called\n' >"${FAKE_STRIX_CALL_LOG:?}" exit 0 EOF - chmod +x "$fake_strix" + chmod 0755 "$fake_strix" printf '%s' 'openai/gpt-4o-mini' >"$strix_llm_file" printf '%s' 'dummy' >"$llm_api_key_file" printf '%s' 'https://example.invalid/generateContent' >"$llm_api_base_file" diff --git a/tests/test_materialize_base_python_requirements.py b/tests/test_materialize_base_python_requirements.py index 8a383f0c2..10f682b3e 100644 --- a/tests/test_materialize_base_python_requirements.py +++ b/tests/test_materialize_base_python_requirements.py @@ -30,6 +30,13 @@ def _created_tool_directory(path: Path) -> str: return str(path) +def _force_linux_x86_64_installer(monkeypatch: pytest.MonkeyPatch) -> None: + """Exercise the installer path that GitHub-hosted linux x86_64 runners use.""" + monkeypatch.setattr(materializer.sys, "platform", "linux") + monkeypatch.setattr(materializer.platform, "machine", lambda: "x86_64") + materializer._install_trusted_uv.cache_clear() + + def test_materializes_only_regular_hash_locks_from_exact_base(tmp_path: Path) -> None: """A PR-modified lock cannot enter the networked coverage image build context.""" repo = tmp_path / "repo" @@ -644,6 +651,7 @@ def test_install_trusted_uv_verifies_version_and_caches_path( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: """The installer writes one executable, verifies its version, and caches it.""" + _force_linux_x86_64_installer(monkeypatch) tool_dir = tmp_path / "uv" monkeypatch.setattr( materializer.tempfile, @@ -690,6 +698,7 @@ def test_install_trusted_uv_rejects_version_process_failures( failure: OSError | subprocess.TimeoutExpired, ) -> None: """A missing or hung downloaded executable is removed and rejected.""" + _force_linux_x86_64_installer(monkeypatch) tool_dir = tmp_path / "uv" monkeypatch.setattr( materializer.tempfile, @@ -721,6 +730,7 @@ def test_install_trusted_uv_rejects_wrong_version_or_exit_status( completed: subprocess.CompletedProcess[bytes], ) -> None: """Unexpected version output or a nonzero status cannot satisfy the pin.""" + _force_linux_x86_64_installer(monkeypatch) tool_dir = tmp_path / f"uv-{completed.returncode}-{len(completed.stdout)}" monkeypatch.setattr( materializer.tempfile,