@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.
- package/dist/db/search.js +3 -1
- package/dist/db/search.js.map +1 -1
- package/dist/tools/suggest-fix.d.ts.map +1 -1
- package/dist/tools/suggest-fix.js +5 -1
- package/dist/tools/suggest-fix.js.map +1 -1
- package/errors/caching-artifacts/cache-key-too-long.yml +93 -0
- package/errors/caching-artifacts/cache-path-not-exist-skipped.yml +152 -0
- package/errors/caching-artifacts/cache-save-same-key-html-conflict.yml +109 -0
- package/errors/caching-artifacts/docker-buildx-gha-cache-capacity.yml +107 -0
- package/errors/caching-artifacts/setup-ruby-bundler-ephemeral-workdir-cache-miss.yml +147 -0
- package/errors/caching-artifacts/upload-artifact-v3-retirement-blocked.yml +123 -0
- package/errors/caching-artifacts/upload-artifact-v4-large-file-macos-hang.yml +111 -0
- package/errors/concurrency-timing/always-cleanup-5min-forced-kill.yml +140 -0
- package/errors/concurrency-timing/concurrency-group-env-context-undefined.yml +99 -0
- package/errors/concurrency-timing/required-check-pending-path-filter-skip.yml +160 -0
- package/errors/concurrency-timing/wait-timer-cancel-in-progress-starvation.yml +125 -0
- package/errors/known-unsolved/composite-action-step-timeout-minutes-ignored.yml +146 -0
- package/errors/known-unsolved/reusable-workflow-no-composite-action-call.yml +116 -0
- package/errors/known-unsolved/schedule-trigger-default-branch-only.yml +113 -0
- package/errors/known-unsolved/secrets-not-allowed-in-if-conditions.yml +149 -0
- package/errors/known-unsolved/workflow-50-rerun-limit.yml +110 -0
- package/errors/permissions-auth/check-run-status-modification-blocked.yml +134 -0
- package/errors/permissions-auth/dependabot-pr-secrets-unavailable.yml +133 -0
- package/errors/permissions-auth/fine-grained-pat-deployment-write-required.yml +146 -0
- package/errors/permissions-auth/github-app-installation-token-new-format.yml +124 -0
- package/errors/permissions-auth/github-packages-read-requires-packages-permission.yml +128 -0
- package/errors/permissions-auth/oidc-id-token-write-permission-missing.yml +169 -0
- package/errors/permissions-auth/permissions-empty-block-removes-contents-read.yml +97 -0
- package/errors/permissions-auth/reusable-workflow-permissions-not-inherited.yml +114 -0
- package/errors/runner-environment/checkout-windows-ebusy-lock.yml +124 -0
- package/errors/runner-environment/deprecated-action-version-auto-rejected.yml +89 -0
- package/errors/runner-environment/github-hosted-runner-disk-space-full.yml +85 -0
- package/errors/runner-environment/github-path-same-step-not-found.yml +114 -0
- package/errors/runner-environment/github-script-v6-octokit-rest-actions-not-function.yml +87 -0
- package/errors/runner-environment/macos-13-deprecation-brownout.yml +93 -0
- package/errors/runner-environment/macos-15-mono-nuget-removed.yml +151 -0
- package/errors/runner-environment/macos-15-xcode-simulator-sdk-policy.yml +141 -0
- package/errors/runner-environment/multi-runtime-nov2025-removal.yml +120 -0
- package/errors/runner-environment/runner-oom-exit-code-137.yml +117 -0
- package/errors/runner-environment/setup-go-go123-telemetry-cache-failure.yml +92 -0
- package/errors/runner-environment/setup-java-distribution-required.yml +108 -0
- package/errors/runner-environment/ubuntu-2004-retirement-brownout.yml +107 -0
- package/errors/runner-environment/windows-latest-d-drive-removed.yml +104 -0
- package/errors/runner-environment/windows-vs2026-cuda-host-compiler-unsupported.yml +145 -0
- package/errors/silent-failures/event-commits-empty-on-workflow-dispatch.yml +110 -0
- package/errors/silent-failures/fetch-tags-depth-one-silent-no-op.yml +77 -0
- package/errors/silent-failures/github-env-multiline-value-truncated.yml +127 -0
- package/errors/silent-failures/github-sha-pr-merge-commit-not-head.yml +150 -0
- package/errors/silent-failures/job-output-masked-as-secret-empty.yml +147 -0
- package/errors/silent-failures/upload-artifact-permissions-stripped.yml +98 -0
- package/errors/triggers/pull-request-branches-filter-matches-base-not-head.yml +140 -0
- package/errors/triggers/push-event-fires-on-branch-delete.yml +129 -0
- package/errors/triggers/push-first-commit-before-sha-zeros.yml +160 -0
- package/errors/yaml-syntax/continue-on-error-env-context-rejected.yml +130 -0
- package/errors/yaml-syntax/fromjson-empty-string-crash.yml +99 -0
- package/errors/yaml-syntax/if-bang-negation-yaml-tag.yml +145 -0
- package/errors/yaml-syntax/local-action-path-always-top-level.yml +142 -0
- 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"
|