Skip to content
Closed
8 changes: 4 additions & 4 deletions .github/workflows/codeql-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,13 @@ jobs:
ref: ${{ github.event.pull_request.head.sha }}

- name: Initialize CodeQL
uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
uses: github/codeql-action/init@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
uses: github/codeql-action/analyze@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
with:
category: "/language:${{ matrix.language }}"
upload: false
Expand Down Expand Up @@ -197,13 +197,13 @@ jobs:
ref: ${{ format('refs/pull/{0}/merge', github.event.pull_request.number) }}

- name: Initialize CodeQL
uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
uses: github/codeql-action/init@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0
uses: github/codeql-action/analyze@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
with:
category: "/language:${{ matrix.language }}-merge"
upload: false
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/python-security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ jobs:
if: always() && hashFiles('bandit-results.sarif') != ''
# The explicit gate below still fails on every Medium+ Bandit result.
continue-on-error: true
uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4
uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
with:
sarif_file: bandit-results.sarif
category: bandit
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sast-semgrep.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ jobs:
- name: Upload Semgrep SARIF to code scanning
if: always() && hashFiles('semgrep-results.sarif') != ''
continue-on-error: true
uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4
uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
with:
sarif_file: semgrep-results.sarif
category: semgrep
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/scheduled-security-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,13 @@ jobs:
with:
persist-credentials: false
- name: Initialize CodeQL
uses: github/codeql-action/init@d1ba80a13dd99fba24a470575428917156a28b43 # v4.37.5
uses: github/codeql-action/init@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
- name: Perform CodeQL Analysis
continue-on-error: true
uses: github/codeql-action/analyze@d1ba80a13dd99fba24a470575428917156a28b43 # v4.37.5
uses: github/codeql-action/analyze@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
with:
category: "/language:${{ matrix.language }}-scheduled"

Expand Down Expand Up @@ -131,7 +131,7 @@ jobs:
- name: Upload Trivy SARIF to code scanning
if: always() && hashFiles('trivy-results.sarif') != ''
continue-on-error: true
uses: github/codeql-action/upload-sarif@d1ba80a13dd99fba24a470575428917156a28b43 # v4.37.5
uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
with:
sarif_file: trivy-results.sarif
category: trivy-fs-scheduled
2 changes: 1 addition & 1 deletion .github/workflows/scorecard-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,6 @@ jobs:
# Scorecard posture is preserved in its SARIF-generation log; an
# installation upload quota outage must not fail the default branch.
continue-on-error: true
uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4
uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
with:
sarif_file: results.sarif
2 changes: 1 addition & 1 deletion .github/workflows/secret-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ jobs:
- name: Upload gitleaks SARIF to code scanning
if: always() && hashFiles('gitleaks-results.upload.sarif') != ''
continue-on-error: true
uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4
uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
with:
sarif_file: gitleaks-results.upload.sarif
category: gitleaks
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/security-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ jobs:
# The reporter above is the vulnerability gate. Preserve an upload
# quota failure in this step's log without reclassifying it as a CVE.
continue-on-error: true
uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4
uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
with:
sarif_file: results.sarif
# results.sarif is produced after checkout of the pull request head.
Expand Down Expand Up @@ -383,7 +383,7 @@ jobs:
if: always() && hashFiles('trivy-results.sarif') != ''
# The parser above fails on every fixable Medium+ finding independently.
continue-on-error: true
uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4
uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
with:
sarif_file: trivy-results.sarif
category: trivy-fs
Expand Down Expand Up @@ -457,7 +457,7 @@ jobs:
id: upload_scorecard_sarif
# Scorecard is soft repository-posture evidence; upload quota is external.
continue-on-error: true
uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4
uses: github/codeql-action/upload-sarif@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6
with:
sarif_file: results.sarif
category: scorecard
Expand Down
80 changes: 80 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# 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<br/>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
```

## CodeQL single-SHA gate

```mermaid
flowchart TD
Dep["Dependabot upload-sarif bump"]
Pin{"init analyze upload-sarif same SHA?"}
Use["Every workflow uses 4.37.6"]
Split["Fail contract: mixed trusted action"]

Dep --> Pin
Pin -->|"yes"| Use
Pin -->|"no"| Split
```

CWE-829: a second SHA is a second control sphere.

## 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.
- Reviewer agents stay `edit: deny`.
- Logs redact credential shapes. They do not mask operational PII.
- LLM and scheduled agents bind `NVIDIA_NIM_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.

## Related durable documents

- [`docs/CWL-MASTER-CONTEXT.md`](docs/CWL-MASTER-CONTEXT.md)
- [`docs/doctoring/codeql-action-single-version.md`](docs/doctoring/codeql-action-single-version.md)
- [`docs/agent-github-project-protocol.md`](docs/agent-github-project-protocol.md)
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Semantic Versioning where the repository publishes a release.

### Fixed

- Aligned every `github/codeql-action` `init`/`analyze`/`upload-sarif` pin to one immutable SHA (`5595ccaf912efad79be6eef63a5619ff05969be3`, v4.37.6) after the upload-sarif-only Dependabot bump left analyzers on 4.37.0/4.37.5 (CWE-829).
- 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.
- Bound each review-agent invocation key to the wrapper's complete canonical payload, including the base branch and requesting actor; altered fields with a valid-format key now fail before durable-leader election or forwarding, and wrapper write permission is job-scoped.
Expand Down
3 changes: 3 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ Details: `README.md` and `PR_GOVERNANCE_AUDIT.md`.
dependency sets (see below).
- `fuzz/` + `.clusterfuzzlite/` — Atheris fuzz targets for the review-output normalizer and the
ClusterFuzzLite discovery marker.
- `ARCHITECTURE.md` — control-plane mermaid (system context, CodeQL
single-SHA gate, review sequence, trust boundaries). Reconstruct from
the repo, not private agent memory.
- `docs/` — master context, Project protocol, `org-required-workflow-rollout.md`,
`scorecard-governance.md`, SBOM inventory.
- `.jules/` — recorded performance (`bolt.md`) and security (`sentinel.md`) learnings from past work
Expand Down
26 changes: 26 additions & 0 deletions docs/doctoring/codeql-action-single-version.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# CodeQL action single-version pin

## Incident and buyer impact

Dependabot opened an `upload-sarif` 4.37.6 bump while `codeql-pr.yml`
still ran `init`/`analyze` at 4.37.0 and `scheduled-security-scan.yml`
ran them at 4.37.5. A green upload does not prove the analyzer executed
the reviewed action.

## Decision

Pin every `github/codeql-action/{init,analyze,upload-sarif}` use to
`5595ccaf912efad79be6eef63a5619ff05969be3` (v4.37.6). Contract tests
reject per-file and org-wide splits.

CWE-829 forbids including functionality from an untrusted or unreviewed
control sphere (MITRE, 2026). A second SHA is a second control sphere.

## References

MITRE. (2026). *CWE-829: Inclusion of functionality from untrusted
control sphere*. https://cwe.mitre.org/data/definitions/829.html

GitHub. (n.d.). *Using the CodeQL action*. GitHub Docs. Retrieved
August 13, 2026, from
https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/using-the-codeql-action
21 changes: 21 additions & 0 deletions tests/test_codeql_pr_workflow_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,27 @@ def test_codeql_action_steps_use_one_version_per_workflow() -> None:
assert len(refs) == 1, f"{filename} mixes CodeQL action refs: {sorted(refs)}"


def test_codeql_workflows_share_one_upload_sarif_sha() -> None:
"""CWE-829: every CodeQL action pin must be the Dependabot-reviewed SHA.

An upload-sarif-only bump that leaves init/analyze on 4.37.0/4.37.5
would analyze or upload with a second unreviewed control sphere.
"""
shas: set[str] = set()
tags: set[str] = set()
pin = re.compile(
r"github/codeql-action/(?:init|analyze|upload-sarif)@"
r"([0-9a-f]{40}) # (v\d+\.\d+\.\d+)"
)
for path in (REPO_ROOT / ".github/workflows").glob("*.yml"):
for sha, tag in pin.findall(path.read_text(encoding="utf-8")):
shas.add(sha)
tags.add(tag)

assert shas == {"5595ccaf912efad79be6eef63a5619ff05969be3"}
assert tags == {"v4.37.6"}


def test_codeql_sarif_gate_logs_and_fails_only_unsuppressed_medium_plus(
tmp_path: Path,
) -> None:
Expand Down
16 changes: 16 additions & 0 deletions tests/test_materialize_base_python_requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,19 @@ def _created_tool_directory(path: Path) -> str:
return str(path)


def _simulate_linux_x86_64_runner(monkeypatch: pytest.MonkeyPatch) -> None:
"""Let installer verification tests run on a non-Linux developer host.

Production still fail-closes unless ``sys.platform`` is Linux and
``platform.machine()`` is ``x86_64``. These unit tests pin both values so
they measure version verification, caching, and cleanup instead of the
host architecture gate already covered by the portability contract.
"""
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"
Expand Down Expand Up @@ -644,6 +657,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."""
_simulate_linux_x86_64_runner(monkeypatch)
tool_dir = tmp_path / "uv"
monkeypatch.setattr(
materializer.tempfile,
Expand Down Expand Up @@ -690,6 +704,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."""
_simulate_linux_x86_64_runner(monkeypatch)
tool_dir = tmp_path / "uv"
monkeypatch.setattr(
materializer.tempfile,
Expand Down Expand Up @@ -721,6 +736,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."""
_simulate_linux_x86_64_runner(monkeypatch)
tool_dir = tmp_path / f"uv-{completed.returncode}-{len(completed.stdout)}"
monkeypatch.setattr(
materializer.tempfile,
Expand Down
Loading