Skip to content

Detect and block secrets in env section including sub-expressions#16722

Merged
pelikhan merged 3 commits intomainfrom
copilot/detect-secrets-in-env-section
Feb 19, 2026
Merged

Detect and block secrets in env section including sub-expressions#16722
pelikhan merged 3 commits intomainfrom
copilot/detect-secrets-in-env-section

Conversation

Copy link
Contributor

Copilot AI commented Feb 19, 2026

Secrets defined in the workflow env section are leaked to the agent container. The compiler now detects ${{ secrets.* }} expressions in environment variables and enforces proper secret handling, including secrets embedded in complex sub-expressions.

Changes

Validation logic (pkg/workflow/strict_mode_validation.go)

  • Added validateEnvSecrets() using existing ExtractSecretsFromMap utility
  • Strict mode: compilation error with actionable message
  • Non-strict mode: warning with guidance to use engine-specific configuration

Enhanced secret detection (pkg/workflow/secret_extraction.go)

  • Enhanced ExtractSecretsFromValue() to detect secrets in complex GitHub Actions sub-expressions
  • Uses regex to find all ${{...}} expressions, then searches for secrets.* patterns within each
  • Detects secrets in complex expressions like:
    • ${{ github.workflow && secrets.TOKEN }}
    • ${{ (github.actor || secrets.HIDDEN) }}
    • ${{ !secrets.PRIVATE_KEY && github.workflow }}
    • ${{ secrets.KEY1 && secrets.KEY2 }}

Integration (pkg/workflow/compiler_orchestrator_engine.go)

  • Invoked during workflow compilation after strict mode checks
  • Preserves strict mode state for correct behavior

Test coverage (pkg/workflow/env_secrets_validation_test.go, pkg/workflow/secret_extraction_test.go)

  • 21+ test cases: single/multiple secrets, fallbacks, embedded secrets, sub-expressions, edge cases
  • Both strict and non-strict mode scenarios
  • Comprehensive coverage of sub-expression patterns

Example

---
engine: copilot
strict: true
env:
  API_KEY: ${{ secrets.API_KEY }}                      # Detected and blocked
  SUB_EXPR: ${{ github.workflow && secrets.TOKEN }}   # Also detected and blocked
  NODE_ENV: production                                 # Allowed
---

Strict mode:

✗ strict mode: secrets detected in 'env' section will be leaked to the agent container. 
Found: ${{ secrets.API_KEY }}, ${{ github.workflow && secrets.TOKEN }}. 
Use engine-specific secret configuration instead.

Non-strict mode:

⚠ Warning: secrets detected in 'env' section will be leaked to the agent container. 
Found: ${{ secrets.API_KEY }}, ${{ github.workflow && secrets.TOKEN }}. 
Consider using engine-specific secret configuration instead.

[!WARNING]


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

- Add validateEnvSecrets function to detect secrets in env section
- In strict mode: raise error when secrets are detected
- In non-strict mode: emit warning when secrets are detected
- Add comprehensive test suite with 16+ test cases
- Manually verified with test workflows

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Copilot AI changed the title [WIP] Add detection of secrets in env section Detect and block secrets in env section Feb 19, 2026
Copilot AI requested a review from pelikhan February 19, 2026 02:48
@pelikhan
Copy link
Contributor

@copilot also detect sub expressions

Enhanced ExtractSecretsFromValue to detect secrets in complex GitHub Actions
expressions like:
- ${{ github.workflow && secrets.TOKEN }}
- ${{ (github.actor || secrets.HIDDEN) }}
- ${{ !secrets.PRIVATE_KEY && github.workflow }}

Uses regex to find all ${{...}} expressions, then searches for secrets.*
pattern within each expression. Handles multiple secrets in same expression.

Added comprehensive test coverage for sub-expression cases in both
secret_extraction_test.go and env_secrets_validation_test.go.

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

Copilot AI commented Feb 19, 2026

@copilot also detect sub expressions

Enhanced secret detection to catch sub-expressions like ${{ github.workflow && secrets.TOKEN }}, ${{ (github.actor || secrets.HIDDEN) }}, and ${{ !secrets.PRIVATE_KEY && github.workflow }}.

The implementation now uses regex to find all ${{...}} expressions and searches for secrets.* patterns within each. All existing tests pass, plus new test coverage for sub-expression cases.

Commit: 44fdfc6

Copilot AI changed the title Detect and block secrets in env section Detect and block secrets in env section including sub-expressions Feb 19, 2026
@pelikhan pelikhan marked this pull request as ready for review February 19, 2026 03:20
Copilot AI review requested due to automatic review settings February 19, 2026 03:20
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 compilation-time detection of ${{ secrets.* }} usage in the workflow top-level env section to prevent secrets leaking into the agent container, including secrets embedded inside complex GitHub Actions expressions.

Changes:

  • Added validateEnvSecrets() to error in strict mode and warn in non-strict mode when secrets are found in top-level env.
  • Enhanced secret extraction to detect secrets.* inside ${{ ... }} sub-expressions.
  • Added/expanded tests covering strict vs non-strict behavior and sub-expression patterns.

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/strict_mode_validation.go Introduces env secret validation with strict error / non-strict warning behavior.
pkg/workflow/secret_extraction.go Updates extraction logic to find secrets inside ${{ ... }} blocks and complex expressions.
pkg/workflow/compiler_orchestrator_engine.go Integrates env secret validation into the compilation flow.
pkg/workflow/secret_extraction_test.go Adds test cases for secret detection in sub-expressions.
pkg/workflow/env_secrets_validation_test.go Adds comprehensive tests for env secret validation in strict and non-strict modes.
Comments suppressed due to low confidence (2)

pkg/workflow/strict_mode_validation.go:266

  • validateEnvSecrets only type-asserts env as map[string]any. In this codebase env is sometimes stored as map[string]string inside a map[string]any (e.g., FrontmatterConfig.ToMap() sets result["env"] = fc.Env). In those cases this validation will be silently skipped and secrets can still leak. Handle both map[string]any and map[string]string (and/or normalize via a helper) so validation is applied consistently.
	// Parse env as map[string]string
	envMap, ok := envValue.(map[string]any)
	if !ok {
		strictModeValidationLog.Printf("Env section is not a map, skipping validation")
		return nil
	}

pkg/workflow/strict_mode_validation.go:287

  • secretRefs is built by iterating a map (secrets), so the order is nondeterministic and duplicate expressions can appear (e.g., ${{ secrets.KEY1 && secrets.KEY2 }} will append the same expression twice). This makes the emitted error/warning unstable and harder to read. Consider de-duplicating expressions (set) and sorting before strings.Join, or alternatively listing the secret names (map keys) instead of the expression values.
	// Build list of secret references found
	var secretRefs []string
	for _, secretExpr := range secrets {
		secretRefs = append(secretRefs, secretExpr)
	}

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

Comment on lines +47 to +56
// Find all ${{ ... }} expressions in the value
// Pattern matches from ${{ to }} allowing nested content
exprPattern := regexp.MustCompile(`\$\{\{[^}]+\}\}`)
expressions := exprPattern.FindAllString(value, -1)

// For each expression, check if it contains secrets.VARIABLE_NAME
// This handles both simple cases like "${{ secrets.TOKEN }}"
// and complex sub-expressions like "${{ github.workflow && secrets.TOKEN }}"
secretPattern := regexp.MustCompile(`secrets\.([A-Z_][A-Z0-9_]*)`)
for _, expr := range expressions {
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.

exprPattern := regexp.MustCompile(${{[^}]+}}) will stop at the first } inside the expression, so it can fail to match valid ${{ ... }} blocks that contain } characters (e.g., JSON literals/strings like fromJSON('{"a":1}')). That can cause secret references inside those expressions to be missed. Consider switching to a non-greedy DOTALL pattern (e.g., (?s)\$\{\{.*?\}\}) or a small scanner that finds the next }} after ${{.

Also, both regexes are compiled on every call; they should be package-level precompiled vars like secretExprPattern to avoid repeated compilation.

Copilot uses AI. Check for mistakes.
@pelikhan pelikhan merged commit ae6c68f into main Feb 19, 2026
130 checks passed
@pelikhan pelikhan deleted the copilot/detect-secrets-in-env-section branch February 19, 2026 03:27
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.

2 participants

Comments