Skip to content

Commit 3550b6c

Browse files
authored
Merge pull request #5 from Coding-Autopilot-System/hardening/enterprise-audit-20260610
Harden autopilot control-plane execution
2 parents ffbce4d + be8d91f commit 3550b6c

22 files changed

Lines changed: 275 additions & 87 deletions

.github/workflows/autopilot-create-issue.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
runs-on: ubuntu-latest
1818
steps:
1919
- name: Create or update issue
20-
uses: actions/github-script@v7
20+
uses: actions/github-script@v9
2121
with:
2222
script: |
2323
const run = context.payload.workflow_run;

.github/workflows/autopilot-docs-daily.yml

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ jobs:
1919
GH_TOKEN: ${{ secrets.ORG_READ_TOKEN || github.token }}
2020
steps:
2121
- name: Checkout
22-
uses: actions/checkout@v4
22+
uses: actions/checkout@v6
2323

2424
- name: Update status docs
2525
run: |
@@ -41,28 +41,7 @@ jobs:
4141
table="${table}\n| ${ORG}/${repo} | ${queued} | ${inprogress} | ${blocked} | ${done} |"
4242
done
4343
44-
cat > docs/status.md <<EOF
45-
# Autopilot Status
46-
47-
Last updated: ${ts}
48-
49-
## Repository coverage
50-
51-
${table}
52-
EOF
53-
54-
python - <<'PY'
55-
import re
56-
from pathlib import Path
57-
58-
ts = """Last updated: ${ts}
59-
See `docs/status.md` for the full status table."""
60-
path = Path("README.md")
61-
text = path.read_text()
62-
block = "<!-- AUTOPILOT-STATUS:START -->\n" + ts + "\n<!-- AUTOPILOT-STATUS:END -->"
63-
text = re.sub(r"<!-- AUTOPILOT-STATUS:START -->(.|\n)*?<!-- AUTOPILOT-STATUS:END -->", block, text)
64-
path.write_text(text)
65-
PY
44+
printf '# Autopilot Status\n\nLast updated: %s\n\n## Repository coverage\n\n%b\n' "${ts}" "${table}" > docs/status.md
6645
6746
- name: Create PR
6847
run: |
@@ -73,7 +52,7 @@ PY
7352
fi
7453
branch="docs/daily-status"
7554
git checkout -B "${branch}"
76-
git add docs/status.md README.md
55+
git add docs/status.md
7756
git commit -m "Update autopilot status"
7857
git push -f origin "${branch}"
7958
gh pr create -t "Daily autopilot status update" -b "Automated docs update." --base main --head "${branch}" || true

.github/workflows/autopilot-operator.yml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ jobs:
1515
runs-on: [self-hosted, Windows]
1616
env:
1717
ORG: ${{ vars.ORG }}
18-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
19-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
18+
GH_TOKEN: ${{ secrets.ORG_AUTOPILOT_TOKEN }}
2019
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
2120
steps:
2221
- name: Ensure Codex on PATH
@@ -28,7 +27,7 @@ jobs:
2827
}
2928
3029
- name: Checkout
31-
uses: actions/checkout@v4
30+
uses: actions/checkout@v6
3231
with:
3332
clean: false
3433

@@ -44,6 +43,14 @@ jobs:
4443
exit 1
4544
}
4645
46+
- name: Validate org mutation token
47+
shell: pwsh
48+
run: |
49+
if (-not $env:GH_TOKEN) {
50+
Write-Host "ORG_AUTOPILOT_TOKEN is not set."
51+
exit 1
52+
}
53+
4754
- name: Validate Codex auth
4855
shell: pwsh
4956
run: |

.github/workflows/autopilot-org-installer.yml

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
1919
steps:
2020
- name: Checkout installer
21-
uses: actions/checkout@v4
21+
uses: actions/checkout@v6
2222

2323
- name: Sync intake workflow
2424
run: |
@@ -60,24 +60,11 @@ jobs:
6060
if [ "${opt_in}" != "true" ]; then
6161
marker="Autopilot Installer"
6262
existing=$(gh issue list -R ${ORG}/${repo} -s open -S "\"${marker}\"" --json number --jq '.[0].number')
63-
body=$(cat <<EOF
64-
${marker}
65-
66-
This repo does not have the Autopilot intake workflow installed.
67-
To opt in, add a file at:
68-
69-
.autopilot/opt-in
70-
71-
Optional: specify monitored workflows in:
72-
.autopilot/workflows.txt
73-
74-
Once opt-in is present, Autopilot will open a PR to install the workflow.
75-
EOF
76-
)
63+
body=$(printf '%s\n' "${marker}" "" "This repo does not have the Autopilot intake workflow installed." "To opt in, add a file at:" "" ".autopilot/opt-in" "" "Optional: specify monitored workflows in:" ".autopilot/workflows.txt" "" "Once opt-in is present, Autopilot will open a PR to install the workflow.")
7764
if [ -n "${existing}" ]; then
7865
gh issue comment -R ${ORG}/${repo} "${existing}" -b "${body}"
7966
else
80-
gh issue create -R ${ORG}/${repo} -t "Autopilot intake not installed" -b "${body}" -l "autofix,queued,docs"
67+
gh issue create -R ${ORG}/${repo} -t "Autopilot opt-in available" -b "${body}"
8168
fi
8269
continue
8370
fi

.github/workflows/ci.yml

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,22 @@ on:
66
pull_request:
77
branches: [main]
88

9+
permissions:
10+
contents: read
11+
912
jobs:
1013
validate:
1114
runs-on: ubuntu-latest
15+
timeout-minutes: 10
1216
steps:
13-
- uses: actions/checkout@v4
17+
- uses: actions/checkout@v6
18+
19+
- name: Install workflow validator dependency
20+
run: python -m pip install --disable-pip-version-check PyYAML==6.0.2
1421

1522
- name: Validate workflow YAML
16-
run: |
17-
python -c "
18-
import yaml, glob, sys
19-
files = glob.glob('.github/workflows/*.yml')
20-
errors = []
21-
for f in files:
22-
try:
23-
with open(f) as fh:
24-
yaml.safe_load(fh)
25-
print(' OK:', f)
26-
except yaml.YAMLError as e:
27-
# GitHub Actions parses some heredoc patterns that Python
28-
# yaml.safe_load rejects (heredoc terminators at column 0
29-
# inside block scalars). Warn but do not fail.
30-
print(' WARN:', f, '-', str(e).split('\n')[0])
31-
print('Validated', len(files), 'workflow files')
32-
"
23+
run: python tests/validate_workflows.py
24+
25+
- name: Validate control-plane contracts
26+
shell: pwsh
27+
run: ./tests/contract-tests.ps1
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Enterprise Control-Plane Audit
2+
3+
Audit source: `gsd-audit-fix --severity all --max 8`
4+
5+
## Findings
6+
7+
| ID | Severity | Classification | Description | File references |
8+
| --- | --- | --- | --- | --- |
9+
| F-01 | high | auto-fixable | Unlabeled issues bypass the documented `autofix + queued` authorization gate. | `scripts/autopilot-operator.ps1` |
10+
| F-02 | high | auto-fixable | Issues with exhausted `try-3` attempts can be selected repeatedly. | `scripts/autopilot-operator.ps1` |
11+
| F-03 | high | auto-fixable | Untrusted issue and comment text is passed to Codex without prompt-injection boundaries. | `scripts/autopilot-operator.ps1` |
12+
| F-04 | high | auto-fixable | The operator stages arbitrary generated files without sensitive-path or change-size policy enforcement. | `scripts/autopilot-operator.ps1` |
13+
| F-05 | high | auto-fixable | Changes without supported verification can still be pushed and marked done. | `scripts/autopilot-operator.ps1` |
14+
| F-06 | high | auto-fixable | The operator workflow uses the repository-scoped token for org-wide mutations. | `.github/workflows/autopilot-operator.yml`, `docs/runbooks/operator.md` |
15+
| F-07 | medium | auto-fixable | CI intentionally ignores workflow YAML errors and has no control-plane contract tests. | `.github/workflows/ci.yml`, `tests/contract-tests.ps1` |
16+
| F-08 | medium | auto-fixable | The installer creates actionable autofix issues for repositories that have not opted in. | `.github/workflows/autopilot-org-installer.yml` |
17+
| M-01 | high | manual-only | Provisioning a production GitHub App or fine-grained token and org-level RBAC requires administrator action. | Organization settings |
18+
| M-02 | medium | manual-only | Replacing secret-based Codex auth with workload identity depends on provider support and an architecture decision. | `.github/workflows/autopilot-operator.yml` |
19+
20+
## Verification policy
21+
22+
- Run `pwsh -NoProfile -File tests/contract-tests.ps1` after each applicable fix once the contract suite exists.
23+
- Run `git diff --check` before every commit.
24+
- Retain this audit and the final verification report as GSD evidence.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Enterprise Control-Plane Verification
2+
3+
Status: passed with manual deployment prerequisites
4+
5+
## Fixed findings
6+
7+
| ID | Status | Evidence |
8+
| --- | --- | --- |
9+
| F-01 | fixed | `0733a4b` removes unlabeled issue discovery and promotion. |
10+
| F-02 | fixed | `0532832` excludes and defensively skips exhausted `try-3` issues. |
11+
| F-03 | fixed | `35119dc` adds an explicit untrusted-content boundary to Codex prompts. |
12+
| F-04 | fixed | `36daf3c`, `e34d380` enforce sensitive-path and change-size policies. |
13+
| F-05 | fixed | `7bbcf3c`, `bc1e0e8` require supported verification by default and document exceptions. |
14+
| F-06 | fixed | `f15b9f8`, `bc1e0e8` require and document the org mutation token contract. |
15+
| F-07 | fixed | `26cf4f4`, `f2e6e43` repair workflow YAML, add contract tests, and migrate actions to Node.js 24-compatible majors. |
16+
| F-08 | fixed | `c7b10fc` makes pre-opt-in installer notices non-actionable. |
17+
18+
## Validation
19+
20+
- `python tests/validate_workflows.py`: passed, 5 workflow files.
21+
- `powershell -NoProfile -ExecutionPolicy Bypass -File tests/contract-tests.ps1`: passed.
22+
- `python -m compileall tests`: passed.
23+
- `yamllint` with GitHub Actions-compatible truthy rule disabled: passed.
24+
- `git diff --check`: passed.
25+
- Remote CI run `27290403822`: passed without Node.js runtime deprecation annotations.
26+
27+
## Manual-only findings
28+
29+
- M-01: An administrator must provision `ORG_AUTOPILOT_TOKEN` using a short-lived GitHub App installation token or a fine-grained token with access only to opted-in repositories. Required repository permissions are Contents write, Issues write, and Pull requests write.
30+
- M-02: Secretless Codex authentication remains an architecture decision dependent on provider workload-identity support.
31+
- M-03: A sandboxed staging organization and self-hosted Windows runner are required for a live end-to-end test of issue intake, mutation, verification, push, and pull-request creation.

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ flowchart LR
3434
## Quick start
3535

3636
1. Set org variable `ORG` in GitHub Actions for this repo.
37-
2. Install `autopilot-create-issue.yml` into target repos, or use `autopilot-org-installer.yml`.
38-
3. Ensure a self-hosted Windows runner with Codex and `OPENAI_API_KEY` is online.
39-
4. Trigger `autopilot-operator.yml` manually to validate the setup.
37+
2. Configure the least-privilege `ORG_AUTOPILOT_TOKEN` secret for opted-in repository mutations.
38+
3. Install `autopilot-create-issue.yml` into target repos, or use `autopilot-org-installer.yml`.
39+
4. Ensure a self-hosted Windows runner with Codex and `OPENAI_API_KEY` is online.
40+
5. Trigger `autopilot-operator.yml` manually to validate the setup.
4041

4142
## Enterprise proof points
4243

@@ -50,7 +51,7 @@ flowchart LR
5051
- Acts only on issues labeled `autofix + queued`.
5152
- Skips issues labeled `risky` or `needs-design`.
5253
- Minimal diffs only - no secrets, no destructive operations.
53-
- Best-effort verification before PR creation.
54+
- Required supported verification before PR creation, with explicit approved exceptions only.
5455

5556
## Workflows
5657

docs/runbooks/operator.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,17 @@ $env:DRY_RUN = "true"
1717
- `MAX_ISSUES` (default 5)
1818
- `DRY_RUN` (default false)
1919
- `BASE_BRANCH_OVERRIDE` (optional)
20+
- `MAX_CHANGED_FILES` (default 20)
21+
- `MAX_CHANGED_LINES` (default 1000)
22+
- `ALLOW_UNVERIFIED` (default false; approved exceptions only)
2023

24+
25+
## GitHub authorization
26+
The scheduled workflow requires an `ORG_AUTOPILOT_TOKEN` secret backed by a short-lived GitHub App installation token or fine-grained token. Grant access only to opted-in repositories with Issues and Pull requests write plus Contents write. The repository-scoped `GITHUB_TOKEN` cannot perform cross-repository control-plane mutations.
2127
## Behavior
2228
- Only processes issues labeled `autofix` + `queued`
2329
- Skips `risky` and `needs-design`
24-
- Attempts best-effort verification
30+
- Requires a supported verification command unless an approved exception sets `ALLOW_UNVERIFIED=true`
2531
- Opens a PR if changes are safe and tests pass
2632

2733
## Logging
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# F-01 label-gated intake
2+
3+
Issue Description: Unlabeled issues bypassed the documented automation authorization gate.
4+
State: The operator searched for all open unlabeled issues and promoted them into the queue.
5+
Action: Removed unlabeled-issue discovery and automatic promotion.
6+
Result: Only issues explicitly labeled `autofix` and `queued` are executable.
7+
Diff Patch: Removed the `no:label` query and unlabeled issue mutation block.
8+
Rationale: Explicit opt-in labels are a security boundary.

0 commit comments

Comments
 (0)