Skip to content

Commit d36c58a

Browse files
committed
Address security review: harden disallowedTools, scan-secrets, and docs
Addresses review feedback from waynesun09 on PR #189. disallowedTools (H1, M1): Add bare-form (zero-arg) variants for all blocked commands, add gh api blocking, add git reset --hard and git rebase. Documented that disallowedTools is belt-and-suspenders — sandbox network policy and post-script validation are the load-bearing layers. scan-secrets (H3, M3, L1): Pin gitleaks version with SHA256 checksum verification for all supported platforms. Add tar extraction error handling. Fix pre-commit fallback to require the gitleaks-specific hook rather than falling through to generic hooks. Add sha256sum/shasum portability for macOS. architecture.md (M2, I2): Document the four-layer defense-in-depth model, three-layer secret scanning architecture, and protected-path enforcement via post-script. skill (M4, I3, L2, L3): Add MAX_RETRIES env var for retry limit. Add multi-run branch reuse guidance for review-rejected commits. Add partial work section. Remove constraint duplication — skill defers to agent definition as authoritative. H2 (protected-path enforcement in post-script) deferred to PR #208. Sandbox network policy and harness timeout deferred to PR #231. Signed-off-by: Adam Scerra <ascerra@redhat.com> Made-with: Cursor
1 parent 5a2173e commit d36c58a

4 files changed

Lines changed: 99 additions & 22 deletions

File tree

‎agents/code.md‎

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,19 @@ description: >-
44
Implementation specialist for GitHub issues. Reads triaged issues, implements
55
fixes following repo conventions, runs tests and linters, and commits to a
66
feature branch. Use when implementing a fix or feature from a triaged issue.
7-
disallowedTools: Bash(sed *), Bash(awk *), Bash(git push *), Bash(git add -A *), Bash(git add --all *), Bash(git add . *), Bash(git commit --amend *), Bash(gh pr create *), Bash(gh pr edit *), Bash(gh pr merge *), Bash(gh issue edit *), Bash(gh issue comment *)
7+
disallowedTools: >-
8+
Bash(sed *), Bash(sed),
9+
Bash(awk *), Bash(awk),
10+
Bash(git push *), Bash(git push),
11+
Bash(git add -A *), Bash(git add -A),
12+
Bash(git add --all *), Bash(git add --all),
13+
Bash(git add . *), Bash(git add .),
14+
Bash(git commit --amend *), Bash(git commit --amend),
15+
Bash(git reset --hard *), Bash(git reset --hard),
16+
Bash(git rebase *), Bash(git rebase),
17+
Bash(gh pr create *), Bash(gh pr edit *), Bash(gh pr merge *),
18+
Bash(gh issue edit *), Bash(gh issue comment *),
19+
Bash(gh api *)
820
model: opus
921
skills:
1022
- code-implementation

‎docs/architecture.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,11 @@ The code agent definition ([`agents/code.md`](../agents/code.md)) and its implem
226226

227227
**Stream editors blocked.** `sed` and `awk` are disallowed for source file modification. The agent must use structured Write/Edit tools, which produce cleaner diffs and avoid silent corruption from regex misfires.
228228

229-
**Protected paths.** The agent cannot modify CODEOWNERS, CI workflows, agent configurations, harness definitions, sandbox policies, or pre/post scripts — preserving the integrity of its own guardrails.
229+
**Protected paths.** The agent cannot modify CODEOWNERS, CI workflows, agent configurations, harness definitions, sandbox policies, or pre/post scripts — preserving the integrity of its own guardrails. Protected-path enforcement is layered: the agent definition declares which paths are off-limits, `disallowedTools` catches obvious violations at the tool level, and the post-script independently verifies that no protected files appear in the commit diff before pushing. The post-script check is the authoritative gate — the agent cannot be trusted to police its own constraints.
230+
231+
**Defense-in-depth model.** No single enforcement layer is airtight. Security relies on the combination of four layers: (1) agent instructions (prose constraints the model follows), (2) `disallowedTools` patterns (belt-and-suspenders catch for obvious violations — pattern-matching has known limitations around argument reordering and subshell wrapping), (3) sandbox network policy (the authoritative control for preventing exfiltration and unauthorized API access), and (4) post-script validation (independent verification of the agent's output before it leaves the sandbox boundary). Sandbox-level enforcement and post-script validation are the load-bearing layers; agent instructions and `disallowedTools` are defense-in-depth.
232+
233+
**Three-layer secret scanning.** Secret scanning runs at three points: (1) a file-level scan during development (step 9a — early warning), (2) a staged-content scan before commit (step 10b — catches anything that slipped through or changed after the first scan), and (3) a post-script scan before push (the authoritative gate, independent of the agent). Layers 1 and 2 run inside the sandbox under agent control; layer 3 runs outside the sandbox under the automation layer's control.
230234

231235
### 9. PR sandbox / CI mirror
232236

‎scripts/scan-secrets‎

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,38 @@
22
# Run secret scanning against specified files or the current staging area.
33
#
44
# Usage:
5-
# scan-secrets file1 [file2 ...] Stage files, scan, unstage (for step 8a)
6-
# scan-secrets --staged Scan already-staged files in place (for step 9b)
5+
# scan-secrets file1 [file2 ...] Stage files, scan, unstage (for step 9a)
6+
# scan-secrets --staged Scan already-staged files in place (for step 10b)
77
#
88
# Self-bootstrapping: if gitleaks is not on PATH, the script downloads it
99
# to a temporary directory. Works on any Linux/macOS runner (GitHub Actions,
1010
# Tekton, GitLab CI, local) — requires only curl or wget plus tar.
1111
#
12-
# Prefers gitleaks (protect --staged); falls back to pre-commit hooks if
13-
# a .pre-commit-config.yaml exists in the repo.
12+
# Prefers gitleaks (protect --staged); falls back to the gitleaks
13+
# pre-commit hook if a .pre-commit-config.yaml exists in the repo.
1414
# Exits non-zero if secrets are detected or no scanner can be obtained.
1515
set -euo pipefail
1616

17-
GITLEAKS_VERSION="${GITLEAKS_VERSION:-8.30.1}"
17+
# Self-bootstrap version — used only when gitleaks is not already on PATH
18+
# (e.g. local dev, CI without a pre-built image). When running inside a
19+
# sandbox image, the image-provided gitleaks takes precedence and this
20+
# version is never consulted. To update: bump the version here and
21+
# refresh the checksums from the gitleaks release page.
22+
GITLEAKS_VERSION="8.30.1"
23+
24+
# Pinned checksums for the supported platforms. Source:
25+
# https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_checksums.txt
26+
declare -A GITLEAKS_SHA256=(
27+
[linux_x64]="551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb"
28+
[linux_arm64]="e4a487ee7ccd7d3a7f7ec08657610aa3606637dab924210b3aee62570fb4b080"
29+
[darwin_x64]="dfe101a4db2255fc85120ac7f3d25e4342c3c20cf749f2c20a18081af1952709"
30+
[darwin_arm64]="b40ab0ae55c505963e365f271a8d3846efbc170aa17f2607f13df610a9aeb6a5"
31+
)
1832

1933
# --- locate or install gitleaks -------------------------------------------
2034

2135
resolve_gitleaks() {
36+
# Prefer a pre-installed binary (e.g. baked into a sandbox image).
2237
if command -v gitleaks &>/dev/null; then
2338
echo "gitleaks"
2439
return
@@ -40,7 +55,14 @@ resolve_gitleaks() {
4055
*) echo "error: unsupported architecture $(uname -m)" >&2; return 1 ;;
4156
esac
4257

43-
local url="https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_${os}_${arch}.tar.gz"
58+
local platform_key="${os}_${arch}"
59+
local expected_sha="${GITLEAKS_SHA256[${platform_key}]:-}"
60+
if [[ -z "${expected_sha}" ]]; then
61+
echo "error: no pinned checksum for ${platform_key}" >&2
62+
return 1
63+
fi
64+
65+
local url="https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_${platform_key}.tar.gz"
4466
local tmp
4567
tmp="$(mktemp -d)"
4668
trap 'rm -rf "${tmp}"' RETURN
@@ -54,7 +76,26 @@ resolve_gitleaks() {
5476
return 1
5577
fi
5678

57-
tar -xzf "${tmp}/gitleaks.tar.gz" -C "${tmp}" gitleaks
79+
local actual_sha
80+
if command -v sha256sum &>/dev/null; then
81+
actual_sha="$(sha256sum "${tmp}/gitleaks.tar.gz" | cut -d' ' -f1)"
82+
elif command -v shasum &>/dev/null; then
83+
actual_sha="$(shasum -a 256 "${tmp}/gitleaks.tar.gz" | cut -d' ' -f1)"
84+
else
85+
echo "error: no sha256 tool available (need sha256sum or shasum)" >&2
86+
return 1
87+
fi
88+
if [[ "${actual_sha}" != "${expected_sha}" ]]; then
89+
echo "error: gitleaks tarball checksum mismatch — possible tampering" >&2
90+
echo " expected: ${expected_sha}" >&2
91+
echo " actual: ${actual_sha}" >&2
92+
return 1
93+
fi
94+
95+
tar -xzf "${tmp}/gitleaks.tar.gz" -C "${tmp}" gitleaks || {
96+
echo "error: failed to extract gitleaks from tarball" >&2
97+
return 1
98+
}
5899
mkdir -p "${cache_dir}"
59100
mv "${tmp}/gitleaks" "${cached}"
60101
chmod +x "${cached}"
@@ -105,10 +146,10 @@ if [[ -n "${GITLEAKS}" ]]; then
105146
scan_exit=1
106147
fi
107148
else
108-
if ! pre-commit run gitleaks --files "${files[@]}" 2>/dev/null; then
109-
if ! pre-commit run --files "${files[@]}"; then
110-
scan_exit=1
111-
fi
149+
# Pre-commit fallback: only accept a gitleaks hook, not generic hooks.
150+
if ! pre-commit run gitleaks --files "${files[@]}"; then
151+
echo "error: pre-commit gitleaks hook failed or not found — cannot verify secrets" >&2
152+
scan_exit=1
112153
fi
113154
fi
114155

‎skills/code-implementation/SKILL.md‎

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ Commands you will need during this procedure:
2828
- `pre-commit run --files <files>` — linting and secret scanning
2929
- `go build ./...`, `go vet ./...` — compilation checks
3030

31-
Use `Read`/`Write`/`Grep`/`Glob` for file operations. Do not use `sed` or
32-
`awk` for edits.
31+
Use `Read`/`Write`/`Grep`/`Glob` for file operations.
3332

3433
### Secret scanning
3534

@@ -97,7 +96,7 @@ code before relying on it.
9796
### 3. Discover repo conventions
9897

9998
Before writing any code, understand how this repository works. Use `Read`
100-
and `Glob` — not `cat` or `ls` — to inspect project configuration:
99+
and `Glob` to inspect project configuration:
101100

102101
1. **Read project-level instructions.** Use `Read` on `CLAUDE.md`,
103102
`CONTRIBUTING.md`, and `AGENTS.md` (if they exist).
@@ -131,7 +130,12 @@ issue from a previous run:
131130
git branch -a | grep "agent/<number>-"
132131
```
133132

134-
**If a branch exists:** Check it out and work on top of it.
133+
**If a branch exists:** Check it out and work on top of it. Previous runs
134+
may have left commits that were later rejected by the review agent. Before
135+
building on top, read the existing commits (`git log --oneline origin/<target>..HEAD`)
136+
and understand the delta from the target branch. If a previous commit
137+
introduced a problem the review agent flagged, fix it in your new commit
138+
rather than amending.
135139

136140
**If no branch exists:** Proceed to step 5.
137141

@@ -198,8 +202,10 @@ Before writing code, form a concrete plan:
198202
4. **Follow cross-repo references** — if the issue, docs, or triage comments
199203
link to other repos (e.g., an e2e test suite, a dependent service, a
200204
related PR in another repo), read those references to understand the full
201-
picture. Use `gh issue view`, `gh pr view`, or
202-
`gh api repos/{owner}/{repo}/contents/{path}` to fetch what you need.
205+
picture. Use `gh issue view`, `gh pr view`, or `gh pr diff` to fetch
206+
what you need. For files in other repos that are not part of an issue
207+
or PR, use `Read` on a local clone if available, or note the gap in
208+
your plan and proceed with the context you have.
203209
Do not chase every import — focus on references that the issue context
204210
points you toward.
205211
5. **Identify what to change** — list the specific files and functions you will
@@ -258,7 +264,12 @@ make lint # or: pre-commit run --files <changed-files>
258264
1. Read the failure output. Identify the root cause.
259265
2. Fix the issue in your implementation. Do not weaken or skip tests.
260266
3. Re-run secret scan (9a), then tests. This consumes one retry iteration.
261-
4. Repeat until tests pass or the retry limit (default: 2) is reached.
267+
4. Repeat until tests pass or the retry limit is reached.
268+
269+
The retry limit is read from the `MAX_RETRIES` environment variable
270+
(default: 2 if unset). The harness may also enforce a hard timeout
271+
independently — if the harness kills the session, your retry count is
272+
irrelevant.
262273

263274
If the retry limit is reached and tests still fail, do not commit. Stop.
264275

@@ -274,8 +285,8 @@ Read every line. Check for:
274285

275286
- Changes that don't serve the issue (scope creep, unrelated formatting)
276287
- Accidental artifacts: debug prints, commented-out code, TODO comments
277-
- Forbidden files in the diff: `.env`, `*.pem`, `*.key`, `credentials.json`,
278-
`CODEOWNERS`, `.github/workflows/`
288+
- Secret material: `.env`, `*.pem`, `*.key`, `credentials.json`
289+
- Protected-path files (see agent definition for the authoritative list)
279290

280291
If you added more than necessary, revert the extras before staging.
281292

@@ -338,6 +349,15 @@ it also fails on the base branch before skipping it.
338349
**Do not push the branch.** The post-script handles pushing, PR creation,
339350
and failure reporting.
340351

352+
## Partial work
353+
354+
If you hit a token limit or context window boundary before completing the
355+
implementation, and the tests pass on the partial work: commit what you have.
356+
The review agent downstream will evaluate completeness — incomplete-but-passing
357+
code is caught at the review stage, not the implementation stage. The commit
358+
message should note that the work is partial (e.g., "partial implementation"
359+
in the description) so the review agent and post-script can act accordingly.
360+
341361
## Constraints
342362

343363
The agent definition (`agents/code.md`) is the authoritative list of

0 commit comments

Comments
 (0)