@htekdev/actions-debugger 1.0.14 → 1.0.16

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 (58) hide show
  1. package/dist/db/search.js +3 -1
  2. package/dist/db/search.js.map +1 -1
  3. package/dist/tools/suggest-fix.d.ts.map +1 -1
  4. package/dist/tools/suggest-fix.js +5 -1
  5. package/dist/tools/suggest-fix.js.map +1 -1
  6. package/errors/caching-artifacts/cache-key-too-long.yml +93 -0
  7. package/errors/caching-artifacts/cache-path-not-exist-skipped.yml +152 -0
  8. package/errors/caching-artifacts/cache-save-same-key-html-conflict.yml +109 -0
  9. package/errors/caching-artifacts/docker-buildx-gha-cache-capacity.yml +107 -0
  10. package/errors/caching-artifacts/setup-ruby-bundler-ephemeral-workdir-cache-miss.yml +147 -0
  11. package/errors/caching-artifacts/upload-artifact-v3-retirement-blocked.yml +123 -0
  12. package/errors/caching-artifacts/upload-artifact-v4-large-file-macos-hang.yml +111 -0
  13. package/errors/concurrency-timing/always-cleanup-5min-forced-kill.yml +140 -0
  14. package/errors/concurrency-timing/concurrency-group-env-context-undefined.yml +99 -0
  15. package/errors/concurrency-timing/required-check-pending-path-filter-skip.yml +160 -0
  16. package/errors/concurrency-timing/wait-timer-cancel-in-progress-starvation.yml +125 -0
  17. package/errors/known-unsolved/composite-action-step-timeout-minutes-ignored.yml +146 -0
  18. package/errors/known-unsolved/reusable-workflow-no-composite-action-call.yml +116 -0
  19. package/errors/known-unsolved/schedule-trigger-default-branch-only.yml +113 -0
  20. package/errors/known-unsolved/secrets-not-allowed-in-if-conditions.yml +149 -0
  21. package/errors/known-unsolved/workflow-50-rerun-limit.yml +110 -0
  22. package/errors/permissions-auth/check-run-status-modification-blocked.yml +134 -0
  23. package/errors/permissions-auth/dependabot-pr-secrets-unavailable.yml +133 -0
  24. package/errors/permissions-auth/fine-grained-pat-deployment-write-required.yml +146 -0
  25. package/errors/permissions-auth/github-app-installation-token-new-format.yml +124 -0
  26. package/errors/permissions-auth/github-packages-read-requires-packages-permission.yml +128 -0
  27. package/errors/permissions-auth/oidc-id-token-write-permission-missing.yml +169 -0
  28. package/errors/permissions-auth/permissions-empty-block-removes-contents-read.yml +97 -0
  29. package/errors/permissions-auth/reusable-workflow-permissions-not-inherited.yml +114 -0
  30. package/errors/runner-environment/checkout-windows-ebusy-lock.yml +124 -0
  31. package/errors/runner-environment/deprecated-action-version-auto-rejected.yml +89 -0
  32. package/errors/runner-environment/github-hosted-runner-disk-space-full.yml +85 -0
  33. package/errors/runner-environment/github-path-same-step-not-found.yml +114 -0
  34. package/errors/runner-environment/github-script-v6-octokit-rest-actions-not-function.yml +87 -0
  35. package/errors/runner-environment/macos-13-deprecation-brownout.yml +93 -0
  36. package/errors/runner-environment/macos-15-mono-nuget-removed.yml +151 -0
  37. package/errors/runner-environment/macos-15-xcode-simulator-sdk-policy.yml +141 -0
  38. package/errors/runner-environment/multi-runtime-nov2025-removal.yml +120 -0
  39. package/errors/runner-environment/runner-oom-exit-code-137.yml +117 -0
  40. package/errors/runner-environment/setup-go-go123-telemetry-cache-failure.yml +92 -0
  41. package/errors/runner-environment/setup-java-distribution-required.yml +108 -0
  42. package/errors/runner-environment/ubuntu-2004-retirement-brownout.yml +107 -0
  43. package/errors/runner-environment/windows-latest-d-drive-removed.yml +104 -0
  44. package/errors/runner-environment/windows-vs2026-cuda-host-compiler-unsupported.yml +145 -0
  45. package/errors/silent-failures/event-commits-empty-on-workflow-dispatch.yml +110 -0
  46. package/errors/silent-failures/fetch-tags-depth-one-silent-no-op.yml +77 -0
  47. package/errors/silent-failures/github-env-multiline-value-truncated.yml +127 -0
  48. package/errors/silent-failures/github-sha-pr-merge-commit-not-head.yml +150 -0
  49. package/errors/silent-failures/job-output-masked-as-secret-empty.yml +147 -0
  50. package/errors/silent-failures/upload-artifact-permissions-stripped.yml +98 -0
  51. package/errors/triggers/pull-request-branches-filter-matches-base-not-head.yml +140 -0
  52. package/errors/triggers/push-event-fires-on-branch-delete.yml +129 -0
  53. package/errors/triggers/push-first-commit-before-sha-zeros.yml +160 -0
  54. package/errors/yaml-syntax/continue-on-error-env-context-rejected.yml +130 -0
  55. package/errors/yaml-syntax/fromjson-empty-string-crash.yml +99 -0
  56. package/errors/yaml-syntax/if-bang-negation-yaml-tag.yml +145 -0
  57. package/errors/yaml-syntax/local-action-path-always-top-level.yml +142 -0
  58. package/package.json +1 -1
@@ -0,0 +1,149 @@
1
+ id: known-unsolved-016
2
+ title: "secrets Context Cannot Be Directly Referenced in if: Conditions"
3
+ category: known-unsolved
4
+ severity: limitation
5
+ tags:
6
+ - secrets
7
+ - if-condition
8
+ - conditional
9
+ - env
10
+ - limitation
11
+ - expressions
12
+ - step-condition
13
+ patterns:
14
+ - regex: "if:.*\\$\\{\\{.*secrets\\..*\\}\\}"
15
+ flags: "i"
16
+ - regex: "secrets\\.[A-Z_a-z]+\\s*!=\\s*''"
17
+ flags: "i"
18
+ error_messages:
19
+ - "This job was skipped."
20
+ root_cause: |
21
+ GitHub Actions explicitly prohibits direct use of the `secrets` context in `if:`
22
+ conditional expressions at the job or step level. The platform blocks this as a
23
+ security measure to prevent secret values from being leaked through expression
24
+ evaluation in workflow logs.
25
+
26
+ Attempting `if: ${{ secrets.MY_SECRET != '' }}` or `if: secrets.MY_SECRET` silently
27
+ fails — the condition evaluates to an empty string (falsy), causing the step or job
28
+ to be skipped with no error message, warning, or explanation.
29
+
30
+ The same restriction applies at the job level:
31
+ ```yaml
32
+ jobs:
33
+ deploy:
34
+ if: ${{ secrets.DEPLOY_KEY != '' }} # silently evaluates wrong
35
+ ```
36
+
37
+ This catches developers off guard because:
38
+ - The workflow is syntactically valid and passes YAML linting — no parse error
39
+ - The failure mode is silent: the job/step is skipped with "This job was skipped"
40
+ rather than an explicit error about the secrets restriction
41
+ - The pattern looks correct by analogy with other contexts (`github.*`, `env.*`,
42
+ `vars.*`) that DO work in `if:` conditions
43
+ - Secrets used in `run:` scripts and `with:` inputs work fine — only `if:` is blocked
44
+
45
+ GitHub's documentation explicitly states: "Secrets cannot be directly referenced
46
+ in `if:` conditionals. Instead, consider setting secrets as job-level environment
47
+ variables, then referencing the environment variables to conditionally run steps."
48
+
49
+ As of 2026 this is a by-design limitation with no planned native fix.
50
+ `actionlint` now flags direct secret references in `if:` expressions as an error.
51
+
52
+ Sources:
53
+ - GitHub Docs — Using secrets in GitHub Actions (use-secrets.md)
54
+ - github/docs#12722 — PR documenting the `if:` condition restriction
55
+ fix: |
56
+ Promote the secret to a job-level `env:` variable that evaluates the boolean (not the
57
+ secret value itself), then reference `env.VARIABLE_NAME` in the `if:` condition.
58
+
59
+ For job-level `if:` (not step-level), use a preceding detection job that outputs
60
+ whether the secret is present, and condition the downstream job on that output.
61
+ fix_code:
62
+ - language: yaml
63
+ label: "WRONG — direct secrets reference in step if: condition"
64
+ code: |
65
+ jobs:
66
+ deploy:
67
+ runs-on: ubuntu-latest
68
+ steps:
69
+ - name: Deploy to production
70
+ if: ${{ secrets.DEPLOY_KEY != '' }} # silently wrong — step is skipped
71
+ run: ./deploy.sh
72
+ env:
73
+ DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
74
+
75
+ - language: yaml
76
+ label: "CORRECT — promote secret presence to job-level env, reference in step if:"
77
+ code: |
78
+ jobs:
79
+ deploy:
80
+ runs-on: ubuntu-latest
81
+ env:
82
+ # Safe: evaluates to "true"/"false" string — not the secret value itself
83
+ HAS_DEPLOY_KEY: ${{ secrets.DEPLOY_KEY != '' }}
84
+ steps:
85
+ - name: Deploy to production
86
+ if: env.HAS_DEPLOY_KEY == 'true'
87
+ run: ./deploy.sh
88
+ env:
89
+ DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
90
+
91
+ - language: yaml
92
+ label: "CORRECT — job-level conditional via detection job + output"
93
+ code: |
94
+ jobs:
95
+ check-secrets:
96
+ runs-on: ubuntu-latest
97
+ outputs:
98
+ has_key: ${{ steps.check.outputs.has_key }}
99
+ steps:
100
+ - id: check
101
+ env:
102
+ DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
103
+ run: |
104
+ if [ -n "$DEPLOY_KEY" ]; then
105
+ echo "has_key=true" >> "$GITHUB_OUTPUT"
106
+ else
107
+ echo "has_key=false" >> "$GITHUB_OUTPUT"
108
+ fi
109
+
110
+ deploy:
111
+ needs: check-secrets
112
+ # Job-level if: can reference job outputs — not secrets directly
113
+ if: needs.check-secrets.outputs.has_key == 'true'
114
+ runs-on: ubuntu-latest
115
+ steps:
116
+ - run: ./deploy.sh
117
+ env:
118
+ DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
119
+
120
+ - language: yaml
121
+ label: "CORRECT — inline env check using bash within step"
122
+ code: |
123
+ jobs:
124
+ deploy:
125
+ runs-on: ubuntu-latest
126
+ steps:
127
+ - name: Deploy (skip gracefully if secret absent)
128
+ env:
129
+ DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
130
+ run: |
131
+ if [ -z "$DEPLOY_KEY" ]; then
132
+ echo "No DEPLOY_KEY configured — skipping deployment"
133
+ exit 0
134
+ fi
135
+ ./deploy.sh
136
+ prevention:
137
+ - "Never reference `secrets.*` directly in `if:` conditions — always promote to `env:` first."
138
+ - "Use `env.HAS_SECRET: ${{ secrets.MY_SECRET != '' }}` at the job level to create a safe boolean env var."
139
+ - "Install and run `actionlint` in CI — it now flags direct secret references in `if:` as an error."
140
+ - "For job-level conditionals based on secret presence, use a dedicated detection job with outputs."
141
+ docs:
142
+ - url: "https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#using-secrets-in-a-workflow"
143
+ label: "GitHub Docs: Using secrets in a workflow (secrets cannot be in if: conditionals)"
144
+ - url: "https://github.com/github/docs/pull/12722"
145
+ label: "github/docs#12722 — Document secrets in if: conditionals limitation"
146
+ - url: "https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsif"
147
+ label: "Workflow syntax: steps[*].if"
148
+ - url: "https://github.com/rhysd/actionlint"
149
+ label: "actionlint — flags direct secrets in if: conditions as an error"
@@ -0,0 +1,110 @@
1
+ id: known-unsolved-018
2
+ title: "Workflow rerun limit of 50 exceeded — subsequent reruns fail permanently"
3
+ category: known-unsolved
4
+ severity: limitation
5
+ tags:
6
+ - rerun
7
+ - retry
8
+ - automation
9
+ - limits
10
+ - platform-limit
11
+ patterns:
12
+ - regex: "exceeded.*maximum.*reruns|maximum.*reruns.*exceeded"
13
+ flags: "i"
14
+ - regex: "rerun limit.*50|50.*rerun limit"
15
+ flags: "i"
16
+ - regex: "This workflow run was cancelled because it exceeded"
17
+ flags: "i"
18
+ error_messages:
19
+ - "This workflow run exceeded the limit of 50 reruns"
20
+ - "exceeded the maximum number of reruns (50)"
21
+ - "Cannot rerun this workflow: rerun limit reached"
22
+ root_cause: |
23
+ Since April 10, 2026, GitHub Actions enforces a hard limit of 50 reruns
24
+ per individual workflow run (counting both full reruns and re-runs of
25
+ individual job subsets). Once a workflow run reaches 50 attempts, any
26
+ further rerun request — whether triggered manually, via the GitHub CLI,
27
+ the REST API, or an automated rerun action — will fail with an error
28
+ annotation on the check suite.
29
+
30
+ The limit was introduced to stop automation patterns that use infinite-retry
31
+ loops (e.g., rerunning every failed job automatically, retrying flaky
32
+ infrastructure hundreds of times) from adding excessive load to GitHub's
33
+ infrastructure. The count is tied to the original workflow run ID — creating
34
+ a new workflow run by re-pushing or re-dispatching does NOT inherit the
35
+ count.
36
+
37
+ This limitation is most frequently encountered by:
38
+ - Flaky test environments that rely on automated rerun tooling
39
+ (k1LoW/rerun-action, lewagon/retry-workflow-action, etc.)
40
+ - Long-running deployment pipelines with retry-until-success patterns
41
+ - Teams using merge queues or merge trains that auto-requeue failed checks
42
+ - Internal automation that retries status checks repeatedly against a PR
43
+
44
+ There is no way to exceed this limit — it is enforced server-side with no
45
+ configuration option. The only resolution is to trigger a fresh workflow run.
46
+ fix: |
47
+ There is no way to raise or bypass the 50-rerun limit. Mitigation strategies:
48
+
49
+ 1. Fix the underlying flakiness so retries are rare (<50 per workflow run).
50
+ 2. Trigger a fresh workflow run rather than retrying the same run ID:
51
+ - Re-push to the branch or rebase (creates new push event → new run)
52
+ - For pull_request: close and reopen the PR (creates new run ID)
53
+ - For workflow_dispatch: dispatch again (creates a completely new run)
54
+ 3. Redesign retry logic to use a separate `workflow_run` triggered workflow
55
+ that dispatches a NEW workflow instead of rerunning the same one.
56
+ 4. For flaky infra: fix at the infra layer (idempotent step design, retry
57
+ at the shell/script level within a single attempt) rather than rerunning
58
+ the entire workflow.
59
+ fix_code:
60
+ - language: yaml
61
+ label: "Use shell-level retry for flaky steps instead of workflow-level rerun"
62
+ code: |
63
+ jobs:
64
+ deploy:
65
+ runs-on: ubuntu-latest
66
+ steps:
67
+ - name: Flaky deploy step with shell retry
68
+ run: |
69
+ # Retry up to 5 times with 30s backoff — no workflow rerun needed
70
+ for i in 1 2 3 4 5; do
71
+ echo "Attempt $i..."
72
+ ./scripts/deploy.sh && break || {
73
+ if [ $i -lt 5 ]; then
74
+ echo "Deploy failed, retrying in 30s..."
75
+ sleep 30
76
+ else
77
+ echo "All $i attempts failed"
78
+ exit 1
79
+ fi
80
+ }
81
+ done
82
+ - language: yaml
83
+ label: "Dispatch a new workflow run instead of rerunning the same run"
84
+ code: |
85
+ # If you need automated rerun logic, trigger a fresh dispatch
86
+ # rather than using gh run rerun (which increments the rerun counter)
87
+ jobs:
88
+ retry-via-dispatch:
89
+ runs-on: ubuntu-latest
90
+ if: failure()
91
+ steps:
92
+ - name: Trigger fresh run via workflow_dispatch
93
+ run: |
94
+ gh workflow run ci.yml \
95
+ --ref ${{ github.ref }} \
96
+ --field triggered_by=auto-retry
97
+ env:
98
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
99
+ prevention:
100
+ - "Treat workflow reruns as a debugging tool, not a reliability mechanism — fix flakiness at the source"
101
+ - "Use shell-level retry loops (for loop with sleep) for individual steps that are inherently flaky"
102
+ - "Use workflow_dispatch to start a fresh run rather than rerunning the same run ID when automation needs to retry"
103
+ - "Monitor workflows that regularly exceed 5 reruns — they signal a deeper reliability problem that needs fixing"
104
+ docs:
105
+ - url: "https://github.blog/changelog/2026-04-10-actions-workflows-are-limited-to-50-reruns/"
106
+ label: "GitHub Changelog — Actions workflows are limited to 50 reruns (Apr 2026)"
107
+ - url: "https://docs.github.com/en/actions/managing-workflow-runs/re-running-workflows-and-jobs"
108
+ label: "GitHub Docs — Re-running workflows and jobs"
109
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_dispatch"
110
+ label: "GitHub Docs — workflow_dispatch event"
@@ -0,0 +1,134 @@
1
+ id: permissions-auth-018
2
+ title: "GITHUB_TOKEN cannot modify check run status or conclusion after March 2025"
3
+ category: permissions-auth
4
+ severity: error
5
+ tags:
6
+ - check-runs
7
+ - github-token
8
+ - checks-api
9
+ - permissions
10
+ - breaking-change
11
+ patterns:
12
+ - regex: "Check run status and conclusions can only be updated internally by GitHub Actions"
13
+ flags: "i"
14
+ - regex: "check_run.*status.*conclusion.*updated internally"
15
+ flags: "i"
16
+ - regex: "Changes to check run status modification"
17
+ flags: "i"
18
+ error_messages:
19
+ - "Error: Check run status and conclusions can only be updated internally by GitHub Actions. Please see https://github.blog/changelog/2025-02-12-notice-of-upcoming-deprecations-and-breaking-changes-for-github-actions/#changes-to-check-run-status-modification"
20
+ - "Check run status and conclusions can only be updated internally by GitHub Actions"
21
+ root_cause: |
22
+ GitHub deprecated external modification of check run status and conclusion
23
+ fields on March 31, 2025. Prior to this change, workflows and third-party
24
+ actions (e.g., LouisBrunner/checks-action, custom Checks API calls) could
25
+ use the repository's GITHUB_TOKEN to call the REST API and update the status
26
+ (queued, in_progress, completed) or conclusion (success, failure, etc.) of
27
+ an existing check run that was originally created by GitHub Actions.
28
+
29
+ GitHub blocked this pattern to prevent confusion and race conditions that
30
+ could arise when external code overwrote the status of a check run that
31
+ GitHub Actions was still managing. Starting March 31, 2025, any attempt
32
+ to PATCH an existing Actions-created check run's status or conclusion via
33
+ GITHUB_TOKEN returns an error.
34
+
35
+ This affects:
36
+ - Actions that wrap the GitHub Checks API to create multi-step annotations
37
+ (e.g., LouisBrunner/checks-action)
38
+ - Custom workflows that read a check_run ID from a prior step and patch it
39
+ - External tooling that patches check run state using the built-in token
40
+ - Any pattern where one step creates a check run and a later step or job
41
+ tries to set it to "completed"
42
+
43
+ Check runs that were NOT created by GitHub Actions (created via a GitHub App
44
+ or OAuth token) are not affected and can still be updated by their creator.
45
+ fix: |
46
+ Workflows that need to update check run status or conclusion must create
47
+ those check runs with a non-GITHUB_TOKEN credential so that the same
48
+ credential can also update them later.
49
+
50
+ Option 1 (GitHub App): Use actions/create-github-app-token to generate an
51
+ installation token and create check runs with that token. The same GitHub App
52
+ token can then update the check runs it created.
53
+
54
+ Option 2 (Remove update steps): If the check run is only being updated to
55
+ mark it "completed" at the end of a job, let GitHub Actions manage the
56
+ conclusion automatically. Remove the final PATCH step.
57
+
58
+ Option 3 (Migrate to annotations): Replace custom check run status tracking
59
+ with GitHub Actions step annotations (::notice::, ::warning::, ::error::).
60
+ These use the workflow's built-in UI and require no Checks API calls.
61
+ fix_code:
62
+ - language: yaml
63
+ label: "Create and update check runs using a GitHub App installation token"
64
+ code: |
65
+ jobs:
66
+ annotate:
67
+ runs-on: ubuntu-latest
68
+ permissions:
69
+ # id-token:write needed if using OIDC-based App token
70
+ contents: read
71
+ steps:
72
+ - name: Generate GitHub App token
73
+ id: app-token
74
+ uses: actions/create-github-app-token@v2
75
+ with:
76
+ app-id: ${{ vars.CHECKS_APP_ID }}
77
+ private-key: ${{ secrets.CHECKS_APP_PRIVATE_KEY }}
78
+
79
+ - name: Create check run
80
+ id: create-check
81
+ uses: actions/github-script@v7
82
+ with:
83
+ github-token: ${{ steps.app-token.outputs.token }}
84
+ script: |
85
+ const { data } = await github.rest.checks.create({
86
+ owner: context.repo.owner,
87
+ repo: context.repo.repo,
88
+ name: 'My Custom Check',
89
+ head_sha: context.sha,
90
+ status: 'in_progress',
91
+ });
92
+ return data.id;
93
+
94
+ - name: Do work...
95
+ run: echo "running checks..."
96
+
97
+ - name: Update check run to completed
98
+ uses: actions/github-script@v7
99
+ with:
100
+ # Use the App token — NOT github.token — to update the check
101
+ github-token: ${{ steps.app-token.outputs.token }}
102
+ script: |
103
+ await github.rest.checks.update({
104
+ owner: context.repo.owner,
105
+ repo: context.repo.repo,
106
+ check_run_id: ${{ steps.create-check.outputs.result }},
107
+ status: 'completed',
108
+ conclusion: 'success',
109
+ });
110
+ - language: yaml
111
+ label: "Use built-in step annotations instead of custom check runs"
112
+ code: |
113
+ - name: Emit warning annotation
114
+ run: echo "::warning file=src/main.js,line=42::Deprecated API usage detected"
115
+
116
+ - name: Emit error annotation (fails step)
117
+ run: echo "::error title=Lint Failed::3 lint violations found in src/"
118
+
119
+ # These annotations appear in the workflow summary and PR checks UI
120
+ # without any Checks API calls or external tokens
121
+ prevention:
122
+ - "Never use GITHUB_TOKEN to PATCH the status or conclusion of an Actions-created check run"
123
+ - "Create custom check runs using a GitHub App installation token so the same credential owns them end-to-end"
124
+ - "Prefer built-in step annotations (::warning::, ::error::) over custom Checks API calls for simple annotation needs"
125
+ - "Audit third-party actions that wrap the Checks API (e.g., LouisBrunner/checks-action) for compatibility with the March 2025 change"
126
+ docs:
127
+ - url: "https://github.blog/changelog/2025-02-12-notice-of-upcoming-deprecations-and-breaking-changes-for-github-actions/#changes-to-check-run-status-modification"
128
+ label: "GitHub Changelog — Changes to check run status modification (Feb 2025)"
129
+ - url: "https://docs.github.com/en/rest/checks/runs"
130
+ label: "GitHub REST API — Check Runs"
131
+ - url: "https://github.com/LouisBrunner/checks-action/issues/369"
132
+ label: "LouisBrunner/checks-action#369 — Error after March 2025 enforcement"
133
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions#setting-a-warning-message"
134
+ label: "GitHub Docs — Workflow commands for annotations"
@@ -0,0 +1,133 @@
1
+ id: permissions-auth-013
2
+ title: "Dependabot Pull Requests Cannot Access Repository Secrets — Read-Only Token"
3
+ category: permissions-auth
4
+ severity: silent-failure
5
+ tags:
6
+ - dependabot
7
+ - secrets
8
+ - github-token
9
+ - pull-request
10
+ - read-only
11
+ patterns:
12
+ - regex: "Context access might be invalid: secrets\\."
13
+ flags: "i"
14
+ - regex: "denied|forbidden|403"
15
+ flags: "i"
16
+ - regex: "Resource not accessible by integration"
17
+ flags: "i"
18
+ error_messages:
19
+ - "Context access might be invalid: secrets.NPM_TOKEN"
20
+ - "Error: Process completed with exit code 1."
21
+ - "HttpError: Resource not accessible by integration"
22
+ root_cause: |
23
+ For security reasons, GitHub Actions workflows triggered by Dependabot pull requests
24
+ are granted a **read-only GITHUB_TOKEN** and **cannot access repository secrets**.
25
+
26
+ When a Dependabot PR triggers an `on: pull_request` workflow:
27
+ - `secrets.*` expressions evaluate to empty string (not an error — silently empty)
28
+ - `GITHUB_TOKEN` has read-only permissions (cannot write to the repository, push
29
+ tags, create releases, comment on PRs, etc.)
30
+ - Steps that need secrets (publishing to npm, Docker Hub, PyPI, etc.) fail with
31
+ authentication errors or silently produce wrong results
32
+
33
+ This is intentional: Dependabot opens PRs that update dependencies from external
34
+ sources. If Dependabot-triggered workflows had full secret access, a malicious
35
+ package update could exfiltrate your credentials via a modified build step.
36
+
37
+ The failure is often silent because:
38
+ - The secret evaluates to empty string — tools may fail with "missing credentials"
39
+ rather than a clear "secret unavailable" error
40
+ - `GITHUB_TOKEN` write failures show as 403 "Resource not accessible by integration"
41
+
42
+ Sources: dependabot/dependabot-core#3253, #5464
43
+ fix: |
44
+ Choose one of these approaches depending on your security requirements:
45
+
46
+ **Option 1 (Recommended): Use Dependabot secrets**
47
+ Add the required secrets to the Dependabot secrets store (Settings > Secrets and
48
+ variables > Dependabot). Dependabot-triggered workflows can access these separately
49
+ from repository secrets.
50
+
51
+ **Option 2: Auto-approve and merge without secrets**
52
+ For dependency updates, use `pull_request_review` + `pull_request` auto-merge
53
+ pattern — approve Dependabot PRs that pass CI without needing secrets.
54
+
55
+ **Option 3: Use `pull_request_target` with caution**
56
+ `pull_request_target` runs in the base repo context and has full secret access.
57
+ Only use this for trusted, controlled steps (like leaving a comment) — never
58
+ checkout and run untrusted fork code in a `pull_request_target` step with secrets.
59
+
60
+ **Option 4: Filter out Dependabot runs**
61
+ Add `if: github.actor != 'dependabot[bot]'` to skip secret-requiring steps for
62
+ Dependabot PRs, letting CI pass without the publishing step.
63
+ fix_code:
64
+ - language: yaml
65
+ label: "Add secrets to Dependabot secrets store (Settings → Secrets → Dependabot)"
66
+ code: |
67
+ # Add NPM_TOKEN (and any other required secrets) to:
68
+ # Settings > Secrets and variables > Dependabot
69
+ # These are separate from repository secrets and accessible to Dependabot runs.
70
+
71
+ # No workflow change needed — ${{ secrets.NPM_TOKEN }} will resolve correctly
72
+ # once the secret is added to the Dependabot secrets store.
73
+ - language: yaml
74
+ label: "Skip secret-requiring steps for Dependabot PRs"
75
+ code: |
76
+ jobs:
77
+ build:
78
+ runs-on: ubuntu-latest
79
+ steps:
80
+ - uses: actions/checkout@v4
81
+
82
+ - name: Run tests
83
+ run: npm test
84
+ # Tests run for all PRs including Dependabot
85
+
86
+ - name: Publish to npm (skip for Dependabot)
87
+ if: github.actor != 'dependabot[bot]'
88
+ run: npm publish
89
+ env:
90
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
91
+ # NPM_TOKEN is empty for Dependabot — skip the step entirely
92
+ - language: yaml
93
+ label: "Auto-approve Dependabot PRs using Dependabot secrets + gh CLI"
94
+ code: |
95
+ name: Auto-approve Dependabot
96
+ on: pull_request
97
+
98
+ permissions:
99
+ pull-requests: write
100
+ contents: write
101
+
102
+ jobs:
103
+ auto-approve:
104
+ runs-on: ubuntu-latest
105
+ if: github.actor == 'dependabot[bot]'
106
+ steps:
107
+ - name: Fetch Dependabot metadata
108
+ id: meta
109
+ uses: dependabot/fetch-metadata@v2
110
+
111
+ - name: Auto-approve minor and patch updates
112
+ if: steps.meta.outputs.update-type != 'version-update:semver-major'
113
+ run: gh pr review --approve "$PR_URL"
114
+ env:
115
+ PR_URL: ${{ github.event.pull_request.html_url }}
116
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
117
+ # GITHUB_TOKEN in dependabot context is read-only for repo content
118
+ # but CAN approve PRs when permissions: pull-requests: write is set
119
+ prevention:
120
+ - "Never assume repository secrets are available in Dependabot-triggered workflows — they aren't."
121
+ - "Use `github.actor == 'dependabot[bot]'` conditions to branch logic for Dependabot vs human PRs."
122
+ - "Add required secrets to the Dependabot secrets store separately from repository secrets."
123
+ - "Audit CI workflows for `${{ secrets.* }}` usage and test with a Dependabot PR to confirm behavior."
124
+ - "Do not use `pull_request_target` with `actions/checkout` on the PR head ref — this is a security vulnerability."
125
+ docs:
126
+ - url: "https://docs.github.com/en/code-security/dependabot/working-with-dependabot/automating-dependabot-with-github-actions"
127
+ label: "Automating Dependabot with GitHub Actions"
128
+ - url: "https://docs.github.com/en/code-security/dependabot/working-with-dependabot/configuring-access-to-private-registries-for-dependabot"
129
+ label: "Configuring Dependabot secrets"
130
+ - url: "https://github.com/dependabot/dependabot-core/issues/3253"
131
+ label: "dependabot-core#3253 — secrets unavailable in Dependabot-triggered workflows"
132
+ - url: "https://github.com/dependabot/dependabot-core/issues/5464"
133
+ label: "dependabot-core#5464 — Dependabot read-only token behavior"
@@ -0,0 +1,146 @@
1
+ id: permissions-auth-015
2
+ title: "Fine-Grained PAT Requires 'deployments: write' to Approve or Reject Deployments (Was 'read')"
3
+ category: permissions-auth
4
+ severity: error
5
+ tags:
6
+ - fine-grained-pat
7
+ - deployments
8
+ - permissions
9
+ - pat
10
+ - environments
11
+ - approval
12
+ - breaking-change
13
+ patterns:
14
+ - regex: "Resource not accessible by integration.*deployment|403.*deployment.*approve"
15
+ flags: "i"
16
+ - regex: "Must have admin rights to Repository.*approve.*deployment|permission.*deployments.*write"
17
+ flags: "i"
18
+ - regex: "HttpError: Resource not accessible by integration.*reviewers"
19
+ flags: "i"
20
+ - regex: "403.*Unable to approve deployment|Forbidden.*approve.*pending.*deployment"
21
+ flags: "i"
22
+ error_messages:
23
+ - "Resource not accessible by integration"
24
+ - "HttpError: Resource not accessible by integration"
25
+ - "Error: RequestError [HttpError]: Resource not accessible by integration"
26
+ - "Must have admin rights to Repository. - https://docs.github.com/rest/deployments/deployments"
27
+ - "403: Forbidden - You need deployments:write permission to approve or reject a deployment."
28
+ root_cause: |
29
+ On April 1, 2025, GitHub changed how deployment approval permissions work for
30
+ fine-grained personal access tokens (PATs). Prior to this change, a fine-grained PAT
31
+ with `deployments: read` permission could approve, approve-with-comment, or reject pending
32
+ deployments in protected environments. After April 1, 2025, the `deployments: write`
33
+ permission is **required** to review deployments.
34
+
35
+ This change was announced in the GitHub Changelog on March 20, 2025. Impacted customers
36
+ were notified by email in early March 2025. Fine-grained PATs that were created before
37
+ the change and only granted `deployments: read` stopped being able to approve or reject
38
+ deployments on April 1, 2025 without any change to the token itself — existing tokens
39
+ suddenly lost the ability they previously had.
40
+
41
+ **Affected scenarios:**
42
+ - GitHub Actions workflows that call `POST /repos/{owner}/{repo}/actions/runs/{run_id}/approvals`
43
+ or `POST /repos/{owner}/{repo}/actions/runs/{run_id}/pending_deployments` with a
44
+ fine-grained PAT that only has `deployments: read`
45
+ - Custom deployment orchestration scripts using fine-grained PATs
46
+ - GitHub Apps or OAuth apps authenticated via fine-grained PAT that approved deployments
47
+
48
+ **Note:** Classic PATs and GitHub Apps with `deployments: write` scope are unaffected.
49
+ GitHub Apps using their own installation token (not a fine-grained PAT) are also
50
+ unaffected as long as they have the `deployments: write` permission in their app settings.
51
+ fix: |
52
+ Update the fine-grained PAT to grant `deployments: write` permission instead of
53
+ `deployments: read`:
54
+
55
+ 1. Go to GitHub Settings → Developer settings → Personal access tokens → Fine-grained tokens
56
+ 2. Click the PAT used in your workflow
57
+ 3. Click "Regenerate token"
58
+ 4. Under "Permissions → Repository permissions → Deployments", change from "Read" to "Read and write"
59
+ 5. Update the secret in your repository/organization with the new token value
60
+
61
+ If you use a GitHub App instead of a PAT for deployments, ensure the App's repository
62
+ permission for "Deployments" is set to "Read and write" in the App settings.
63
+ fix_code:
64
+ - language: yaml
65
+ label: "Workflow using octokit to approve deployments — ensure PAT has deployments:write"
66
+ code: |
67
+ jobs:
68
+ approve-deployment:
69
+ runs-on: ubuntu-latest
70
+ steps:
71
+ - name: Approve pending deployment
72
+ uses: actions/github-script@v7
73
+ with:
74
+ # This PAT MUST have deployments:write permission (not just read)
75
+ github-token: ${{ secrets.DEPLOYMENT_APPROVAL_PAT }}
76
+ script: |
77
+ const pendingDeployments = await github.rest.actions.getPendingDeploymentsForRun({
78
+ owner: context.repo.owner,
79
+ repo: context.repo.repo,
80
+ run_id: context.runId
81
+ });
82
+
83
+ const environmentIds = pendingDeployments.data
84
+ .filter(d => d.environment.name === 'production')
85
+ .map(d => d.environment.id);
86
+
87
+ await github.rest.actions.reviewPendingDeploymentsForRun({
88
+ owner: context.repo.owner,
89
+ repo: context.repo.repo,
90
+ run_id: context.runId,
91
+ environment_ids: environmentIds,
92
+ state: 'approved',
93
+ comment: 'Auto-approved by CI orchestration'
94
+ });
95
+ - language: yaml
96
+ label: "Verify token permissions before attempting deployment approval"
97
+ code: |
98
+ jobs:
99
+ check-permissions:
100
+ runs-on: ubuntu-latest
101
+ steps:
102
+ - name: Verify deployment token scope
103
+ uses: actions/github-script@v7
104
+ with:
105
+ github-token: ${{ secrets.DEPLOYMENT_APPROVAL_PAT }}
106
+ script: |
107
+ // Try a read operation first to confirm token is valid
108
+ const token = process.env.GITHUB_TOKEN;
109
+
110
+ // Check deployments permission by listing — uses read; write needed for approve
111
+ const deployments = await github.rest.repos.listDeployments({
112
+ owner: context.repo.owner,
113
+ repo: context.repo.repo
114
+ });
115
+ console.log(`Token valid — found ${deployments.data.length} deployments.`);
116
+ console.log('NOTE: To APPROVE deployments, this PAT needs deployments:write, not just read.');
117
+ - language: yaml
118
+ label: "Use GITHUB_TOKEN with environment protection rules instead of fine-grained PAT"
119
+ code: |
120
+ # Better pattern: let GitHub's environment protection handle approvals
121
+ # rather than auto-approving via API with a PAT
122
+
123
+ jobs:
124
+ deploy:
125
+ runs-on: ubuntu-latest
126
+ environment:
127
+ name: production
128
+ # Configure required reviewers in Environment settings
129
+ # No custom PAT needed — GITHUB_TOKEN works for normal deployment flows
130
+ steps:
131
+ - name: Deploy
132
+ run: echo "Deploying to production..."
133
+ # The 'environment' key above enforces approval through GitHub UI
134
+ # without needing deployments:write in a PAT
135
+ prevention:
136
+ - "When creating fine-grained PATs for CI/CD deployment workflows, always grant `deployments: write` upfront — even if you only need read today, write is required for any approval-related operation."
137
+ - "Audit all fine-grained PATs used in GitHub Actions for their `deployments` permission level after the April 2025 change."
138
+ - "Prefer using GitHub's built-in environment protection rules (required reviewers, wait timers) over custom approval scripts with PATs — fewer moving parts."
139
+ - "Subscribe to the GitHub Changelog to receive advance notice of breaking permission changes."
140
+ docs:
141
+ - url: "https://github.blog/changelog/2025-03-20-notification-of-upcoming-breaking-changes-in-github-actions/"
142
+ label: "GitHub Changelog: Modification to deployment permissions (March 20, 2025)"
143
+ - url: "https://docs.github.com/en/rest/actions/workflow-runs#review-pending-deployments-for-a-workflow-run"
144
+ label: "GitHub REST API: Review pending deployments for a workflow run"
145
+ - url: "https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token"
146
+ label: "GitHub Docs: Creating a fine-grained personal access token"