Skip to content

Strict mode: allow secrets.* in step-level with: for uses: action steps#25871

Merged
pelikhan merged 4 commits intomainfrom
copilot/allow-secrets-in-step-level
Apr 12, 2026
Merged

Strict mode: allow secrets.* in step-level with: for uses: action steps#25871
pelikhan merged 4 commits intomainfrom
copilot/allow-secrets-in-step-level

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 12, 2026

Workflows using dedicated GitHub Actions to fetch secrets from external managers (Conjur, HashiCorp Vault, etc.) cannot use strict mode because secrets.* in with: is classified as unsafe — forcing strict: false and disabling all protections.

with: inputs on uses: action steps are passed to the external action, not interpolated into shell scripts, and the runner masks them identically to env: bindings. This PR extends the secret classifier to treat them as safe.

Core change

In classifyStepSecrets(), when a step has a uses: field, with: secrets in well-formed maps are classified as safe (same as env:). All safety guards carry over:

  • Malformed with: (string/slice instead of map) → still unsafe
  • with: without uses: → still unsafe
  • GITHUB_ENV write detection → reclassifies to unsafe
# ✅ Now allowed in strict mode
steps:
  - uses: my-org/secrets-action@v2
    with:
      username: ${{ secrets.VAULT_USERNAME }}
      password: ${{ secrets.VAULT_PASSWORD }}

Changes

  • strict_mode_steps_validation.goclassifyStepSecrets() checks for uses: field; treats with: map secrets as safe for action steps. Renamed envRefssafeRefs throughout.
  • strict_mode_steps_validation_test.go — 6 new unit test cases covering with:±uses:, malformed with:, multiple secrets, mixed env:+with:.
  • strict_mode_steps_secrets_integration_test.go — Updated blocked case to non-uses: step; added integration tests for with: in action steps compiling successfully.
  • ADR-0002 — Updated classification spec (items 4–6) and consequences to reflect with: as a safe surface.
  • scratchpad/dev.md — Updated validation docs with new safe binding types and examples.

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 g/fileutil/fileurev-parse ache/go/1.25.8/x--show-toplevel git rev-�� --show-toplevel ache/go/1.25.8/x64/pkg/tool/linuTest User /usr/bin/git 3118019/b256/_pkgit stmain.go 1/x64/bin/node git (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 -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/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 sm_wasm.s /opt/hostedtoolcache/node/24.14.1/x64/bin/node -json GO111MODULE 64/pkg/tool/linu--show-toplevel /opt/hostedtoolcache/node/24.14.1/x64/bin/node /tmp�� No expressions here 64/pkg/tool/linurev-parse /usr/bin/git LsRemoteWithRealgit LsRemoteWithRealrev-parse 64/pkg/tool/linu--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 --show-toplevel (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 ger.test GO111MODULE ortcfg.link GOINSECURE (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha --show-toplevel 64/pkg/tool/linuremote.origin.url /usr/bin/git ut3570179196/001git ne_constants.go 64/pkg/tool/linu--show-toplevel git rev-�� --show-toplevel 64/pkg/tool/linux_amd64/compile /usr/bin/git g_.a VGplouFzy ck git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha --show-toplevel /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linustatus /usr/bin/git artifacts-summargit -trimpath /opt/hostedtoolc--show-toplevel git rev-�� --show-toplevel /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/vet /usr/bin/git -bool -buildtags 1/x64/bin/node 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 add origin /usr/bin/git ub/workflows GO111MODULE x_amd64/vet git rev-�� --show-toplevel x_amd64/vet /opt/hostedtoolcache/node/24.14.1/x64/bin/node -json GO111MODULE 64/pkg/tool/linu--show-toplevel node (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v6 --jq .object.sha add other /usr/bin/git repo3751875275/0git GO111MODULE x_amd64/vet git rev-�� --show-toplevel x_amd64/vet /usr/bin/git -json GO111MODULE 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 --show-toplevel /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linu-trimpath /usr/bin/git sync/atomic pkg/mod/github.crev-parse 64/pkg/tool/linu--show-toplevel git rev-�� --show-toplevel 64/pkg/tool/linux_amd64/compile /usr/bin/git architecture-guagit -trimpath /bin/sh 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 --show-toplevel ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile /usr/bin/git 47/001/test-froncut GO111MODULE ache/go/1.25.8/x64/pkg/tool/linu--show-toplevel git rev-�� --show-toplevel ache/go/1.25.8/x^remote\..*\.gh-resolved$ /usr/bin/git 6992/001/stabiligit edcfg ache/go/1.25.8/x--show-toplevel git (http block)
  • https://api.github.com/repos/actions/github-script/git/ref/tags/v9
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v9 --jq .object.sha go1.25.8 -c=4 -nolocalimports -importcfg /tmp/go-build2763118019/b243/importcfg -pack /home/REDACTED/go/pkg/mod/golang.org/x/text@v0.35.0/internal/language/compact/compact.go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v9 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json 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/v9 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE B5y8l-x/B7jCmPoOX41tMhlghKCL (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 v1.0.0 remote.origin.url /usr/bin/git -json GO111MODULE x_amd64/vet git init�� sm_wasm.s x_amd64/vet /usr/bin/git m/workflows 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 remove origin /usr/bin/git ub/workflows GO111MODULE x_amd64/vet git rev-�� --show-toplevel x_amd64/vet /usr/bin/git -json 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 -test.paniconexit0 -test.v=true /usr/bin/find -test.timeout=10git -test.run=^Test -test.short=true--show-toplevel find -O3 image:v1.0.0 -ignore_readdir_race /usr/bin/git -mindepth 1 -name git (http block)
  • https://api.github.com/repos/actions/upload-artifact/git/ref/tags/v7
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v7 --jq .object.sha 3118019/b124/_pkg_.a GO111MODULE ache/go/1.25.8/x64/bin/go GOINSECURE /go-yaml/token ache/go/1.25.8/x--show-toplevel J_/CWrYu2czG7Ca7ylQP4Z8/vCNYLdc7test@example.com env zu3u/WYeMzep8SbKgk88dzu3u GO111MODULE /opt/hostedtoolcache/go/1.25.8/x64/pkg/tool/linux_amd64/asm GOINSECURE GOMOD GOMODCACHE jGe2quy/MdoaSnTlUlRo3E__G0n2 (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v7 --jq .object.sha 3118019/b229/_pkg_.a vNkW/MmwpPo_3e3tB-Au8vNkW verutil.test GOINSECURE contextprotocol/rev-parse GOMODCACHE verutil.test 1612�� RRV4/RyQ7A4ZGgY3yG-aYRRV4 -o 64/pkg/tool/linux_amd64/link ./cmd/gh-aw-wasmgit GOWORK 64/bin/go 64/pkg/tool/linux_amd64/link (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/upload-artifact/git/ref/tags/v7 --jq .object.sha 3118019/b162/_pkg_.a GO111MODULE 64/pkg/tool/linux_amd64/compile GOINSECURE th2/internal GOMODCACHE 64/pkg/tool/linux_amd64/compile faul�� g_.a pkg/mod/github.com/segmentio/encoding@v0.5.4/iso8601/parse.go 1/x64/bin/node -p internal/runtimerev-parse -lang=go1.25 /opt/hostedtoolcache/go/1.25.8/xrepos/{owner}/{repo}/actions/runs/3/artifacts (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 /tmp/gh-aw-test-runs/20260412-010902-32220/test-1427570566 rev-parse /usr/bin/git @{u} GO111MODULE x_amd64/vet git rev-�� --show-toplevel x_amd64/vet /opt/hostedtoolcache/node/24.14.1/x64/bin/node -json GO111MODULE 64/pkg/tool/linu--show-toplevel node (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 1254001/b432/_pkg_.a stmain.go ache/go/1.25.8/x64/pkg/tool/linux_amd64/link wasm/ for Go <1.git -c=4 -nolocalimports ache/go/1.25.8/x64/pkg/tool/linux_amd64/link pack�� 1254001/b449/typeutil.test --revs 1254001/b449/importcfg.link --thin --delta-base-offrev-parse -q r96T_MkWd-kcE/_kz8wMEZNDzYl9ptOGPo/5GniUk8RBwwF8S8eKncR/xY8IG6Cr96T_MkWd-kcE (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 -aw/git/ref/tags/v1.2.3 -buildtags ache/node/24.14.1/x64/bin/node -errorsas -ifaceassert -nilfunc /bin/sh t-16�� bility_SameInputSameOutput1761116992/001/stability-test.md git-receive-pack '/tmp/TestParseDefaultBranchFromLsRemoteWithRealGitbranch_with_hyphen270388263/rev-parse /usr/bin/git -json GO111MODULE 64/bin/go git (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 GO111MODULE 64/pkg/tool/linux_amd64/compile GOINSECURE o8601 abis 64/pkg/tool/linux_amd64/compile env g_.a GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh run download 1 --dir test-logs/run-1 4GDF0MOYT 64/pkg/tool/linux_amd64/asm GOINSECURE (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 til.go 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD abis 64/pkg/tool/linuTest User 64/s�� g_.a k1Ubnk-ff ache/go/1.25.8/x64/pkg/tool/linux_amd64/asm GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/xremote.origin.url (http block)
    • Triggering command: /usr/bin/gh gh run download 12345 --dir test-logs/run-12345 0/message/catalog/catalog.go 64/pkg/tool/linux_amd64/compile GOINSECURE al_wasm.o 64/src/internal/--show-toplevel 64/pkg/tool/linux_amd64/compile env g_.a J9_2Hh5RJ ache/go/1.25.8/x64/pkg/tool/linux_amd64/asm GOINSECURE t GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linux_amd64/asm (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 GO111MODULE 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD abis 64/pkg/tool/linutest@example.com env g_.a GO111MODULE 64/pkg/tool/linux_amd64/link GOINSECURE _wasm.o 64/src/reflect/a--show-toplevel 64/pkg/tool/linux_amd64/link (http block)
    • Triggering command: /usr/bin/gh gh run download 12346 --dir test-logs/run-12346 0/message/catalog.go 64/pkg/tool/linux_amd64/compile GOINSECURE cha8_stub.o 64/src/internal/user.name 64/pkg/tool/linuTest User env g_.a QuPWq4ACQ x_amd64/compile GOINSECURE chema/v6/kind GOMODCACHE x_amd64/compile (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 GOMOD GOMODCACHE 64/pkg/tool/linuTest User env g_.a NG8R67gve x_amd64/vet GOINSECURE go-sdk/mcp GOMODCACHE x_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh run download 2 --dir test-logs/run-2 NgK5Xenpy 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/compile env g_.a GO111MODULE ache/go/1.25.8/x64/pkg/tool/linu-buildmode=exe GOINSECURE setup/js/node_morev-parse GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linu-extld=gcc (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 5.0/internal/doc.go 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/compile env 3724277240 tVIFB3NxN ache/go/1.25.8/x64/pkg/tool/linu-test.short=true GOINSECURE GOMOD GOMODCACHE r (http block)
    • Triggering command: /usr/bin/gh gh run download 3 --dir test-logs/run-3 OhJqGnTLC 64/pkg/tool/linux_amd64/vet GOINSECURE er GOMODCACHE 64/pkg/tool/linuTest User env PefC8rlji GO111MODULE ache/go/1.25.8/x64/pkg/tool/linux_amd64/asm GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linux_amd64/asm (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 64 GOMODCACHE 64/pkg/tool/linu/tmp/go-build3161254001/b444/_testmain.go env 3724277240 GO111MODULE ache/go/1.25.8/x64/pkg/tool/linu-test.short=true GOINSECURE go-sdk/auth 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 S1XHWmzm6 64/pkg/tool/linux_amd64/vet GOINSECURE l/errors GOMODCACHE 64/pkg/tool/linux_amd64/vet env 4261419498 GO111MODULE ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linu/tmp/file-tracker-test3282008154/test2.lock.yml (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 5.0/deviceauth.g-nolocalimports 64/pkg/tool/linu-importcfg GOINSECURE (http block)
    • Triggering command: /usr/bin/gh gh run download 5 --dir test-logs/run-5 sYYP-7R33 ache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go env 4261419498/.github/workflows vohS5K4mu k GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile (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 -json GO111MODULE 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/asm 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 WKKjjpb/pnOHWmOW0VPsKVyXPZlC env rity366606795/001 LZuHOSZyr /opt/hostedtoolcache/go/1.25.8/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (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 64/pkg/tool/linux_amd64/vet /usr/bin/git iyLxzLvWQ GO111MODULE ache/go/1.25.8/x--show-toplevel git rev-�� --show-toplevel ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile /usr/bin/git )$/\1/p g/stringutil/ans-c .cfg 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 edOutput1266573789/001 QyquJZDcH 64/pkg/tool/linux_amd64/compile GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/compile env g_.a EocudEgAi ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile GOINSECURE util GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linux_amd64/compile (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/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile env -json GO111MODULE 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 go1.25.8 -c=4 -nolocalimports -importcfg /tmp/go-build3161254001/b422/importcfg -pack /tmp/go-build3161254001/b422/_testmain.go env 298312261/001 298312261/002/work 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/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 -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile env -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/asm GOINSECURE GOMOD GOMODCACHE x_amd64/asm env -json GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (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 edOutput1266573789/001 l_test.go /opt/hostedtoolcache/go/1.25.8/x64/bin/go GOINSECURE go-sdk/internal/config GOMODCACHE go estl�� L5Pf2dzjr GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile (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/linux_amd64/compile env g_.a 5Lj-UidHf k GOINSECURE GOMOD GOMODCACHE ache/go/1.25.8/x64/pkg/tool/linuremote (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 -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/owner/repo/contents/file.md
    • Triggering command: /tmp/go-build3161254001/b397/cli.test /tmp/go-build3161254001/b397/cli.test -test.testlogfile=/tmp/go-build3161254001/b397/testlog.txt -test.paniconexit0 -test.v=true -test.parallel=4 -test.timeout=10m0s -test.run=^Test -test.short=true -nolocalimports -importcfg /tmp/go-build2763118019/b222/importcfg -pack -o /tmp/go-build658-p -trimpath 64/bin/go -p main -lang=go1.25 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 latted/flatted.go GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (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 12, 2026 01:21
…trict mode

Extends the secret classification in strict mode to treat secrets.* in
step-level with: as safe when the step has a uses: field. This enables
enterprise workflows that use centralized secret managers (e.g., Conjur,
HashiCorp Vault) via dedicated GitHub Actions to use strict mode.

- with: inputs for uses: action steps are passed to external actions,
  not interpolated into shell scripts
- The GitHub Actions runner masks with: values derived from secrets
- Malformed with: (non-map) and with: without uses: remain unsafe

Fixes #26008

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/e753fa76-8705-4fe2-8cb1-734e7d26133a

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Update strict mode to allow secrets in step-level with for action steps Strict mode: allow secrets.* in step-level with: for uses: action steps Apr 12, 2026
Copilot AI requested a review from pelikhan April 12, 2026 01:24
@pelikhan pelikhan marked this pull request as ready for review April 12, 2026 01:26
Copilot AI review requested due to automatic review settings April 12, 2026 01:26
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

Enables strict mode workflows to safely use ${{ secrets.* }} within step-level with: inputs for uses: action steps, aligning behavior with GitHub Actions masking semantics and reducing the need to disable strict mode.

Changes:

  • Extends classifyStepSecrets() to treat with: secrets as safe when used on uses: action steps (keeping malformed with: unsafe and preserving $GITHUB_ENV leak detection).
  • Adds/updates unit + integration tests covering with:+uses: allowlisting and negative cases.
  • Updates ADR + developer documentation to reflect the new “safe binding” surface area and guidance.
Show a summary per file
File Description
pkg/workflow/strict_mode_steps_validation.go Core secret-classification logic updated to allow with: secrets for uses: steps under strict mode.
pkg/workflow/strict_mode_steps_validation_test.go Adds unit tests for with:/uses: cases and renames “env-bound” expectations to “safe”.
pkg/workflow/strict_mode_steps_secrets_integration_test.go Adds integration coverage ensuring strict-mode compilation succeeds for with: secrets on action steps.
docs/adr/0002-allow-secrets-in-step-env-bindings-under-strict-mode.md Updates the normative spec and consequences to include with: for uses: steps as a safe binding.
scratchpad/dev.md Updates internal developer docs/examples for strict-mode secrets validation and safe binding types.

Copilot's findings

Tip

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

Comments suppressed due to low confidence (1)

docs/adr/0002-allow-secrets-in-step-env-bindings-under-strict-mode.md:80

  • Non-strict mode spec still refers to “env-bound” secret references, but the ADR’s terminology and the code now use “safe” references (env: + with: for uses: steps). Update this bullet to use the new “safe” terminology (and ensure the rest of the section is consistent).
### Non-Strict Mode Behavior

1. When strict mode is not active, implementations **MUST** emit a warning (to stderr) if any secret references — whether *env-bound* or *unsafe* — are found in a steps section.
2. Implementations **MUST NOT** return an error in non-strict mode for secret references in steps sections; the warning is advisory only.
3. Implementations **SHOULD** deduplicate secret reference identifiers before including them in warning or error messages.
  • Files reviewed: 5/5 changed files
  • Comments generated: 2

// Check if this is a uses: action step. For action steps, with: inputs are
// passed to the external action (not interpolated into shell scripts), and
// the GitHub Actions runner masks with: values derived from secrets.
_, hasUses := stepMap["uses"]
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

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

hasUses is currently true for any step that merely contains a uses key, even if the value is nil/non-string. Since the safety rationale for with: relies on GitHub Actions treating the step as a valid action step, consider only treating with: as safe when uses is a non-empty string (and otherwise classify with: secrets as unsafe).

Suggested change
_, hasUses := stepMap["uses"]
usesVal, hasUses := stepMap["uses"]
if hasUses {
usesStr, isString := usesVal.(string)
hasUses = isString && strings.TrimSpace(usesStr) != ""
}

Copilot uses AI. Check for mistakes.
Comment on lines +175 to +179
// If the step has safe-bound secrets AND references $GITHUB_ENV in any
// non-env/non-with field, reclassify all safe refs as unsafe. Writing to
// $GITHUB_ENV would persist the secret to subsequent steps.
if len(localEnv) > 0 && stepReferencesGitHubEnv(stepMap) {
localUnsafe = append(localUnsafe, localEnv...)
localEnv = nil
if len(localSafe) > 0 && stepReferencesGitHubEnv(stepMap) {
localUnsafe = append(localUnsafe, localSafe...)
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

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

classifyStepSecrets reclassifies safe refs as unsafe when stepReferencesGitHubEnv(stepMap) is true, and the comment explicitly says this should only consider “non-env/non-with” fields. However stepReferencesGitHubEnv currently ignores only env, so a with: value containing the string GITHUB_ENV can incorrectly trigger reclassification and block otherwise-safe with: secrets. Update stepReferencesGitHubEnv to also skip with (at least when uses is present) to match the intended policy/ADR.

Copilot uses AI. Check for mistakes.
@github-actions
Copy link
Copy Markdown
Contributor

✅ Design Decision Gate — ADR Verified

The implementation in this PR aligns with the stated Architecture Decision Record.

ADR reviewed: ADR-0002: Allow Secrets in Step-Level env: Bindings Under Strict Mode

Verification Summary

The PR extends ADR-0002 (updated inline on this branch) to classify with: inputs for uses: action steps as a safe binding surface alongside env: maps. The code in classifyStepSecrets() faithfully implements all normative requirements from the updated spec:

Normative Requirement Implementation
with: in a uses: step → safe (item 4) _, hasUses := stepMap["uses"] guard + safe-list append for well-formed maps
with: without uses:unsafe (item 5) key == "with" && hasUses conditional
Malformed with: (string/slice) → unsafe (item 6) else branch on isMap type assertion
GITHUB_ENV write reclassifies safe refs as unsafe (item 9) if len(localSafe) > 0 && stepReferencesGitHubEnv(stepMap)
Error message SHOULD suggest with: inputs (Strict Mode item 3) "or use step-level env: bindings (for run: steps) or with: inputs (for uses: action steps) instead"
GITHUB_TOKEN filtered from safe refs (Strict Mode item 4) filterBuiltinTokens(safeSecretRefs)

Unit tests cover all branches (safe with: with uses:, unsafe with: without uses:, malformed with:, multiple secrets, mixed env: + with:), and the integration tests confirm end-to-end compilation behaviour in strict mode.

The design decision has been recorded and the implementation follows it. Great work! 🏗️

Note

🔒 Integrity filter blocked 1 item

The following item were blocked because they don't meet the GitHub integrity level.

To allow these resources, lower min-integrity in your GitHub frontmatter:

tools:
  github:
    min-integrity: approved  # merged | approved | unapproved | none

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · ● 87.7K ·

@github-actions
Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 73/100

⚠️ Acceptable, with suggestions

Metric Value
New/modified tests analyzed 16
✅ Design tests (behavioral contracts) 16 (100%)
⚠️ Implementation tests (low value) 0 (0%)
Tests with error/edge cases 7 (43.75%)
Duplicate test clusters 0
Test inflation detected Yes — strict_mode_steps_validation_test.go +161 lines vs production strict_mode_steps_validation.go +55 lines (ratio ≈ 2.9:1)
🚨 Coding-guideline violations None

Test Classification Details

All 16 test scenarios (click to expand)
Test File Classification Issues Detected
secrets in with for uses action step compile in strict mode strict_mode_steps_secrets_integration_test.go ✅ Design Happy path only
mixed env and with secrets for uses action step compile in strict mode strict_mode_steps_secrets_integration_test.go ✅ Design Happy path only
secret in with field without uses is blocked in strict mode strict_mode_steps_secrets_integration_test.go ✅ Design Error case ✅
error message suggests with: inputs for action steps (added assertion) strict_mode_steps_secrets_integration_test.go ✅ Design Error message assertion ✅
steps with secret in with field for uses action step in strict mode is allowed strict_mode_steps_validation_test.go ✅ Design Contract change (was expectError:true → false)
steps with secret in with field without uses in strict mode fails strict_mode_steps_validation_test.go ✅ Design Error case ✅
vault-style action with multiple secrets in with is allowed in strict mode strict_mode_steps_validation_test.go ✅ Design Happy path only
pre-steps with secret in with for uses action step is allowed in strict mode strict_mode_steps_validation_test.go ✅ Design Happy path only
post-steps with secret in with for uses action step is allowed in strict mode strict_mode_steps_validation_test.go ✅ Design Happy path only
error message suggests with: inputs for uses: action steps strict_mode_steps_validation_test.go ✅ Design Error message assertion ✅
secret in with field for uses action step is classified as safe strict_mode_steps_validation_test.go ✅ Design Behavioral contract change
secret in with field without uses is unsafe strict_mode_steps_validation_test.go ✅ Design Security boundary / edge case ✅
malformed string with in uses action step is unsafe strict_mode_steps_validation_test.go ✅ Design Edge case (malformed input) ✅
malformed slice with in uses action step is unsafe strict_mode_steps_validation_test.go ✅ Design Edge case (malformed input) ✅
multiple secrets in with for uses action step are safe strict_mode_steps_validation_test.go ✅ Design Happy path only; uses ElementsMatch for order-independence ✅
secrets in both env and with for uses action step are safe strict_mode_steps_validation_test.go ✅ Design Happy path only

Flagged Tests — Requires Review

No tests are flagged for behavioral quality issues — all 16 new/modified scenarios are design tests. One observation worth noting:

i️ Test Inflation — strict_mode_steps_validation_test.go (+161 lines vs +55 production)

Classification: Informational (not a failure)
Context: The inflation is partly explained by the expectedEnvexpectedSafe variable rename across ~14 existing test cases (adds ~28 lines of churn) and the inherent verbosity of Go table-driven test structs. The 5 genuinely new unit test cases add real coverage and are all well-structured.
Suggestion: No action required — the ratio is driven by refactoring churn, not test padding. The table-driven format is idiomatic and correct.

i️ Happy-path bias in allowed-cases (minor)

7 of the 9 "allowed" scenarios test only that compilation succeeds without asserting any specific property of the output. For a security-sensitive feature like strict mode secret validation, consider adding at least one case that asserts the compiled output does not contain plaintext secret values (e.g., inspecting the generated YAML). This is a suggestion, not a blocker.


Language Support

Tests analyzed:

  • 🐹 Go (*_test.go): 16 test scenarios across 2 files — integration (//go:build integration) and unit (//go:build !integration)
  • 🟨 JavaScript (*.test.cjs, *.test.js): 0 tests changed

Coding-Guideline Compliance

Check Result
Build tags present //go:build integration and //go:build !integration
No mock libraries (gomock, testify/mock, EXPECT) ✅ Clean
Assertion messages on all assert calls ✅ Descriptive messages present
require.* vs assert.* discipline ✅ Correct usage

Verdict

Check passed. 0% of new tests are implementation tests (threshold: 30%). All 16 new/modified scenarios enforce behavioral contracts — security boundaries, error messages, and feature correctness — with good coverage of edge and error cases (malformed inputs, with: without uses:, error message content).


📖 Understanding Test Classifications

Design Tests (High Value) verify what the system does:

  • Assert on observable outputs, return values, or state changes
  • Cover error paths and boundary conditions
  • Would catch a behavioral regression if deleted
  • Remain valid even after internal refactoring

Implementation Tests (Low Value) verify how the system does it:

  • Assert on internal function calls (mocking internals)
  • Only test the happy path with typical inputs
  • Break during legitimate refactoring even when behavior is correct
  • Give false assurance: they pass even when the system is wrong

Goal: Shift toward tests that describe the system's behavioral contract — the promises it makes to its users and collaborators.

🧪 Test quality analysis by Test Quality Sentinel · ● 592.1K ·

Copy link
Copy Markdown
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

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

✅ Test Quality Sentinel: 73/100. Test quality is acceptable — 0% of new tests are implementation tests (threshold: 30%). All 16 new/modified scenarios enforce behavioral contracts with good edge-case coverage.

@pelikhan
Copy link
Copy Markdown
Collaborator

@copilot review all comments

…tection

Address review feedback:
1. Only treat with: as safe when uses: is a non-empty string (not nil,
   empty, whitespace-only, or non-string values)
2. Skip with: field in stepReferencesGitHubEnv to prevent false
   GITHUB_ENV detection from with: values triggering reclassification

Agent-Logs-Url: https://github.com/github/gh-aw/sessions/2aa804f6-c6a1-420d-bec8-d2b545d38ea6

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 12, 2026

@copilot review all comments

Addressed both review comments in 31745ee:

  1. hasUses now validates that uses is a non-empty string — nil, empty, whitespace-only, and non-string values are all treated as unsafe. Added 4 new test cases for these edge cases.
  2. stepReferencesGitHubEnv now skips both env and with fields, so a with: value containing GITHUB_ENV won't incorrectly trigger reclassification. Added test coverage for this case.

@pelikhan pelikhan merged commit e9ddcd7 into main Apr 12, 2026
@pelikhan pelikhan deleted the copilot/allow-secrets-in-step-level branch April 12, 2026 01:57
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.

Strict mode: allow secrets.* in step-level with: for action steps in pre-agent custom steps

3 participants