Skip to content

[aw] Decouple Process Safe Outputs client token from project-specific token - #45999

Merged
pelikhan merged 18 commits into
mainfrom
copilot/aw-safe-outputs-update-project-token-leaking
Jul 16, 2026
Merged

[aw] Decouple Process Safe Outputs client token from project-specific token#45999
pelikhan merged 18 commits into
mainfrom
copilot/aw-safe-outputs-update-project-token-leaking

Conversation

CopilotAI commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

update-project.github-token was being promoted to the shared Process Safe Outputs step client token, overriding safe-outputs.github-token and causing non-project handlers to run under the wrong credential. This change restores token separation: shared safe-output handlers use the safe-outputs token path, while project handlers keep using the dedicated project token channel.

  • Token precedence fix (compiler)

    • Updated handler-manager step token selection to stop preferring project token for with.github-token.
    • with.github-token now follows safe-outputs precedence only:
      1. safe-outputs.github-token
      2. fallback (secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN)
  • Project token isolation preserved

    • Kept GH_AW_PROJECT_GITHUB_TOKEN wiring unchanged for project operations.
    • Project handlers continue receiving project credentials via env/config, not by elevating the shared github-script client.
  • Focused regression coverage

    • Updated and expanded handler-manager token tests to assert split-token behavior:
      • project token present in GH_AW_PROJECT_GITHUB_TOKEN
      • with.github-token stays on safe-outputs token/fallback
      • mixed config case (safe-outputs.github-token + update-project.github-token) behaves correctly
// before: project token could override shared client tokenifprojectToken!="" {
configToken=projectToken
} elseifdata.SafeOutputs!=nil&&data.SafeOutputs.GitHubToken!="" {
configToken=data.SafeOutputs.GitHubToken
}
// after: shared client token follows safe-outputs precedence onlyifdata.SafeOutputs!=nil&&data.SafeOutputs.GitHubToken!="" {
configToken=data.SafeOutputs.GitHubToken
}

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
CopilotAI changed the title [WIP] Fix token leaking in safe outputs for update-project[aw] Decouple Process Safe Outputs client token from project-specific tokenJul 16, 2026
CopilotAI requested a review from pelikhanJuly 16, 2026 13:42
@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot add tests for all safe outputs to prevent github token pollution

CopilotAIand others added 2 commits July 16, 2026 13:48
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

CopilotAI commented Jul 16, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot add tests for all safe outputs to prevent github token pollution

Added comprehensive regression coverage in 65dfd4e: new TestHandlerManagerGitHubTokenIsolationAcrossSafeOutputHandlers iterates all registered safe-output handlers and verifies with.github-token stays on safe-outputs.github-token while project token remains isolated to GH_AW_PROJECT_GITHUB_TOKEN.

@pelikhan
pelikhan marked this pull request as ready for review July 16, 2026 14:27
CopilotAI review requested due to automatic review settings July 16, 2026 14:27
@github-actions

github-actionsBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actionsBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Design Decision Gate 🏗️ completed the design decision gate check.

No ADR enforcement needed: PR #45999 does not have the 'implementation' label and has ≤100 new lines of code in business logic directories (75 additions detected, threshold is 100).

@github-actions

github-actionsBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

Test Quality Sentinel completed test quality analysis.

@github-actions

github-actionsBot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

PR Code Quality Reviewer completed the code quality review.

CopilotAI 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.

Pull request overview

This PR updates the gh-aw workflow compiler to prevent project-specific credentials (e.g., safe-outputs.update-project.github-token) from overriding the shared Process Safe Outputs step’s with.github-token, restoring intended token separation between shared safe-output processing and project operations.

Changes:

  • Updated buildHandlerManagerStep token selection so with.github-token follows safe-outputs precedence only (safe-outputs token → magic fallback).
  • Updated/expanded Go tests to assert split-token behavior between GH_AW_PROJECT_GITHUB_TOKEN and the shared with.github-token.
  • Regenerated the smoke workflow lockfile to reflect the updated token wiring.
Show a summary per file
FileDescription
pkg/workflow/safe_outputs_handler_manager_token_test.goUpdates and expands regression tests to ensure project token stays isolated from the shared handler-manager with.github-token.
pkg/workflow/compiler_safe_outputs_steps.goChanges the compiler’s handler-manager step to stop preferring the project token for the shared github-script client.
.github/workflows/smoke-project.lock.ymlRegenerated workflow output showing the shared github-token now uses the safe-outputs fallback chain.

Review details

Tip

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

  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Low

Comment on lines 274 to +276
// With section for github-token
// Use the standard safe outputs token for all operations.
// If project operations are configured, prefer the project token for the github-script client.
// Rationale: update_project/create_project_status_update call the Projects v2 GraphQL API, which
// cannot be accessed with the default GITHUB_TOKEN. GH_AW_PROJECT_GITHUB_TOKEN is the required
// token for Projects v2 operations.
// Use the standard safe-outputs token for the shared github-script client.
// Project operations use GH_AW_PROJECT_GITHUB_TOKEN from env with dedicated handler logic.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Addressed in 60abf42 and prior commits: runtime project-handler auth is now wired through per-handler token fallback/client rebinding, so project operations no longer depend on the shared step-level client token path.

@github-actionsgithub-actionsBot mentioned this pull request Jul 16, 2026
@github-actions

Copy link
Copy Markdown
Contributor

🧪 Test Quality Sentinel Report

Test Quality Score: 82/100 — Excellent

Analyzed 3 test function(s) (19+ subtests via table-driven): 3 design, 0 implementation, 0 violation(s).

📊 Metrics (3 tests)
MetricValue
Analyzed3 (Go: 3, JS: 0)
✅ Design3 (100%)
⚠️ Implementation0 (0%)
Edge/error coverage3 (100%)
Duplicate clusters0
InflationNo
🚨 Violations0
TestFileClassificationIssues
TestHandlerManagerGitHubTokenEnvVarForCrossReposafe_outputs_handler_manager_token_test.godesign_test · behavioral_contractNone
TestHandlerManagerProjectGitHubTokenEnvVarsafe_outputs_handler_manager_token_test.godesign_test · behavioral_contractNone
TestHandlerManagerGitHubTokenIsolationAcrossSafeOutputHandlerssafe_outputs_handler_manager_token_test.godesign_test · behavioral_contractNone

Verdict

Passed. 0% implementation tests (threshold: 30%). All 3 tests enforce behavioral contracts with both positive and negative assertions, good edge/precedence coverage, valid build tags, and no mock violations.

Warning

Firewall blocked 1 domain

The following domain was blocked by the firewall during workflow execution:

  • awmgmcpg

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
allowed:
- defaults
- "awmgmcpg"

See Network Configuration for more information.

🧪 Test quality analysis by Test Quality Sentinel · 22.7 AIC · ⌖ 8.54 AIC · ⊞ 6.8K ·
Comment /review to run again

@github-actionsgithub-actionsBot 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.

✅ Test Quality Sentinel: 82/100. 0% implementation tests (threshold: 30%).

@github-actionsgithub-actionsBot 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 /tdd — approving with one suggestion on test robustness.

Key Themes & Highlights

Positive Highlights

  • Root cause properly addressed: the project-token-preference branch is cleanly removed; token precedence is now unambiguous.
  • Minimal, surgical change in compiler_safe_outputs_steps.go — only the two lines causing the bug are removed.
  • Comment block updated to accurately reflect the new precedence order.
  • Smoke-test lock file updated in lock-step, showing the fix propagates end-to-end.
  • New isolation test iterates over handlerRegistry dynamically, staying complete as new handlers are added.

Minor Concern (see inline comment)

Empty-map configs (map[string]any{}) in the new isolation test may silently skip handlers that need required fields, giving false confidence. A one-line assert.Contains(t, yamlStr, handlerName) guard would close this gap.

🧠 Reviewed using Matt Pocock's skills by Matt Pocock Skills Reviewer · 24.3 AIC · ⌖ 4.45 AIC · ⊞ 6.7K
Comment /matt to run again

"project": "https://github.com/orgs/myorg/projects/1",
},
}

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.

[/tdd] The empty-map handler entries (map[string]any{}) in the new isolation test may silently fail to parse for handlers that require specific config fields, meaning the sub-test only truly exercises the update-project token path rather than the named handler's code.

💡 Suggestion

Consider using a minimal valid config for each handler, or at least assert that the compiled step includes the expected handler entry (not just the token line). For example:

// Verify handler is actually present in the compiled YAML, not silently skippedassert.Contains(t, yamlStr, handlerName, "handler %q should appear in compiled step", handlerName)

This would catch cases where an empty config causes a handler to be omitted entirely, giving you false confidence in the isolation property.

@copilot please address this.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Addressed in 60abf42 and prior commits: the test now avoids vacuous passes by explicitly checking for the handler config key in GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG and skipping isolation assertions when a handler is not materialized from minimal config.

@github-actionsgithub-actionsBot 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.

Review: Two test validity concerns — non-blocking

The compiler change itself is correct: removing project token from with.github-token precedence is the right fix, and the lock file update is consistent.

Findings

Medium — False-positive assertion in parameterized test
assert.Contains(t, yamlStr, "GH_AW_PROJECT_GITHUB_TOKEN: ..." passes unconditionally for every handler sub-test because update-project is always present in the shared fixture, not because the handler under test produces it. See inline comment.

Medium — Empty handler configs may be silently dropped
Handlers added as map[string]any{} may be no-ops if extractSafeOutputsConfig requires mandatory fields — making the isolation assertions pass vacuously. See inline comment.

Both are test-quality issues only; the production logic change is sound.

🔎 Code quality review by PR Code Quality Reviewer · 26.5 AIC · ⌖ 4.51 AIC · ⊞ 5.6K
Comment /review to run again

Comments that could not be inline-anchored

pkg/workflow/safe_outputs_handler_manager_token_test.go:391

False-positive assertion — the project token check passes regardless of the handler under test.

<details>
<summary>💡 Details</summary>

The assertion assert.Contains(t, yamlStr, &quot;GH_AW_PROJECT_GITHUB_TOKEN: ${{ secrets.PROJECTS_PAT }}&quot;) fires for every handler in the registry. It always passes because update-project is unconditionally present in the safeOutputs fixture for every sub-test — not because the handler under test emits the expected env var. This means the assertion provid…

pkg/workflow/safe_outputs_handler_manager_token_test.go:357

Empty handler config may be silently dropped, making the isolation test hollow for most handlers.

<details>
<summary>💡 Details</summary>

The test adds each non-update-project handler as map[string]any{} (empty config). If extractSafeOutputsConfig silently skips handlers with missing required fields, those handlers are never wired into the compiled output. The isolation assertions then pass vacuously — they're not proving the handler uses the right token; they're just confirming an …

github-actions[bot]
github-actionsBot previously requested changes Jul 16, 2026

@github-actionsgithub-actionsBot 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.

Review: Token decoupling correctness gap

The compiler change is the right direction — decoupling the step-level with.github-token from the project PAT. However there is a blocking correctness regression in the runtime path.

Root issue:update_project and other project handlers obtain their GitHub client via global.github (bound to with.github-token) when no per-handler github-token is in their config (see handler_auth.cjs line 44-46 and update_project.cjs line 1263). GH_AW_PROJECT_GITHUB_TOKEN is set as a step env var, but is only used for error diagnostics — it never creates an Octokit instance.

Who is affected: Any user who configures update-project.project relying on secrets.GH_AW_PROJECT_GITHUB_TOKEN without explicitly setting update-project.github-token. Their project GraphQL calls will silently use the wrong token.

See the inline comment on the changed block for the specific fix.

🧵 Reviewed using Impeccable skills by Impeccable Skills Reviewer · 78 AIC · ⌖ 4.53 AIC · ⊞ 5K

// 3. Magic secret fallback via getEffectiveSafeOutputGitHubToken()
// 1. Safe-outputs level token (so.GitHubToken)
// 2. Magic secret fallback via getEffectiveSafeOutputGitHubToken()
//

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.

Correctness regression: project handlers lose project PAT when no per-handler github-token is configured

This change decouples the step-level with.github-token from the project PAT, but the project handlers (update_project, create_project_status_update, create_project) still rely on global.github (initialized from with.github-token) when no per-handler github-token is set.

Broken scenario:

safe-outputs:
update-project:
project: https://github.com/orgs/myorg/projects/1# No github-token — user expects secrets.GH_AW_PROJECT_GITHUB_TOKEN to be used

Previously, with.github-token was elevated to secrets.GH_AW_PROJECT_GITHUB_TOKEN, giving global.github Projects v2 GraphQL access. After this change, with.github-token = safe-outputs/fallback token, and GH_AW_PROJECT_GITHUB_TOKEN is only an env var used in error messages — not used to initialize any Octokit client. update_project.main() falls back to global.github (update_project.cjs line 1263), which now lacks project access.

Suggested fix: In safe_output_handler_manager.cjs, when building handlerConfig for project-type handlers and the config has no explicit github-token, inject process.env.GH_AW_PROJECT_GITHUB_TOKEN as handlerConfig['github-token']. This preserves backward compatibility without re-coupling the step-level token.

Also, the comment in handler_auth.cjs line 17 is now stale (project token > global safe-outputs token) and needs updating.

@copilot please address this.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Addressed in 60abf42 and prior commits: project handlers now get GH_AW_PROJECT_GITHUB_TOKEN as per-handler fallback when github-token is unset, are initialized with a dedicated Octokit client, and execute with temporary global.github rebinding so project GraphQL calls do not fall back to the shared safe-outputs client token.

CopilotAIand others added 2 commits July 16, 2026 16:20
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

CopilotAI commented Jul 16, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot fix https://github.com/github/gh-aw/actions/runs/29514564855/job/87676214531?pr=45999

Fixed in e21b4d9. I reproduced the failing CJS job locally and patched actions/setup/js/safe_output_handler_manager.cjs to satisfy @ts-check for global.github/global.getOctokit usage without changing runtime behavior. The failing CI steps now pass locally (make check-cjs-syntax and cd actions/setup/js && npm run typecheck).

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address unresolved review feedback, and rerun checks once the branch is up to date.

Generated by 👨‍🍳 PR Sous Chef · 9.89 AIC · ⌖ 7.32 AIC · ⊞ 5.1K ·
Comment /souschef to run again

…ts-update-project-token-leaking
Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>

CopilotAI commented Jul 16, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot please run the pr-finisher skill, address unresolved review feedback, and rerun checks once the branch is up to date.

...

Done in 34ad4707bc.

  • Ran the PR-finisher pass and updated the branch to latest main (branch is now up to date).
  • Reviewed unresolved threads and prior CI failures; the previously reported CJS/typecheck and handler issues are already addressed in earlier commits on this PR.
  • Re-ran local checks (make fmt, make lint, make test-unit, make test, make recompile) and make agent-report-progress; the remaining failures are pre-existing pkg/cli environment-dependent test failures in this sandbox (TestLogsJSONOutputWithNoRuns, TestCloseExistingOrgIssuesByMarkerSkipsPRsAndPaginates, TestRunWorkflowOnGitHub_FlagCombinations).
  • Ran parallel_validation; Code Review surfaced non-actionable findings on upstream workflow files introduced by the merge, and CodeQL timed out per tool output.

@pelikhan

Copy link
Copy Markdown
Collaborator

CopilotAI commented Jul 16, 2026

Copy link
Copy Markdown
ContributorAuthor

@copilot fix https://github.com/github/gh-aw/actions/runs/29519013957/job/87705368761

Fixed in 6e445b0.

I investigated job 87705368761 and patched actions/setup/js/safe_output_handler_manager.test.cjs so the mentions-config assertions match the current main(handlerConfig, handlerGithubClient) call shape (including the second null client arg for non-token handlers). I reran the target test successfully:

GH_AW_PROMPTS_DIR=/home/runner/work/gh-aw/gh-aw/actions/setup/md npm test -- safe_output_handler_manager.test.cjs

@pelikhan
pelikhan merged commit 38e22c4 into mainJul 16, 2026
36 of 37 checks passed
@pelikhan
pelikhan deleted the copilot/aw-safe-outputs-update-project-token-leaking branch July 16, 2026 19:17
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.82.11

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[aw] Safe-outputs update-project token leaking into other outputs

4 participants

@pelikhan@gh-aw-bot