Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions pkg/parser/schemas/main_workflow_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -9657,9 +9657,9 @@
},
"github_token": {
"type": "string",
"pattern": "^\\$\\{\\{\\s*secrets\\.[A-Za-z_][A-Za-z0-9_]*(\\s*\\|\\|\\s*secrets\\.[A-Za-z_][A-Za-z0-9_]*)*\\s*\\}\\}$",
"description": "GitHub token expression using secrets. Pattern details: `[A-Za-z_][A-Za-z0-9_]*` matches a valid secret name (starts with a letter or underscore, followed by letters, digits, or underscores). The full pattern matches expressions like `${{ secrets.NAME }}` or `${{ secrets.NAME1 || secrets.NAME2 }}`.",
"examples": ["${{ secrets.GITHUB_TOKEN }}", "${{ secrets.CUSTOM_PAT }}", "${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}"]
"pattern": "^\\$\\{\\{\\s*(secrets\\.[A-Za-z_][A-Za-z0-9_]*(\\s*\\|\\|\\s*secrets\\.[A-Za-z_][A-Za-z0-9_]*)*|needs\\.[A-Za-z_][A-Za-z0-9_]*\\.outputs\\.[A-Za-z_][A-Za-z0-9_]*)\\s*\\}\\}$",
"description": "GitHub token expression. Accepts a secrets expression (e.g., `${{ secrets.NAME }}` or `${{ secrets.NAME1 || secrets.NAME2 }}`) or a job output expression (e.g., `${{ needs.auth.outputs.token }}`). Pattern details: secret names match `[A-Za-z_][A-Za-z0-9_]*`; job IDs and output names in dot notation match `[A-Za-z_][A-Za-z0-9_]*` (identifiers without hyphens).",
"examples": ["${{ secrets.GITHUB_TOKEN }}", "${{ secrets.CUSTOM_PAT }}", "${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}", "${{ needs.auth.outputs.token }}"]
},
"github_app": {
"type": "object",
Expand Down
26 changes: 26 additions & 0 deletions pkg/workflow/github_token_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,17 @@ func TestGitHubTokenValidation(t *testing.T) {
token: "${{ secrets.TOKEN1 || secrets.TOKEN2 }}",
expectError: false,
},
// Valid cases - job output expressions
{
name: "valid job output expression - needs.auth.outputs.token",
token: "${{ needs.auth.outputs.token }}",
expectError: false,
},
{
name: "valid job output expression - with spaces",
token: "${{ needs.auth.outputs.token }}",
expectError: false,
},
// Invalid cases - plaintext secrets
{
name: "invalid - plaintext GitHub PAT",
Expand Down Expand Up @@ -158,6 +169,11 @@ func TestGitHubTokenValidationInSafeOutputs(t *testing.T) {
token: "${{ secrets.SAFE_OUTPUTS_PAT }}",
expectError: false,
},
{
name: "valid job output token in safe-outputs",
token: "${{ needs.auth.outputs.token }}",
expectError: false,
},
{
name: "invalid token in safe-outputs",
token: "ghp_plaintext_token",
Expand Down Expand Up @@ -215,6 +231,11 @@ func TestGitHubTokenValidationInIndividualSafeOutput(t *testing.T) {
token: "${{ secrets.INDIVIDUAL_PAT }}",
expectError: false,
},
{
name: "valid job output token in individual safe-output",
token: "${{ needs.auth.outputs.token }}",
expectError: false,
},
{
name: "invalid token in individual safe-output",
token: "github_pat_plaintext",
Expand Down Expand Up @@ -272,6 +293,11 @@ func TestGitHubTokenValidationInGitHubTool(t *testing.T) {
token: "${{ secrets.GITHUB_TOOL_PAT }}",
expectError: false,
},
{
name: "valid job output token in github tool",
token: "${{ needs.auth.outputs.token }}",
expectError: false,
},
{
name: "invalid token in github tool",
token: "plaintext_secret",
Expand Down
5 changes: 3 additions & 2 deletions specs/security-architecture-spec.md
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ The output isolation layer enforces separation between AI agent operations (read
3. Workflow-level token
4. Default token

**OI-10**: Tokens MUST be GitHub Actions secret expressions (e.g., `${{ secrets.TOKEN_NAME }}`). Plaintext tokens MUST cause compilation failure.
**OI-10**: Tokens MUST be GitHub Actions expressions referencing secrets or job outputs (e.g., `${{ secrets.TOKEN_NAME }}` or `${{ needs.auth.outputs.token }}`). Plaintext tokens MUST cause compilation failure.

### 5.6 Output Isolation Guarantees

Expand Down Expand Up @@ -621,13 +621,14 @@ roles: all # Least restrictive

### 7.7 Token Validation

**PM-13**: The implementation MUST validate that `github-token` fields contain GitHub Actions secret expressions.
**PM-13**: The implementation MUST validate that `github-token` fields contain GitHub Actions expressions referencing secrets or job outputs.

**PM-14**: Plaintext tokens or environment variables MUST cause compilation failure.

**PM-15**: Valid token formats:
- `${{ secrets.TOKEN_NAME }}`
- `${{ secrets.ORG_TOKEN || secrets.FALLBACK_TOKEN }}`
- `${{ needs.auth.outputs.token }}`

---

Expand Down
Loading