Skip to content

Fix: blocked constraints dropped inconsistently from safe-outputs configs#18140

Merged
pelikhan merged 3 commits intomainfrom
copilot/fix-blocked-constraints-issue
Feb 24, 2026
Merged

Fix: blocked constraints dropped inconsistently from safe-outputs configs#18140
pelikhan merged 3 commits intomainfrom
copilot/fix-blocked-constraints-issue

Conversation

Copy link
Contributor

Copilot AI commented Feb 24, 2026

The compiler was silently dropping blocked constraints from one of the two generated safe-outputs configs depending on the output type — leaving either MCP server validation (config.json) or the handler job (GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG) without the constraint as a last line of defense.

Bugs fixed

  • add_labelsconfig.json (safe_outputs_config_generation.go): blocked was never added to additionalFields, only allowed was
  • assign_to_userHANDLER_CONFIG (compiler_safe_outputs_config.go): missing AddStringSlice("blocked", c.Blocked) in the handler builder
  • unassign_from_userHANDLER_CONFIG: same omission as assign_to_user

Example

safe-outputs:
  add-labels:
    blocked: ["[*]*", "~spam"]      # now present in config.json ✓
  assign-to-user:
    blocked: [copilot, "*[bot]"]    # now present in HANDLER_CONFIG ✓
  unassign-from-user:
    blocked: [copilot, "*[bot]"]    # now present in HANDLER_CONFIG ✓

Tests added

  • TestGenerateSafeOutputsConfigAddLabelsBlocked — verifies blocked in config.json for add_labels
  • TestHandlerConfigAssignToUserWithBlocked — verifies blocked in HANDLER_CONFIG for assign_to_user
  • TestHandlerConfigUnassignFromUserWithBlocked — verifies blocked in HANDLER_CONFIG for unassign_from_user

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 -c=4 -nolocalimports git t-30�� k/gh-aw/gh-aw/.github/workflows/ai-moderator.md /tmp/go-build476865492/b411/_testmain.go /usr/bin/git Z93v/GBvFB-R7qNzgit GO111MODULE 64/bin/go 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 GOMOD GOMODCACHE go k/gh�� -json GO111MODULE /opt/hostedtoolcache/go/1.25.0/x64/bin/go GOINSECURE GOMOD erignore 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 KxI4/aiEy0dPZIRGnmVdmKxI4 (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/ai-inference/git/ref/tags/v1 --jq .object.sha -json GO111MODULE ache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go 0/x6�� -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (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 -json GO111MODULE ache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go env 0019-27734/test-3161016930/.github/workflows GO111MODULE cfg GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v3 --jq .object.sha -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE ode_modules/.bin/node GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/actions/checkout/git/ref/tags/v4
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v4 --jq .object.sha -json GO111MODULE /opt/hostedtoolcache/go/1.25.0/x-nilfunc GOINSECURE GOMOD GOMODCACHE go env runs/20260224-130019-27734/test-4119043155/.github/workflows GO111MODULE /opt/hostedtoolcache/go/1.25.0/x64/bin/go l GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v4 --jq .object.sha 69 --log-level Name,createdAt,startedAt,updatedAt,event,headBranch,headSha,displayTitle --log-target journal-or-kmsg ache/go/1.25.0/x--show-toplevel 64/pkg/tool/linux_amd64/compile env _.a GO111MODULE 865492/b297/vet.cfg GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v4 --jq .object.sha ere origin /usr/bin/git (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 -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/pkg/tool/linux_amd64/link GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/link (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha /tmp/go-build476865492/b001/gh-aw.test -importcfg /usr/bin/git -s -w -buildmode=exe git rev-�� --show-toplevel -extld=gcc /usr/bin/git -json GO111MODULE 64/bin/go git (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha --show-toplevel sh /usr/bin/git "prettier" --chegit node 64/bin/go git rev-�� --show-toplevel 8afRszc/J-5ptimqu69dtnPwupbS /usr/bin/git -json GO111MODULE 64/bin/go 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 GOSUMDB GOWORK 64/bin/go GOINSECURE GOMOD GOMODCACHE erignore env tformat GO111MODULE 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 ck 'scripts/**/*GOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE sh (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 go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE sh (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 brave.md GO111MODULE rtcfg.link GOINSECURE GOMOD GOMODCACHE 2GrHKbdPFsnY6xQzyS/oFy7E6D7PTBexQgP2YYC/SYVWI1Pjb7L_MWnrvwk8 env runs/20260224-130019-27734/test-4119043155/.github/workflows GO111MODULE _.a l GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v4 --jq .object.sha th .prettierignore GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE 0/x64/bin/node GOINSECURE GOMOD igFiles,SwigCXXF--show-toplevel go (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 bot-detection.md GO111MODULE /opt/hostedtoolcache/go/1.25.0/x-test.short=true GOINSECURE GOMOD GOMODCACHE go env snY6xQzyS GO111MODULE /opt/hostedtoolcache/go/1.25.0/x64/bin/go l GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v4 --jq .object.sha th .prettierignore GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE ache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/1/artifacts
    • Triggering command: /usr/bin/gh gh run download 1 --dir test-logs/run-1 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/actions/runs/12345/artifacts
    • Triggering command: /usr/bin/gh gh run download 12345 --dir test-logs/run-12345 GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go stlo�� -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/12346/artifacts
    • Triggering command: /usr/bin/gh gh run download 12346 --dir test-logs/run-12346 GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go stlo�� -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/2/artifacts
    • Triggering command: /usr/bin/gh gh run download 2 --dir test-logs/run-2 GO111MODULE x_amd64/compile GOINSECURE GOMOD GOMODCACHE x_amd64/compile env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/runs/3/artifacts
    • Triggering command: /usr/bin/gh gh run download 3 --dir test-logs/run-3 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/actions/runs/4/artifacts
    • Triggering command: /usr/bin/gh gh run download 4 --dir test-logs/run-4 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/actions/runs/5/artifacts
    • Triggering command: /usr/bin/gh gh run download 5 --dir test-logs/run-5 GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env tmatter-with-arrays.md GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/github/gh-aw/actions/workflows
    • Triggering command: /usr/bin/gh gh workflow list --json name,state,path GOSUMDB GOWORK 64/bin/go GOINSECURE GOMOD GOMODCACHE ache/go/1.25.0/xGO111MODULE env 1141928/b383/_pkGOINSECURE GO111MODULE 64/bin/go GOINSECURE bagabas/go-udiffenv 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 go env O_53/inWBUy0r91aGOSUMDB GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE 1141928/b386/importcfg (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 x_amd64/compile env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (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 -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 api /repos/github/gh-aw/git/ref/tags/v1.0.0 --jq .object.sha --write **/*.cjs 64/bin/go **/*.json --ignore-path ../../../.pretti-json sh -c "prettier" --wriGOINSECURE git 64/bin/go --show-toplevel -extld=gcc /usr/bin/git 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 -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env 799924427/.github/workflows GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
    • Triggering command: /usr/bin/gh gh api /repos/nonexistent/action/git/ref/tags/v999.999.999 --jq .object.sha --write **/*.cjs 64/bin/go **/*.json --ignore-path ../../../.pretti-json sh -c "prettier" --wriGOINSECURE git 64/bin/go --show-toplevel -tests /usr/bin/git go (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 GOMOD GOMODCACHE go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (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 ache/go/1.25.0/xGO111MODULE env 1141928/b398/_pkGOINSECURE GO111MODULE 64/bin/go GOINSECURE b/gh-aw/pkg/pars-c 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 ache/go/1.25.0/xGO111MODULE env 1141928/b406/_pkGOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/repos/owner/repo/contents/file.md
    • Triggering command: /tmp/go-build476865492/b381/cli.test /tmp/go-build476865492/b381/cli.test -test.testlogfile=/tmp/go-build476865492/b381/testlog.txt -test.paniconexit0 -test.v=true -test.parallel=4 -test.timeout=10m0s -test.run=^Test -test.short=true GOINSECURE GOMOD GOMODCACHE go m/_n�� -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 GOSUMDB GOWORK 64/bin/go GOINSECURE GOMOD GOMODCACHE ache/go/1.25.0/xGO111MODULE env 1141928/b387/_pkGOINSECURE GO111MODULE 64/bin/go GOINSECURE b/gh-aw/pkg/consenv GOMODCACHE go (http block)

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

Original prompt

This section details on the original issue you should resolve

<issue_title>Compiler drops 'blocked' constraints from safe-outputs configs inconsistently</issue_title>
<issue_description>## Summary

When compiling a workflow with blocked constraints on safe-outputs (e.g. assign-to-user, unassign-from-user, add-labels), the compiler inconsistently distributes the blocked field between the two generated configs:

  1. config.json (MCP server validation during agent execution)
  2. GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG (handler job, last line of defense before writes)

Steps to Reproduce

  1. Define a workflow with blocked on multiple safe-outputs:
    safe-outputs:
      add-labels:
        target: "*"
        target-repo: "microsoft/vscode"
        max: 5
        blocked: ["[*]*", "~spam", "stale", "triage-needed"]
      assign-to-user:
        target: "*"
        target-repo: "microsoft/vscode"
        max: 1
        blocked: [copilot, "*[bot]"]
      unassign-from-user:
        target: "*"
        target-repo: "microsoft/vscode"
        max: 2
        blocked: [copilot, "*[bot]"]
  2. Run gh aw compile
  3. Inspect the generated .lock.yml

Expected Behavior

Both config.json and GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG should include the blocked constraint for all safe-output types that define it.

Actual Behavior

The blocked constraint is split inconsistently:

Config add_labels.blocked assign_to_user.blocked unassign_from_user.blocked
config.json (MCP server) missing ✅ present ✅ present
HANDLER_CONFIG (handler job) ✅ present missing missing

Security Impact

The GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG is the last line of defense before writes are applied to the GitHub API. Since the agent job has broad filesystem access, it could theoretically bypass MCP server validation by writing safe-output entries directly. The handler config should enforce all constraints defined in the workflow .md source, including blocked for assign/unassign operations.

Similarly, config.json should enforce blocked for add_labels to provide defense-in-depth during agent execution.

Environment

  • gh aw version: v0.50.0
  • OS: Windows
  • Workflow: issue-triage.md in microsoft/vscode-engineering

Workaround

Manually patch the .lock.yml after compilation to add the missing blocked fields to both configs.</issue_description>

Comments on the Issue (you are @copilot in this section)


🔒 GitHub Advanced Security automatically protects Copilot coding agent pull requests. You can protect all pull requests by enabling Advanced Security for your repositories. Learn more about Advanced Security.

Copilot AI and others added 2 commits February 24, 2026 12:57
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
…onsistently

- Add blocked field to add_labels config.json generation (safe_outputs_config_generation.go)
- Add blocked field to assign_to_user handler config (compiler_safe_outputs_config.go)
- Add blocked field to unassign_from_user handler config (compiler_safe_outputs_config.go)
- Add tests for all three fixes

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix inconsistent handling of 'blocked' constraints in configs Fix: blocked constraints dropped inconsistently from safe-outputs configs Feb 24, 2026
@pelikhan pelikhan marked this pull request as ready for review February 24, 2026 13:08
Copilot AI review requested due to automatic review settings February 24, 2026 13:08
@pelikhan pelikhan merged commit cf7339c into main Feb 24, 2026
106 checks passed
@pelikhan pelikhan deleted the copilot/fix-blocked-constraints-issue branch February 24, 2026 13:09
Copy link
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 fixes a critical security issue where blocked constraints were inconsistently distributed between two safe-outputs validation configs (config.json for MCP server and GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG for the handler job), leaving gaps in the defense-in-depth security model.

Changes:

  • Fixed add_labels to include blocked in config.json (MCP server validation)
  • Fixed assign_to_user to include blocked in handler config (last line of defense)
  • Fixed unassign_from_user to include blocked in handler config (last line of defense)

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
pkg/workflow/safe_outputs_config_generation.go Adds blocked field to additionalFields for add_labels config.json generation
pkg/workflow/compiler_safe_outputs_config.go Adds blocked field to handler config builder for assign_to_user and unassign_from_user
pkg/workflow/safe_outputs_config_generation_test.go Adds test verifying blocked appears in add_labels config.json
pkg/workflow/compiler_safe_outputs_config_test.go Adds tests verifying blocked appears in handler config for assign_to_user and unassign_from_user
.github/workflows/smoke-copilot.lock.yml Adds MCP configuration cleanup step (unrelated to PR purpose)

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

Comment on lines +1864 to +1869
- name: Clear MCP configuration for detection
if: always() && steps.detection_guard.outputs.run_detection == 'true'
run: |
rm -f /tmp/gh-aw/mcp-config/mcp-servers.json
rm -f /home/runner/.copilot/mcp-config.json
rm -f "$GITHUB_WORKSPACE/.gemini/settings.json"
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

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

This MCP configuration cleanup step appears to be unrelated to the PR's stated purpose of fixing blocked constraints in safe-outputs configs. Consider moving this change to a separate PR with its own description and rationale, or update the PR description to explain why this change is needed.

Copilot uses AI. Check for mistakes.
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.

Compiler drops 'blocked' constraints from safe-outputs configs inconsistently

3 participants