feat: add code agent scaffold, image, and push pipeline - #286
Conversation
Integrate the proven code-agent configuration into the fullsend installer scaffold so that `fullsend install` sets up a fully working code agent with bot-authored PRs in any target org. New scaffold files (internal/scaffold/fullsend-repo/): - agents/code.md — agent definition with disallowed tools - env/code-agent.env — sandbox environment (git identity, SSL, timeouts) - harness/code.yaml — harness config with pre/post scripts and runner_env - policies/code.yaml — network policy (Vertex AI, GitHub, registries) - scripts/pre-code.sh — input validation before sandbox creation - scripts/post-code.sh — secret scan, pre-commit, push, and PR creation - scripts/scan-secrets — gitleaks wrapper with SHA256 self-bootstrap - skills/code-implementation/SKILL.md — 10-step implementation procedure Updated scaffold files: - .github/workflows/code.yml — uses installer-provisioned GitHub App token (FULLSEND_CODER_APP_ID / FULLSEND_CODER_APP_PRIVATE_KEY), scoped to the target repo for push access New image build pipeline: - .github/workflows/build-images.yml — builds sandbox base then agent images on push to main or manual dispatch, publishes to GHCR - images/code/Containerfile — extends base sandbox with Go, gitleaks, pre-commit, gitlint, and scan-secrets Tests updated for 22 scaffold files (was 14). Made-with: Cursor
Site previewPreview: https://58d08299-site.fullsend-ai.workers.dev Commit: |
waynesun09
left a comment
There was a problem hiding this comment.
Multi-Agent Review: 4 reviewers (Claude Security, Claude Architecture, Gemini, Cursor)
17 inline findings — 4 critical/must-fix, 2 high, 9 medium, 7 low/info.
Must Fix Before Merge
- Token isolation broken — same GitHub App token in sandbox (
GH_TOKEN) and runner (PUSH_TOKEN);nodebinary can bypassdisallowedToolsvia raw HTTP toapi.github.com .pre-commit-config.yamlnot in protected paths — agent can inject malicious hooks that execute on the runner withPUSH_TOKEN- Hardcoded
mainbranch — breaks repos with different default branches - GitHub Actions outdated —
docker/build-push-action(v6→v7),docker/login-action(v3→v4),docker/metadata-action(v5→v6),docker/setup-buildx-action(v3→v4)
Positive
- Defense-in-depth architecture is strong overall
- SHA256 supply chain pinning for Go and gitleaks is solid
- Zero-trust agent principle well-implemented
- SKILL.md is comprehensive and internally non-contradictory
Full review documents: see Research/fullsend-pr286-code-agent-scaffold-review-summary.md
…aths, hardcoded branch Resolves all findings from the multi-agent security review. Critical/High: - Split single app token into read-only sandbox token (contents:read, issues:read, pull_requests:read) and separate write-enabled push token. Sandbox can no longer push even if disallowedTools is bypassed. - Add .pre-commit-config.yaml and .gitattributes to protected paths — prevents malicious hook injection via agent. - Replace hardcoded origin/main with TARGET_BRANCH (threaded via runner_env, defaults to main). Fixes merge-base, fallback diff, and PR --base for non-main repos. Medium: - Merge-base fallback now tries origin/TARGET_BRANCH..HEAD before HEAD~1 so multi-commit branches are fully checked. - Replace unquoted variable expansion with mapfile array for pre-commit file list (handles spaces/globs safely). - Remove curl from Vertex AI and gitleaks_releases binary allowlists — reduces sandbox attack surface. - Pin pre-commit==4.5.1 on runner, replace silent || true with warning log. - Bump docker actions: login v4, setup-buildx v4, metadata v6, build-push v7. - Use SHA-tagged base image instead of mutable :latest for deterministic agent image builds. - Replace git commit --amend in SKILL.md with git reset --soft HEAD~1 + new commit (amend is blocked by disallowedTools). Low: - Add -- separator to git push for branch name safety. - Add cross-field validation in pre-code.sh (URL repo/number must match REPO_FULL_NAME/ISSUE_NUMBER). - Remove || true from Containerfile pip install — fail fast. - Add TestScanSecretsImageMatchesScaffold drift test. Made-with: Cursor
…aths, hardcoded branch Resolves all findings from the multi-agent security review. Critical/High: - Split single app token into read-only sandbox token (contents:read, issues:read, pull_requests:read) and separate write-enabled push token. Sandbox can no longer push even if disallowedTools is bypassed. - Add .pre-commit-config.yaml and .gitattributes to protected paths — prevents malicious hook injection via agent. - Replace hardcoded origin/main with TARGET_BRANCH (threaded via runner_env, defaults to main). Fixes merge-base, fallback diff, and PR --base for non-main repos. Medium: - Merge-base fallback now tries origin/TARGET_BRANCH..HEAD before HEAD~1 so multi-commit branches are fully checked. - Replace unquoted variable expansion with mapfile array for pre-commit file list (handles spaces/globs safely). - Remove curl from Vertex AI and gitleaks_releases binary allowlists — reduces sandbox attack surface. - Pin pre-commit==4.5.1 on runner, replace silent || true with warning log. - Bump docker actions: login v4, setup-buildx v4, metadata v6, build-push v7. - Use SHA-tagged base image instead of mutable :latest for deterministic agent image builds. - Replace git commit --amend in SKILL.md with git reset --soft HEAD~1 + new commit (amend is blocked by disallowedTools). Low: - Add -- separator to git push for branch name safety. - Add cross-field validation in pre-code.sh (URL repo/number must match REPO_FULL_NAME/ISSUE_NUMBER). - Remove || true from Containerfile pip install — fail fast. - Add TestScanSecretsImageMatchesScaffold drift test. Made-with: Cursor
d1033f1 to
dce7816
Compare
waynesun09
left a comment
There was a problem hiding this comment.
CRITICAL: Token isolation is silently non-functional — the permission_* inputs use underscores but actions/create-github-app-token@v3 requires hyphens. All four permission inputs are silently ignored, causing the sandbox token to inherit full app installation permissions (including contents:write and pull_requests:write). See inline comment for details and fix.
| permission_contents: read | ||
| permission_issues: read | ||
| permission_pull_requests: read | ||
| permission_metadata: read |
There was a problem hiding this comment.
CRITICAL: Permission inputs use wrong delimiter — token isolation is silently broken
These four inputs use underscores (permission_contents) but actions/create-github-app-token@v3 requires hyphens (permission-contents). The permissions are silently ignored, and the sandbox token inherits the full app installation permissions (including contents:write, pull_requests:write).
Evidence chain (verified against action source code):
action.ymldefines inputs with hyphens:permission-contents,permission-issues, etc.- GitHub Actions Runner (
Handler.cs:AddInputsToEnvironment) creates env vars asINPUT_{key.Replace(' ', '_').ToUpperInvariant()}— only spaces are normalized, underscores/hyphens preserved as-is - Action source (
lib/get-permissions-from-inputs.js) scans forkey.startsWith("INPUT_PERMISSION-")(hyphen) - Action tests (
main-token-permissions-set.test.js) confirm hyphen format:process.env["INPUT_PERMISSION-ISSUES"] = "write" - Action README examples:
permission-issues: write
Result: permission_contents → env var INPUT_PERMISSION_CONTENTS (underscore) → does NOT match startsWith("INPUT_PERMISSION-") → permission ignored → token gets full default permissions
Fix:
permission-contents: read
permission-issues: read
permission-pull-requests: read
permission-metadata: readAlso update TestCodeWorkflowContent assertion to match:
assert.Contains(t, s, "permission-contents: read")Found independently by 3/4 review agents (Claude Security, Claude Architecture, orchestrator verification). Gemini and Cursor did not catch this.
There was a problem hiding this comment.
Good catch — fixed. Changed all four inputs from underscores to hyphens:
permission-contents: read
permission-issues: read
permission-pull-requests: read
permission-metadata: readAlso updated TestCodeWorkflowContent assertion to match permission-contents: read.
…aths, hardcoded branch Resolves all findings from the multi-agent security review. Critical/High: - Split single app token into read-only sandbox token (contents:read, issues:read, pull_requests:read) and separate write-enabled push token. Sandbox can no longer push even if disallowedTools is bypassed. - Add .pre-commit-config.yaml and .gitattributes to protected paths — prevents malicious hook injection via agent. - Replace hardcoded origin/main with TARGET_BRANCH (threaded via runner_env, defaults to main). Fixes merge-base, fallback diff, and PR --base for non-main repos. Medium: - Merge-base fallback now tries origin/TARGET_BRANCH..HEAD before HEAD~1 so multi-commit branches are fully checked. - Replace unquoted variable expansion with mapfile array for pre-commit file list (handles spaces/globs safely). - Remove curl from Vertex AI and gitleaks_releases binary allowlists — reduces sandbox attack surface. - Pin pre-commit==4.5.1 on runner, replace silent || true with warning log. - Bump docker actions: login v4, setup-buildx v4, metadata v6, build-push v7. - Use SHA-tagged base image instead of mutable :latest for deterministic agent image builds. - Replace git commit --amend in SKILL.md with git reset --soft HEAD~1 + new commit (amend is blocked by disallowedTools). Low: - Add -- separator to git push for branch name safety. - Add cross-field validation in pre-code.sh (URL repo/number must match REPO_FULL_NAME/ISSUE_NUMBER). - Remove || true from Containerfile pip install — fail fast. - Add TestScanSecretsImageMatchesScaffold drift test. Made-with: Cursor
dce7816 to
9e16018
Compare
waynesun09
left a comment
There was a problem hiding this comment.
All 14 review findings verified as resolved across 3 review rounds using 4 independent agents (Claude Security, Claude Architecture, Gemini, Cursor).
Critical fixes confirmed:
- Token isolation: separate read-only sandbox token (
permission-contents/issues/pull-requests/metadata: read) and write push token — sandbox token never gets write access - Permission delimiter: correctly uses hyphens (
permission-contents) matchingactions/create-github-app-token@v3action.yml inputs, with regression test inTestCodeWorkflowContent
Other fixes confirmed:
.pre-commit-config.yamland.gitattributesadded toPROTECTED_PATHSTARGET_BRANCHthreaded end-to-end (workflow → harness → post-code.sh → PR--base)- Array-safe
mapfile -tfor pre-commit invocation curl/wgetremoved from network policy binaries- Pre-commit pinned
==4.5.1with::warningon install failure - Docker actions bumped (login@v4, buildx@v4, metadata@v6, build-push@v7)
- SHA-tagged base image (
${{ github.sha }}) in CI pipeline - SKILL.md uses
git reset --soft HEAD~1instead of blocked--amend --separator ingit pushprevents branch name option injection- Cross-field validation in
pre-code.sh(URL ↔ REPO_FULL_NAME/ISSUE_NUMBER) - Containerfile fails fast on pip errors (removed
|| true) TestScanSecretsImageMatchesScaffolddrift prevention test added
…aths, hardcoded branch Resolves all findings from the multi-agent security review. Critical/High: - Split single app token into read-only sandbox token (contents:read, issues:read, pull_requests:read) and separate write-enabled push token. Sandbox can no longer push even if disallowedTools is bypassed. - Add .pre-commit-config.yaml and .gitattributes to protected paths — prevents malicious hook injection via agent. - Replace hardcoded origin/main with TARGET_BRANCH (threaded via runner_env, defaults to main). Fixes merge-base, fallback diff, and PR --base for non-main repos. Medium: - Merge-base fallback now tries origin/TARGET_BRANCH..HEAD before HEAD~1 so multi-commit branches are fully checked. - Replace unquoted variable expansion with mapfile array for pre-commit file list (handles spaces/globs safely). - Remove curl from Vertex AI and gitleaks_releases binary allowlists — reduces sandbox attack surface. - Pin pre-commit==4.5.1 on runner, replace silent || true with warning log. - Bump docker actions: login v4, setup-buildx v4, metadata v6, build-push v7. - Use SHA-tagged base image instead of mutable :latest for deterministic agent image builds. - Replace git commit --amend in SKILL.md with git reset --soft HEAD~1 + new commit (amend is blocked by disallowedTools). Low: - Add -- separator to git push for branch name safety. - Add cross-field validation in pre-code.sh (URL repo/number must match REPO_FULL_NAME/ISSUE_NUMBER). - Remove || true from Containerfile pip install — fail fast. - Add TestScanSecretsImageMatchesScaffold drift test. Made-with: Cursor
…ox-images Address review feedback from @ascerra: remove the build-images.yml workflow (from PR fullsend-ai#286) and rename sandbox-image.yml to sandbox-images.yml to reflect that it builds more than one image. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Integrate the proven code-agent configuration into the fullsend installer scaffold so that
fullsend installsets up a fully working code agent with bot-authored PRs in any target org.New scaffold files (internal/scaffold/fullsend-repo/):
Updated scaffold files:
New image build pipeline:
Tests updated for 22 scaffold files (was 14).
Made-with: Cursor