Skip to content

Commit 7c2d203

Browse files
committed
docs(ci): record npm staging proper red
1 parent 2397e17 commit 7c2d203

2 files changed

Lines changed: 38 additions & 10 deletions

File tree

.github/scripts/test_verify_required_ci.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,18 @@ def test_mutable_npm_install_is_rejected(self) -> None:
5555
verify_required_ci.npm_contract_errors(broken),
5656
)
5757

58+
def test_npm_staging_proper_red_is_documented(self) -> None:
59+
threat_model = Path("docs/security/threat-model.md").read_text(
60+
encoding="utf-8"
61+
)
62+
self.assertIn("CODEX_VERSION=0.115.0", self.npm_workflow)
63+
self.assertIn("GH_TOKEN: ${{ github.token }}", self.npm_workflow)
64+
self.assertIn("- [x] Staging failure propagation", threat_model)
65+
self.assertIn("- [ ] Successful npm staging", threat_model)
66+
self.assertNotIn("- [x] Successful npm staging", threat_model)
67+
self.assertIn("pinned `0.115.0` upstream artifacts expired", threat_model)
68+
self.assertIn("no upstream `Actions: read`\n credential", threat_model)
69+
5870

5971
if __name__ == "__main__":
6072
unittest.main()

docs/security/threat-model.md

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,9 @@ HeliosCLI is a Rust CLI fork of OpenAI Codex. The audited components are:
3939
`unknown-git = "deny"`, crates.io-only registry) and the
4040
`rustsec/audit-check@v2` weekly cargo-audit job in
4141
`.github/workflows/cargo-audit.yml`.
42-
4. **CI/CD + release pipeline** — 52 workflows under `.github/workflows/`
43-
(SHA-pinned, S9=3), tag-driven release in
44-
`.github/workflows/helios-cli-release.yml` (matrix builds for
45-
x86_64/aarch64 Linux + macOS, musl + gnu).
42+
4. **CI/CD + release pipeline** — 26 workflows under `.github/workflows/`,
43+
including tag-driven release in `.github/workflows/rust-release.yml` and
44+
pull-request npm staging in `.github/workflows/ci.yml`.
4645

4746
Out of scope for S7-2 (deferred to S7-3 or future waves): TUI
4847
(`codex-rs/tui/`), non-interactive exec (`codex-rs/exec/`), apply-patch
@@ -92,12 +91,29 @@ S7-3 adds the 90-day CI gate.
9291

9392
| Threat | Rating | Specific attack vector | Mitigation | Owner | Last reviewed |
9493
|--------|--------|------------------------|------------|-------|---------------|
95-
| **S — Spoofing** | med | Compromised third-party GitHub Action runs attacker code in CI | All 52 workflows SHA-pinned (S9=3 verified 2026-06-15): e.g. `actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683`, `dtolnay/rust-toolchain@29eef336...` | ci-ops | 2026-06-16 |
96-
| **T — Tampering** | med | Malicious workflow change lands via a PR that bypasses CODEOWNERS | `CODEOWNERS` + required 1-reviewer branch protection; `workflow-maintenance.yml` alerts on unsigned edits; `permissions:` block on every workflow | ci-ops | 2026-06-16 |
97-
| **R — Repudiation** | low | Workflow authorship ambiguous | GitHub commit log + CODEOWNERS entry; `helios-cli-release.yml` includes the tag SHAs in the release notes | ci-ops | 2026-06-16 |
98-
| **I — Info disclosure** | med | Workflow logs leak the `OPENAI_API_KEY` passed for live integration tests | All secrets in GitHub Actions secrets (encrypted at rest); `permissions: contents: read` is the default; only `helios-cli-release.yml` upgrades to `contents: write` for tag-pushes | security | 2026-06-16 |
99-
| **D — DoS** | med | Attacker spams PRs to exhaust the (constrained) Actions budget | Concurrency groups on all long-running workflows; `pr-babysit-watch.yml` closes stale PRs; standard Linux runners only (no macOS/Windows) per global CI billing policy | infra | 2026-06-16 |
100-
| **E — Elevation** | med | A workflow gains write access via a compromised PAT or `GITHUB_TOKEN` | `permissions: contents: read` is the default across all workflows; only `helios-cli-release.yml` requests `contents: write` and only on the tag-push path; `pages.yml` uses a deploy key | ci-ops | 2026-06-16 |
94+
| **S — Spoofing** | med | Compromised third-party GitHub Action runs attacker code in CI | Critical actions in `.github/workflows/ci.yml` are commit-pinned; repository-wide pin coverage remains a review item rather than an assumed invariant | ci-ops | 2026-07-18 |
95+
| **T — Tampering** | high | A pull request changes staging code and exfiltrates a cross-repository release credential | Pull-request staging executes the checked-out PR merge ref. Never expose an upstream PAT or GitHub App key directly to this mutable job; move authenticated staging behind a trusted workflow boundary first | ci-ops | 2026-07-18 |
96+
| **R — Repudiation** | low | Workflow authorship or the artifact source is ambiguous | Git commit and Actions run logs identify the executed revision; staging must additionally retain upstream run, artifact ID, and digest evidence | ci-ops | 2026-07-18 |
97+
| **I — Info disclosure** | high | Workflow logs or PR-controlled code leak a release credential | `.github/workflows/ci.yml` currently uses only the repository-scoped `github.token`; no upstream `Actions: read` credential is configured. This keeps the credential boundary closed but leaves staging proper red | security | 2026-07-18 |
98+
| **D — DoS** | med | PRs trigger expensive multi-platform jobs or downloads of stale artifacts | The npm workflow has a concurrency group and ten-minute timeout. Current CI also uses macOS and Windows runners, so runner cost is not Linux-only | infra | 2026-07-18 |
99+
| **E — Elevation** | high | A workflow inherits broad repository permissions or a compromised cross-repository token | Repository workflow permissions currently default to `write`; only 5 of 26 workflow files declare a top-level `permissions:` block, and `ci.yml` does not. The built-in token remains limited to this repository and cannot authorize upstream artifact downloads | ci-ops | 2026-07-18 |
100+
101+
### npm staging boundary (PR #605)
102+
103+
Evidence reviewed at PR head `2397e170` on 2026-07-18:
104+
105+
- [x] Staging failure propagation — `.github/workflows/ci.yml` does not swallow
106+
the staging exit status, and the required-CI contract rejects
107+
`continue-on-error: true`.
108+
- [ ] Successful npm staging — the pinned `0.115.0` upstream artifacts expired
109+
on 2026-06-14, and this repository has no upstream `Actions: read`
110+
credential. Hosted run `29636493522`, job `88059648853`, resolves the exact
111+
upstream workflow and artifact inventory, then fails at archive download.
112+
113+
Keep successful staging unchecked until a live immutable artifact set is
114+
selected, archive downloads use exact artifact IDs and verified SHA-256
115+
digests, and any cross-repository credential is delivered only through a
116+
trusted workflow boundary that pull-request code cannot modify.
101117

102118
---
103119

0 commit comments

Comments
 (0)