Skip to content

Comments

Add blocked pattern matching for add-labels and remove-labels#16690

Merged
pelikhan merged 4 commits intomainfrom
copilot/add-blocked-pattern-matching-again
Feb 19, 2026
Merged

Add blocked pattern matching for add-labels and remove-labels#16690
pelikhan merged 4 commits intomainfrom
copilot/add-blocked-pattern-matching-again

Conversation

Copy link
Contributor

Copilot AI commented Feb 19, 2026

Problem

Large repositories with 600+ labels cannot maintain exhaustive allowlists. Certain label patterns should never be applied by agentic workflows (e.g., ~stale triggers workflows, *[bot] are administrative), but prompt-level instructions are vulnerable to injection attacks.

Changes

Schema & Types

  • Added blocked: []string field to AddLabelsConfig and RemoveLabelsConfig
  • Updated JSON schema validation in main_workflow_schema.json
  • Modified handler config builders to serialize blocked patterns to JavaScript runtime

Runtime Validation

  • Updated validateLabels() to filter blocked patterns using matchesSimpleGlob
  • Security precedence: blocked patterns evaluated before allowed list
  • Updated add_labels.cjs and remove_labels.cjs to pass blocked config

Example

safe-outputs:
  add-labels:
    blocked: ["~*", "*[bot]"]  # Deny workflow triggers and bot labels
    allowed: ["bug", "enhancement"]
    max: 5

Blocked labels matching ~* or *[bot] are rejected regardless of allowed list, providing infrastructure-level enforcement against prompt injection.

Pattern Support

  • Prefix: ~* blocks labels starting with ~
  • Suffix: *[bot] blocks labels ending with [bot]
  • Exact: stale blocks only that label
  • Standard glob wildcards supported

Backward compatible: undefined/empty blocked list means no restrictions.

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 3383282/b223/vet^From [0-9a-f]\{40\} ache/go/1.25.0/x/tmp/gh-aw/aw.patch /tmp/go-build2309303883/b412/testutil.test -tes�� te 'scripts/**/*.js' --ignore-path .prettierignore -test.v=true e_modules/.bin/node -test.timeout=10git -test.run=^Test -test.short=true--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 GOWORK 64/bin/go ache/node/24.13.0/x64/bin/node 2408�� /tmp/gh-aw-test-runs/20260219-005552-34564/test-1137714267 rev-parse /usr/bin/git b136a0101c461533git GO111MODULE 64/bin/go 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 js/**/*.json' --ignore-path ../../../.prettierignore x_amd64/vet .cfg (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v3 --jq .object.sha -json GO111MODULE 64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE 64/pkg/tool/linux_amd64/vet env 3313979602 GO111MODULE ache/go/1.25.0/x64/bin/go on; \ echo "���git 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 ./../pkg/workflow/js/**/*.json' --ignore-path typecheck .cfg l .cfg abelRemoveBlocke--show-toplevel /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/vet -ato�� -bool -buildtags 0/x64/bin/node -errorsas -ifaceassert -nilfunc 0/x64/bin/node (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v4 --jq .object.sha ./../pkg/workflow/js/**/*.json' --ignore-path ../../../.prettierignore x_amd64/vet /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/vet --local .cfg 64/pkg/tool/linu--show-toplevel ortcfg -ato�� g/timeutil/format.go g/timeutil/format_test.go ache/go/1.25.0/x64/pkg/tool/linux_amd64/compile -errorsas -ifaceassert -nilfunc ache/go/1.25.0/x64/pkg/tool/linux_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v4 --jq .object.sha /tmp/gh-aw-test-runs/20260219-005135-15639/test-1322788439 status /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 --noprofile (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha /repos/test-owner/test-repo/actions/secrets --jq de_modules/.bin/node tierignore 3383282/b039/vetrev-parse ode_modules/.bin--show-toplevel git tion�� /tmp/gh-aw-test-runs/20260219-005135-15639/test-4179609409/.github/workflows config son ignore (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/checkout/git/ref/tags/v5 --jq .object.sha --show-toplevel on rkflow/js/**/*.json /../../.prettiergit erignore t git rev-�� --show-toplevel /opt/hostedtoolcache/go/1.25.0/x--write /usr/bin/git ked-pattern-matcgit -buildtags ode_modules/.bin--show-toplevel /usr/bin/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 tierignore (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha th .prettierignore 3383282/b100/vet.cfg 64/pkg/tool/linux_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/github-script/git/ref/tags/v8 --jq .object.sha t_validator.test.cjs (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 ./../pkg/workflow/js/**/*.json' --ignore-path typecheck /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/vet l .cfg 64/pkg/tool/linu--show-toplevel /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/vet -ato�� -bool -buildtags de_modules/.bin/sh -errorsas -ifaceassert -nilfunc 9303883/b410/styles.test (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-go/git/ref/tags/v4 --jq .object.sha -json GO111MODULE ache/go/1.25.0/x64/pkg/tool/linux_amd64/vet GOINSECURE GOMOD GOMODCACHE ache/go/1.25.0/x64/pkg/tool/linux_amd64/vet env -json GO111MODULE /opt/hostedtoolcache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE 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 ./../pkg/workflow/js/**/*.json' --ignore-path k/gh-aw/gh-aw/cmd/gh-aw/capitali../../../.prettierignore 0/x64/bin/node h ../../../.pretgit .cfg 64/pkg/tool/linu--show-toplevel /opt/hostedtoolcache/go/1.25.0/x64/pkg/tool/linux_amd64/vet t-ha�� ithub/workflows/ai-moderator.md -buildtags ache/node/24.13.0/x64/bin/node -errorsas -ifaceassert -nilfunc node (http block)
    • Triggering command: /usr/bin/gh gh api /repos/actions/setup-node/git/ref/tags/v4 --jq .object.sha -json GO111MODULE /opt/hostedtoolcache/go/1.25.0/x64/bin/go GOINSECURE GOMOD GOMODCACHE go env -json GO111MODULE ache/node/24.13.0/x64/bin/node 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 -tests ache/go/1.25.0/x64/bin/node (http block)
    • 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 -tests x_amd64/vet (http block)
    • Triggering command: /usr/bin/gh gh run download 12345 --dir test-logs/run-12345 GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env ithub/workflows 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 -tests x_amd64/compile (http block)
    • Triggering command: /usr/bin/gh gh run download 12346 --dir test-logs/run-12346 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/2/artifacts
    • Triggering command: /usr/bin/gh gh run download 2 --dir test-logs/run-2 pkg/parser/schemas/main_workflow_schema.json ache/node/24.13.0/x64/bin/node (http block)
    • Triggering command: /usr/bin/gh gh run download 2 --dir test-logs/run-2 GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env 943541375 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 -tests x_amd64/link logs/command.sh (http block)
    • 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 -tests ndor/bin/sh (http block)
    • 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 (http block)
    • Triggering command: /usr/bin/gh gh run download 5 --dir test-logs/run-5 GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go env 943541375 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 json' --ignore-path ../../../.prettierignore 3383282/b047/vet.cfg .cfg pkg/workflow/actbash pkg/workflow/act--norc pkg/workflow/act--noprofile ache/go/1.25.0/x64/pkg/tool/linu../../../**/*.json (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 (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 -ifaceassert -nilfunc x_amd64/vet tion�� te '**/*.cjs' '*.github/workflows/test.md -tests es/.bin/sh (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 ignore-path ../../../.prettierignore (http block)
    • Triggering command: /usr/bin/gh gh api /repos/github/gh-aw/git/ref/tags/v1.0.0 --jq .object.sha --format %(refname) 64/bin/go -d a87e368f275e9fe7commit 64/bin/go 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 ignore-path ../../../.prettierignore (http block)
    • Triggering command: /usr/bin/gh gh api /repos/nonexistent/action/git/ref/tags/v999.999.999 --jq .object.sha /tmp/go-build1751814234/b381/_pkGOINSECURE -trimpath 64/bin/go -p main -lang=go1.25 go env -json GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE 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 (http block)
    • Triggering command: /usr/bin/gh gh run view 12345 --repo nonexistent/repo --json status,conclusion 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/owner/repo/actions/workflows
    • Triggering command: `/usr/bin/gh gh workflow list --json name,state,path --repo owner/repo .cfg OUTPUT fig
  • Update JSO--norc 168.63.129.16 ache/go/1.25.0/x64/pkg/tool/linupkg/workflow/engine_helpers_github_test.go` (http block)
  • Triggering command: `/usr/bin/gh gh workflow list --json name,state,path --repo owner/repo .cfg OUTPUT fig
  • Update JSO--norc 168.63.129.16 ache/go/1.25.0/x64/pkg/tool/linux_amd64/vet` (http block)
  • Triggering command: /usr/bin/gh gh workflow list --json name,state,path --repo owner/repo 64/bin/go GOINSECURE GOMOD GOMODCACHE go k/gh�� -json GO111MODULE 64/bin/go r code style"; tgo GOMOD erignore go (http block)
  • https://api.github.com/repos/owner/repo/contents/file.md
    • Triggering command: /tmp/go-build2309303883/b368/cli.test /tmp/go-build2309303883/b368/cli.test -test.testlogfile=/tmp/go-build2309303883/b368/testlog.txt -test.paniconexit0 -test.v=true -test.parallel=4 -test.timeout=10m0s -test.run=^Test -test.short=true (http block)
    • Triggering command: /tmp/go-build2384211526/b368/cli.test /tmp/go-build2384211526/b368/cli.test -test.testlogfile=/tmp/go-build2384211526/b368/testlog.txt -test.paniconexit0 -test.v=true -test.parallel=4 -test.timeout=10m0s -test.run=^Test -test.short=true GOINSECURE GOMOD GOMODCACHE go env ck '**/*.cjs' '*GOINSECURE GO111MODULE 64/bin/go "; \ else \ echo "��� Warning: .github/aw/actions-lock.json does not exist yet"; \ fi GOMOD GOMODCACHE ache/go/1.25.0/xGO111MODULE (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 tierignore 3383282/b039/vet.cfg ode_modules/.bin/sh (http block)
    • 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 go env ck '**/*.cjs' '*GOINSECURE GO111MODULE 64/bin/go GOINSECURE GOMOD GOMODCACHE go (http block)
  • https://api.github.com/user
    • Triggering command: `/usr/bin/gh gh api user --jq .login ettierignore emove-labels
  • Update Go types: AddLabelsConfig, RemoveLabelsConfig, ListJobConfig
  • Update JSO--norc .cfg` (http block)
  • Triggering command: /usr/bin/gh gh api user --jq .login t_validator.test.cjs ion_reference_test.go ache/go/1.25.0/x64/pkg/tool/linux_amd64/vet ion_resolver_tessh ion_sha_checker.-c ion_sha_checker_tsc --noEmit ache/go/1.25.0/x64/pkg/tool/linu--write ode_�� se 3383282/b132/vet.cfg ode_modules/.bin/node (http block)
  • Triggering command: /usr/bin/gh gh api user --jq .login -c=4 -nolocalimports -importcfg /tmp/go-build2309303883/b355/importcfg -pack /home/REDACTED/work/gh-aw/gh-aw/internal/tools/actions-build/main.go ache/go/1.25.0/x64/pkg/tool/linu--write ode_�� se 3383282/b084/vet.cfg es/.bin/node (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>Feature request: blocked pattern matching for add-labels safe output</issue_title>
<issue_description>🤖 Filed with agentic-workflows agent mid-design conversation

Problem

The add-labels safe output currently supports allowed: [list] (allowlist) and max: N (count cap), but has no way to deny labels matching a pattern.

In large repositories like microsoft/vscode with 600+ labels, maintaining an exhaustive allowlist is impractical. However, there are classes of labels that should never be applied by an agentic workflow — for example:

  • Labels prefixed with ~ (tilde) are used as workflow trigger labels (e.g., ~stale triggers the triage workflow). An agent applying these could cause unintended workflow cascades.
  • Labels prefixed with * have special administrative meaning.

Without infrastructure-level enforcement, these constraints can only be expressed in the prompt ("please don't apply labels starting with ~"), which is a weak defense against prompt injection attacks on workflows that process untrusted public input.

Proposed Solution

Add a blocked: field to add-labels (and potentially remove-labels) safe outputs that supports pattern matching:

safe-outputs:
  add-labels:
    blocked: ["~*", "*\\**"]   # deny labels starting with ~ or *
    max: 5

Ideally this would support at minimum prefix matching (e.g., ~* matches any label starting with ~), and potentially the same glob/wildcard syntax used elsewhere in gh-aw (e.g., forks: patterns).

Why This Matters

For workflows that triage public issues (where the issue content is untrusted and may contain prompt injection payloads), the safe-outputs config is the hard security boundary — it's the "you literally can't" layer vs. the prompt-level "please don't" layer. Being able to deny dangerous label patterns at this layer would meaningfully reduce the attack surface for agentic triage workflows.

Current Workaround

Prompt-level instructions telling the agent not to apply certain labels. This works under normal conditions but is not a reliable defense against adversarial input.</issue_description>

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

@pelikhan Review other blocked feature and reuse glob functions.

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.



✨ PR Review Safe Output Test - Run 22164030442

💥 [THE END] — Illustrated by Smoke Claude

Copilot AI and others added 2 commits February 19, 2026 00:49
- Update Go types: AddLabelsConfig, RemoveLabelsConfig, ListJobConfig
- Update JSON schema: add blocked field validation
- Update handler config builders to include blocked patterns
- Add Go tests for blocked pattern parsing and compilation
- JavaScript validation and handler updates coming next

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
- Add comprehensive tests for validateLabels with blocked patterns
- Test security precedence (blocked before allowed)
- Test exact match, prefix patterns, and bot patterns
- All tests passing

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Add blocked pattern matching for add-labels safe output Add blocked pattern matching for add-labels and remove-labels Feb 19, 2026
@pelikhan pelikhan marked this pull request as ready for review February 19, 2026 01:06
Copilot AI review requested due to automatic review settings February 19, 2026 01:06
@pelikhan pelikhan merged commit 70d0bd1 into main Feb 19, 2026
60 checks passed
@pelikhan pelikhan deleted the copilot/add-blocked-pattern-matching-again branch February 19, 2026 01: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

Adds denylist-style pattern matching (blocked) for add-labels and remove-labels safe outputs to prevent unsafe label application/removal (e.g., workflow trigger labels or bot/admin labels), strengthening defense-in-depth against prompt injection.

Changes:

  • Extend workflow config types and JSON schema to accept blocked: []string for add/remove label safe outputs.
  • Pass blocked through handler config into the JavaScript label handlers.
  • Update validateLabels() to filter blocked patterns (via matchesSimpleGlob) before applying allowlists, with accompanying tests.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
pkg/workflow/safe_output_builder.go Adds Blocked to shared list-job config and emits a _BLOCKED env var.
pkg/workflow/add_labels.go Adds Blocked to AddLabelsConfig and threads it into list-job config/logging.
pkg/workflow/remove_labels.go Adds Blocked to RemoveLabelsConfig and logs blocked count.
pkg/workflow/compiler_safe_outputs_config.go Serializes blocked into JS handler config for add/remove labels.
pkg/parser/schemas/main_workflow_schema.json Adds blocked array to add/remove label safe-output schema.
actions/setup/js/safe_output_validator.cjs Implements blocked-pattern filtering inside validateLabels().
actions/setup/js/add_labels.cjs Reads config.blocked and passes it into validateLabels().
actions/setup/js/remove_labels.cjs Reads config.blocked and passes it into validateLabels().
actions/setup/js/safe_output_validator.test.cjs Adds unit tests for blocked pattern behavior and precedence.
pkg/workflow/compile_outputs_label_test.go Adds compile/parse tests ensuring blocked is parsed and emitted into handler config.
Comments suppressed due to low confidence (1)

pkg/parser/schemas/main_workflow_schema.json:5242

  • The schema sets blocked.minItems to 1, which rejects blocked: []. The PR description and runtime validator both treat an empty blocked list as “no restrictions”, so this is inconsistent. Either allow empty arrays here (minItems: 0) or update the documented behavior to require omitting blocked entirely when you want no blocking.
                "blocked": {
                  "type": "array",
                  "description": "Optional list of blocked label patterns (supports glob patterns like '~*', '*[bot]'). Labels matching these patterns will be rejected. Applied before allowed list filtering for security.",
                  "items": {
                    "type": "string"
                  },
                  "minItems": 1,
                  "maxItems": 50

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

Comment on lines +5293 to +5300
"blocked": {
"type": "array",
"description": "Optional list of blocked label patterns (supports glob patterns like '~*', '*[bot]'). Labels matching these patterns will be rejected. Applied before allowed list filtering for security.",
"items": {
"type": "string"
},
"minItems": 1,
"maxItems": 50
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

Same issue as add-labels: blocked.minItems is 1, so blocked: [] is schema-invalid even though the PR description/runtime treat empty as “no restrictions”. Consider minItems: 0 (or update the documented behavior to require omitting the field).

This issue also appears on line 5235 of the same file.

Copilot uses AI. Check for mistakes.
strict: false
safe-outputs:
add-labels:
blocked: ["~*", "*\\**"]
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

The test config uses the blocked pattern "*\\**", which is not explained elsewhere (schema/docs/examples mention patterns like "~*" and "*[bot]"). This makes the test harder to understand and may imply an escape syntax that matchesSimpleGlob doesn’t actually support for literal *. Consider switching this test data to a documented pattern (e.g., "*[bot]") or explicitly documenting/implementing the intended escaping behavior.

Suggested change
blocked: ["~*", "*\\**"]
blocked: ["~*", "*[bot]"]

Copilot uses AI. Check for mistakes.
Comment on lines +107 to +114
validLabels = labels.filter(label => {
const labelStr = String(label).trim();
const isBlocked = blockedPatterns.some(pattern => matchesSimpleGlob(labelStr, pattern));
if (isBlocked) {
blockedLabels.push(labelStr);
}
return !isBlocked;
});
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

Blocked-pattern matching is currently performed on labelStr = String(label).trim() before sanitizeLabelContent/Unicode hardening. Since sanitizeLabelContent removes zero-width chars and normalizes text, a label like ~\u200bstale could evade the ~* blocked pattern check and then be normalized to ~stale later, defeating the security boundary. Consider normalizing/sanitizing the label string (at least hardenUnicodeText, or reuse sanitizeLabelContent) before evaluating blocked patterns (and ideally use the same normalized value for subsequent allowlist checks).

Copilot uses AI. Check for mistakes.
return !isBlocked;
});
if (blockedLabels.length > 0) {
core.info(`Filtered out ${blockedLabels.length} blocked labels: ${blockedLabels.join(", ")}`);
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

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

validateLabels logs blocked labels by joining raw, untrusted label strings. Since labels come from agent output, this can inject newlines/control chars into logs and make traces hard to read. Consider logging a sanitized/escaped representation instead (e.g., JSON.stringify(blockedLabels) and/or sanitizing each label before logging).

Suggested change
core.info(`Filtered out ${blockedLabels.length} blocked labels: ${blockedLabels.join(", ")}`);
core.info(`Filtered out ${blockedLabels.length} blocked labels: ${JSON.stringify(blockedLabels)}`);

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.

Feature request: blocked pattern matching for add-labels safe output

2 participants