Skip to content

Fix mention allowlist token selection during safe-output ingestion - #66571

Merged
pelikhan merged 7 commits into
mainfrom
pelikhan-safe-output-mention-token
Oct 7, 2026
Merged

pelikhan merged 7 commits into
mainfrom
pelikhan-safe-output-mention-token

Conversation

@pelikhan

@pelikhan pelikhan commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Team members allowed by safe-outputs.mentions.allowed-teams could have their mentions escaped during ingestion because that step used the default Actions token without access to team membership. Downstream handlers could not restore notifications after the body had already been sanitized.

Add dedicated ingestion credentials under safe-outputs.mentions.github-token and safe-outputs.mentions.github-app. Ingestion never inherits global safe-outputs.github-token, safe-outputs.github-app, per-handler credentials, or GH_AW_GITHUB_TOKEN. Without mention-specific credentials it uses the default Actions token. Omitting the mention-specific app skips ingestion token minting without disabling mention filtering or changing downstream write credentials.

safe-outputs:
  mentions:
    allowed-teams: [my-org/my-team]
    github-token: ${{ secrets.MENTIONS_READ_PAT }}
  add-comment:

A mention-specific app token is minted in the agent job after agent execution and MCP gateway shutdown, immediately before Ingest agent output. It takes precedence over the mention PAT, requests comment-author read scopes and members: read for configured teams, and preserves explicit app permission overrides. With ignore-if-missing: true, missing app credentials fall back to the mention PAT, then GITHUB_TOKEN only. Installation owner/repository selection, wildcard scoping, relay repository selection, and missing-key guards reuse existing compiler helpers; minting and owner resolution match ingestion's always() behavior.

Mention credentials are excluded from agent-visible validation and handler configuration. Write app keys remain absent from the agent job. Same-job mention-token references require an earlier producer only in the agent job, with mention-specific diagnostics. Updated schema, generated reference, normative specification, and secret-safe logging describe the configuration; regression coverage checks selection, scoping, fallback, ordering, and credential isolation.

Fixes: #50282

Validation

  • Passed build, formatting, schema reference generation, and focused credential/isolation tests.
  • Passed the full compiler and parser suites: PATH="/opt/homebrew/bin:$PATH" TMPDIR=/private/tmp go test ./pkg/workflow ./pkg/parser -count=1.
  • Passed the final focused app ordering and same-job token tests.
  • Passed make check-workflow-drift: all 324 compiled workflows remain in sync.
  • Publication gates remain blocked by pre-existing custom-linter findings for unchecked fmt.Fprintf calls in the lifecycle compiler and slice indexing in step-token validation. Standard Go lint passed.
  • Existing JavaScript collector tests could not start because dependency restoration received HTTP 404 from the npm feed for pinned vite@8.3.2. No runtime JavaScript was changed.

pelikhan and others added 2 commits October 7, 2026 05:47
Resolve mention allowlists with the configured global token before sanitization. Cover credential selection and same-job token validation.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Document mention allowlist credential requirements and add secret-safe compiler debug logs for the selected ingestion token source.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Comment on lines +2139 to +2141
- When `safe-outputs.github-token` is configured, **Ingest agent output** MUST use that global token to resolve mention allowlists, including `mentions.allowed-teams`, before sanitization.
- Allowed team members' raw `@login` mentions MUST be preserved during ingestion so downstream handlers can notify those users.
- Without a global `safe-outputs.github-token`, ingestion MUST retain the default GitHub Actions token. Per-output token overrides and GitHub App tokens minted in the `safe_outputs` job MUST NOT be used by ingestion in the `agent` job.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Addressed in 344905c: mention/team resolution now runs only in the trusted safe_outputs job. Agent-job ingestion performs no privileged mention lookups and receives no mention-specific credentials.

pelikhan and others added 2 commits October 7, 2026 06:09
Mint a dedicated installation token after agent execution and gateway shutdown, request ingestion read scopes, and preserve ignore-if-missing fallback semantics. Cover credential scope, ordering, isolation, and relay workflows, and update the specification.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@pelikhan
pelikhan marked this pull request as ready for review October 7, 2026 14:19
Copilot AI balanced review requested due to automatic review settings October 7, 2026 14:19
@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

✅ Test Quality Sentinel completed test quality analysis.

Test Quality Sentinel skipped because pre-fetch PR data was unavailable: unable to fetch test file diff

🧪 Test quality analysis by Test Quality Sentinel

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

✅ Ponytail Reviewer completed successfully!

Lean already. Ship.

Generated by Ponytail Reviewer for #66571

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

🧠 Matt Pocock Skills Reviewer has completed the skills-based review. ✅

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

✅ PR Code Quality Reviewer completed the code quality review.

🔎 Code quality review by PR Code Quality Reviewer

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

✅ Design Decision Gate 🏗️ completed the design decision gate check. See the comment below for the result and any generated ADR draft.

🏗️ ADR gate enforced by Design Decision Gate 🏗️

@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Comment Memory

Peek at saved memory (pr-code-quality-reviewer)
reviewed_at: 2026-10-07T14:22:33Z
review_event: REQUEST_CHANGES
top_themes:
  - mention-ingestion token permissions don't match the Issues API lookup used for explicit add_comment targets
files_reviewed:
  - docs/src/content/docs/reference/frontmatter-full.md
  - docs/src/content/docs/reference/safe-outputs.md
  - docs/src/content/docs/specs/safe-outputs-specification.md
  - pkg/parser/schemas/main_workflow_schema.json
  - pkg/workflow/agentic_output_test.go
  - pkg/workflow/compiler_yaml_step_lifecycle.go
  - pkg/workflow/safe_outputs_config_types.go
  - pkg/workflow/safe_outputs_mentions_test.go
  - pkg/workflow/safe_outputs_messages_config.go
  - pkg/workflow/safe_outputs_step_token_validation.go
  - pkg/workflow/safe_outputs_step_token_validation_test.go
  - pkg/workflow/security_architecture_formal_test.go
comment_count: 1

Note

This comment is managed by comment memory.

Expand the saved memory block to view or edit the persistent context for this thread.
Edit only the text inside the backtick fences; workflow metadata and the footer are regenerated automatically.

Learn more about comment memory

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 114.9 AIC · ⌖ 5.52 AIC · ⊞ 19.6K · ◷
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The mention-ingestion credential split is headed the right way, but the new app-token permission derivation is still wrong for PR-only comment workflows: the ingestion pre-scan fetches explicit comment targets through the Issues API, while the minted token can now omit issues: read. That breaks target-author allowlisting and silently re-escapes valid mentions in one of the narrower supported configurations.

### Blocking theme

The new token should be scoped to the endpoints ingestion actually calls, not mirrored from the enabled write targets on add-comment. Right now a workflow with add-comment.pull-requests: true and add-comment.issues: false can compile a mention-ingestion token that is too weak for github.rest.issues.get(...), so the feature regresses precisely the PR-only case it is supposed to preserve.

🔎 Code quality review by PR Code Quality Reviewer · copilot · gpt54 · 114.9 AIC · ⌖ 5.52 AIC · ⊞ 19.6K
Comment /review to run again

permissions := NewPermissions()
if data.SafeOutputs.AddComments != nil {
commentPermissions := buildAddCommentPermissions(data.SafeOutputs.AddComments)
for _, scope := range []PermissionScope{PermissionIssues, PermissionPullRequests} {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This permission derivation is too narrow for the lookup the ingestion step actually performs, so PR-only comment workflows can still lose allowed mentions.

💡 Why this blocks the change

The new token minting logic only copies issues/pull-requests scopes that happen to be enabled on add-comment, but the ingestion pre-scan always resolves explicit add_comment.item_number targets through github.rest.issues.get(...) before sanitization. A workflow that intentionally allows only pull-request comments (pull-requests: true, issues: false) will therefore mint an ingestion token without issues: read, causing the target-author lookup to fail and valid @author mentions in PR comments to be escaped.

A safer fix is to derive ingestion permissions from the endpoints this step calls, not from the handler toggles. At minimum, the mention-ingestion token needs issues: read whenever add-comment is enabled, because the pre-scan unconditionally uses the Issues API for explicit comment targets.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Addressed in 344905c: the safe-output job now retains issues: read for explicit comment-target author lookups when comments are PR-only, with regression coverage for that configuration.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Mention-app overrides can currently mint a write-capable token despite the read-only ingestion contract.

Review effort: Balanced
Findings: 1 High severity

Open (1)
What changed in this PR

Adds dedicated mention-resolution credentials so ingestion can preserve allowed team mentions without exposing downstream write credentials.

Changes:

  • Adds mention-specific PAT and GitHub App configuration.
  • Generates post-agent ingestion tokens with scoped permissions and fallback behavior.
  • Adds validation, tests, schema, and documentation updates.
File Description
pkg/​workflow/​security_architecture_formal_test.go Updates credential-isolation assertions.
pkg/​workflow/​safe_outputs_step_token_validation.go Validates mention token step references.
pkg/​workflow/​safe_outputs_step_token_validation_test.go Tests same-job mention tokens.
pkg/​workflow/​safe_outputs_messages_config.go Parses mention credentials.
pkg/​workflow/​safe_outputs_mentions_test.go Tests parsing and credential isolation.
pkg/​workflow/​safe_outputs_config_types.go Adds mention credential fields.
pkg/​workflow/​compiler_yaml_step_lifecycle.go Selects and mints ingestion credentials.
pkg/​workflow/​agentic_output_test.go Covers token selection, fallback, and ordering.
pkg/​parser/​schemas/​main_workflow_schema.json Adds schema fields.
docs/​src/​content/​docs/​specs/​safe-outputs-specification.md Defines ingestion credential requirements.
docs/​src/​content/​docs/​reference/​safe-outputs.md Documents configuration and behavior.
docs/​src/​content/​docs/​reference/​frontmatter-full.md Updates generated frontmatter reference.

Comment on lines +298 to +300
steps := c.buildGitHubAppTokenMintStepForJob("agent", app, permissions, fallbackRepo,
inferSingleCheckoutRepositoryForGitHubAppOwner(data),
"Generate GitHub App token for output ingestion", stepID)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Addressed in 344905c: mention GitHub App permission overrides are validated as read-only; only read and none are accepted, and write-level overrides fail compilation.

@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

🏗️ Design Decision Gate: ADR Required

This PR triggers ADR enforcement and no Architecture Decision Record was found.

Why enforcement applies

  • has_implementation_label: false
  • New lines in business-logic directories (pkg/): 444 additions across 12 files — above the default threshold of 100
  • No .design-gate.yml override present (defaults used)

ADR search results

Action taken

I drafted an ADR from the PR evidence and committed it to this branch:

  • docs/adr/66571-dedicated-mention-ingestion-credentials.md — Use Dedicated, Non-Inheriting Credentials for Mention Allowlist Resolution During Ingestion (Status: Draft)

Inferred decision summary:

Item Finding
Decision Introduce safe-outputs.mentions.github-token / safe-outputs.mentions.github-app as ingestion-only credentials that never inherit from any other safe-output credential; mint the mention app token in the agent job after agent execution and gateway shutdown
Driver Ingestion ran with the default Actions token and could not read team membership, so allowed @login mentions were escaped before handlers could restore them (#50282); write credentials cannot be placed in the agent job
Alternatives (1) Inherit existing safe-output write credentials; (2) defer mention restoration to downstream handlers; (3) mint the token at job start
Consequences +Allowed mentions survive sanitization, +least-privilege read-only scope preserved / −third credential concept with its own precedence chain, −non-inheritance is a latent surprise for users expecting cascade

Next step for the author: review docs/adr/66571-dedicated-mention-ingestion-credentials.md, correct any [TODO: verify] items and the decider list, and change the status from Draft to Proposed/Accepted before merge.

🏗️ ADR gate enforced by Design Decision Gate 🏗️ · pi · opus50 · 41.3 AIC · ⌖ 50.4 AIC · ⊞ 1.7K · ◷
Comment /review to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Skills-Based Review 🧠

Applied /diagnosing-bugs and /codebase-design to the mention-credential isolation fix. The change is well-scoped: it introduces dedicated safe-outputs.mentions.github-token/github-app credentials, reuses existing app-token-minting helpers (buildGitHubAppTokenMintStepForJob, resolveGitHubAppOwner, combineGitHubIfExpressions) rather than duplicating logic, and ships extensive regression coverage for ordering, fallback chains (app → PAT → default token), wildcard/owner resolution, and credential isolation (TestOutputCollectionGitHubAppToken, TestOutputCollectionGitHubAppOwnerAndWildcard, TestOutputCollectionMentionCredentialsIndependent, TestFormal_P10_WriteTokenIsolatedToSafeOutput).

No blocking issues found. A few minor/non-blocking observations below.

📋 Key Themes & Highlights

Key Themes

  • Root cause correctly addressed, not just the symptom (/diagnosing-bugs): the fix mints a dedicated, properly-scoped (read-only + members:read) ingestion token rather than broadening the default Actions token or reusing the write-scoped safe-outputs.github-token, which would have reintroduced the isolation problem the spec (AR1) guards against.
  • Consistent with existing conventions (/codebase-design): the if: always() rewrite in generateOutputCollectionGitHubToken mirrors the existing pattern in compiler_safe_outputs_job.go (strings.Cut(step, " if: ")), so it's not a novel risk, just a repeated idiom.
  • Credential isolation is enforced at multiple layers: buildMentionsHandlerConfig whitelists only non-credential fields for the runtime handler config, json:"-" tags keep GitHubToken/GitHubApp out of any JSON marshaling, and TestParseMentionsCredentials explicitly asserts secret values never leak into serialized config.
  • Good regression-test design: TestSameJobStepTokenMissingInAgentIngestionFails and TestSameJobStepTokenMentionsOnlyMintedInAgentCompiles exercise the exact same-job step-token-ordering bug class the step-token validator is meant to catch, with mention-specific error-field naming (safe-outputs.mentions.github-token) for clearer diagnostics.

Minor Observations (non-blocking)

  • generateOutputCollectionGitHubToken derives read permissions for comment-author lookups only from AddComments (buildAddCommentPermissions). If a future handler besides add-comment also needs ingestion-time author/collaborator lookups, this list would need to grow in lockstep — worth a short code comment noting that the permission set is deliberately tied to today's resolve_mentions.cjs consumers, to prevent silent permission drift if new handlers are added later.
  • The bot-flagged spec line (docs/specs/safe-outputs-specification.md) about privileged token injection appears to refer to pre-existing text rather than this PR's new bullet list — the new "Ingestion credential requirements" section already explicitly documents that safe-outputs.github-token is not inherited, which resolves that concern for new readers.

Positive Highlights

  • ✅ Minting step reuses inferSingleCheckoutRepositoryForGitHubAppOwner and hasWorkflowCallTrigger instead of reimplementing owner/fallback-repo derivation.
  • ✅ assert.Nil(t, app.Permissions, "minting must not mutate the configured app") is a nice defensive test guarding against accidental shared-state mutation.
  • ✅ Formal security test (TestFormal_P10_WriteTokenIsolatedToSafeOutput) was updated to also assert the ingestion token never requests safe_outputs-job write permissions, closing a gap the new feature could have reopened.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · copilot · sonnet50 · 120.8 AIC · ⌖ 15.7 AIC · ⊞ 10.1K
Comment /matt to run again

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Impeccable Skills Review

Applied modes: harden, audit (bug-fix/credential-isolation change).

Summary

This PR adds dedicated safe-outputs.mentions.github-token / safe-outputs.mentions.github-app credentials for mention-allowlist resolution during output ingestion, decoupling it from the default Actions token and from write credentials.

Findings

No high-signal, blocking issues found tied to changed lines. Notable positive security properties verified:

  • Mention credentials are correctly isolated from write credentials (safe-outputs.github-token/github-app) — verified via TestAgenticOutputCollectionWithGitHubApp and TestFormal_P10_WriteTokenIsolatedToSafeOutput.
  • Mention app token minting runs in the agent job only after Stop MCP Gateway/agent execution completes (asserted order in tests), consistent with the "trusted post-agent step" design goal.
  • always() is correctly combined with any existing ignore-if-missing condition rather than overwriting it.
  • Mention credentials are excluded from JSON-serialized/agent-visible config (json:"-" tags) and from buildMentionsHandlerConfig output — verified by TestParseMentionsCredentials.
  • Literal (non-expression) mentions.github-token values are rejected by existing schema validation.
  • Same-job step-token validation (collectSafeOutputStepTokenIDs/validateSafeOutputStepTokenReferences) was correctly extended to cover mentions.github-token with an accurate field name in the error (safe-outputs.mentions.github-token).
  • Owner/repository fallback reuses the same needs.activation.outputs.target_repo_name pattern already used by safe_outputs/conclusion jobs, and the agent job already depends on activation, so no new wiring risk.
  • Ran go build ./... and the full set of new/modified tests (TestOutputCollection*, TestSameJobStepToken*, TestFormal_P10_WriteTokenIsolatedToSafeOutput, TestParseMentionsCredentials, TestAgenticOutputCollection*) — all pass.

A pre-existing GHAS comment flagged documentation wording about token privilege; this PR's actual code path only uses mentions.github-token/mentions.github-app, not the global write token, so the implementation is not affected — likely a stale/doc-only concern outside the scope of actionable code changes.

No inline comments added since no blocking or high-confidence issues were found on changed lines.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · copilot · sonnet50 · 170.4 AIC · ⌖ 13.3 AIC · ⊞ 8.1K

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot address the following outstanding work in one pass:

  1. Update this branch with the latest main using make merge-main, resolving any conflicts and preserving the intended changes.
  2. Review (docs/src/content/docs/specs/safe-outputs-specification.md:2141): This spec change requires the privileged global safe-outputs.github-token (typically a PAT with read:org, and often broader scopes) to be injected into the Ingest agent output step, which runs inside the agent job. That job is the untrusted boundary: the agent (subject to prompt injection from issue/PR content) runs with filesystem access and can modify ${ runner.temp }/gh-aw/actions/collect_ndjson_output.cjs, which the ingest step requires and executes with the token available via github-token. The whole reason ... - Fix mention allowlist token selection during safe-output ingestion #66571 (comment)
  3. Review (pkg/workflow/compiler_yaml_step_lifecycle.go:284): This permission derivation is too narrow for the lookup the ingestion step actually performs, so PR-only comment workflows can still lose allowed mentions. - Fix mention allowlist token selection during safe-output ingestion #66571 (comment)
  4. Review (pkg/workflow/compiler_yaml_step_lifecycle.go:300): Restrict mention-app permission overrides to read-only values before minting this token. The reused github_app schema accepts write for App-only scopes, while the existing no-write validator only checks tools.github.github-app; appTokenPermissionFields therefore allows configurations such as safe-outputs.mentions.github-app.permissions.members: write to mint an unnecessarily write-capable token in the agent job. This contradicts the documented read-only ingestion purpose and expands the impact of any post-agent step compromise. - Fix mention allowlist token selection during safe-output ingestion #66571 (comment)

Push the necessary fixes, reply to each listed review thread and resolve it when addressed. Ignore feedback already answered or resolved. Use the pr-finisher skill and stop when only human review or CI remains; do not trigger CI.

Sous-chef head: b8df84b
Sous-chef work: 51275ae9f488d009ddf035762b897cc501b0d4e162e5e5ede9a1a84c7e81f07e 5418e221a620849543b778a6cf4db77cd8ca9821469242363b4d5e617df98112 bfd646a994f0e7eb66f46b2cbaee65ff9483b4d586931ee09baac241fe3b7ac3
Sous-chef state: af09e9b61eb7913bbfbeb7e62f51b95ef70beb3acde5ebfe103a3ac0d6caec9f

Generated by 👨‍🍳 PR Sous Chef · pi · haiku45 · 5.77 AIC · ⌖ 7.82 AIC · ⊞ 1K · ◷
Comment /souschef to run again

Copilot AI and others added 2 commits October 7, 2026 15:05
…mention-token

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Copilot AI requested a review from gh-aw-bot October 7, 2026 15:39
@pelikhan
pelikhan merged commit 1ad2b9f into main Oct 7, 2026
1 check passed
@pelikhan
pelikhan deleted the pelikhan-safe-output-mention-token branch October 7, 2026 15:47
@github-actions

github-actions Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.91.5

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.

Safe-outputs allowed-teams ignored and mentions sanitized because of wrong github token

5 participants