@htekdev/actions-debugger 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +108 -108
  3. package/errors/_schema.json +89 -89
  4. package/errors/caching-artifacts/artifact-storage-quota-exceeded.yml +118 -0
  5. package/errors/caching-artifacts/cache-miss.yml +56 -56
  6. package/errors/caching-artifacts/cache-save-cancelled-job.yml +82 -0
  7. package/errors/caching-artifacts/cache-v3-to-v4-breaking-changes.yml +95 -0
  8. package/errors/caching-artifacts/cross-repo-artifacts-not-supported.yml +102 -0
  9. package/errors/caching-artifacts/upload-artifact-no-files-found.yml +92 -0
  10. package/errors/caching-artifacts/upload-artifact-v4-breaking.yml +67 -67
  11. package/errors/concurrency-timing/cancel-in-progress-deploy-drops.yml +97 -0
  12. package/errors/concurrency-timing/jobs-cancelled-unexpectedly.yml +60 -60
  13. package/errors/concurrency-timing/skipped-needs-cascade.yml +103 -0
  14. package/errors/concurrency-timing/workflow-run-conclusion-unchecked.yml +100 -0
  15. package/errors/known-unsolved/composite-input-env-vars-missing.yml +91 -0
  16. package/errors/known-unsolved/composite-nested-outputs-null.yml +101 -0
  17. package/errors/known-unsolved/no-dynamic-secret-access.yml +111 -0
  18. package/errors/known-unsolved/no-step-level-rerun.yml +94 -0
  19. package/errors/known-unsolved/no-step-retry.yml +53 -53
  20. package/errors/permissions-auth/checkout-submodule-private-auth.yml +91 -0
  21. package/errors/permissions-auth/fork-pr-secrets-unavailable.yml +97 -0
  22. package/errors/permissions-auth/github-token-403.yml +64 -64
  23. package/errors/permissions-auth/github-token-protected-branch-push.yml +109 -0
  24. package/errors/permissions-auth/oidc-aws-failure.yml +85 -85
  25. package/errors/permissions-auth/oidc-azure-subject-mismatch.yml +91 -0
  26. package/errors/runner-environment/disk-space.yml +57 -57
  27. package/errors/runner-environment/docker-buildx-not-setup.yml +106 -0
  28. package/errors/runner-environment/macos-homebrew-path.yml +90 -0
  29. package/errors/runner-environment/node-runtime-deprecation.yml +56 -56
  30. package/errors/runner-environment/npm-ci-lockfile-mismatch.yml +112 -0
  31. package/errors/runner-environment/self-hosted-stale-toolcache.yml +73 -0
  32. package/errors/runner-environment/setup-node-version-file-missing.yml +105 -0
  33. package/errors/runner-environment/windows-execution-policy.yml +83 -0
  34. package/errors/silent-failures/add-mask-no-retroactive-masking.yml +75 -0
  35. package/errors/silent-failures/composite-boolean-inputs-as-strings.yml +110 -0
  36. package/errors/silent-failures/conditional-output-null-downstream.yml +82 -0
  37. package/errors/silent-failures/continue-on-error-masks-failure.yml +86 -0
  38. package/errors/silent-failures/github-token-no-trigger.yml +57 -57
  39. package/errors/silent-failures/reusable-workflow-env-secrets-empty.yml +90 -0
  40. package/errors/silent-failures/scheduled-workflow-disabled.yml +59 -59
  41. package/errors/triggers/cron-schedule-late.yml +59 -59
  42. package/errors/triggers/pull-request-target-rce-risk.yml +117 -0
  43. package/errors/triggers/workflow-not-triggering.yml +60 -60
  44. package/errors/triggers/workflow-run-default-branch-requirement.yml +78 -0
  45. package/errors/yaml-syntax/anchors-not-supported.yml +95 -0
  46. package/errors/yaml-syntax/dynamic-matrix-fromjson-failure.yml +99 -0
  47. package/errors/yaml-syntax/if-always-true.yml +52 -52
  48. package/errors/yaml-syntax/missing-expression-wrapper.yml +67 -0
  49. package/errors/yaml-syntax/needs-indirect-outputs.yml +91 -0
  50. package/errors/yaml-syntax/secrets-in-if.yml +55 -55
  51. package/errors/yaml-syntax/unexpected-yaml-key.yml +69 -69
  52. package/errors/yaml-syntax/working-directory-ignored-on-uses.yml +66 -0
  53. package/package.json +70 -67
@@ -1,59 +1,59 @@
1
- id: triggers-003
2
- title: "Cron Schedule Running Late or Not Running"
3
- category: triggers
4
- severity: warning
5
- tags:
6
- - cron
7
- - schedule
8
- - delay
9
- - forks
10
- - best-effort
11
- patterns:
12
- - regex: "schedule"
13
- flags: "i"
14
- - regex: "Delayed"
15
- flags: "i"
16
- - regex: "This event will only trigger a workflow run if the workflow file exists on the default branch"
17
- flags: "i"
18
- error_messages:
19
- - "Scheduled workflows can be delayed during periods of high loads of GitHub Actions workflow runs."
20
- root_cause: |
21
- GitHub Actions cron is best-effort, not real-time scheduling. During peak usage windows,
22
- scheduled workflows can start 15 to 45 minutes late. In forks, scheduled workflows are also
23
- disabled by default, so a copied workflow may never run until someone explicitly enables it.
24
-
25
- The YAML can be correct and the repository can still see timing drift because the platform
26
- does not guarantee precise minute-by-minute execution.
27
- fix: |
28
- Treat cron timing as approximate. Schedule important automations away from the top of the
29
- hour, add tolerance for startup drift, and provide a `workflow_dispatch` fallback for jobs
30
- that sometimes need manual recovery.
31
- fix_code:
32
- - language: yaml
33
- label: "Add jitter-friendly scheduling and manual fallback"
34
- code: |
35
- name: Nightly maintenance
36
-
37
- on:
38
- schedule:
39
- - cron: '17 3 * * *'
40
- workflow_dispatch:
41
-
42
- jobs:
43
- maintain:
44
- runs-on: ubuntu-latest
45
- steps:
46
- - uses: actions/checkout@v4
47
- - run: ./scripts/maintenance.sh
48
- prevention:
49
- - "Avoid scheduling critical jobs exactly at minute 0 because that is a common contention window."
50
- - "Use external monitoring if a schedule must be near-real-time."
51
- - "Enable scheduled workflows manually after creating a fork or after long inactivity."
52
- docs:
53
- - url: "https://docs.github.com/en/actions/reference/events-that-trigger-workflows#schedule"
54
- label: "schedule event"
55
- - url: "https://docs.github.com/en/actions/managing-workflow-runs/disabling-and-enabling-a-workflow"
56
- label: "Disable and enable a workflow"
57
- source:
58
- article: "https://htek.dev/articles/github-actions-debugging-guide"
59
- section: "Cron schedules running late"
1
+ id: triggers-003
2
+ title: "Cron Schedule Running Late or Not Running"
3
+ category: triggers
4
+ severity: warning
5
+ tags:
6
+ - cron
7
+ - schedule
8
+ - delay
9
+ - forks
10
+ - best-effort
11
+ patterns:
12
+ - regex: "schedule"
13
+ flags: "i"
14
+ - regex: "Delayed"
15
+ flags: "i"
16
+ - regex: "This event will only trigger a workflow run if the workflow file exists on the default branch"
17
+ flags: "i"
18
+ error_messages:
19
+ - "Scheduled workflows can be delayed during periods of high loads of GitHub Actions workflow runs."
20
+ root_cause: |
21
+ GitHub Actions cron is best-effort, not real-time scheduling. During peak usage windows,
22
+ scheduled workflows can start 15 to 45 minutes late. In forks, scheduled workflows are also
23
+ disabled by default, so a copied workflow may never run until someone explicitly enables it.
24
+
25
+ The YAML can be correct and the repository can still see timing drift because the platform
26
+ does not guarantee precise minute-by-minute execution.
27
+ fix: |
28
+ Treat cron timing as approximate. Schedule important automations away from the top of the
29
+ hour, add tolerance for startup drift, and provide a `workflow_dispatch` fallback for jobs
30
+ that sometimes need manual recovery.
31
+ fix_code:
32
+ - language: yaml
33
+ label: "Add jitter-friendly scheduling and manual fallback"
34
+ code: |
35
+ name: Nightly maintenance
36
+
37
+ on:
38
+ schedule:
39
+ - cron: '17 3 * * *'
40
+ workflow_dispatch:
41
+
42
+ jobs:
43
+ maintain:
44
+ runs-on: ubuntu-latest
45
+ steps:
46
+ - uses: actions/checkout@v4
47
+ - run: ./scripts/maintenance.sh
48
+ prevention:
49
+ - "Avoid scheduling critical jobs exactly at minute 0 because that is a common contention window."
50
+ - "Use external monitoring if a schedule must be near-real-time."
51
+ - "Enable scheduled workflows manually after creating a fork or after long inactivity."
52
+ docs:
53
+ - url: "https://docs.github.com/en/actions/reference/events-that-trigger-workflows#schedule"
54
+ label: "schedule event"
55
+ - url: "https://docs.github.com/en/actions/managing-workflow-runs/disabling-and-enabling-a-workflow"
56
+ label: "Disable and enable a workflow"
57
+ source:
58
+ article: "https://htek.dev/articles/github-actions-debugging-guide"
59
+ section: "Cron schedules running late"
@@ -0,0 +1,117 @@
1
+ id: triggers-005
2
+ title: "pull_request_target Runs Fork Code with Base Branch Secrets — RCE Risk"
3
+ category: triggers
4
+ severity: error
5
+ tags:
6
+ - pull_request_target
7
+ - security
8
+ - fork
9
+ - secrets
10
+ - code-injection
11
+ - pwn-request
12
+ - base-branch
13
+ patterns:
14
+ - regex: "pull_request_target"
15
+ flags: "i"
16
+ - regex: "github\\.event\\.pull_request\\.head\\.sha"
17
+ flags: "i"
18
+ error_messages:
19
+ - "Warning: Workflow uses pull_request_target with actions/checkout on PR head — potential code injection vulnerability."
20
+ root_cause: |
21
+ `pull_request_target` was designed to give workflows triggered by fork PRs access to
22
+ **base branch secrets and write permissions**. Unlike `pull_request`, it runs in the
23
+ context of the **base repository**, not the fork — so `secrets.*` is available and
24
+ `GITHUB_TOKEN` has write permissions.
25
+
26
+ The danger: many developers combine `pull_request_target` with
27
+ `actions/checkout@v4 ref: ${{ github.event.pull_request.head.sha }}` to check out the
28
+ **fork's code** for testing. Running untrusted fork code with full secret access is a
29
+ critical Remote Code Execution (RCE) vulnerability — the contributor can exfiltrate
30
+ all repository secrets simply by adding malicious code to their PR.
31
+
32
+ This class of vulnerability is known as "pwn request" and has been exploited against
33
+ major open-source projects.
34
+ fix: |
35
+ When using `pull_request_target`, ALWAYS check out the base branch (not the PR head).
36
+ If you need to run the PR author's code AND have secret access, use a two-workflow pattern:
37
+ an unprivileged `pull_request` workflow builds and uploads artifacts, then a privileged
38
+ `workflow_run` workflow (with no access to fork code) deploys them.
39
+ fix_code:
40
+ - language: yaml
41
+ label: "CRITICAL VULNERABILITY — checkout of fork code with secret access"
42
+ code: |
43
+ on: pull_request_target # has secret access
44
+
45
+ jobs:
46
+ test:
47
+ runs-on: ubuntu-latest
48
+ steps:
49
+ # DANGEROUS: checking out untrusted PR code with base repo secrets
50
+ - uses: actions/checkout@v4
51
+ with:
52
+ ref: ${{ github.event.pull_request.head.sha }} # NEVER DO THIS
53
+ - run: npm test # attacker's code runs with your secrets
54
+ - language: yaml
55
+ label: "SAFE — pull_request_target checking out base only (for labels/comments)"
56
+ code: |
57
+ on:
58
+ pull_request_target:
59
+ types: [opened, labeled]
60
+
61
+ jobs:
62
+ label:
63
+ runs-on: ubuntu-latest
64
+ permissions:
65
+ pull-requests: write
66
+ steps:
67
+ # SAFE: checking out base repo code only (not the PR branch)
68
+ - uses: actions/checkout@v4
69
+ # No 'ref:' — defaults to base branch
70
+ - name: Add label
71
+ uses: actions-ecosystem/action-add-labels@v1
72
+ with:
73
+ github_token: ${{ secrets.GITHUB_TOKEN }}
74
+ labels: needs-review
75
+ - language: yaml
76
+ label: "SAFE — two-workflow pattern: build unprivileged, deploy privileged"
77
+ code: |
78
+ # Workflow 1: ci.yml — triggered by pull_request (no secrets, untrusted code OK)
79
+ on: pull_request
80
+ jobs:
81
+ build:
82
+ runs-on: ubuntu-latest
83
+ steps:
84
+ - uses: actions/checkout@v4 # fork code, but no secrets
85
+ - run: npm ci && npm run build
86
+ - uses: actions/upload-artifact@v4
87
+ with:
88
+ name: build-output
89
+ path: dist/
90
+
91
+ # Workflow 2: deploy-pr-preview.yml — triggered by workflow_run (has secrets, no fork code)
92
+ on:
93
+ workflow_run:
94
+ workflows: [CI]
95
+ types: [completed]
96
+ jobs:
97
+ deploy:
98
+ runs-on: ubuntu-latest
99
+ if: github.event.workflow_run.conclusion == 'success'
100
+ steps:
101
+ - uses: actions/download-artifact@v4 # downloads artifact, not fork code
102
+ with:
103
+ github-token: ${{ secrets.GITHUB_TOKEN }}
104
+ run-id: ${{ github.event.workflow_run.id }}
105
+ name: build-output
106
+ - run: ./deploy-preview.sh # safe: runs base repo script with downloaded artifact
107
+ prevention:
108
+ - "NEVER use `actions/checkout ref: github.event.pull_request.head.sha` inside a `pull_request_target` workflow."
109
+ - "Use the two-workflow pattern (unprivileged `pull_request` + privileged `workflow_run`) for PR preview deployments."
110
+ - "Restrict `pull_request_target` to safe operations: labeling, commenting, and running base repo scripts only."
111
+ docs:
112
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#pull_request_target"
113
+ label: "pull_request_target event"
114
+ - url: "https://securitylab.github.com/resources/github-actions-preventing-pwn-requests/"
115
+ label: "Preventing pwn requests (GitHub Security Lab)"
116
+ - url: "https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#understanding-the-risk-of-script-injections"
117
+ label: "Security hardening — script injection"
@@ -1,60 +1,60 @@
1
- id: triggers-001
2
- title: "Workflow Not Triggering At All"
3
- category: triggers
4
- severity: error
5
- tags:
6
- - triggers
7
- - workflow-file
8
- - default-branch
9
- - on
10
- - events
11
- patterns:
12
- - regex: "This event will only trigger a workflow run if the workflow file exists on the default branch"
13
- flags: "i"
14
- - regex: "Workflow file .* not found on the default branch"
15
- flags: "i"
16
- - regex: "No runs found for this workflow"
17
- flags: "i"
18
- error_messages:
19
- - "This event will only trigger a workflow run if the workflow file exists on the default branch"
20
- root_cause: |
21
- Some events only trigger if the workflow file itself exists on the repository's default
22
- branch. A workflow can look perfectly valid in a feature branch, but GitHub will ignore it
23
- for certain event types until the file is present on the default branch with correct `on:`
24
- syntax and placement under `.github/workflows/`.
25
-
26
- A wrong indentation level under `on:`, a typo in the event name, or putting the file in the
27
- wrong directory can create the same symptom: nothing runs.
28
- fix: |
29
- Confirm the workflow file lives in `.github/workflows/`, validate the `on:` block, and make
30
- sure the workflow file already exists on the default branch for the event you expect to fire.
31
- fix_code:
32
- - language: yaml
33
- label: "Use valid workflow location and trigger syntax"
34
- code: |
35
- name: CI
36
-
37
- on:
38
- push:
39
- branches:
40
- - main
41
- pull_request:
42
-
43
- jobs:
44
- test:
45
- runs-on: ubuntu-latest
46
- steps:
47
- - uses: actions/checkout@v4
48
- - run: npm test
49
- prevention:
50
- - "Keep workflow files only under `.github/workflows/`."
51
- - "Merge new workflow files to the default branch before relying on events like `schedule` or `workflow_dispatch`."
52
- - "Validate event names and indentation in every `on:` block."
53
- docs:
54
- - url: "https://docs.github.com/en/actions/reference/events-that-trigger-workflows"
55
- label: "Events that trigger workflows"
56
- - url: "https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions"
57
- label: "Workflow syntax for GitHub Actions"
58
- source:
59
- article: "https://htek.dev/articles/github-actions-debugging-guide"
60
- section: "Workflow not triggering"
1
+ id: triggers-001
2
+ title: "Workflow Not Triggering At All"
3
+ category: triggers
4
+ severity: error
5
+ tags:
6
+ - triggers
7
+ - workflow-file
8
+ - default-branch
9
+ - on
10
+ - events
11
+ patterns:
12
+ - regex: "This event will only trigger a workflow run if the workflow file exists on the default branch"
13
+ flags: "i"
14
+ - regex: "Workflow file .* not found on the default branch"
15
+ flags: "i"
16
+ - regex: "No runs found for this workflow"
17
+ flags: "i"
18
+ error_messages:
19
+ - "This event will only trigger a workflow run if the workflow file exists on the default branch"
20
+ root_cause: |
21
+ Some events only trigger if the workflow file itself exists on the repository's default
22
+ branch. A workflow can look perfectly valid in a feature branch, but GitHub will ignore it
23
+ for certain event types until the file is present on the default branch with correct `on:`
24
+ syntax and placement under `.github/workflows/`.
25
+
26
+ A wrong indentation level under `on:`, a typo in the event name, or putting the file in the
27
+ wrong directory can create the same symptom: nothing runs.
28
+ fix: |
29
+ Confirm the workflow file lives in `.github/workflows/`, validate the `on:` block, and make
30
+ sure the workflow file already exists on the default branch for the event you expect to fire.
31
+ fix_code:
32
+ - language: yaml
33
+ label: "Use valid workflow location and trigger syntax"
34
+ code: |
35
+ name: CI
36
+
37
+ on:
38
+ push:
39
+ branches:
40
+ - main
41
+ pull_request:
42
+
43
+ jobs:
44
+ test:
45
+ runs-on: ubuntu-latest
46
+ steps:
47
+ - uses: actions/checkout@v4
48
+ - run: npm test
49
+ prevention:
50
+ - "Keep workflow files only under `.github/workflows/`."
51
+ - "Merge new workflow files to the default branch before relying on events like `schedule` or `workflow_dispatch`."
52
+ - "Validate event names and indentation in every `on:` block."
53
+ docs:
54
+ - url: "https://docs.github.com/en/actions/reference/events-that-trigger-workflows"
55
+ label: "Events that trigger workflows"
56
+ - url: "https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions"
57
+ label: "Workflow syntax for GitHub Actions"
58
+ source:
59
+ article: "https://htek.dev/articles/github-actions-debugging-guide"
60
+ section: "Workflow not triggering"
@@ -0,0 +1,78 @@
1
+ id: triggers-004
2
+ title: "workflow_run Trigger Fires Only When Source Workflow Exists on Default Branch"
3
+ category: triggers
4
+ severity: error
5
+ tags:
6
+ - workflow_run
7
+ - triggers
8
+ - default-branch
9
+ - main
10
+ - workflow-file
11
+ - not-triggering
12
+ patterns:
13
+ - regex: "workflow_run.*not triggering"
14
+ flags: "i"
15
+ - regex: "workflow_run.*workflow.*not found"
16
+ flags: "i"
17
+ error_messages:
18
+ - "The workflow 'CI' referenced by workflow_run trigger was not found or does not exist on the default branch."
19
+ root_cause: |
20
+ GitHub resolves `workflow_run` triggers by looking up the **source workflow's file name
21
+ on the repository's default branch** at trigger time. This has two important implications:
22
+
23
+ 1. **The source workflow file must exist on the default branch** — a `workflow_run` trigger
24
+ in a PR branch will not fire when the referenced workflow runs on that branch, if the
25
+ source workflow file doesn't exist on `main`/`master` yet.
26
+
27
+ 2. **The trigger workflow itself must also be on the default branch** — the `workflow_run`
28
+ trigger in `on:` is only read from the default branch, regardless of which branch
29
+ the triggering push happened on.
30
+
31
+ This is a common blocker during initial feature development: a developer adds both the
32
+ source workflow and the `workflow_run` consumer in the same PR branch. Neither fires
33
+ correctly until both files are merged to the default branch.
34
+ fix: |
35
+ Ensure both the source workflow and the `workflow_run` consumer exist on the default
36
+ branch before testing. When adding new CI workflows, merge to main/master first, then
37
+ test from a feature branch.
38
+ fix_code:
39
+ - language: yaml
40
+ label: "Example workflow_run trigger — source must be on default branch"
41
+ code: |
42
+ # .github/workflows/deploy.yml (must be on default branch to work)
43
+ on:
44
+ workflow_run:
45
+ workflows: ["CI"] # "CI" must match the 'name:' field in ci.yml
46
+ types: [completed] # fires when CI workflow completes (any branch)
47
+ branches: [main] # optional: only when CI ran on these branches
48
+
49
+ jobs:
50
+ deploy:
51
+ runs-on: ubuntu-latest
52
+ if: ${{ github.event.workflow_run.conclusion == 'success' }}
53
+ steps:
54
+ - run: ./deploy.sh
55
+ - language: yaml
56
+ label: "Check workflow name matches exactly (case-sensitive)"
57
+ code: |
58
+ # .github/workflows/ci.yml
59
+ name: CI # this exact string must match workflow_run trigger
60
+
61
+ on:
62
+ push:
63
+ branches: [main, 'feature/**']
64
+
65
+ jobs:
66
+ build:
67
+ runs-on: ubuntu-latest
68
+ steps:
69
+ - run: npm test
70
+ prevention:
71
+ - "Merge the source workflow to the default branch before adding a `workflow_run` consumer — test with both files on `main`."
72
+ - "The `workflows:` list matches on the workflow's `name:` field (the YAML `name:` key), not the filename."
73
+ - "Use `branches:` filter in `workflow_run` to prevent deploy from triggering on every feature branch run."
74
+ docs:
75
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_run"
76
+ label: "workflow_run trigger"
77
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/triggering-a-workflow#triggering-a-workflow-from-a-workflow"
78
+ label: "Triggering a workflow from a workflow"
@@ -0,0 +1,95 @@
1
+ id: yaml-syntax-009
2
+ title: "YAML Anchors and Aliases Rejected by Workflow Parser"
3
+ category: yaml-syntax
4
+ severity: error
5
+ tags:
6
+ - yaml
7
+ - anchors
8
+ - aliases
9
+ - dry
10
+ - reuse
11
+ - syntax
12
+ patterns:
13
+ - regex: "Anchors are not supported"
14
+ flags: "i"
15
+ - regex: "could not parse the yaml file"
16
+ flags: "i"
17
+ - regex: "mapping values are not allowed in this context"
18
+ flags: "i"
19
+ error_messages:
20
+ - "The workflow is not valid. .github/workflows/ci.yml: Anchors are not supported."
21
+ - "could not parse the yaml file '.github/workflows/ci.yml'"
22
+ root_cause: |
23
+ GitHub Actions' workflow parser explicitly rejects YAML anchors (`&`) and aliases (`*`)
24
+ even though they are valid YAML 1.2 constructs. Developers familiar with Docker Compose
25
+ or Kubernetes manifests commonly attempt to DRY up repetitive step blocks using anchors
26
+ and merge keys (`<<: *defaults`), but the GitHub-hosted workflow validator rejects the
27
+ file on parse rather than silently ignoring the constructs.
28
+
29
+ The restriction is documented and intentional — GitHub's parser does not implement the
30
+ full YAML 1.2 merge-key extension.
31
+ fix: |
32
+ Remove all YAML anchors and aliases from workflow files. Use one of GitHub Actions'
33
+ native reuse mechanisms instead: composite actions for shared step sequences, reusable
34
+ workflows (`workflow_call`) for sharing entire job graphs, or a matrix strategy for
35
+ parameterized repetition.
36
+ fix_code:
37
+ - language: yaml
38
+ label: "WRONG — YAML anchor and merge key (rejected)"
39
+ code: |
40
+ .defaults: &defaults
41
+ runs-on: ubuntu-latest
42
+ timeout-minutes: 10
43
+
44
+ jobs:
45
+ build:
46
+ <<: *defaults
47
+ steps:
48
+ - uses: actions/checkout@v4
49
+ - run: npm ci && npm run build
50
+
51
+ test:
52
+ <<: *defaults
53
+ steps:
54
+ - uses: actions/checkout@v4
55
+ - run: npm test
56
+ - language: yaml
57
+ label: "CORRECT — composite action for shared steps"
58
+ code: |
59
+ # .github/actions/setup/action.yml
60
+ name: Setup
61
+ runs:
62
+ using: composite
63
+ steps:
64
+ - uses: actions/checkout@v4
65
+ - uses: actions/setup-node@v4
66
+ with:
67
+ node-version: 20
68
+ cache: npm
69
+
70
+ # .github/workflows/ci.yml
71
+ jobs:
72
+ build:
73
+ runs-on: ubuntu-latest
74
+ timeout-minutes: 10
75
+ steps:
76
+ - uses: ./.github/actions/setup
77
+ - run: npm run build
78
+
79
+ test:
80
+ runs-on: ubuntu-latest
81
+ timeout-minutes: 10
82
+ steps:
83
+ - uses: ./.github/actions/setup
84
+ - run: npm test
85
+ prevention:
86
+ - "Use composite actions (`.github/actions/`) to share step sequences across jobs."
87
+ - "Use reusable workflows (`workflow_call`) to share entire job structures."
88
+ - "Lint workflow files with `actionlint` locally — it reports anchor usage before pushing."
89
+ docs:
90
+ - url: "https://docs.github.com/en/actions/sharing-automations/reusing-workflows"
91
+ label: "Reusing workflows"
92
+ - url: "https://docs.github.com/en/actions/sharing-automations/creating-actions/creating-a-composite-action"
93
+ label: "Creating a composite action"
94
+ - url: "https://rhysd.github.io/actionlint/"
95
+ label: "actionlint — static checker for GitHub Actions"
@@ -0,0 +1,99 @@
1
+ id: yaml-syntax-008
2
+ title: "Dynamic Matrix fromJSON Fails on Empty or Malformed JSON"
3
+ category: yaml-syntax
4
+ severity: error
5
+ tags:
6
+ - matrix
7
+ - fromJSON
8
+ - dynamic-matrix
9
+ - strategy
10
+ - expressions
11
+ patterns:
12
+ - regex: "Error when evaluating 'strategy' for job"
13
+ flags: "i"
14
+ - regex: "Error parsing fromJson"
15
+ flags: "i"
16
+ - regex: "Error reading JToken from JsonReader"
17
+ flags: "i"
18
+ - regex: "matrix contains zero elements"
19
+ flags: "i"
20
+ - regex: "Unexpected type of value '', expected type: Sequence"
21
+ flags: "i"
22
+ error_messages:
23
+ - "Error when evaluating 'strategy' for job 'build'. .github/workflows/ci.yml (Line: 12, Col: 14): Error parsing fromJson"
24
+ - "Error reading JToken from JsonReader. Path '', line 0, position 0."
25
+ - "Unexpected type of value '', expected type: Sequence."
26
+ - "Error when evaluating 'strategy' for job 'test'. matrix contains zero elements"
27
+ root_cause: |
28
+ When using `fromJSON()` in a matrix strategy to build a dynamic matrix from a previous
29
+ job's output, three common failure modes occur:
30
+
31
+ 1. **Empty string**: The upstream step produced no output (empty stdout or unset output
32
+ variable), so `fromJSON('')` receives an empty string and the JSON parser fails with
33
+ "Error reading JToken from JsonReader."
34
+
35
+ 2. **Zero-element array**: The upstream step produces a valid JSON array but it is empty
36
+ (`[]`). GitHub Actions evaluates the strategy and then immediately fails the job
37
+ because it has nothing to schedule — "matrix contains zero elements."
38
+
39
+ 3. **Malformed JSON**: Newlines, unescaped quotes, or shell word-splitting corrupts the
40
+ JSON string before it reaches fromJSON, producing parse errors such as
41
+ "Unexpected type of value '', expected type: Sequence."
42
+
43
+ All three root causes trace back to the step that generates the matrix JSON, not to
44
+ fromJSON itself. Using `jq` without `-c` (compact output) can embed literal newlines
45
+ that break the output variable; similarly, forgetting to set the output with
46
+ `echo "matrix=..." >> $GITHUB_OUTPUT` leaves the variable empty.
47
+ fix: |
48
+ 1. Always produce compact, single-line JSON with `jq -c` and write it to
49
+ `$GITHUB_OUTPUT` (not `$GITHUB_ENV`) in the generator step.
50
+ 2. Guard against an empty matrix by providing a fallback single-element array, or add
51
+ an `if` condition to skip the matrix job when the generator output is empty.
52
+ 3. Echo and verify the generated JSON in a debug step before the matrix job runs.
53
+ fix_code:
54
+ - language: yaml
55
+ label: "Correct dynamic matrix pattern with empty-guard"
56
+ code: |
57
+ jobs:
58
+ generate:
59
+ runs-on: ubuntu-latest
60
+ outputs:
61
+ matrix: ${{ steps.set-matrix.outputs.matrix }}
62
+ steps:
63
+ - id: set-matrix
64
+ run: |
65
+ # Produce compact JSON; default to ["none"] if list is empty
66
+ ITEMS=$(cat targets.txt | jq -Rc '[.]' | jq -sc 'add // ["none"]')
67
+ echo "matrix=${ITEMS}" >> $GITHUB_OUTPUT
68
+
69
+ build:
70
+ needs: generate
71
+ # Skip if only the fallback placeholder is present
72
+ if: ${{ needs.generate.outputs.matrix != '["none"]' }}
73
+ strategy:
74
+ matrix:
75
+ target: ${{ fromJSON(needs.generate.outputs.matrix) }}
76
+ runs-on: ubuntu-latest
77
+ steps:
78
+ - run: echo "Building ${{ matrix.target }}"
79
+ - language: yaml
80
+ label: "Debug step to verify JSON before matrix job"
81
+ code: |
82
+ - name: Verify matrix JSON
83
+ run: |
84
+ echo "Matrix value: ${{ needs.generate.outputs.matrix }}"
85
+ echo '${{ needs.generate.outputs.matrix }}' | jq .
86
+ prevention:
87
+ - "Always use `jq -c` (compact output) when producing JSON for matrix outputs to avoid embedded newlines."
88
+ - "Write matrix JSON to `$GITHUB_OUTPUT`, not `$GITHUB_ENV` — environment variables are not available as job outputs."
89
+ - "Add an empty-matrix guard (`if` condition or fallback value) so a zero-element array does not silently break your pipeline."
90
+ - "Add a debug step after the generator to echo and validate the JSON before the dependent job runs."
91
+ docs:
92
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/running-variations-of-jobs-in-a-workflow"
93
+ label: "Running variations of jobs in a workflow (matrix)"
94
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/evaluate-expressions-in-workflows-and-actions#fromjson"
95
+ label: "fromJSON expression function"
96
+ - url: "https://github.com/actions/runner/issues/2424"
97
+ label: "actions/runner#2424 — fromJSON parsing failures in nested workflows"
98
+ - url: "https://github.com/orgs/community/discussions/27096"
99
+ label: "Community: matrix contains zero elements"