Skip to content

Add ValidateHeredocContent checks to 5 user-controlled heredoc sites#25510

Merged
lpcox merged 5 commits intomainfrom
copilot/fix-heredoc-delimiter-validation
Apr 9, 2026
Merged

Add ValidateHeredocContent checks to 5 user-controlled heredoc sites#25510
lpcox merged 5 commits intomainfrom
copilot/fix-heredoc-delimiter-validation

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 9, 2026

Five heredoc call sites embedding user-controlled content (frontmatter scripts) lack ValidateHeredocContent checks. While practical exploitation is infeasible (content is included in FrontmatterHash, creating a fixed-point problem with HMAC-SHA256), adding validation is zero-cost defense-in-depth.

Changes

  • strings.go — Add ValidateHeredocContent(content, delimiter) with explicit empty-delimiter guard
  • mcp_setup_generator.go — Validate before writing user scripts for all 4 MCP script types (JS, SH, PY, GO)
  • compiler_safe_outputs_job.go — Validate safe-output custom scripts; change buildCustomScriptFilesStep return to ([]string, error) for propagation
  • strings_test.go / safe_scripts_test.go — Tests for new function and updated signature
jsDelimiter := GenerateHeredocDelimiterFromSeed("MCP_SCRIPTS_JS_"+strings.ToUpper(toolName), workflowData.FrontmatterHash)
if err := ValidateHeredocContent(toolScript, jsDelimiter); err != nil {
    return fmt.Errorf("mcp-scripts tool %q (js): %w", toolName, err)
}

Same pattern applied at all 5 sites per the heredoc call site inventory in the issue.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • https://api.github.com/graphql
    • Triggering command: /usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw -trimpath /opt/hostedtoolc--show-toplevel git rev-�� --show-toplevel /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linuTest User /usr/bin/git -bool -buildtags ache/go/1.25.8/x--show-toplevel git (http block)
    • Triggering command: /usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw ../../../.pretti-c /usr/bin/git go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh /usr/bin/gh api graphql -f query=query($owner: String!, $name: String!) { repository(owner: $owner, name: $name) { hasDiscussionsEnabled } } -f owner=github -f name=gh-aw ../../../.pretti-c /usr/bin/bash go env -json GO111MODULE de GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/orgs/test-owner/actions/secrets
    • Triggering command: /usr/bin/gh gh api /orgs/test-owner/actions/secrets --jq .secrets[].name 102639789/001' 102639789/001' -importcfg /tmp/go-build3993802964/b414/importcfg -pack /home/REDACTED/work/gh-aw/gh-aw/pkg/gitutil/gitutil.go /home/REDACTED/work/gh-aw/gh-aw/pkg/gitutil/gitutil_test.go env heck '**/*.cjs' -p GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/actions/ai-inference/git/ref/tags/v1
    • Triggering command: /usr/bin/gh gh api /repos/actions/ai-inference/git/ref/tags/v1 --jq .object.sha --show-toplevel 64/pkg/tool/linux_amd64/compile /usr/bin/git g_.a GO111MODULE x_amd64/compile git rev-�� --show-toplevel x_amd64/compile /usr/bin/git 5592251/b029/_pkgit PSlwfWm62 ache/go/1.25.8/x--show-toplevel git (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v3
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v3 --jq .object.sha k/gh-aw/gh-aw/.github/workflows/approach-validator.md /tmp/go-build3993802964/b426/_testmain.go /usr/bin/git -json GO111MODULE x_amd64/cgo git rev-�� --show-toplevel x_amd64/cgo /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/vet -json GO111MODULE x_amd64/compile /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/vet (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v5
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha efaultBranchFromLsRemoteWithReal-test.timeout=10m0s efaultBranchFromLsRemoteWithReal-test.run=^Test .cfg GOINSECURE 5592251/b011/ GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linuorigin env Onlyrepos_only_without_min-integrity2626727607/001 GO111MODULE ache/go/1.25.8/x64/pkg/tool/linux_amd64/vet GOINSECURE 5592251/b011/memrev-parse ache/go/1.25.8/x--show-toplevel ache/go/1.25.8/x64/pkg/tool/linux_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha --show-toplevel ache/go/1.25.8/x64/pkg/tool/linuTest User /usr/bin/git p6K5uGVr_ rg/x/mod@v0.34.0rev-parse ache/go/1.25.8/x--show-toplevel git rev-�� --show-toplevel ortcfg /usr/bin/git 2343923193 g/logger/logger.rev-parse 64/pkg/tool/linu--show-toplevel git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha --show-toplevel 64/pkg/tool/linux_amd64/vet /usr/bin/git HEAD:.github/worgit o3KgZaglTou9 /usr/bin/git git rev-�� --show-toplevel git /usr/bin/git erignore go .cfg git (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v6
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha GOMODCACHE 64/pkg/tool/linu-trimpath /usr/bin/gh -json rotocol/go-sdk@vrev-parse 64/pkg/tool/linu--show-toplevel gh run view 12345 /usr/bin/git nonexistent/repogit --json status,conclusio--show-toplevel git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha --show-toplevel 64/pkg/tool/linux_amd64/compile /usr/lib/git-core/git-remote-https g_.a GO111MODULE 64/pkg/tool/linu--show-toplevel /usr/lib/git-core/git-remote-https orig�� REDACTED 64/pkg/tool/linumyorg /usr/bin/git g_.a DW6KATJ4J 64/pkg/tool/linu--show-toplevel git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha uest|push_to_pull_request_branch)" git /usr/bin/git ithub-script/gitgit rev-parse /usr/bin/git git rev-�� --show-toplevel git /usr/bin/git /tmp/gh-aw-test-git rev-parse /usr/bin/git git (http block)
  • https://api.github.com/repos/actions/github-script/git/ref/tags/v8
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha go1.25.8 -c=4 -nolocalimports -importcfg /tmp/go-build3515592251/b240/importcfg -pack /home/REDACTED/go/pkg/mod/golang.org/x/text@v0.35.0/language/coverage.go env -json go 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE erignore m/_n�� sm-opt -Oz (size optimization)..."; \ BEFORE=$(wc -c < gh-aw.wasm); \ wasm-opt -Oz --enable-bugit GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE g/sig_other.s env tformat GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
  • https://api.github.com/repos/actions/setup-go/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v4 --jq .object.sha github.event.inputs.branch 64/pkg/tool/linuconfig /usr/bin/git mLsRemoteWithReagit mLsRemoteWithRearev-parse 64/pkg/tool/linu--show-toplevel git arch�� --remote=REDACTED v1.0.0 /usr/bin/git g_.a GO111MODULE 64/pkg/tool/linu--show-toplevel git (http block)
  • https://api.github.com/repos/actions/setup-node/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v4 --jq .object.sha --show-toplevel 64/pkg/tool/linux_amd64/asm /usr/bin/infocmp -json GO111MODULE 64/pkg/tool/linu--show-toplevel infocmp -1 xterm-color 64/pkg/tool/linux_amd64/compile /usr/bin/git ut146554561/001 GO111MODULE 64/pkg/tool/linu--show-toplevel git (http block)
  • https://api.github.com/repos/actions/upload-artifact/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v4 --jq .object.sha git-upload-pack '/tmp/TestParseDefaultBranchFromLsRemoteWithReal-test.timeout=10m0s (http block)
  • https://api.github.com/repos/astral-sh/setup-uv/git/ref/tags/eac588ad8def6316056a12d4907a9d4d84ff7a3b
    • Triggering command: /usr/bin/gh gh api /repos/astral-sh/setup-uv/git/ref/tags/eac588ad8def6316056a12d4907a9d4d84ff7a3b --jq .object.sha -json GO111MODULE $name) { hasDiscussionsEnabled } } GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/astral-sh/setup-uv/git/ref/tags/eac588ad8def6316056a12d4907a9d4d84ff7a3b --jq .object.sha h ../../../.prettierignore GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw --jq .visibility -json GO111MODULE tions/setup/node-f GOINSECURE GOMOD GOMODCACHE go 1/x6�� iew.lock.yml GO111MODULE r: $owner, name: $name) { hasDiscussionsEnabled } } GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw-actions/git/ref/tags/v0.1.2
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v0.1.2 --jq .object.sha --show-toplevel 64/pkg/tool/linux_amd64/asm /usr/bin/gh mLsRemoteWithReagit mLsRemoteWithRearev-parse 64/pkg/tool/linu--show-toplevel gh run download 5 /usr/bin/git test-logs/run-5 GO111MODULE 64/pkg/tool/linu--show-toplevel git (http block)
  • https://api.github.com/repos/github/gh-aw-actions/git/ref/tags/v1.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.0.0 --jq .object.sha -m (http block)
  • https://api.github.com/repos/github/gh-aw-actions/git/ref/tags/v1.2.3
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw-actions/git/ref/tags/v1.2.3 --jq .object.sha -stringintconv -tests /usr/bin/git -json GO111MODULE 64/bin/go git -C /tmp/TestGuardPolicyBlockedUsersCommaSeparatedCoOUTPUT remote clusion,workflowName,createdAt,startedAt,updated168.63.129.16 heck '**/*.cjs' git 90 64/bin/go /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linuconntrack (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/1/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/1/artifacts --jq .artifacts[].name rotocol/go-sdk@v-nolocalimports 64/pkg/tool/linu-importcfg GOINSECURE (http block)
    • Triggering command: /usr/bin/gh gh run download 1 --dir test-logs/run-1 kVjpTbR-V 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE ylQP4Z8/vCNYLdc7D8RXanEmFBss env g_.a GO111MODULE k GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linu/tmp/file-tracker-test1026952292/test2.lock.yml (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/12345/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/12345/artifacts --jq .artifacts[].name 0/internal/catmsg/catmsg.go 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD abis 64/pkg/tool/linux_amd64/compile env g_.a cuiX4cYAj ache/go/1.25.8/x64/pkg/tool/linux_amd64/asm GOINSECURE r GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linuremote.origin.url (http block)
    • Triggering command: /usr/bin/gh gh run download 12345 --dir test-logs/run-12345 ri/jsonschema/v6@v6.0.2/kind/kind.go 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/compile env g_.a QuPWq4ACQ ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE chema/v6 GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/12346/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/12346/artifacts --jq .artifacts[].name 0/internal/number/common.go 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD abis 64/pkg/tool/linux_amd64/compile env g_.a GO111MODULE ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE _wasm.o 64/src/reflect/aadd ache/go/1.25.8/xorigin (http block)
    • Triggering command: /usr/bin/gh gh run download 12346 --dir test-logs/run-12346 4LOc7tzcC 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/compile env 3578736632 64jHUho52 ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linu-buildtags (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/2/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/2/artifacts --jq .artifacts[].name rotocol/go-sdk@v1.5.0/auth/auth.go 64/pkg/tool/linux_amd64/compile GOINSECURE o8601 GOMODCACHE 64/pkg/tool/linux_amd64/compile env 612881359 GO111MODULE ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE go-sdk/mcp GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linu-trimpath (http block)
    • Triggering command: /usr/bin/gh gh run download 2 --dir test-logs/run-2 NgK5Xenpy ache/go/1.25.8/x64/bin/go GOINSECURE l/errors GOMODCACHE go env SaeZgGgAk GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/3/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/3/artifacts --jq .artifacts[].name GO111MODULE 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD abis 64/pkg/tool/linux_amd64/compile env ility-kit.md ortcfg ache/go/1.25.8/x64/pkg/tool/linu-nilfunc GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linu-tests (http block)
    • Triggering command: /usr/bin/gh gh run download 3 --dir test-logs/run-3 sYYP-7R33 64/pkg/tool/linux_amd64/cgo GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/cgo env ortcfg GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD abis 64/pkg/tool/linux_amd64/vet (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/4/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/4/artifacts --jq .artifacts[].name rotocol/go-sdk@v-c=4 64/pkg/tool/linu-nolocalimports GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linu/tmp/go-build3993802964/b444/_testmain.go env g_.a GO111MODULE ache/go/1.25.8/x64/pkg/tool/linux_amd64/asm GOINSECURE er GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linu-importcfg (http block)
    • Triggering command: /usr/bin/gh gh run download 4 --dir test-logs/run-4 GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet ortc�� -json uKJh7UXOD ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linu-importcfg (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/5/artifacts
    • Triggering command: /usr/bin/gh gh api --paginate repos/{owner}/{repo}/actions/runs/5/artifacts --jq .artifacts[].name GO111MODULE 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD abis 64/pkg/tool/linux_amd64/compile env plorer.md GO111MODULE ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE flow GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh run download 5 --dir test-logs/run-5 GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE or_wasm.o 64/src/math/floor_wasm.s 64/pkg/tool/linux_amd64/vet env 4045047072/.github/workflows GO111MODULE x_amd64/vet GOINSECURE th_wasm.o 64/src/math/big/--show-toplevel x_amd64/vet (http block)
  • https://api.github.com/repos/github/gh-aw/actions/workflows
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env heck '**/*.cjs' -p go 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh run list --json databaseId,number,url,status,conclusion,workflowName,createdAt,startedAt,updatedAt,event,headBranch,headSha,displayTitle --workflow nonexistent-workflow-12345 --limit 100 GOMOD GOMODCACHE x_amd64/compile env -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh run list --json databaseId,number,url,status,conclusion,workflowName,createdAt,startedAt,updatedAt,event,headBranch,headSha,displayTitle --workflow nonexistent-workflow-12345 --limit 6 GOMOD GOMODCACHE 64/pkg/tool/linuremote.myorg.url env g_.a JytyNgNV_ ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linuTest User (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v0.47.4
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v0.47.4 --jq .object.sha --show-toplevel ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile /usr/bin/git 5592251/b236/_pkgit NNuM/NZNs7zEf3uyrev-parse 64/pkg/tool/linu--show-toplevel git rev-�� --show-toplevel 64/pkg/tool/linux_amd64/vet /usr/bin/git artifacts-summargit -trimpath /opt/hostedtoolc--show-toplevel git (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v1.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.0.0 --jq .object.sha g_.a LamLkoYmy 64/pkg/tool/linux_amd64/link GOINSECURE age GOMODCACHE 64/pkg/tool/linux_amd64/link env eutil.test GO111MODULE ortcfg.link GOINSECURE eader GOMODCACHE ucaT9ZeFc2kAZlPnX2/LKPpO0EjZIaEYtest@example.com (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v1.2.3
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.2.3 --jq .object.sha -json GO111MODULE x_amd64/asm GOINSECURE GOMOD GOMODCACHE x_amd64/asm env -json go x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v2.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v2.0.0 --jq .object.sha -json GO111MODULE x_amd64/asm GOINSECURE GOMOD GOMODCACHE x_amd64/asm env -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v2.0.0 --jq .object.sha go1.25.8 -c=4 -nolocalimports -importcfg /tmp/go-build3993802964/b422/importcfg -pack /tmp/go-build3993802964/b422/_testmain.go env -json color.go x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v2.0.0 --jq .object.sha -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile 0754�� -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
  • https://api.github.com/repos/github/gh-aw/git/ref/tags/v3.0.0
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v3.0.0 --jq .object.sha -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile 0754�� -json exer.go x_amd64/asm GOINSECURE GOMOD GOMODCACHE x_amd64/asm (http block)
  • https://api.github.com/repos/githubnext/agentics/git/ref/tags/-
    • Triggering command: /usr/bin/gh gh api /repos/githubnext/agentics/git/ref/tags/- --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/nonexistent/action/git/ref/tags/v999.999.999
    • Triggering command: /usr/bin/gh gh api /repos/nonexistent/action/git/ref/tags/v999.999.999 --jq .object.sha g_.a GO111MODULE ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/xtest@example.com ortc�� 57/001/test-empty-frontmatter.md ASggpb24G ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/x^remote\..*\.gh-resolved$ (http block)
  • https://api.github.com/repos/nonexistent/repo/actions/runs/12345
    • Triggering command: /usr/bin/gh gh run view 12345 --repo nonexistent/repo --json status,conclusion GOINSECURE _wasm.o 64/src/runtime/s--show-toplevel 64/pkg/tool/linu/home/REDACTED/work/gh-aw/gh-aw/scripts/lint_error_messages_test.go env g_.a Q8gElMZ6A ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile (http block)
  • https://api.github.com/repos/owner/repo/actions/workflows
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path --repo owner/repo 64/bin/go GOINSECURE GOMOD GOMODCACHE go env heck '**/*.cjs' -p GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path --repo owner/repo 64/bin/go GOINSECURE GOMOD GOMODCACHE go ache�� -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE erignore (http block)
  • https://api.github.com/repos/owner/repo/contents/file.md
    • Triggering command: /tmp/go-build3993802964/b397/cli.test /tmp/go-build3993802964/b397/cli.test -test.testlogfile=/tmp/go-build3993802964/b397/testlog.txt -test.paniconexit0 -test.v=true -test.parallel=4 -test.timeout=10m0s -test.run=^Test -test.short=true -nolocalimports -importcfg /tmp/go-build3515592251/b219/importcfg -pack env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/test-owner/test-repo/actions/secrets
    • Triggering command: /usr/bin/gh gh api /repos/test-owner/test-repo/actions/secrets --jq .secrets[].name -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env heck '**/*.cjs' -p GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE y.s (http block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI and others added 2 commits April 9, 2026 17:31
…d heredoc sites

Add ValidateHeredocContent() function to validate that user-controlled content
does not contain the heredoc delimiter before embedding in shell heredocs.

Wire validation into 5 previously unvalidated user-controlled heredoc sites:
- mcp_setup_generator.go: JS, SH, PY, GO script tools (4 sites)
- compiler_safe_outputs_job.go: safe-output custom scripts (1 site)

Update buildCustomScriptFilesStep to return error for validation propagation.
Add comprehensive unit tests for ValidateHeredocContent.

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/01237308-eefd-47d6-b257-83791da040a8

Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix incomplete heredoc delimiter validation for user-controlled sites Add ValidateHeredocContent checks to 5 user-controlled heredoc sites Apr 9, 2026
Copilot AI requested a review from lpcox April 9, 2026 17:33
Copilot finished work on behalf of lpcox April 9, 2026 17:33
@lpcox lpcox marked this pull request as ready for review April 9, 2026 17:59
Copilot AI review requested due to automatic review settings April 9, 2026 17:59
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds defense-in-depth validation to prevent heredoc delimiter injection when embedding user-influenced script content into generated GitHub Actions YAML.

Changes:

  • Added ValidateHeredocContent(content, delimiter) helper (with empty-delimiter guard).
  • Added validation before writing MCP per-tool scripts (JS/SH/PY/GO) into heredocs.
  • Added validation and error propagation for safe-outputs custom scripts heredoc generation, updating the helper signature and tests accordingly.
Show a summary per file
File Description
pkg/workflow/strings.go Adds ValidateHeredocContent helper used by heredoc-emitting generators.
pkg/workflow/mcp_setup_generator.go Validates per-tool MCP scripts before embedding into heredocs.
pkg/workflow/compiler_safe_outputs_job.go Validates safe-output custom script content and propagates errors via updated return signature.
pkg/workflow/strings_test.go Adds unit tests for ValidateHeredocContent.
pkg/workflow/safe_scripts_test.go Updates tests for new buildCustomScriptFilesStep signature and error behavior.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 5/5 changed files
  • Comments generated: 3

Comment on lines +299 to +305
// ValidateHeredocContent checks that content does not contain the heredoc delimiter.
// If the content contains the delimiter on its own line, an attacker could prematurely
// close the heredoc and inject arbitrary shell commands.
//
// Callers that wrap user-influenced content (e.g. the markdown body, frontmatter scripts)
// MUST call ValidateHeredocContent before embedding that content in a heredoc.
//
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring says injection is only possible when the delimiter appears on its own line, but the implementation rejects any occurrence of the delimiter as a substring. Either update the comment to match the stricter behavior, or change the check to only match a full delimiter line (e.g., at start of string or preceded by \n, and followed by \n or end of string).

Copilot uses AI. Check for mistakes.
Comment on lines +310 to +316
func ValidateHeredocContent(content, delimiter string) error {
if delimiter == "" {
return fmt.Errorf("heredoc delimiter cannot be empty")
}
if strings.Contains(content, delimiter) {
return fmt.Errorf("content contains heredoc delimiter %q — possible injection attempt", delimiter)
}
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ValidateHeredocContent only validates the content, but the delimiter itself can be user-influenced via GenerateHeredocDelimiterFromSeed(name, ...) where name is derived from user-controlled identifiers (e.g., mcp-scripts tool names / safe-output script names). Since call sites embed the delimiter inside single quotes (<< 'DELIM'), a delimiter containing a single quote or newline would break the generated shell/YAML and could reintroduce injection risk. Consider rejecting delimiters containing "'", "\n", or "\r" (and possibly other non-printable chars) here as part of validation.

Copilot uses AI. Check for mistakes.
Comment on lines 409 to 416
// JavaScript tool
toolScript := GenerateMCPScriptJavaScriptToolScript(toolConfig)
jsDelimiter := GenerateHeredocDelimiterFromSeed("MCP_SCRIPTS_JS_"+strings.ToUpper(toolName), workflowData.FrontmatterHash)
if err := ValidateHeredocContent(toolScript, jsDelimiter); err != nil {
return fmt.Errorf("mcp-scripts tool %q (js): %w", toolName, err)
}
fmt.Fprintf(yaml, " cat > \"${RUNNER_TEMP}/gh-aw/mcp-scripts/%s.cjs\" << '%s'\n", toolName, jsDelimiter)
for _, line := range FormatJavaScriptForYAML(toolScript) {
Copy link

Copilot AI Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This adds ValidateHeredocContent checks for the per-tool script heredocs, but the earlier tools.json heredoc in generateMCPSetup still writes user-influenced content (tool names/descriptions/inputs) without any validation. If the PR intent is to cover all user-controlled heredoc sites, consider applying the same ValidateHeredocContent(toolsJSON, toolsDelimiter) check there as well (or clarify why it is excluded).

Copilot uses AI. Check for mistakes.
lpcox and others added 2 commits April 9, 2026 11:21
1. Fix docstring: clarify substring match is intentional (stricter than
   shell requires), not a bug.
2. Add ValidateHeredocDelimiter(): reject delimiters containing single
   quotes, newlines, or non-printable chars that could break shell/YAML
   when used in << 'DELIM' syntax.
3. Add ValidateHeredocContent checks for tools.json and mcp-server.cjs
   heredocs in generateMCPSetup (previously uncovered).
4. Add tests for the new delimiter validation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@lpcox lpcox merged commit be20353 into main Apr 9, 2026
57 of 58 checks passed
@lpcox lpcox deleted the copilot/fix-heredoc-delimiter-validation branch April 9, 2026 18:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incomplete heredoc delimiter validation: 5 user-controlled heredoc sites lack ValidateHeredocContent checks

3 participants