Skip to content

fix: exclude job-output credential env vars from agent sandbox (--exclude-env)#46076

Merged
pelikhan merged 8 commits into
mainfrom
copilot/aw-job-output-exclude-env
Jul 17, 2026
Merged

fix: exclude job-output credential env vars from agent sandbox (--exclude-env)#46076
pelikhan merged 8 commits into
mainfrom
copilot/aw-job-output-exclude-env

Conversation

Copilot AI commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

ComputeAWFExcludeEnvVarNames only detected ${{ secrets.* }} as credential-bearing, so env vars set via job outputs (e.g. GH_TOKEN: ${{ needs.fetch_token.outputs.token }}) were never passed to --exclude-env, leaving ephemeral tokens fully visible inside the AWF container.

Changes

  • secret_extraction.go — adds jobOutputExprPattern regex and ContainsJobOutputExpr() helper that detects ${{ needs.JOB.outputs.OUTPUT }} anywhere in a value string
  • awf_helpers.go — extends the credential check in all three env sections (mcp-scripts, engine.env, agent.env) to OR ContainsJobOutputExpr() with the existing secrets. check; updates doc comment on ComputeAWFExcludeEnvVarNames
  • Tests — new table-driven cases in awf_helpers_test.go and secret_extraction_test.go covering job-output exclusion, static-value pass-through, and mixed secrets+outputs

Example: the following mcp-scripts env block now produces --exclude-env GH_TOKEN in the compiled lock file, matching the behaviour of GH_TOKEN: ${{ secrets.SOME_PAT }}:

mcp-scripts:
  example:
    env:
      GH_TOKEN: ${{ needs.fetch_token.outputs.token }}

…lude-env)

Extend ComputeAWFExcludeEnvVarNames to treat needs.*.outputs.* expressions
as credential-bearing (same as secrets.*), so env vars set from job outputs
(e.g. GH_TOKEN: ${{ needs.fetch_token.outputs.token }}) are excluded from
the agent container via AWF --exclude-env.

Closes #46012

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix exclusion of job-output token values from agent sandbox fix: exclude job-output credential env vars from agent sandbox (--exclude-env) Jul 16, 2026
Copilot AI requested a review from pelikhan July 16, 2026 20:00
@pelikhan
pelikhan marked this pull request as ready for review July 16, 2026 21:11
Copilot AI review requested due to automatic review settings July 16, 2026 21:11

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.

Pull request overview

Extends AWF sandbox credential protection to job-output-derived environment variables.

Changes:

  • Detects needs.JOB.outputs.OUTPUT expressions.
  • Excludes matching MCP, engine, and agent environment variables.
  • Adds unit tests and a patch changeset.
Show a summary per file
File Description
pkg/workflow/secret_extraction.go Adds job-output expression detection.
pkg/workflow/secret_extraction_test.go Tests expression matching.
pkg/workflow/awf_helpers.go Applies detection to sandbox exclusions.
pkg/workflow/awf_helpers_test.go Tests environment-variable exclusions.
.changeset/patch-exclude-job-output-credential-env-vars.md Documents the security fix.

Review details

Tip

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

  • Files reviewed: 5/5 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment thread pkg/workflow/secret_extraction.go Outdated
// jobOutputExprPattern matches a ${{ needs.JOB.outputs.OUTPUT }} expression anywhere in a string.
// Used to detect job-output credential references for --exclude-env, treating them
// the same as ${{ secrets.* }} references.
jobOutputExprPattern = regexp.MustCompile(`\$\{\{\s*needs\.[A-Za-z_][A-Za-z0-9_-]*\.outputs\.[A-Za-z_][A-Za-z0-9_-]*`)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in the latest commit. ContainsJobOutputExpr now uses InlineExpressionPattern to extract all ${{ ... }} blocks in the value, then searches within each expression body using two patterns:

  • jobOutputBodyDotPattern\bneeds\.[A-Za-z_][A-Za-z0-9_-]*\.outputs\.[A-Za-z_][A-Za-z0-9_-]* — matches dot notation anywhere in the expression body, so ${{ github.ref && needs.auth.outputs.token }} now returns true.
  • jobOutputBodyBracketPattern\bneeds\[['"][A-Za-z_][A-Za-z0-9_-]*['"]\](?:\.outputs\b|\[['"]outputs['"]\]) — matches single- and double-quote bracket notation, so ${{ needs['auth'].outputs['token'] }} and ${{ needs['auth']['outputs']['token'] }} now return true.

Regression tests added for: sub-expression with &&, sub-expression with ||, bracket with single quotes, bracket with double quotes, full bracket notation (['JOB']['outputs']['OUTPUT']), and bracket notation after another token.

@github-actions

This comment has been minimized.

@github-actions

Copy link
Copy Markdown
Contributor

Hey @app/copilot-swe-agent -- great catch on the ${{ needs.JOB.outputs.OUTPUT }} gap in ComputeAWFExcludeEnvVarNames! Ephemeral tokens flowing through job outputs are just as sensitive as secrets.* references, so closing this exclusion gap is an important security fix.

The PR looks well-structured: the change is tightly scoped to the credential-detection path, the new ContainsJobOutputExpr() helper is cleanly separated, and the table-driven tests cover the key scenarios (job-output exclusion, static-value pass-through, and mixed secrets+outputs). This looks ready for review.

Generated by ✅ Contribution Check · 155.6 AIC · ⌖ 8.97 AIC · ⊞ 6.2K ·

@pelikhan

Copy link
Copy Markdown
Collaborator

/matt

@github-actions

github-actions Bot commented Jul 16, 2026

Copy link
Copy Markdown
Contributor

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

@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 — requesting changes for one broken test.

📋 Key Themes & Highlights

Issue

  • Broken test case: "job output embedded in a larger string" uses a value without ${{, so ContainsJobOutputExpr returns false but want: true — the test will always fail.

Positive Highlights

  • ✅ Clean, focused fix addressing a real security gap (ephemeral token leakage).
  • ✅ Good table-driven test coverage across all three env sections (mcp-scripts, engine.env, agent.env).
  • ✅ Regex correctly handles extra whitespace and hyphenated job names.
  • ✅ Doc comment on ComputeAWFExcludeEnvVarNames is updated accurately.

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

@github-actions

Copy link
Copy Markdown
Contributor

🤖 PR Triage

Field Value
Category bug (security)
Risk 🔴 High
Score 91/100
Action 🚀 fast_track

Score breakdown: Impact 45 + Urgency 30 + Quality 16

Rationale: Security-sensitive fix — credential env vars from job outputs were not excluded from agent sandbox --exclude-env, leaving ephemeral tokens visible inside AWF containers. CI passes (30 checks, all green). Small focused change (5 files, +167/-11). Top-priority open PR.

Run §29545908133

Generated by 🔧 PR Triage Agent · 181.7 AIC · ⌖ 5.04 AIC · ⊞ 5.6K ·

@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot add frontmatter field to specify additional envs to exclude.

excluded-env: list of string

those get added to the awf excludes env list

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI and others added 2 commits July 17, 2026 03:44
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

@copilot ensure support for imported agentic workflows to unionize the list of excluded envs. Deduplicate, Sort for stability

Done in commit feat: union excluded-env from imported workflows with dedup and sort.

Imported workflow excluded-env lists are now unioned with the main workflow's list in buildInitialWorkflowData:

  • ImportsResult gains MergedExcludedEnv []string populated by the import BFS accumulator via mergeExcludedEnv() (deduplicates across imports using a seen-set)
  • mergeExcludedEnvVarNames(fromImports, fromMain) in workflow_builder.go unions both slices, deduplicates, and sorts for stability before assigning to WorkflowData.ExcludedEnv

The pkg/cli test failures visible in CI are pre-existing and unrelated to these changes.

@gh-aw-bot

Copy link
Copy Markdown
Collaborator

@copilot please run the pr-finisher skill, address the unresolved review thread below, and rerun checks once updated.

Unresolved review feedback:

Generated by 👨‍🍳 PR Sous Chef · 5.77 AIC · ⌖ 5.81 AIC · ⊞ 7.5K ·
Comment /souschef to run again

…ainsJobOutputExpr

- Replace `jobOutputExprPattern` (anchored to `${{ needs.`) with two body
  patterns checked within `InlineExpressionPattern` extractions:
  * `jobOutputBodyDotPattern`: `\bneeds.JOB.outputs.OUTPUT` (dot notation,
    anywhere in expression body)
  * `jobOutputBodyBracketPattern`: `needs['JOB'].outputs` / `needs['JOB']['outputs']`
    (single- and double-quote bracket notation)
- `ContainsJobOutputExpr` now iterates `${{ ... }}` blocks found by
  `InlineExpressionPattern` and tests each body, matching the approach
  used by `ExtractSecretsFromValue`
- Adds 7 new test cases covering sub-expressions (`github.ref && needs.*`),
  bracket notation (single/double quote, full bracket), and embedded bracket
  after another token

Co-authored-by: gh-aw-bot <259018956+gh-aw-bot@users.noreply.github.com>
Copilot AI requested a review from gh-aw-bot July 17, 2026 04:34
@pelikhan

Copy link
Copy Markdown
Collaborator

@copilot update main json schema with new field

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

Copilot AI commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

@copilot update main json schema with new field

Done in commit feat: add excluded-env field to main workflow JSON schema. The excluded-env array field has been added to pkg/parser/schemas/main_workflow_schema.json after check-for-updates, with a description explaining its purpose and merge/dedup behaviour, plus examples.

@pelikhan
pelikhan merged commit b944311 into main Jul 17, 2026
28 checks passed
@pelikhan
pelikhan deleted the copilot/aw-job-output-exclude-env branch July 17, 2026 06:53
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This pull request is included in a new release.

Release: v0.82.12

github-actions Bot added a commit that referenced this pull request Jul 17, 2026
Add excluded-env frontmatter field definition introduced in
fix: exclude job-output credential env vars from agent sandbox (#46076).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
pelikhan pushed a commit that referenced this pull request Jul 17, 2026
Add excluded-env frontmatter field definition introduced in
fix: exclude job-output credential env vars from agent sandbox (#46076).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to 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] Job-output github-token values are not excluded from the agent sandbox (--exclude-env)

4 participants