Skip to content

Commit 99222d7

Browse files
committed
Enforce repository I/O security boundary
1 parent 155ce60 commit 99222d7

76 files changed

Lines changed: 41670 additions & 3423 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/validate.yml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ jobs:
4444
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
4545
with:
4646
fetch-depth: 0
47+
persist-credentials: false
4748
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
4849
with:
4950
python-version: ${{ matrix.python-version }}
@@ -72,6 +73,7 @@ jobs:
7273
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
7374
with:
7475
fetch-depth: 0
76+
persist-credentials: false
7577
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
7678
with:
7779
python-version: ${{ matrix.python-version }}
@@ -85,6 +87,7 @@ jobs:
8587
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
8688
with:
8789
fetch-depth: 0
90+
persist-credentials: false
8891
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
8992
with:
9093
python-version: "3.13"
@@ -129,10 +132,13 @@ jobs:
129132
test -f "$tmpdir/plugin/.codex-plugin/plugin.json"
130133
test ! -e "$tmpdir/plugin/tests"
131134
test ! -e "$tmpdir/source/CodexQB/.git"
132-
cd "$tmpdir/source/CodexQB"
133-
CODEXQB_VALIDATE_SKIP_UNITTESTS=1 \
134-
CODEXQB_VALIDATE_SKIP_BEHAVIOR_SMOKE=1 \
135-
bash scripts/validate.sh
135+
python3 -I -S -B scripts/run_extracted_validation.py \
136+
--expected-head "$GITHUB_SHA" \
137+
--zip "$tmpdir/CodexQB-source-worktree.zip" \
138+
--root "$tmpdir/source/CodexQB" \
139+
--profile static \
140+
--skip-unit-tests \
141+
--skip-behavior-smoke
136142
137143
required:
138144
name: required / CodexQB
@@ -161,6 +167,7 @@ jobs:
161167
- uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6
162168
with:
163169
fetch-depth: 0
170+
persist-credentials: false
164171
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6
165172
with:
166173
python-version: "3.13"

Makefile

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ SHELL := /bin/bash
22
PYTHON ?= python3
33
NO_BYTECODE = PYTHONDONTWRITEBYTECODE=1
44
PLATFORM_POLICY ?= auto
5+
CODEXQB_SKILL_ROOT := $(abspath plugins/codexqb/skills/codexqb)
6+
CODEXQB_LAUNCHER := $(CODEXQB_SKILL_ROOT)/scripts/skill_launcher.py
57

68
.PHONY: check check-fast check-static check-unit check-platform check-schema check-behavior check-package check-public-privacy check-release test export-plugin export-source export-plugin-worktree export-source-worktree export-source-package export-sanitized export-sanitized-worktree export-sanitized-source-package
79

@@ -27,8 +29,8 @@ check-platform:
2729
@set -euo pipefail; \
2830
tmpdir="$$(mktemp -d)"; \
2931
trap 'rm -rf "$$tmpdir"' EXIT; \
30-
$(NO_BYTECODE) $(PYTHON) plugins/codexqb/skills/codexqb/scripts/doctor.py --json > "$$tmpdir/doctor.json"; \
31-
$(NO_BYTECODE) $(PYTHON) plugins/codexqb/skills/codexqb/scripts/doctor.py; \
32+
$(NO_BYTECODE) $(PYTHON) -I -S -B "$(CODEXQB_LAUNCHER)" --active-skill-md "$(CODEXQB_SKILL_ROOT)/SKILL.md" --controller doctor -- --json > "$$tmpdir/doctor.json"; \
33+
$(NO_BYTECODE) $(PYTHON) -I -S -B "$(CODEXQB_LAUNCHER)" --active-skill-md "$(CODEXQB_SKILL_ROOT)/SKILL.md" --controller doctor --; \
3234
probe_output="$$( $(NO_BYTECODE) $(PYTHON) tests/platform/run_mount_identity_probe.py )"; \
3335
echo "$$probe_output"; \
3436
if [[ "$(PLATFORM_POLICY)" == "required" && "$$probe_output" != *"status=ready "* ]]; then \
@@ -71,7 +73,7 @@ check-release:
7173
$(NO_BYTECODE) $(PYTHON) scripts/verify_package_manifest.py --root "$$tmpdir/plugin" --strict-artifact --expected-artifact-type plugin; \
7274
test -f "$$tmpdir/plugin/.codex-plugin/plugin.json"; \
7375
$(NO_BYTECODE) $(PYTHON) scripts/verify_package_manifest.py --root "$$tmpdir/source/CodexQB" --strict-artifact --expected-artifact-type source; \
74-
cd "$$tmpdir/source/CodexQB" && CODEXQB_VALIDATE_SKIP_UNITTESTS=1 CODEXQB_VALIDATE_SKIP_BEHAVIOR_SMOKE=1 bash scripts/validate.sh
76+
$(NO_BYTECODE) $(PYTHON) -I -S -B scripts/run_extracted_validation.py --expected-head "$$(git rev-parse --verify HEAD)" --zip "$$tmpdir/CodexQB-source-release.zip" --root "$$tmpdir/source/CodexQB" --profile static --skip-unit-tests --skip-behavior-smoke
7577

7678
# Legacy full discovery remains available for maintainers. Gate-aware CI uses
7779
# the explicit targets above so schema/package/platform behavior cannot drift.

README.md

Lines changed: 45 additions & 69 deletions
Large diffs are not rendered by default.

docs/INSTALLATION.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ CodexQB publishes two different ZIP layouts. They are deliberately not interchan
6868
| Artifact | Extracted root | Intended use |
6969
| --- | --- | --- |
7070
| `codexqb-plugin-<version>.zip` | `.codex-plugin/`, `skills/`, `PACKAGE-MANIFEST.json` | Plugin payload for a marketplace publisher, cache, or air-gapped local-marketplace assembly |
71-
| `CodexQB-source-<version>.zip` | `CodexQB/` | Complete source, tests, docs, CI, and maintenance tooling |
71+
| `CodexQB-source-<version>.zip` | `CodexQB/` | Reviewable source, tests, docs, CI, and maintenance tooling, excluding the two checkout-only validation controllers |
7272

7373
The Codex CLI installs plugins from a configured marketplace snapshot; `codex plugin add` does not accept a ZIP path directly. The repository marketplace commands above are therefore the canonical end-user installation path. If a plugin ZIP is used for offline assembly, verify it before extraction, place the extracted plugin root at the path referenced by a local marketplace's `.agents/plugins/marketplace.json`, add that marketplace, and then install `codexqb@<marketplace-name>`.
7474

@@ -147,7 +147,7 @@ CodexQB 0.3.0 keeps older planner artifacts readable outside strict execution ga
147147

148148
New Apply runs use `apply_run_schema_version: 3`. Apply schema-v1 and schema-v2 run directories predate the signed live change-set, complete command-receipt, and ordered reviewer-receipt contract; preserve them as archive-only evidence and prepare a new v3 run instead of attempting validation, resume, replacement, trusted verification, finalization, or in-place migration. A v3 evidence chain uses `capture-evidence`, `run-validation`, and phase-aware reviewer `dispatch`/`record-agent` followed by `publish-review` in spec, quality, optional security, and final order. Direct mode cannot independently produce that reviewer-agent chain. `subagent_serial` can build the complete but unattested chain, but in the current runtime trusted `VERIFIED` and `finalize` remain blocked until a host-issued agent attestation contract is available.
149149

150-
Extracted source packages without `.git/` metadata verify `PACKAGE-MANIFEST.json` before Git discovery and then validate in filesystem hygiene mode. Use `CODEXQB_VALIDATE_SKIP_UNITTESTS=1 CODEXQB_VALIDATE_SKIP_BEHAVIOR_SMOKE=1 bash scripts/validate.sh` only for that bounded package-copy fallback; normal repository validation should run the behavior smokes and release privacy scan. A plugin payload intentionally omits repository-level validation tooling and is verified through its manifest plus the local-marketplace installation smoke.
150+
Extracted source packages have no self-validation authority and case-insensitively omit the checkout-only `scripts/validate.sh` and `scripts/run_extracted_validation.py` entrypoints. From the exact selected checkout, run `python3 -I -S -B scripts/run_extracted_validation.py --expected-head <externally-asserted-full-HEAD> --zip <source.zip> --root <extracted-root> --profile static`. The launcher pair-binds that HEAD, checkout/root identity, held controller bundle, archive, manifest, and extracted inventory; materializes controller bytes into a private fsynced snapshot; and treats the target only as descriptor-bound data for static policy. Its unsigned diagnostic explicitly reports `controller_observed_explicit_source_selection`, `host_attested=false`, `verified=false`, and `finalization_allowed=false`. It is not publisher authentication, host attestation, Goal/Apply authority, Step 4 readiness, or release finalization. Dynamic extracted tests remain deferred until the PR4 host-native sandbox. A plugin payload likewise omits repository-level validation tooling and is verified through its manifest plus the local-marketplace installation smoke.
151151

152152
Package export CLI failures emit stable path-safe error codes. A successful export reports `output=created` instead of echoing the destination path; treat the requested destination supplied by the caller as the location to inspect.
153153

@@ -178,8 +178,8 @@ If `$codexqb` is not recognized:
178178
If Goal preparation, repository evidence capture, packaging, or Apply reports `secure_repository_mount_identity_unavailable`, run the dependency-free capability doctor:
179179

180180
```bash
181-
python3 plugins/codexqb/skills/codexqb/scripts/doctor.py
182-
python3 plugins/codexqb/skills/codexqb/scripts/doctor.py --json
181+
python3 -I -S -B plugins/codexqb/skills/codexqb/scripts/doctor.py
182+
python3 -I -S -B plugins/codexqb/skills/codexqb/scripts/doctor.py --json
183183
```
184184

185185
`status=expected_unsupported` means the runtime offers no usable descriptor-bound mount provider; move the operation to a supported Linux or macOS host. `status=probe_failed` means an advertised provider failed or comparable providers disagreed; treat that as a blocker and repair the host/runtime before retrying. The report never includes raw mount IDs, repository/home paths, credentials, or environment values.

0 commit comments

Comments
 (0)