This repository has been archived on 2026-08-04. You can view files and clone it, but cannot push or open issues or pull requests.
orchestrator/agent-team/ci/README.md

228 lines
13 KiB
Markdown
Raw Normal View History

Add Plane-2 leaf scaffold (pipeline graph, nodes, HITL, transports, CI) Consolidates the 18 leaf modules from the r720-plane2-scaffold workflow onto the foundation commit. Full suite: 535 passed, 1 skipped; ruff + format clean. Built (pre-deployment scaffold only — nothing provisioned/enabled): - LangGraph pipeline graph.py (INTAKE->CLARIFY->PLAN, interrupt()/resume, checkpointer-injectable) - nodes: clarifier (98% gate), planner, review_loop (GPT-4.1), builders->candidate diff, verifier - §3.3.1 HITL: ledger ops, resume_worker, deadline_timer, recovery sweep, responder - transports: slack / github / claude_code adapters - ci_gate (pure-code pass/fail), operator_cli, run-team.py entry, P1 sim harness - ci/agent-team-apply-verify.yml (split untrusted/privileged jobs) — authored, disabled KNOWN OPEN FINDINGS (verifier/cross-review, not yet fixed — see follow-up): - builders denylist: 4 execution-proven bypasses (delete, mode-change, copy-to, out-of-scope delete) - §3.3.1 CAS: BEGIN IMMEDIATE outside try/except; shared-connection txn nesting unsafe under concurrency - operator_cli: missing re-deliver/force-resume; audit-after-mutate ordering gap - ci yaml: GPT-4.1 cross-review PASS w/ 4 FIX items (symlink path escape, etc.) - P1 sim harness models the ledger layer, not real LangGraph interrupt/resume; P1 exit criteria not yet truly proven Deploy-gated (NOT done): IAM/step-ca/Roles Anywhere/confluence-bot provisioning, /sh-security-review sign-off, live Slack/CI, rsync, live dry-runs, Adam approval.
2026-06-17 14:17:44 -04:00
# agent-team/ci — split-job CI apply/verify workflow (Plane-2 leaf)
> **MOVED + LIVE (2026-06-22):** the workflow is now a registered GitHub Actions
> workflow at **`.github/workflows/agent-team-apply-verify.yml`** (repo root) —
> GitHub Actions only runs workflows under `.github/workflows/`, so the prior
> `agent-team/ci/` location was inert scaffolding. The privileged steps are
> flipped live, gated by the `agent-apply` environment's required reviewer; the
> trusted-dispatcher transport is `agent_team/dispatcher.py`. This directory now
> holds docs only.
The **split-job CI apply/verify workflow** turns a builder agent's
Add Plane-2 leaf scaffold (pipeline graph, nodes, HITL, transports, CI) Consolidates the 18 leaf modules from the r720-plane2-scaffold workflow onto the foundation commit. Full suite: 535 passed, 1 skipped; ruff + format clean. Built (pre-deployment scaffold only — nothing provisioned/enabled): - LangGraph pipeline graph.py (INTAKE->CLARIFY->PLAN, interrupt()/resume, checkpointer-injectable) - nodes: clarifier (98% gate), planner, review_loop (GPT-4.1), builders->candidate diff, verifier - §3.3.1 HITL: ledger ops, resume_worker, deadline_timer, recovery sweep, responder - transports: slack / github / claude_code adapters - ci_gate (pure-code pass/fail), operator_cli, run-team.py entry, P1 sim harness - ci/agent-team-apply-verify.yml (split untrusted/privileged jobs) — authored, disabled KNOWN OPEN FINDINGS (verifier/cross-review, not yet fixed — see follow-up): - builders denylist: 4 execution-proven bypasses (delete, mode-change, copy-to, out-of-scope delete) - §3.3.1 CAS: BEGIN IMMEDIATE outside try/except; shared-connection txn nesting unsafe under concurrency - operator_cli: missing re-deliver/force-resume; audit-after-mutate ordering gap - ci yaml: GPT-4.1 cross-review PASS w/ 4 FIX items (symlink path escape, etc.) - P1 sim harness models the ledger layer, not real LangGraph interrupt/resume; P1 exit criteria not yet truly proven Deploy-gated (NOT done): IAM/step-ca/Roles Anywhere/confluence-bot provisioning, /sh-security-review sign-off, live Slack/CI, rsync, live dry-runs, Adam approval.
2026-06-17 14:17:44 -04:00
**untrusted candidate diff** into a verified **draft PR** — the §3.3.2 trust
boundary, Phase P3 (§7.1) of `../../docs/r720-agent-team-design.md`.
> **STATUS: DEPLOY-GATED. NOT ENABLED, NOT PROVISIONED.** This is authored as
feat(agent-team): P3-live CI apply/verify hardening + ci_fetcher (gate-passed, provisioning-gated) (#17) * feat(agent-team): read-only CI-result fetcher for P3 verify gate (opt-in, inert) ci_fetcher.py: fail-closed CiResultFetcher reading the GitHub Actions run conclusion via a read-only PAT (AGENT_TEAM_CI_READ_TOKEN→GITHUB_TOKEN), returns {run_id,conclusion,diff_hash} or None on any error. Data-fetcher only — ci_gate owns the verdict; never writes, no OIDC/AWS, never reads patch artifacts. coordinator gains opt-in gated_build_verify_wiring() composing it via bind_ci_result_fetcher; NOT wired into the default run-team.py path. 20 tests. * harden(agent-team): P3 apply/verify workflow — GitHub App token, CWE-94, fail-closed Decision-1 auth model: gate-and-pr uses a GitHub App installation token (pull-requests:write) behind the agent-apply environment; ALL OIDC/id-token/AWS removed. Hardening: task_id env-indirection (CWE-94 — GitHub expands ${{ }} into the run shell before exec, so %s/quoting is insufficient); run-id pinning on both download-artifact; post-build denied-path check (build-hook writes into denied paths fail the job); empty-hash fail-closed in BOTH the embedded gate (fixed a real ''=='' pass bug) and ci_gate.py. App-token + draft-PR steps stay if:${{ false }} until provisioning (App + environment + branch protection). +17 tests. * harden(agent-team): apply P3-live security-gate fixes (GPT-4.1 xreview + sh-security-review) BLOCK-1/FIX-4: gate-and-pr re-comments pull-requests:write + environment:agent-apply (provisioning-time uncomment) and gains needs.guard/build-test=='success' job guard — zero privilege until provisioning. BLOCK-2/3+FIX-5: ci_fetcher validates run_id (^[0-9]{1,20}$), owner/repo (^[A-Za-z0-9_.-]{1,100}$), and fetched_id (int) — fail closed, no SSRF/path injection. FIX-1: conclusion allowlist. FIX-3: api_root removed from public builder (no injectable endpoint). INJ-02: post-build denied-path check uses NUL-delimited git output + explicit rename parsing, no backslash mangling, non-UTF8=violation. INJ-03: all three trust- control denylists unified to one 22-entry union + drift-guard test. Q1: documented run_id/ diff_hash trust source (dispatcher/ledger only). 884 tests, ruff clean. Privileged steps stay if:${{ false }} until provisioning. * build(security-review): prune .claude worktrees from deterministic scanners Agent worktrees under .claude/worktrees/ are full repo copies; the cfn-lint find|xargs template scan overflowed ('command line cannot be assembled') and the pre-push hook fail-closed to BLOCK whenever a worktree was present. Prune .claude in the cfn-lint find + semgrep/checkov excludes, and gitignore .claude/ so it is never scanned or committed. Unblocks main-tree pushes during parallel agent work.
2026-06-18 15:53:26 -04:00
> files only. Per the design (§3.3.2, §7.1 P3) the workflow must clear **BOTH
> `/sh-security-review` AND the mandatory GPT-4.1 cross-review** before
> deployment, because it is untrusted-input handling + a CI trust boundary. The
> privileged draft-PR step is hard-disabled (`if: ${{ false }}`); the ONLY
> remaining step to go live is the **provisioning flip** (create the GitHub App,
> the `agent-apply` environment with a required reviewer, and branch protection,
> then flip the step's `if:`). Nothing here is wired to a live org repo.
>
> **AUTH MODEL (LOCKED): GitHub App installation token — ZERO cloud credentials,
> no token-federation.** The privileged `gate-and-pr` job authenticates with a
> GitHub App installation token (`pull-requests: write`) minted at run time by
> SHA-pinned `actions/create-github-app-token`. There is **no AWS and no
> cloud-OIDC** anywhere in this workflow. The required human reviewer lives on
> the `agent-apply` GitHub Environment (configured at provisioning, not in YAML).
Add Plane-2 leaf scaffold (pipeline graph, nodes, HITL, transports, CI) Consolidates the 18 leaf modules from the r720-plane2-scaffold workflow onto the foundation commit. Full suite: 535 passed, 1 skipped; ruff + format clean. Built (pre-deployment scaffold only — nothing provisioned/enabled): - LangGraph pipeline graph.py (INTAKE->CLARIFY->PLAN, interrupt()/resume, checkpointer-injectable) - nodes: clarifier (98% gate), planner, review_loop (GPT-4.1), builders->candidate diff, verifier - §3.3.1 HITL: ledger ops, resume_worker, deadline_timer, recovery sweep, responder - transports: slack / github / claude_code adapters - ci_gate (pure-code pass/fail), operator_cli, run-team.py entry, P1 sim harness - ci/agent-team-apply-verify.yml (split untrusted/privileged jobs) — authored, disabled KNOWN OPEN FINDINGS (verifier/cross-review, not yet fixed — see follow-up): - builders denylist: 4 execution-proven bypasses (delete, mode-change, copy-to, out-of-scope delete) - §3.3.1 CAS: BEGIN IMMEDIATE outside try/except; shared-connection txn nesting unsafe under concurrency - operator_cli: missing re-deliver/force-resume; audit-after-mutate ordering gap - ci yaml: GPT-4.1 cross-review PASS w/ 4 FIX items (symlink path escape, etc.) - P1 sim harness models the ledger layer, not real LangGraph interrupt/resume; P1 exit criteria not yet truly proven Deploy-gated (NOT done): IAM/step-ca/Roles Anywhere/confluence-bot provisioning, /sh-security-review sign-off, live Slack/CI, rsync, live dry-runs, Adam approval.
2026-06-17 14:17:44 -04:00
## Files
| File | What it is |
|---|---|
| `agent-team-apply-verify.yml` | The split-job workflow. Self-contained: the load-bearing gate logic (diff integrity, trust-control denylist, declared-scope check, pure-code pass/fail) is embedded inline as stdlib-only, type-hinted Python heredocs, so the workflow has **no external script dependency**. |
| `README.md` | This file. |
The filename is kebab-case per the handbook. Deployment target (later, after the
gates): promote into `Sea-Haven-Industries/.github` as a reusable workflow
feat(agent-team): P3-live CI apply/verify hardening + ci_fetcher (gate-passed, provisioning-gated) (#17) * feat(agent-team): read-only CI-result fetcher for P3 verify gate (opt-in, inert) ci_fetcher.py: fail-closed CiResultFetcher reading the GitHub Actions run conclusion via a read-only PAT (AGENT_TEAM_CI_READ_TOKEN→GITHUB_TOKEN), returns {run_id,conclusion,diff_hash} or None on any error. Data-fetcher only — ci_gate owns the verdict; never writes, no OIDC/AWS, never reads patch artifacts. coordinator gains opt-in gated_build_verify_wiring() composing it via bind_ci_result_fetcher; NOT wired into the default run-team.py path. 20 tests. * harden(agent-team): P3 apply/verify workflow — GitHub App token, CWE-94, fail-closed Decision-1 auth model: gate-and-pr uses a GitHub App installation token (pull-requests:write) behind the agent-apply environment; ALL OIDC/id-token/AWS removed. Hardening: task_id env-indirection (CWE-94 — GitHub expands ${{ }} into the run shell before exec, so %s/quoting is insufficient); run-id pinning on both download-artifact; post-build denied-path check (build-hook writes into denied paths fail the job); empty-hash fail-closed in BOTH the embedded gate (fixed a real ''=='' pass bug) and ci_gate.py. App-token + draft-PR steps stay if:${{ false }} until provisioning (App + environment + branch protection). +17 tests. * harden(agent-team): apply P3-live security-gate fixes (GPT-4.1 xreview + sh-security-review) BLOCK-1/FIX-4: gate-and-pr re-comments pull-requests:write + environment:agent-apply (provisioning-time uncomment) and gains needs.guard/build-test=='success' job guard — zero privilege until provisioning. BLOCK-2/3+FIX-5: ci_fetcher validates run_id (^[0-9]{1,20}$), owner/repo (^[A-Za-z0-9_.-]{1,100}$), and fetched_id (int) — fail closed, no SSRF/path injection. FIX-1: conclusion allowlist. FIX-3: api_root removed from public builder (no injectable endpoint). INJ-02: post-build denied-path check uses NUL-delimited git output + explicit rename parsing, no backslash mangling, non-UTF8=violation. INJ-03: all three trust- control denylists unified to one 22-entry union + drift-guard test. Q1: documented run_id/ diff_hash trust source (dispatcher/ledger only). 884 tests, ruff clean. Privileged steps stay if:${{ false }} until provisioning. * build(security-review): prune .claude worktrees from deterministic scanners Agent worktrees under .claude/worktrees/ are full repo copies; the cfn-lint find|xargs template scan overflowed ('command line cannot be assembled') and the pre-push hook fail-closed to BLOCK whenever a worktree was present. Prune .claude in the cfn-lint find + semgrep/checkov excludes, and gitignore .claude/ so it is never scanned or committed. Unblocks main-tree pushes during parallel agent work.
2026-06-18 15:53:26 -04:00
(`engineering-handbook/cicd.md`); the trusted apply path (which owns the GitHub
App write token) invokes it.
Add Plane-2 leaf scaffold (pipeline graph, nodes, HITL, transports, CI) Consolidates the 18 leaf modules from the r720-plane2-scaffold workflow onto the foundation commit. Full suite: 535 passed, 1 skipped; ruff + format clean. Built (pre-deployment scaffold only — nothing provisioned/enabled): - LangGraph pipeline graph.py (INTAKE->CLARIFY->PLAN, interrupt()/resume, checkpointer-injectable) - nodes: clarifier (98% gate), planner, review_loop (GPT-4.1), builders->candidate diff, verifier - §3.3.1 HITL: ledger ops, resume_worker, deadline_timer, recovery sweep, responder - transports: slack / github / claude_code adapters - ci_gate (pure-code pass/fail), operator_cli, run-team.py entry, P1 sim harness - ci/agent-team-apply-verify.yml (split untrusted/privileged jobs) — authored, disabled KNOWN OPEN FINDINGS (verifier/cross-review, not yet fixed — see follow-up): - builders denylist: 4 execution-proven bypasses (delete, mode-change, copy-to, out-of-scope delete) - §3.3.1 CAS: BEGIN IMMEDIATE outside try/except; shared-connection txn nesting unsafe under concurrency - operator_cli: missing re-deliver/force-resume; audit-after-mutate ordering gap - ci yaml: GPT-4.1 cross-review PASS w/ 4 FIX items (symlink path escape, etc.) - P1 sim harness models the ledger layer, not real LangGraph interrupt/resume; P1 exit criteria not yet truly proven Deploy-gated (NOT done): IAM/step-ca/Roles Anywhere/confluence-bot provisioning, /sh-security-review sign-off, live Slack/CI, rsync, live dry-runs, Adam approval.
2026-06-17 14:17:44 -04:00
## The trust boundary (design §3.3.2)
The builder agents are semi-trusted: an LLM that read repo content can be wrong
or prompt-injected, so **the candidate diff is treated as untrusted code.** The
threat is that executing it in CI with org credentials lets a bad diff exfiltrate
secrets, assume the deploy role, or tamper with other repos. The workflow
implements all five boundaries:
1. **Split CI — untrusted execution is credential-less.** The job that checks
out and runs the diff (`build-test`) runs with `permissions: contents: read`,
feat(agent-team): P3-live CI apply/verify hardening + ci_fetcher (gate-passed, provisioning-gated) (#17) * feat(agent-team): read-only CI-result fetcher for P3 verify gate (opt-in, inert) ci_fetcher.py: fail-closed CiResultFetcher reading the GitHub Actions run conclusion via a read-only PAT (AGENT_TEAM_CI_READ_TOKEN→GITHUB_TOKEN), returns {run_id,conclusion,diff_hash} or None on any error. Data-fetcher only — ci_gate owns the verdict; never writes, no OIDC/AWS, never reads patch artifacts. coordinator gains opt-in gated_build_verify_wiring() composing it via bind_ci_result_fetcher; NOT wired into the default run-team.py path. 20 tests. * harden(agent-team): P3 apply/verify workflow — GitHub App token, CWE-94, fail-closed Decision-1 auth model: gate-and-pr uses a GitHub App installation token (pull-requests:write) behind the agent-apply environment; ALL OIDC/id-token/AWS removed. Hardening: task_id env-indirection (CWE-94 — GitHub expands ${{ }} into the run shell before exec, so %s/quoting is insufficient); run-id pinning on both download-artifact; post-build denied-path check (build-hook writes into denied paths fail the job); empty-hash fail-closed in BOTH the embedded gate (fixed a real ''=='' pass bug) and ci_gate.py. App-token + draft-PR steps stay if:${{ false }} until provisioning (App + environment + branch protection). +17 tests. * harden(agent-team): apply P3-live security-gate fixes (GPT-4.1 xreview + sh-security-review) BLOCK-1/FIX-4: gate-and-pr re-comments pull-requests:write + environment:agent-apply (provisioning-time uncomment) and gains needs.guard/build-test=='success' job guard — zero privilege until provisioning. BLOCK-2/3+FIX-5: ci_fetcher validates run_id (^[0-9]{1,20}$), owner/repo (^[A-Za-z0-9_.-]{1,100}$), and fetched_id (int) — fail closed, no SSRF/path injection. FIX-1: conclusion allowlist. FIX-3: api_root removed from public builder (no injectable endpoint). INJ-02: post-build denied-path check uses NUL-delimited git output + explicit rename parsing, no backslash mangling, non-UTF8=violation. INJ-03: all three trust- control denylists unified to one 22-entry union + drift-guard test. Q1: documented run_id/ diff_hash trust source (dispatcher/ledger only). 884 tests, ruff clean. Privileged steps stay if:${{ false }} until provisioning. * build(security-review): prune .claude worktrees from deterministic scanners Agent worktrees under .claude/worktrees/ are full repo copies; the cfn-lint find|xargs template scan overflowed ('command line cannot be assembled') and the pre-push hook fail-closed to BLOCK whenever a worktree was present. Prune .claude in the cfn-lint find + semgrep/checkov excludes, and gitignore .claude/ so it is never scanned or committed. Unblocks main-tree pushes during parallel agent work.
2026-06-18 15:53:26 -04:00
**no secrets, no App token, no write token**, and egress blocked
Add Plane-2 leaf scaffold (pipeline graph, nodes, HITL, transports, CI) Consolidates the 18 leaf modules from the r720-plane2-scaffold workflow onto the foundation commit. Full suite: 535 passed, 1 skipped; ruff + format clean. Built (pre-deployment scaffold only — nothing provisioned/enabled): - LangGraph pipeline graph.py (INTAKE->CLARIFY->PLAN, interrupt()/resume, checkpointer-injectable) - nodes: clarifier (98% gate), planner, review_loop (GPT-4.1), builders->candidate diff, verifier - §3.3.1 HITL: ledger ops, resume_worker, deadline_timer, recovery sweep, responder - transports: slack / github / claude_code adapters - ci_gate (pure-code pass/fail), operator_cli, run-team.py entry, P1 sim harness - ci/agent-team-apply-verify.yml (split untrusted/privileged jobs) — authored, disabled KNOWN OPEN FINDINGS (verifier/cross-review, not yet fixed — see follow-up): - builders denylist: 4 execution-proven bypasses (delete, mode-change, copy-to, out-of-scope delete) - §3.3.1 CAS: BEGIN IMMEDIATE outside try/except; shared-connection txn nesting unsafe under concurrency - operator_cli: missing re-deliver/force-resume; audit-after-mutate ordering gap - ci yaml: GPT-4.1 cross-review PASS w/ 4 FIX items (symlink path escape, etc.) - P1 sim harness models the ledger layer, not real LangGraph interrupt/resume; P1 exit criteria not yet truly proven Deploy-gated (NOT done): IAM/step-ca/Roles Anywhere/confluence-bot provisioning, /sh-security-review sign-off, live Slack/CI, rsync, live dry-runs, Adam approval.
2026-06-17 14:17:44 -04:00
(harden-runner). The patch executes only there, where there is nothing to
feat(agent-team): P3-live CI apply/verify hardening + ci_fetcher (gate-passed, provisioning-gated) (#17) * feat(agent-team): read-only CI-result fetcher for P3 verify gate (opt-in, inert) ci_fetcher.py: fail-closed CiResultFetcher reading the GitHub Actions run conclusion via a read-only PAT (AGENT_TEAM_CI_READ_TOKEN→GITHUB_TOKEN), returns {run_id,conclusion,diff_hash} or None on any error. Data-fetcher only — ci_gate owns the verdict; never writes, no OIDC/AWS, never reads patch artifacts. coordinator gains opt-in gated_build_verify_wiring() composing it via bind_ci_result_fetcher; NOT wired into the default run-team.py path. 20 tests. * harden(agent-team): P3 apply/verify workflow — GitHub App token, CWE-94, fail-closed Decision-1 auth model: gate-and-pr uses a GitHub App installation token (pull-requests:write) behind the agent-apply environment; ALL OIDC/id-token/AWS removed. Hardening: task_id env-indirection (CWE-94 — GitHub expands ${{ }} into the run shell before exec, so %s/quoting is insufficient); run-id pinning on both download-artifact; post-build denied-path check (build-hook writes into denied paths fail the job); empty-hash fail-closed in BOTH the embedded gate (fixed a real ''=='' pass bug) and ci_gate.py. App-token + draft-PR steps stay if:${{ false }} until provisioning (App + environment + branch protection). +17 tests. * harden(agent-team): apply P3-live security-gate fixes (GPT-4.1 xreview + sh-security-review) BLOCK-1/FIX-4: gate-and-pr re-comments pull-requests:write + environment:agent-apply (provisioning-time uncomment) and gains needs.guard/build-test=='success' job guard — zero privilege until provisioning. BLOCK-2/3+FIX-5: ci_fetcher validates run_id (^[0-9]{1,20}$), owner/repo (^[A-Za-z0-9_.-]{1,100}$), and fetched_id (int) — fail closed, no SSRF/path injection. FIX-1: conclusion allowlist. FIX-3: api_root removed from public builder (no injectable endpoint). INJ-02: post-build denied-path check uses NUL-delimited git output + explicit rename parsing, no backslash mangling, non-UTF8=violation. INJ-03: all three trust- control denylists unified to one 22-entry union + drift-guard test. Q1: documented run_id/ diff_hash trust source (dispatcher/ledger only). 884 tests, ruff clean. Privileged steps stay if:${{ false }} until provisioning. * build(security-review): prune .claude worktrees from deterministic scanners Agent worktrees under .claude/worktrees/ are full repo copies; the cfn-lint find|xargs template scan overflowed ('command line cannot be assembled') and the pre-push hook fail-closed to BLOCK whenever a worktree was present. Prune .claude in the cfn-lint find + semgrep/checkov excludes, and gitignore .claude/ so it is never scanned or committed. Unblocks main-tree pushes during parallel agent work.
2026-06-18 15:53:26 -04:00
steal and nothing to assume. Every privileged action (the GitHub App token
mint, the draft-PR open) runs in a **separate `gate-and-pr` job that never
checks out or executes patch-controlled code** — it consumes the build/test
report as **data only**. There is **no `pull_request_target` + head-ref
checkout** (the "pwn request" anti-pattern). The `build-test` job also runs a
**post-build denied-path check**: after the build/test step, it diffs the
working tree against the committed patched baseline and fails if a build hook
(setup.py, conftest, postinstall, Makefile) wrote into the trust-control
surface or out of declared scope — closing the build-hook write vector.
Add Plane-2 leaf scaffold (pipeline graph, nodes, HITL, transports, CI) Consolidates the 18 leaf modules from the r720-plane2-scaffold workflow onto the foundation commit. Full suite: 535 passed, 1 skipped; ruff + format clean. Built (pre-deployment scaffold only — nothing provisioned/enabled): - LangGraph pipeline graph.py (INTAKE->CLARIFY->PLAN, interrupt()/resume, checkpointer-injectable) - nodes: clarifier (98% gate), planner, review_loop (GPT-4.1), builders->candidate diff, verifier - §3.3.1 HITL: ledger ops, resume_worker, deadline_timer, recovery sweep, responder - transports: slack / github / claude_code adapters - ci_gate (pure-code pass/fail), operator_cli, run-team.py entry, P1 sim harness - ci/agent-team-apply-verify.yml (split untrusted/privileged jobs) — authored, disabled KNOWN OPEN FINDINGS (verifier/cross-review, not yet fixed — see follow-up): - builders denylist: 4 execution-proven bypasses (delete, mode-change, copy-to, out-of-scope delete) - §3.3.1 CAS: BEGIN IMMEDIATE outside try/except; shared-connection txn nesting unsafe under concurrency - operator_cli: missing re-deliver/force-resume; audit-after-mutate ordering gap - ci yaml: GPT-4.1 cross-review PASS w/ 4 FIX items (symlink path escape, etc.) - P1 sim harness models the ledger layer, not real LangGraph interrupt/resume; P1 exit criteria not yet truly proven Deploy-gated (NOT done): IAM/step-ca/Roles Anywhere/confluence-bot provisioning, /sh-security-review sign-off, live Slack/CI, rsync, live dry-runs, Adam approval.
2026-06-17 14:17:44 -04:00
2. **Trust-control-surface denylist (CI-side hard fail).** The `guard` job
rejects any diff that touches `.github/workflows/**`, IAM/policy IaC
(CDK/SAM/Terraform), branch-protection / `CODEOWNERS` / Dependabot config, or
files **outside the task's declared scope**. The match is not naive: it
**canonicalizes paths, rejects parent-directory traversal, and inspects
`rename from/to` headers**, so a rename *into* a denied path — or path
indirection — cannot bypass it. Such a diff is escalated to mandatory human +
GPT cross-review, never auto-built.
3. **Diff integrity, box → CI.** The builder records the candidate diff's sha256
in the task ledger (foundation `agent_team.state_store` content-hash idiom).
Resolve security-review BLOCK: CI-guard bypasses, denylist parity, force-resume Addresses the confirmed findings from /sh-security-review + the GPT-4.1 cross-review of the Plane-2 scaffold. Full suite: 589 passed; ruff clean. FIXED (proven-exploitable): - CI-guard denylist bypass (HIGH): Python fnmatch '**/' is non-recursive, so root-level template.yaml/*.tf/cdk.json/*.pem/*.key/*-stack.* evaded the trust-control surface. Replaced fnmatch with a recursive, case-insensitive glob->regex matcher. (verified: fnmatch('template.yaml','**/template.yaml')==False) - CI-guard scope bypass (HIGH): a '**' declared_scope made every path in-scope. Scope is now concrete-prefix confinement (reduces a glob to its leading metacharacter-free segments; '**' -> empty -> dropped -> unscoped reject). - Box-side vs CI denylist divergence (MED): builders.py _DENY_PATTERNS now covers Terraform, *.pem/*.key, CDK stack files, .github/actions, *iam*, bare policy*.json (case-insensitive), matching the CI surface. - force-resume was backwards (MED): it superseded the answered row recovery resumes from, making a stuck task permanently un-resumable while printing success. Now re-opens an EXPIRED (parked) question via a new reopen_question CAS helper; never supersedes an answered row; honest exit codes. - operator attribution (MED): run-team.py --operator defaulted to "" -> now the OS login, so destructive actions are always attributable. - audit-log append race (MED): replaced read-modify-rewrite (lost records under concurrent operators) with an O_APPEND single-line write, mode 600 enforced. - lstrip("ab/") path-mangling in the symlink error path -> regex prefix strip. Regression tests added across test_ci_gate_workflow / test_builders / test_run_team / test_schema. Design-level findings (resume-worker durability, egress breadth, answered_at ordering, DB-swap TOCTOU, diff-hash threat-model) are pre-deployment / P1-build-proper and recorded with written justification in agent-team/.security-review/suppressions.json; CI README diff-hash wording made honest.
2026-06-17 15:15:50 -04:00
CI **re-hashes the diff and verifies it equals the recorded hash before
applying** (in both `guard` and again pre-apply in `build-test`). Precisely:
this is an **unkeyed** hash that binds *the bytes CI applies* to *the hash the
dispatcher recorded* — it detects accidental corruption or substitution of the
artifact **in transit** between the trusted dispatcher and CI. It does **not**
prove the diff matches the approved plan, and it cannot defend a compromised
box that generates both the diff and its hash (see "Threat-model honesty"
below). A keyed/attested binding to the approval record is a later hardening.
Add Plane-2 leaf scaffold (pipeline graph, nodes, HITL, transports, CI) Consolidates the 18 leaf modules from the r720-plane2-scaffold workflow onto the foundation commit. Full suite: 535 passed, 1 skipped; ruff + format clean. Built (pre-deployment scaffold only — nothing provisioned/enabled): - LangGraph pipeline graph.py (INTAKE->CLARIFY->PLAN, interrupt()/resume, checkpointer-injectable) - nodes: clarifier (98% gate), planner, review_loop (GPT-4.1), builders->candidate diff, verifier - §3.3.1 HITL: ledger ops, resume_worker, deadline_timer, recovery sweep, responder - transports: slack / github / claude_code adapters - ci_gate (pure-code pass/fail), operator_cli, run-team.py entry, P1 sim harness - ci/agent-team-apply-verify.yml (split untrusted/privileged jobs) — authored, disabled KNOWN OPEN FINDINGS (verifier/cross-review, not yet fixed — see follow-up): - builders denylist: 4 execution-proven bypasses (delete, mode-change, copy-to, out-of-scope delete) - §3.3.1 CAS: BEGIN IMMEDIATE outside try/except; shared-connection txn nesting unsafe under concurrency - operator_cli: missing re-deliver/force-resume; audit-after-mutate ordering gap - ci yaml: GPT-4.1 cross-review PASS w/ 4 FIX items (symlink path escape, etc.) - P1 sim harness models the ledger layer, not real LangGraph interrupt/resume; P1 exit criteria not yet truly proven Deploy-gated (NOT done): IAM/step-ca/Roles Anywhere/confluence-bot provisioning, /sh-security-review sign-off, live Slack/CI, rsync, live dry-runs, Adam approval.
2026-06-17 14:17:44 -04:00
4. **Pure-code pass/fail gate over authenticated results.** Mirroring secrev's
"one pure-code script owns the block decision," the `gate-and-pr` gate reads
**only** the authenticated `needs.*.result` job conclusions (GitHub-controlled,
patch-independent) keyed to the run id + diff hash. It **never trusts a
success/failure file or artifact the patch could have written.** The verifier
*agent* only reads failures to propose the next fix; it cannot declare
success.
5. **Branch protection is the final enforcement.** The draft PR targets a
protected branch; the locked-down checks are required; merge needs them green
**plus** the security-review, the Claude Code App review, and **human
approval**. The agent never auto-merges (D2).
**Threat-model honesty (§3.3.2).** The diff hash protects box → CI transit
(tamper/substitution), **not** a box that is itself compromised — the box is in
the trusted computing base for what it *proposes*. The real backstops against a
bad box are the credential-less build job, the trust-control-surface denylist,
and above all the **human review of the draft PR** plus required checks (nothing
merges without Adam).
## Job graph
```
workflow_dispatch (task_id, diff_artifact_name, expected_diff_hash, declared_scope)
│
▼
guard contents:read, no secrets — validates the diff as DATA:
(boundaries 2,3) re-hash + denylist + scope. Never applies it.
│ (needs)
▼
feat(agent-team): P3-live CI apply/verify hardening + ci_fetcher (gate-passed, provisioning-gated) (#17) * feat(agent-team): read-only CI-result fetcher for P3 verify gate (opt-in, inert) ci_fetcher.py: fail-closed CiResultFetcher reading the GitHub Actions run conclusion via a read-only PAT (AGENT_TEAM_CI_READ_TOKEN→GITHUB_TOKEN), returns {run_id,conclusion,diff_hash} or None on any error. Data-fetcher only — ci_gate owns the verdict; never writes, no OIDC/AWS, never reads patch artifacts. coordinator gains opt-in gated_build_verify_wiring() composing it via bind_ci_result_fetcher; NOT wired into the default run-team.py path. 20 tests. * harden(agent-team): P3 apply/verify workflow — GitHub App token, CWE-94, fail-closed Decision-1 auth model: gate-and-pr uses a GitHub App installation token (pull-requests:write) behind the agent-apply environment; ALL OIDC/id-token/AWS removed. Hardening: task_id env-indirection (CWE-94 — GitHub expands ${{ }} into the run shell before exec, so %s/quoting is insufficient); run-id pinning on both download-artifact; post-build denied-path check (build-hook writes into denied paths fail the job); empty-hash fail-closed in BOTH the embedded gate (fixed a real ''=='' pass bug) and ci_gate.py. App-token + draft-PR steps stay if:${{ false }} until provisioning (App + environment + branch protection). +17 tests. * harden(agent-team): apply P3-live security-gate fixes (GPT-4.1 xreview + sh-security-review) BLOCK-1/FIX-4: gate-and-pr re-comments pull-requests:write + environment:agent-apply (provisioning-time uncomment) and gains needs.guard/build-test=='success' job guard — zero privilege until provisioning. BLOCK-2/3+FIX-5: ci_fetcher validates run_id (^[0-9]{1,20}$), owner/repo (^[A-Za-z0-9_.-]{1,100}$), and fetched_id (int) — fail closed, no SSRF/path injection. FIX-1: conclusion allowlist. FIX-3: api_root removed from public builder (no injectable endpoint). INJ-02: post-build denied-path check uses NUL-delimited git output + explicit rename parsing, no backslash mangling, non-UTF8=violation. INJ-03: all three trust- control denylists unified to one 22-entry union + drift-guard test. Q1: documented run_id/ diff_hash trust source (dispatcher/ledger only). 884 tests, ruff clean. Privileged steps stay if:${{ false }} until provisioning. * build(security-review): prune .claude worktrees from deterministic scanners Agent worktrees under .claude/worktrees/ are full repo copies; the cfn-lint find|xargs template scan overflowed ('command line cannot be assembled') and the pre-push hook fail-closed to BLOCK whenever a worktree was present. Prune .claude in the cfn-lint find + semgrep/checkov excludes, and gitignore .claude/ so it is never scanned or committed. Unblocks main-tree pushes during parallel agent work.
2026-06-18 15:53:26 -04:00
build-test contents:read, no secrets, no App token, egress blocked —
Add Plane-2 leaf scaffold (pipeline graph, nodes, HITL, transports, CI) Consolidates the 18 leaf modules from the r720-plane2-scaffold workflow onto the foundation commit. Full suite: 535 passed, 1 skipped; ruff + format clean. Built (pre-deployment scaffold only — nothing provisioned/enabled): - LangGraph pipeline graph.py (INTAKE->CLARIFY->PLAN, interrupt()/resume, checkpointer-injectable) - nodes: clarifier (98% gate), planner, review_loop (GPT-4.1), builders->candidate diff, verifier - §3.3.1 HITL: ledger ops, resume_worker, deadline_timer, recovery sweep, responder - transports: slack / github / claude_code adapters - ci_gate (pure-code pass/fail), operator_cli, run-team.py entry, P1 sim harness - ci/agent-team-apply-verify.yml (split untrusted/privileged jobs) — authored, disabled KNOWN OPEN FINDINGS (verifier/cross-review, not yet fixed — see follow-up): - builders denylist: 4 execution-proven bypasses (delete, mode-change, copy-to, out-of-scope delete) - §3.3.1 CAS: BEGIN IMMEDIATE outside try/except; shared-connection txn nesting unsafe under concurrency - operator_cli: missing re-deliver/force-resume; audit-after-mutate ordering gap - ci yaml: GPT-4.1 cross-review PASS w/ 4 FIX items (symlink path escape, etc.) - P1 sim harness models the ledger layer, not real LangGraph interrupt/resume; P1 exit criteria not yet truly proven Deploy-gated (NOT done): IAM/step-ca/Roles Anywhere/confluence-bot provisioning, /sh-security-review sign-off, live Slack/CI, rsync, live dry-runs, Adam approval.
2026-06-17 14:17:44 -04:00
(boundary 1) the ONLY job that applies + runs the UNTRUSTED patch.
│ (needs) Emits a NON-authoritative report artifact.
▼
gate-and-pr privileged home, but NEVER checks out patch code. Pure-code
(boundaries 1,4,5) gate over authenticated needs.*.result → DRAFT PR
(hard-disabled until the review gates pass).
```
`permissions: {}` at the workflow level (least privilege); each job re-declares
its own grant explicitly. The trigger is `workflow_dispatch` only — the patch
never runs in a context carrying write or secret scope.
## Run-name correlation (box dispatcher → run_id)
The box-side dispatcher (`agent_team/dispatcher.py`) triggers this workflow with
`gh workflow run`, which does **not** return the resulting run id. The dispatcher
must still resolve that `run_id` so the verifier's read-only CI-result fetcher can
poll the correct run (a `None`/unfound run_id fails closed → the verifier gate
BLOCKs / the task parks; never a vacuous pass). The correlation key is the
workflow **run name**:
```yaml
run-name: "agent-team-apply ${{ inputs.task_id }}"
```
Why the run name and not the workflow input or the head branch:
- `gh run list --json name` exposes the run name, but **workflow inputs are not
queryable** via the run list, so the `task_id` cannot be matched on the input.
- A `workflow_dispatch` run reports against the **`main` ref**, not the dispatch
head branch, so the branch is not a usable discriminator either.
So the dispatcher polls `gh run list` read-only, matches the row whose `name`
equals `agent-team-apply <task_id>` (mirrored in code by `dispatcher.run_name_for`),
and bounds the match to runs created after the dispatch watermark. The workflow's
`concurrency` group already guarantees a single in-flight run per `task_id`, so
the run name plus the dispatched-at floor identify the dispatched run
unambiguously even under many simultaneous dispatches; the pure
`dispatcher.select_run_id` then applies the anti-stale tie-break (skip a superseded
cancelled run sharing the name, prefer the later-created `databaseId`).
This `run-name` is **additive**: it adds no job, permission, secret, or trigger,
and changes no privileged step — it only surfaces the dispatching task's id for
correlation. Because the file is nonetheless a CI trust-boundary workflow
(untrusted-input handling), the edit is **flagged for the C1 re-run of
`/sh-security-review` AND the mandatory GPT-4.1 cross-review** before it ships on
this branch, matching the in-YAML `P3-BOX-INTEGRATION` comment.
Add Plane-2 leaf scaffold (pipeline graph, nodes, HITL, transports, CI) Consolidates the 18 leaf modules from the r720-plane2-scaffold workflow onto the foundation commit. Full suite: 535 passed, 1 skipped; ruff + format clean. Built (pre-deployment scaffold only — nothing provisioned/enabled): - LangGraph pipeline graph.py (INTAKE->CLARIFY->PLAN, interrupt()/resume, checkpointer-injectable) - nodes: clarifier (98% gate), planner, review_loop (GPT-4.1), builders->candidate diff, verifier - §3.3.1 HITL: ledger ops, resume_worker, deadline_timer, recovery sweep, responder - transports: slack / github / claude_code adapters - ci_gate (pure-code pass/fail), operator_cli, run-team.py entry, P1 sim harness - ci/agent-team-apply-verify.yml (split untrusted/privileged jobs) — authored, disabled KNOWN OPEN FINDINGS (verifier/cross-review, not yet fixed — see follow-up): - builders denylist: 4 execution-proven bypasses (delete, mode-change, copy-to, out-of-scope delete) - §3.3.1 CAS: BEGIN IMMEDIATE outside try/except; shared-connection txn nesting unsafe under concurrency - operator_cli: missing re-deliver/force-resume; audit-after-mutate ordering gap - ci yaml: GPT-4.1 cross-review PASS w/ 4 FIX items (symlink path escape, etc.) - P1 sim harness models the ledger layer, not real LangGraph interrupt/resume; P1 exit criteria not yet truly proven Deploy-gated (NOT done): IAM/step-ca/Roles Anywhere/confluence-bot provisioning, /sh-security-review sign-off, live Slack/CI, rsync, live dry-runs, Adam approval.
2026-06-17 14:17:44 -04:00
## SHA-pinned actions (handbook Pinning Principle, §3.3.2)
Every third-party action is pinned to a full commit SHA with the human-readable
tag in a trailing comment:
| Action | SHA | Tag |
|---|---|---|
| `actions/checkout` | `11bd71901bbe5b1630ceea73d27597364c9af683` | v4.2.2 |
| `actions/download-artifact` | `fa0a91b85d4f404e444e00e005971372dc801d16` | v4.1.8 |
| `actions/upload-artifact` | `b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882` | v4.4.3 |
| `actions/setup-python` | `0b93645e9fea7318ecaed2b359559ac225c90a2b` | v5.3.0 |
feat(agent-team): P3-live CI apply/verify hardening + ci_fetcher (gate-passed, provisioning-gated) (#17) * feat(agent-team): read-only CI-result fetcher for P3 verify gate (opt-in, inert) ci_fetcher.py: fail-closed CiResultFetcher reading the GitHub Actions run conclusion via a read-only PAT (AGENT_TEAM_CI_READ_TOKEN→GITHUB_TOKEN), returns {run_id,conclusion,diff_hash} or None on any error. Data-fetcher only — ci_gate owns the verdict; never writes, no OIDC/AWS, never reads patch artifacts. coordinator gains opt-in gated_build_verify_wiring() composing it via bind_ci_result_fetcher; NOT wired into the default run-team.py path. 20 tests. * harden(agent-team): P3 apply/verify workflow — GitHub App token, CWE-94, fail-closed Decision-1 auth model: gate-and-pr uses a GitHub App installation token (pull-requests:write) behind the agent-apply environment; ALL OIDC/id-token/AWS removed. Hardening: task_id env-indirection (CWE-94 — GitHub expands ${{ }} into the run shell before exec, so %s/quoting is insufficient); run-id pinning on both download-artifact; post-build denied-path check (build-hook writes into denied paths fail the job); empty-hash fail-closed in BOTH the embedded gate (fixed a real ''=='' pass bug) and ci_gate.py. App-token + draft-PR steps stay if:${{ false }} until provisioning (App + environment + branch protection). +17 tests. * harden(agent-team): apply P3-live security-gate fixes (GPT-4.1 xreview + sh-security-review) BLOCK-1/FIX-4: gate-and-pr re-comments pull-requests:write + environment:agent-apply (provisioning-time uncomment) and gains needs.guard/build-test=='success' job guard — zero privilege until provisioning. BLOCK-2/3+FIX-5: ci_fetcher validates run_id (^[0-9]{1,20}$), owner/repo (^[A-Za-z0-9_.-]{1,100}$), and fetched_id (int) — fail closed, no SSRF/path injection. FIX-1: conclusion allowlist. FIX-3: api_root removed from public builder (no injectable endpoint). INJ-02: post-build denied-path check uses NUL-delimited git output + explicit rename parsing, no backslash mangling, non-UTF8=violation. INJ-03: all three trust- control denylists unified to one 22-entry union + drift-guard test. Q1: documented run_id/ diff_hash trust source (dispatcher/ledger only). 884 tests, ruff clean. Privileged steps stay if:${{ false }} until provisioning. * build(security-review): prune .claude worktrees from deterministic scanners Agent worktrees under .claude/worktrees/ are full repo copies; the cfn-lint find|xargs template scan overflowed ('command line cannot be assembled') and the pre-push hook fail-closed to BLOCK whenever a worktree was present. Prune .claude in the cfn-lint find + semgrep/checkov excludes, and gitignore .claude/ so it is never scanned or committed. Unblocks main-tree pushes during parallel agent work.
2026-06-18 15:53:26 -04:00
| `actions/create-github-app-token` | `5d869da34e18e7287c1daad50e0b8ea0f506ce69` | v1.11.0 |
Add Plane-2 leaf scaffold (pipeline graph, nodes, HITL, transports, CI) Consolidates the 18 leaf modules from the r720-plane2-scaffold workflow onto the foundation commit. Full suite: 535 passed, 1 skipped; ruff + format clean. Built (pre-deployment scaffold only — nothing provisioned/enabled): - LangGraph pipeline graph.py (INTAKE->CLARIFY->PLAN, interrupt()/resume, checkpointer-injectable) - nodes: clarifier (98% gate), planner, review_loop (GPT-4.1), builders->candidate diff, verifier - §3.3.1 HITL: ledger ops, resume_worker, deadline_timer, recovery sweep, responder - transports: slack / github / claude_code adapters - ci_gate (pure-code pass/fail), operator_cli, run-team.py entry, P1 sim harness - ci/agent-team-apply-verify.yml (split untrusted/privileged jobs) — authored, disabled KNOWN OPEN FINDINGS (verifier/cross-review, not yet fixed — see follow-up): - builders denylist: 4 execution-proven bypasses (delete, mode-change, copy-to, out-of-scope delete) - §3.3.1 CAS: BEGIN IMMEDIATE outside try/except; shared-connection txn nesting unsafe under concurrency - operator_cli: missing re-deliver/force-resume; audit-after-mutate ordering gap - ci yaml: GPT-4.1 cross-review PASS w/ 4 FIX items (symlink path escape, etc.) - P1 sim harness models the ledger layer, not real LangGraph interrupt/resume; P1 exit criteria not yet truly proven Deploy-gated (NOT done): IAM/step-ca/Roles Anywhere/confluence-bot provisioning, /sh-security-review sign-off, live Slack/CI, rsync, live dry-runs, Adam approval.
2026-06-17 14:17:44 -04:00
| `step-security/harden-runner` | `0080882f6c36860b6ba35c610c98ce87d4e2f26f` | v2.10.2 |
## Relationship to the foundation
This leaf **imports the committed Plane-2 foundation contracts verbatim** (it
does not redefine them):
- The diff-hash recorded in the ledger and re-checked in CI is the same
content-hash idiom as `agent_team.state_store.compute_content_hash` (§6.7).
- The task this workflow verifies is an `agent_team.task_model.TaskRecord`; its
`candidate_diff` + `diff_hash` fields (§3.3) are exactly the
`expected_diff_hash` this workflow consumes, and `ci_results` is what the
verifier writes back from the authenticated gate (boundary 4).
- The ledger that records provenance (diff hash, run id, gate decision) is the
`agent_team.db` schema (`pending_questions` / `budget_ledger` live there;
per-task CI provenance is recorded against the task thread).
## Tests
The workflow's embedded gate logic (diff integrity, the trust-control denylist
with path-canonicalization + rename/copy/delete detection, the symlink-escape
reject, and declared-scope enforcement) is **stdlib-only, type-hinted, and
ruff-clean**, and is covered by a committed, runnable suite:
`../tests/test_ci_gate_workflow.py` extracts the inline guard script from this
YAML and executes it against good and adversarial diffs — clean in-scope,
hash mismatch, workflow delete, copy-into-denied, symlink addition, non-UTF-8,
out-of-scope, unscoped, and escaping-scope. Run it with the rest of the suite:
`python3 -m pytest agent-team/tests/ -q` from the repo root. (The claim that the
gate is "verified" is therefore backed by that test, not by authoring alone.)
Add Plane-2 leaf scaffold (pipeline graph, nodes, HITL, transports, CI) Consolidates the 18 leaf modules from the r720-plane2-scaffold workflow onto the foundation commit. Full suite: 535 passed, 1 skipped; ruff + format clean. Built (pre-deployment scaffold only — nothing provisioned/enabled): - LangGraph pipeline graph.py (INTAKE->CLARIFY->PLAN, interrupt()/resume, checkpointer-injectable) - nodes: clarifier (98% gate), planner, review_loop (GPT-4.1), builders->candidate diff, verifier - §3.3.1 HITL: ledger ops, resume_worker, deadline_timer, recovery sweep, responder - transports: slack / github / claude_code adapters - ci_gate (pure-code pass/fail), operator_cli, run-team.py entry, P1 sim harness - ci/agent-team-apply-verify.yml (split untrusted/privileged jobs) — authored, disabled KNOWN OPEN FINDINGS (verifier/cross-review, not yet fixed — see follow-up): - builders denylist: 4 execution-proven bypasses (delete, mode-change, copy-to, out-of-scope delete) - §3.3.1 CAS: BEGIN IMMEDIATE outside try/except; shared-connection txn nesting unsafe under concurrency - operator_cli: missing re-deliver/force-resume; audit-after-mutate ordering gap - ci yaml: GPT-4.1 cross-review PASS w/ 4 FIX items (symlink path escape, etc.) - P1 sim harness models the ledger layer, not real LangGraph interrupt/resume; P1 exit criteria not yet truly proven Deploy-gated (NOT done): IAM/step-ca/Roles Anywhere/confluence-bot provisioning, /sh-security-review sign-off, live Slack/CI, rsync, live dry-runs, Adam approval.
2026-06-17 14:17:44 -04:00
## Deploy gating (do NOT skip)
Before this ships (§3.3.2, §7.1 P3):
feat(agent-team): P3-live CI apply/verify hardening + ci_fetcher (gate-passed, provisioning-gated) (#17) * feat(agent-team): read-only CI-result fetcher for P3 verify gate (opt-in, inert) ci_fetcher.py: fail-closed CiResultFetcher reading the GitHub Actions run conclusion via a read-only PAT (AGENT_TEAM_CI_READ_TOKEN→GITHUB_TOKEN), returns {run_id,conclusion,diff_hash} or None on any error. Data-fetcher only — ci_gate owns the verdict; never writes, no OIDC/AWS, never reads patch artifacts. coordinator gains opt-in gated_build_verify_wiring() composing it via bind_ci_result_fetcher; NOT wired into the default run-team.py path. 20 tests. * harden(agent-team): P3 apply/verify workflow — GitHub App token, CWE-94, fail-closed Decision-1 auth model: gate-and-pr uses a GitHub App installation token (pull-requests:write) behind the agent-apply environment; ALL OIDC/id-token/AWS removed. Hardening: task_id env-indirection (CWE-94 — GitHub expands ${{ }} into the run shell before exec, so %s/quoting is insufficient); run-id pinning on both download-artifact; post-build denied-path check (build-hook writes into denied paths fail the job); empty-hash fail-closed in BOTH the embedded gate (fixed a real ''=='' pass bug) and ci_gate.py. App-token + draft-PR steps stay if:${{ false }} until provisioning (App + environment + branch protection). +17 tests. * harden(agent-team): apply P3-live security-gate fixes (GPT-4.1 xreview + sh-security-review) BLOCK-1/FIX-4: gate-and-pr re-comments pull-requests:write + environment:agent-apply (provisioning-time uncomment) and gains needs.guard/build-test=='success' job guard — zero privilege until provisioning. BLOCK-2/3+FIX-5: ci_fetcher validates run_id (^[0-9]{1,20}$), owner/repo (^[A-Za-z0-9_.-]{1,100}$), and fetched_id (int) — fail closed, no SSRF/path injection. FIX-1: conclusion allowlist. FIX-3: api_root removed from public builder (no injectable endpoint). INJ-02: post-build denied-path check uses NUL-delimited git output + explicit rename parsing, no backslash mangling, non-UTF8=violation. INJ-03: all three trust- control denylists unified to one 22-entry union + drift-guard test. Q1: documented run_id/ diff_hash trust source (dispatcher/ledger only). 884 tests, ruff clean. Privileged steps stay if:${{ false }} until provisioning. * build(security-review): prune .claude worktrees from deterministic scanners Agent worktrees under .claude/worktrees/ are full repo copies; the cfn-lint find|xargs template scan overflowed ('command line cannot be assembled') and the pre-push hook fail-closed to BLOCK whenever a worktree was present. Prune .claude in the cfn-lint find + semgrep/checkov excludes, and gitignore .claude/ so it is never scanned or committed. Unblocks main-tree pushes during parallel agent work.
2026-06-18 15:53:26 -04:00
1. `/sh-security-review` over this workflow (untrusted-input handling + CI
trust boundary).
2. Mandatory **GPT-4.1 cross-review** of the workflow. (No IAM/cloud role is
involved — the auth model is a GitHub App installation token, not OIDC/AWS.)
3. **Provisioning** (the single remaining step to go live):
- Create the GitHub App with a single permission (`pull-requests: write`),
install it on the target repo, and store its id + private key as the
`AGENT_APPLY_APP_ID` / `AGENT_APPLY_APP_PRIVATE_KEY` secrets.
- Create the `agent-apply` GitHub Environment with a **required reviewer**
(and optional wait timer) — this is the human gate, configured on the
Environment, not in YAML.
- Configure **branch protection** on the target branch (required checks +
human approval).
- Issue the **read-only** token for the CI-result fetcher
(`AGENT_TEAM_CI_READ_TOKEN`, falling back to `GITHUB_TOKEN`).
4. A documented, **exercised** rollback (uninstall the App, remove the
environment, revert the workflow).
5. Only then: flip the App-token + draft-PR steps' `if: ${{ false }}` to the
live condition documented in the workflow
(`always() && needs.guard.result=='success' &&
needs.build-test.result=='success' && steps.gate.outputs.gate=='pass'`), and
promote to `Sea-Haven-Industries/.github`. Draft PRs only; never auto-merge.