Skip to content

Commit 66122d5

Browse files
committed
feat: secure agent execution and close assurance flow
1 parent 2c7791b commit 66122d5

37 files changed

Lines changed: 1128 additions & 48 deletions

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,9 @@ engineering-process explain \
4545
--base "$BASE_SHA" --head "$GITHUB_SHA" \
4646
--declared '{"observable_behavior":true}'
4747

48-
engineering-process attest --predicate test-result/v1 \
49-
--sha "$GITHUB_SHA" --capability ci-automation --verdict pass \
50-
--identity github-actions --context-id "$GITHUB_RUN_ID" \
51-
--output .engineering/evidence/test.json
48+
engineering-process run-validation --sha "$GITHUB_SHA" \
49+
--obligations .engineering/effective-obligations.json \
50+
--output-dir "$RUNNER_TEMP/objective-evidence"
5251

5352
engineering-process readiness --sha "$GITHUB_SHA"
5453
engineering-process validate
@@ -57,6 +56,8 @@ engineering-process metrics --obligations .engineering/effective-obligations.jso
5756

5857
Trusted classification derives the complete Git change set from exact `--base` and `--head` commits, including deletions and both rename paths. `--path` is untrusted and diagnostic-only. Classification is monotonic: declarations may strengthen routing but may not defeat deterministic Protected signals. Unknown Protected characteristics fail closed. `readiness` rejects malformed, failed, contradictory, wrong-repository, wrong-SHA, wrong-process-revision, unauthorized-producer, and invalid fresh-context review evidence. See the authoritative [evidence lifecycle](docs/evidence-lifecycle.md).
5958

59+
Phase 2 adds a locked [agent-execution security baseline](docs/agent-execution-security.md), a complete [GitHub reference flow](enforcement/github/reference-flow.md), executable `test-result/v2` validation basis, and structured `independent-review/v2` evidence. `explain` reports profile-aware validation categories and the execution boundary. Reference evidence travels through GitHub artifacts and out-of-band provenance rather than source commits.
60+
6061
## Policy model
6162

6263
- **Locked** controls cannot be changed locally (fresh-context independent review, fail-closed Protected routing, exact-revision evidence, secrets, traceability, human production boundary).

docs/adoption.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,26 @@ The initial checkout and runner are the bootstrap trust boundary. Evidence
2020
transport and v1 compatibility are defined in the [exact-revision evidence
2121
lifecycle](evidence-lifecycle.md).
2222

23+
### Phase 2 evidence migration
24+
25+
Process 1.1.0 obligation sets require `test-result/v2` and `independent-review/v2`.
26+
`test-result/v1` and `independent-review/v1` remain schema-valid only for stored
27+
historical obligations that explicitly require v1; they do not satisfy v2.
28+
Configure validation commands with `focused`, `integration`, `negative`, or
29+
`security` categories and use `run-validation` in trusted CI. Use the protected
30+
`review-attest` workflow after reviewing the exact diff and v2 test evidence. A
31+
process upgrade changes obligations only with the new locked process revision.
32+
33+
Validation commands are executable argument strings, not shell programs; pipes,
34+
redirection, interpolation, and compound shell expressions are intentionally not
35+
supported. Put complex validation in a reviewed repository script and configure
36+
that script as the command.
37+
38+
Standard/Protected agent execution requires a native sandbox; Protected also
39+
requires declared network enforcement and an audit sink. See the [security
40+
baseline](agent-execution-security.md) and [GitHub
41+
mapping](../enforcement/github/agent-execution.md).
42+
2343
## Production authority and independent review
2444

2545
The human production boundary and independent review are separate controls. Every change still requires fresh-context, implementation-independent review evidence. Production authorization and promotion remain manual human actions, and automation or agents may not perform them. For an owner-controlled repository, the owner's explicit authorization and manual merge or promotion satisfy the human boundary; a second human GitHub reviewer is not implicitly required. Repositories with multiple authorized maintainers may configure a stronger branch or Environment reviewer gate.

docs/agent-execution-security.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Agent-execution security baseline
2+
3+
Canonical policy governs the security properties of an agent execution; native
4+
platform controls enforce them. Instructions and prompts alone never satisfy an
5+
enforcement requirement.
6+
7+
All executions use least privilege, isolate credentials, deny production
8+
credentials and authority, treat issues, documentation, source, dependency
9+
metadata, tool output, and web content as untrusted data, and preserve an audit
10+
trail. Network and tools are deny-by-default and may be opened only through
11+
repository allowlists. External mutations and destructive actions require
12+
explicit authorization scoped to the exact target. Standard and Protected work
13+
requires a native sandbox. Protected evaluation fails closed unless a sandbox,
14+
network enforcement mechanism, and audit sink are declared.
15+
16+
Repositories may strengthen the baseline under `overrides.agent_execution`:
17+
18+
```yaml
19+
agent_execution:
20+
sandbox: ephemeral-vm
21+
network_enforcement: firewall
22+
audit_sink: github-actions
23+
allowed_network_destinations: [pypi.org, files.pythonhosted.org]
24+
allowed_tools: [git, python, pytest]
25+
additional_denied_tools: [production-deploy]
26+
```
27+
28+
Allowlisting grants only execution access; it does not grant credentials,
29+
approval, merge, deployment, or production authority. Locked controls reject
30+
attempted weakening through `overrides.controls.agent_execution_security`.
31+
32+
## Threat model
33+
34+
- Poisoned issues, docs, source comments, web pages, dependency metadata, and
35+
generated tool output can contain prompt injection. They are evidence to
36+
inspect, never authority to change scope, reveal data, or invoke tools.
37+
- Command execution can overwrite data or cross a trust boundary. Sandboxes,
38+
scoped filesystems, explicit destructive-action authorization, and ephemeral
39+
runners constrain impact.
40+
- Credentials can be read from files, environment, process output, caches, or
41+
tool integrations. Forks receive no secrets; jobs use short-lived,
42+
environment-scoped credentials and redact audit output.
43+
- Unrestricted egress enables data exfiltration. Protected work uses a native
44+
firewall/proxy allowlist and fails when enforcement is unavailable.
45+
- Tool misuse can mutate external systems or production. Tool allowlists and
46+
scoped tokens separate read, write, approval, and promotion capabilities.
47+
- Malicious dependencies can execute during install/build. Lockfiles, trusted
48+
registries, network restrictions, ephemeral runners, and validation evidence
49+
reduce but cannot eliminate this risk.
50+
51+
The process does not claim prompt injection is impossible and does not implement
52+
a proprietary sandbox or firewall.

docs/architecture.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Declared intent, repository policy, changed paths/config, deterministic rules, a
1414

1515
Evidence follows the in-toto Statement shape with one `gitCommit` subject and a Software Signal predicate type. Every predicate carries the process version/revision, producer identity/context, verdict, and timestamp. Readiness joins predicates only for the requested repository SHA and pinned process revision.
1616

17-
Independent review additionally requires `freshContext: true` and a review context ID different from the implementation context ID. Model/vendor diversity is not used as a proxy for independence. V1 evidence is unsigned JSON: signatures and a remote attestation store remain optional until the threat model justifies their operational cost.
17+
Independent review requires a fresh context distinct from implementation plus structured v2 basis: issue/acceptance criteria, exact full diff, applicable context, validation evidence, counterexample search, findings, residual risks, and unverified areas. Model/vendor diversity is not used as a proxy for independence. V1 evidence remains historical compatibility data and cannot satisfy new v2 obligations. Producer trust comes from the Phase 1 out-of-band transport index.
1818

1919
## Solution Sufficiency
2020

@@ -27,4 +27,3 @@ The steward triages non-mandatory recommendations, but mandatory categories are
2727
- Exact Git SHA is authoritative; semantic version is human-readable compatibility metadata.
2828
- Skills are materialized under `.engineering/skills`, a vendor-neutral location. Thin adapters can later synchronize platform-native locations.
2929
- No cryptographic signing, cross-repository coordinator, or custom agent runtime without measured need.
30-

enforcement/github/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ The process reports policy meaning; configure GitHub for the stronger enforcemen
99
| Production human authorization | Manual owner/authorized-maintainer merge or promotion; optionally a protected Environment required reviewer |
1010
| No autonomous production | Do not grant agents or automation production approval or merge authority |
1111
| Minimal token bootstrap | Generated assistant files, not branch prose |
12+
| Agent execution security | Read-only job permissions, ephemeral runners, withheld/environment-scoped secrets, workflow approval, and runner-enforced network/tool allowlists |
1213

13-
The reusable workflow in `reusable-workflows/process-readiness.yml` validates the repository and joins evidence. Repository administrators must configure rulesets and Environments because permissions, plans, and collaborator topology differ; prompt instructions are not a substitute. A required Environment reviewer plus self-review prevention is a valid stronger control when a distinct authorized human exists. It is not the canonical meaning of the human production boundary and is not mandatory for an owner-controlled repository.
14+
The reusable workflow in `reusable-workflows/process-readiness.yml` validates the repository and joins evidence. The [complete reference flow](reference-flow.md) demonstrates trusted classification through readiness, and the [agent-execution mapping](agent-execution.md) distinguishes actual GitHub enforcement from procedural guidance. Repository administrators must configure rulesets, runners, egress controls, and Environments because permissions, plans, and collaborator topology differ; prompt instructions are not a substitute. A required Environment reviewer plus self-review prevention is a valid stronger control when a distinct authorized human exists. It is not the canonical meaning of the human production boundary and is not mandatory for an owner-controlled repository.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# GitHub agent-execution mapping
2+
3+
| Canonical requirement | GitHub enforcement |
4+
|---|---|
5+
| Least privilege | Set workflow/job `permissions: contents: read`; grant individual write scopes only to a separately authorized job. |
6+
| Credential isolation | Withhold secrets from forks and pull-request code; use OIDC and protected Environment secrets with short lifetimes. |
7+
| Sandbox | Use ephemeral GitHub-hosted runners or isolated, single-use self-hosted runners; never a persistent production runner. |
8+
| Network deny/allowlist | Enforce egress on a runner firewall/proxy. GitHub YAML and instructions alone do not satisfy Protected isolation. |
9+
| Tool allowlist | Preinstall/allow approved actions and commands; pin third-party actions to immutable SHAs for Protected use. |
10+
| Untrusted content | Do not interpolate issue, PR, branch, file, or tool-output text into shell commands. Fork code runs without secrets. |
11+
| External mutation | Separate read-only validation from mutation jobs; require Environment/ruleset approval and scoped tokens. |
12+
| Auditability | Retain workflow/job logs, artifact digests, run attempt, actor, workflow ref, and environment decisions. |
13+
| Production separation | Environment approval and manual human promotion remain mandatory; agents and automation cannot approve or promote. |
14+
15+
Untrusted-fork workflows require maintainer approval and run with read-only
16+
permissions. Protected repositories must configure an egress-enforced runner and
17+
declare its mechanism in the manifest; otherwise policy evaluation fails closed.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Reference assurance flow
2+
3+
`reusable-workflows/reference-assurance.yml` demonstrates the complete
4+
`trusted diff → classify → evaluate → objective validation → authenticated
5+
independent review → readiness` lifecycle. Every job checks out the exact head,
6+
installs the lock SHA, and exchanges exact-SHA evidence as short-retention
7+
artifacts outside the source tree. A changed head has a different artifact name
8+
and reruns every job, so earlier readiness cannot carry forward.
9+
10+
The Lean path requires only configured focused validation and one authenticated
11+
fresh-context review artifact. Standard additionally requires integration
12+
validation and functional QA evidence. Protected requires focused, integration,
13+
negative, and security validation, the remaining policy predicates, an
14+
egress-enforced sandbox/audit sink, and manual owner production authorization.
15+
The reference workflow never merges or deploys.
16+
17+
Declare the caller workflow paths in `repository.trusted_ci_workflows` and
18+
`repository.authorized_review_workflows`. The evidence producers compare the
19+
actual GitHub workflow ref with these protected repository-policy allowlists;
20+
an arbitrary workflow cannot label its output trusted.
21+
22+
An independent reviewer runs `review-attest` from a protected review workflow
23+
after inspecting the objective artifact, then uploads
24+
`process-independent-review-<SHA>`. If the reviewer edits, they emit no verdict;
25+
the new SHA repeats the entire flow.
26+
27+
For local reproduction, use exact commits:
28+
29+
```bash
30+
engineering-process evaluate --base "$BASE_SHA" --head "$HEAD_SHA"
31+
engineering-process explain --base "$BASE_SHA" --head "$HEAD_SHA"
32+
```
33+
34+
Trusted `run-validation` and `review-attest` intentionally require GitHub Actions;
35+
local runs may reproduce commands but cannot mint trusted provenance.
36+
37+
Forks do not receive secrets. A maintainer must approve the base-repository
38+
review workflow, and the target fork SHA must remain fetchable. Artifact download
39+
across runs needs `actions: read`; private forks may require an explicitly scoped
40+
token. Missing permissions, artifacts, provenance, or retained logs fail
41+
readiness rather than falling back to committed JSON.
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
name: Reference assurance flow
2+
on:
3+
workflow_call:
4+
inputs:
5+
base-sha: {required: true, type: string}
6+
target-sha: {required: true, type: string}
7+
review-run-id: {required: true, type: string}
8+
permissions:
9+
contents: read
10+
actions: read
11+
jobs:
12+
classify:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v4
16+
with: {ref: "${{ inputs.target-sha }}", fetch-depth: 0}
17+
- uses: actions/setup-python@v5
18+
with: {python-version: "3.12"}
19+
- id: process
20+
shell: bash
21+
run: |
22+
revision="$(python -c 'import json; print(json.load(open(".engineering/process.lock"))["revision"])')"
23+
[[ "$revision" =~ ^[0-9a-f]{40}$ ]] || exit 2
24+
echo "revision=$revision" >> "$GITHUB_OUTPUT"
25+
- run: pip install 'git+https://github.com/suyog19/software-engineering-process@${{ steps.process.outputs.revision }}'
26+
- run: engineering-process validate --runtime-revision '${{ steps.process.outputs.revision }}'
27+
- run: engineering-process evaluate --base '${{ inputs.base-sha }}' --head '${{ inputs.target-sha }}' --output "$RUNNER_TEMP/obligations.json"
28+
- run: engineering-process attest --predicate classification/v1 --sha '${{ inputs.target-sha }}' --capability orchestrator --verdict pass --identity github-actions --context-id '${{ github.run_id }}:classify' --output "$RUNNER_TEMP/classification.json"
29+
- uses: actions/upload-artifact@v4
30+
with:
31+
name: process-classification-${{ inputs.target-sha }}
32+
path: |
33+
${{ runner.temp }}/obligations.json
34+
${{ runner.temp }}/classification.json
35+
retention-days: 7
36+
objective-validation:
37+
needs: classify
38+
runs-on: ubuntu-latest
39+
steps:
40+
- uses: actions/checkout@v4
41+
with: {ref: "${{ inputs.target-sha }}", fetch-depth: 0}
42+
- uses: actions/setup-python@v5
43+
with: {python-version: "3.12"}
44+
- id: process
45+
shell: bash
46+
run: |
47+
revision="$(python -c 'import json; print(json.load(open(".engineering/process.lock"))["revision"])')"
48+
[[ "$revision" =~ ^[0-9a-f]{40}$ ]] || exit 2
49+
echo "revision=$revision" >> "$GITHUB_OUTPUT"
50+
- run: pip install 'git+https://github.com/suyog19/software-engineering-process@${{ steps.process.outputs.revision }}'
51+
- uses: actions/download-artifact@v4
52+
with: {name: "process-classification-${{ inputs.target-sha }}", path: "${{ runner.temp }}/classification"}
53+
- run: engineering-process run-validation --sha '${{ inputs.target-sha }}' --obligations '${{ runner.temp }}/classification/obligations.json' --output-dir '${{ runner.temp }}/objective'
54+
- uses: actions/upload-artifact@v4
55+
if: always()
56+
with:
57+
name: process-objective-${{ inputs.target-sha }}
58+
path: ${{ runner.temp }}/objective
59+
retention-days: 7
60+
readiness:
61+
needs: [classify, objective-validation]
62+
runs-on: ubuntu-latest
63+
steps:
64+
- uses: actions/checkout@v4
65+
with: {ref: "${{ inputs.target-sha }}", fetch-depth: 0}
66+
- uses: actions/setup-python@v5
67+
with: {python-version: "3.12"}
68+
- id: process
69+
shell: bash
70+
run: |
71+
revision="$(python -c 'import json; print(json.load(open(".engineering/process.lock"))["revision"])')"
72+
[[ "$revision" =~ ^[0-9a-f]{40}$ ]] || exit 2
73+
echo "revision=$revision" >> "$GITHUB_OUTPUT"
74+
- run: pip install 'git+https://github.com/suyog19/software-engineering-process@${{ steps.process.outputs.revision }}'
75+
- uses: actions/download-artifact@v4
76+
with: {name: "process-classification-${{ inputs.target-sha }}", path: "${{ runner.temp }}/classification"}
77+
- uses: actions/download-artifact@v4
78+
with: {name: "process-objective-${{ inputs.target-sha }}", path: "${{ runner.temp }}/objective"}
79+
- uses: actions/download-artifact@v4
80+
with:
81+
name: "process-independent-review-${{ inputs.target-sha }}"
82+
run-id: ${{ inputs.review-run-id }}
83+
github-token: ${{ github.token }}
84+
path: ${{ runner.temp }}/review
85+
- name: Assemble exact-SHA evidence and verified provenance
86+
shell: bash
87+
run: |
88+
mkdir -p "$RUNNER_TEMP/evidence"
89+
cp "$RUNNER_TEMP/classification/classification.json" "$RUNNER_TEMP/evidence/"
90+
cp "$RUNNER_TEMP/objective/test-result.json" "$RUNNER_TEMP/evidence/"
91+
cp "$RUNNER_TEMP/review/independent-review.json" "$RUNNER_TEMP/evidence/"
92+
python - <<'PY'
93+
import json, os
94+
root=os.environ['RUNNER_TEMP']; merged={"evidence":{}}
95+
for path in (f"{root}/objective/trust-index.json", f"{root}/review/trust-index.json"):
96+
merged["evidence"].update(json.load(open(path))["evidence"])
97+
json.dump(merged, open(f"{root}/trust-index.json", "w"))
98+
PY
99+
- run: engineering-process readiness --sha '${{ inputs.target-sha }}' --obligations '${{ runner.temp }}/classification/obligations.json' --evidence-dir '${{ runner.temp }}/evidence' --trust-index '${{ runner.temp }}/trust-index.json' --artifact-dir '${{ runner.temp }}/objective'

0 commit comments

Comments
 (0)