@htekdev/actions-debugger 1.0.23 → 1.0.25
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/errors/caching-artifacts/artifact-minimum-retention-one-day.yml +153 -0
- package/errors/caching-artifacts/cache-api-propagation-delay-post-save.yml +128 -0
- package/errors/caching-artifacts/cache-backend-internal-error-skipped.yml +75 -0
- package/errors/caching-artifacts/cache-hit-step-id-case-sensitive-mismatch.yml +95 -0
- package/errors/caching-artifacts/cache-save-post-step-skipped-on-failure.yml +114 -0
- package/errors/concurrency-timing/deploy-pages-in-progress-deployment-wedged.yml +70 -0
- package/errors/concurrency-timing/deployment-review-timeout-expired.yml +88 -0
- package/errors/concurrency-timing/job-concurrency-scope-per-run-not-global.yml +81 -0
- package/errors/concurrency-timing/merge-queue-concurrency-cancel-blocks-all.yml +86 -0
- package/errors/concurrency-timing/reusable-workflow-github-workflow-context-cancel.yml +124 -0
- package/errors/concurrency-timing/runner-scale-set-jobs-never-start.yml +123 -0
- package/errors/concurrency-timing/runner-temp-dir-race-concurrent-workers.yml +90 -0
- package/errors/known-unsolved/artifact-download-url-unauthenticated-404.yml +98 -0
- package/errors/known-unsolved/checkout-v6-credentials-docker-run-manual.yml +105 -0
- package/errors/known-unsolved/concurrency-groups-repo-scoped-only.yml +138 -0
- package/errors/known-unsolved/environment-deployment-false-custom-protection.yml +93 -0
- package/errors/known-unsolved/matrix-256-job-limit.yml +142 -0
- package/errors/known-unsolved/merge-group-paths-filter-not-supported.yml +137 -0
- package/errors/known-unsolved/no-job-allow-failure.yml +73 -0
- package/errors/known-unsolved/schedule-cron-hours-long-queue-drift.yml +101 -0
- package/errors/permissions-auth/checkout-persist-credentials-token-write.yml +90 -0
- package/errors/permissions-auth/checkout-v6-cross-repo-token-override.yml +103 -0
- package/errors/permissions-auth/create-github-app-token-cross-job-token-revoked.yml +95 -0
- package/errors/permissions-auth/github-token-contents-write-missing-git-push.yml +117 -0
- package/errors/permissions-auth/org-actions-policy-blocks-unapproved-action.yml +106 -0
- package/errors/runner-environment/codeql-action-v2-deprecated.yml +110 -0
- package/errors/runner-environment/macos-26-openssl-3-system-library-breaking.yml +114 -0
- package/errors/runner-environment/macos-26-ruby-34-default-upgrade.yml +114 -0
- package/errors/runner-environment/macos-26-xcode-default-265-pin-required.yml +99 -0
- package/errors/runner-environment/macos-latest-label-switches-to-macos26.yml +127 -0
- package/errors/runner-environment/maven-gradle-403-cache-backend-outage.yml +116 -0
- package/errors/runner-environment/node20-removed-toolcache-default-node22.yml +104 -0
- package/errors/runner-environment/powershell-74-76-threadjob-module-rename.yml +124 -0
- package/errors/runner-environment/self-hosted-runner-not-found.yml +134 -0
- package/errors/runner-environment/self-hosted-runner-selinux-service-exec-failure.yml +116 -0
- package/errors/runner-environment/service-container-no-healthcheck.yml +158 -0
- package/errors/runner-environment/setup-node-v5-corepack-pnpm-not-found.yml +101 -0
- package/errors/runner-environment/setup-node-yarn-not-installed-self-hosted.yml +76 -0
- package/errors/runner-environment/setup-python-externally-managed-env-error.yml +95 -0
- package/errors/runner-environment/windows-2019-runner-retired-june2025.yml +118 -0
- package/errors/runner-environment/windows-2022-docker-daemon-not-started.yml +108 -0
- package/errors/silent-failures/cache-hit-output-string-not-boolean.yml +96 -0
- package/errors/silent-failures/checkout-lfs-pointer-not-content.yml +105 -0
- package/errors/silent-failures/reusable-workflow-output-skipped-contains-secret.yml +115 -0
- package/errors/silent-failures/setup-node-silent-download-exit-zero.yml +105 -0
- package/errors/silent-failures/setup-python-truncated-manifest-silent-exit.yml +111 -0
- package/errors/silent-failures/undefined-env-expression-empty-string-silent.yml +115 -0
- package/errors/silent-failures/windows-powershell-github-output-bash-syntax.yml +118 -0
- package/errors/triggers/fork-pr-first-time-contributor-approval-required.yml +142 -0
- package/errors/triggers/on-push-branches-glob-star-no-slash-match.yml +78 -0
- package/errors/triggers/pull-request-target-env-protection-default-branch-eval.yml +117 -0
- package/errors/triggers/required-status-check-renamed-never-passes.yml +87 -0
- package/errors/triggers/schedule-cron-self-hosted-runner-not-triggered.yml +107 -0
- package/errors/yaml-syntax/case-function-runner-version-too-old.yml +100 -0
- package/errors/yaml-syntax/composite-action-run-shell-missing.yml +90 -0
- package/errors/yaml-syntax/composite-action-secrets-context-unavailable.yml +99 -0
- package/errors/yaml-syntax/github-script-octokit-renamed-to-github.yml +130 -0
- package/errors/yaml-syntax/labeler-v5-config-format-breaking.yml +67 -0
- package/errors/yaml-syntax/runs-on-expression-array-syntax-error.yml +121 -0
- package/errors/yaml-syntax/setup-go-matrix-version-float-coercion.yml +69 -0
- package/package.json +1 -1
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
id: known-unsolved-028
|
|
2
|
+
title: "merge_group Event Does Not Support paths, branches, or tags Filters"
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- merge_group
|
|
7
|
+
- merge-queue
|
|
8
|
+
- paths
|
|
9
|
+
- branches
|
|
10
|
+
- filters
|
|
11
|
+
- monorepo
|
|
12
|
+
- known-limitation
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: "merge_group:\\s*\\n\\s+paths:"
|
|
15
|
+
flags: "ms"
|
|
16
|
+
- regex: "merge_group:\\s*\\n\\s+branches:"
|
|
17
|
+
flags: "ms"
|
|
18
|
+
error_messages:
|
|
19
|
+
- "on:\n merge_group:\n paths:\n - 'src/**'"
|
|
20
|
+
- "workflow triggered for merge_group despite paths filter"
|
|
21
|
+
root_cause: |
|
|
22
|
+
The merge_group event (used with GitHub's merge queue feature) does not support
|
|
23
|
+
the standard event activity filters that other events support. Specifically,
|
|
24
|
+
the following filters are silently IGNORED when listed under merge_group:
|
|
25
|
+
|
|
26
|
+
- paths and paths-ignore
|
|
27
|
+
- branches and branches-ignore
|
|
28
|
+
- tags
|
|
29
|
+
|
|
30
|
+
When a workflow defines:
|
|
31
|
+
on:
|
|
32
|
+
pull_request:
|
|
33
|
+
paths: ['src/**']
|
|
34
|
+
merge_group:
|
|
35
|
+
paths: ['src/**'] # silently ignored
|
|
36
|
+
|
|
37
|
+
The paths filter applies to pull_request but NOT to merge_group. The workflow
|
|
38
|
+
always triggers for every merge_group event regardless of which files changed,
|
|
39
|
+
even if no files in src/** were modified.
|
|
40
|
+
|
|
41
|
+
This is particularly painful for monorepos that use paths filters to scope CI
|
|
42
|
+
workflows to service boundaries — the merge queue bypasses those boundaries
|
|
43
|
+
and runs all required checks for every PR regardless of which service changed.
|
|
44
|
+
|
|
45
|
+
The merge_group event only supports filtering by:
|
|
46
|
+
types: [checks_requested] (default and only supported activity type)
|
|
47
|
+
|
|
48
|
+
GitHub has acknowledged this is a known limitation with no native fix planned.
|
|
49
|
+
fix: |
|
|
50
|
+
There is no native path filtering for merge_group. Workarounds:
|
|
51
|
+
|
|
52
|
+
1. Step-level change detection: Use a job step with dorny/paths-filter or
|
|
53
|
+
tj-actions/changed-files to detect whether relevant files changed, then
|
|
54
|
+
gate subsequent steps on that output. This allows individual steps to
|
|
55
|
+
short-circuit while the job still runs (satisfying required check status).
|
|
56
|
+
|
|
57
|
+
2. Gate job pattern: Add a detect-changes job that runs paths detection and
|
|
58
|
+
exposes outputs. Downstream jobs use needs: + if: to conditionally run
|
|
59
|
+
based on the detected changes.
|
|
60
|
+
|
|
61
|
+
3. Accept always-run: For merge queue, it is generally safer to run all
|
|
62
|
+
required checks regardless of file changes. This ensures complete
|
|
63
|
+
validation before merging and avoids stale-cache scenarios.
|
|
64
|
+
fix_code:
|
|
65
|
+
- language: yaml
|
|
66
|
+
label: "Workaround — step-level paths filter inside merge_group triggered job"
|
|
67
|
+
code: |
|
|
68
|
+
on:
|
|
69
|
+
pull_request:
|
|
70
|
+
paths: ['src/**'] # pull_request respects this filter
|
|
71
|
+
merge_group: # merge_group ignores all path/branch filters
|
|
72
|
+
|
|
73
|
+
jobs:
|
|
74
|
+
build:
|
|
75
|
+
runs-on: ubuntu-latest
|
|
76
|
+
steps:
|
|
77
|
+
- uses: actions/checkout@v4
|
|
78
|
+
with:
|
|
79
|
+
fetch-depth: 0
|
|
80
|
+
|
|
81
|
+
- uses: dorny/paths-filter@v3
|
|
82
|
+
id: changes
|
|
83
|
+
with:
|
|
84
|
+
filters: |
|
|
85
|
+
src:
|
|
86
|
+
- 'src/**'
|
|
87
|
+
|
|
88
|
+
- name: Build (skip if no src changes on pull_request)
|
|
89
|
+
if: steps.changes.outputs.src == 'true' || github.event_name == 'merge_group'
|
|
90
|
+
run: npm run build
|
|
91
|
+
shell: bash
|
|
92
|
+
|
|
93
|
+
- language: yaml
|
|
94
|
+
label: "Workaround — detect-changes gate job for monorepo required checks"
|
|
95
|
+
code: |
|
|
96
|
+
on:
|
|
97
|
+
pull_request:
|
|
98
|
+
merge_group:
|
|
99
|
+
|
|
100
|
+
jobs:
|
|
101
|
+
detect-changes:
|
|
102
|
+
runs-on: ubuntu-latest
|
|
103
|
+
outputs:
|
|
104
|
+
src-changed: ${{ steps.filter.outputs.src }}
|
|
105
|
+
steps:
|
|
106
|
+
- uses: actions/checkout@v4
|
|
107
|
+
with:
|
|
108
|
+
fetch-depth: 0
|
|
109
|
+
- uses: dorny/paths-filter@v3
|
|
110
|
+
id: filter
|
|
111
|
+
with:
|
|
112
|
+
filters: |
|
|
113
|
+
src:
|
|
114
|
+
- 'src/**'
|
|
115
|
+
|
|
116
|
+
build:
|
|
117
|
+
needs: detect-changes
|
|
118
|
+
if: needs.detect-changes.outputs.src-changed == 'true' || github.event_name == 'merge_group'
|
|
119
|
+
runs-on: ubuntu-latest
|
|
120
|
+
steps:
|
|
121
|
+
- uses: actions/checkout@v4
|
|
122
|
+
- run: npm run build
|
|
123
|
+
shell: bash
|
|
124
|
+
prevention:
|
|
125
|
+
- "Do not add paths: or branches: under merge_group: — they are silently ignored."
|
|
126
|
+
- "For monorepo required checks, plan for merge_group always running all required checks regardless of file changes."
|
|
127
|
+
- "Use step-level change detection (dorny/paths-filter, tj-actions/changed-files) to short-circuit expensive work inside jobs."
|
|
128
|
+
- "Document that merge queue bypasses path filters in your monorepo CI design documentation."
|
|
129
|
+
docs:
|
|
130
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#merge_group"
|
|
131
|
+
label: "GitHub Docs: merge_group event — supported filter types"
|
|
132
|
+
- url: "https://stackoverflow.com/questions/76655935/when-does-a-github-workflow-trigger-for-merge-group-and-is-it-restricted-by-branch"
|
|
133
|
+
label: "Stack Overflow: merge_group trigger and branch restrictions (Score: 7, 9K views)"
|
|
134
|
+
- url: "https://github.com/orgs/community/discussions/90533"
|
|
135
|
+
label: "GitHub Community: merge_group does not support path filters"
|
|
136
|
+
- url: "https://github.com/dorny/paths-filter"
|
|
137
|
+
label: "dorny/paths-filter: step-level changed-files detection for monorepos"
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
id: known-unsolved-026
|
|
2
|
+
title: "No Native allow-failure Mechanism for Jobs"
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- continue-on-error
|
|
7
|
+
- allow-failure
|
|
8
|
+
- job-status
|
|
9
|
+
- pr-checks
|
|
10
|
+
- known-limitation
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: "allow.?failure"
|
|
13
|
+
flags: "i"
|
|
14
|
+
- regex: "continue-on-error.*(still|marked).*(fail|error)"
|
|
15
|
+
flags: "i"
|
|
16
|
+
error_messages:
|
|
17
|
+
- "allow-failure is not a valid key for jobs"
|
|
18
|
+
- "Job failed but continue-on-error is set — result is still 'failure'"
|
|
19
|
+
root_cause: |
|
|
20
|
+
GitHub Actions has no native `allow-failure` key for jobs (unlike GitLab CI's `allow_failure`).
|
|
21
|
+
Adding `allow-failure: true` to a job produces a YAML validation error or is silently ignored
|
|
22
|
+
because it is not a recognized field.
|
|
23
|
+
|
|
24
|
+
The closest equivalent is `continue-on-error: true`, but this has different semantics:
|
|
25
|
+
- The job still shows as failed in the workflow run summary (yellow warning icon, not green check).
|
|
26
|
+
- If the job is listed as a required status check in branch protection, the PR is still blocked.
|
|
27
|
+
- The overall workflow conclusion becomes "success" but the individual job result remains "failure".
|
|
28
|
+
- Downstream jobs using `needs.<job>.result == 'success'` will not execute.
|
|
29
|
+
|
|
30
|
+
This is a long-standing platform gap with 1,571 GitHub reactions (actions/runner#2347) and a
|
|
31
|
+
community discussion with broad support (orgs/community/discussions/15452).
|
|
32
|
+
fix: |
|
|
33
|
+
There is no perfect equivalent to allow-failure. The recommended workaround is `continue-on-error: true`
|
|
34
|
+
on the optional job, combined with a separate "gate" job that aggregates results and is made the
|
|
35
|
+
required status check in branch protection instead.
|
|
36
|
+
|
|
37
|
+
1. Add `continue-on-error: true` to the optional job.
|
|
38
|
+
2. Add a gate job with `needs: [optional-job]` and `if: always()`.
|
|
39
|
+
3. The gate job always succeeds — make it the required branch-protection check.
|
|
40
|
+
4. Downstream jobs check `needs.optional-job.result` explicitly if needed.
|
|
41
|
+
fix_code:
|
|
42
|
+
- language: yaml
|
|
43
|
+
label: "Workaround: gate job pattern for allow-failure equivalent"
|
|
44
|
+
code: |
|
|
45
|
+
jobs:
|
|
46
|
+
optional-flaky:
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
continue-on-error: true # job can fail without blocking workflow
|
|
49
|
+
steps:
|
|
50
|
+
- run: ./run-flaky-tests.sh
|
|
51
|
+
|
|
52
|
+
required-gate:
|
|
53
|
+
runs-on: ubuntu-latest
|
|
54
|
+
needs: [optional-flaky]
|
|
55
|
+
if: always() # always runs regardless of optional-flaky result
|
|
56
|
+
steps:
|
|
57
|
+
- name: Report optional job outcome
|
|
58
|
+
run: |
|
|
59
|
+
echo "Optional job result: ${{ needs.optional-flaky.result }}"
|
|
60
|
+
# This step always exits 0 — make required-gate the branch protection check
|
|
61
|
+
exit 0
|
|
62
|
+
prevention:
|
|
63
|
+
- "Do not add allow-failure: true to jobs — it is not a recognized Actions field and causes a workflow validation error."
|
|
64
|
+
- "Use continue-on-error: true as the closest equivalent but understand its PR check implications."
|
|
65
|
+
- "For required status checks, use the gate/aggregator job pattern — never the optional job itself."
|
|
66
|
+
- "Track orgs/community/discussions/15452 for official platform support of allow-failure."
|
|
67
|
+
docs:
|
|
68
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/using-jobs-in-a-workflow"
|
|
69
|
+
label: "GitHub Actions — Using jobs in a workflow (continue-on-error)"
|
|
70
|
+
- url: "https://github.com/actions/runner/issues/2347"
|
|
71
|
+
label: "actions/runner #2347 — allow-failure support request (1571 reactions)"
|
|
72
|
+
- url: "https://github.com/orgs/community/discussions/15452"
|
|
73
|
+
label: "Community discussion #15452 — allow-failure for jobs"
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
id: known-unsolved-025
|
|
2
|
+
title: "Scheduled Workflow Queue Drift Exceeds Hours and Worsens Over Time"
|
|
3
|
+
category: known-unsolved
|
|
4
|
+
severity: limitation
|
|
5
|
+
tags:
|
|
6
|
+
- cron
|
|
7
|
+
- schedule
|
|
8
|
+
- drift
|
|
9
|
+
- queue-delay
|
|
10
|
+
- best-effort
|
|
11
|
+
- platform-load
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: "schedule"
|
|
14
|
+
flags: "i"
|
|
15
|
+
error_messages:
|
|
16
|
+
- "Scheduled workflows can be delayed during periods of high loads of GitHub Actions workflow runs."
|
|
17
|
+
root_cause: |
|
|
18
|
+
GitHub Actions scheduled workflows use a best-effort queue. GitHub does not guarantee
|
|
19
|
+
that a schedule: trigger fires at the exact cron time. During periods of high platform
|
|
20
|
+
load, workflows are queued and the dispatch delay can reach multiple hours.
|
|
21
|
+
|
|
22
|
+
Since 2025, average delays have been growing on the GitHub-hosted infrastructure:
|
|
23
|
+
workflows that previously started within 10-20 minutes now commonly wait 1-4+ hours.
|
|
24
|
+
The drift is not constant — it varies by time of day and platform load and compounds
|
|
25
|
+
over months for daily/nightly jobs.
|
|
26
|
+
|
|
27
|
+
Key contributing factors:
|
|
28
|
+
- Growth in GitHub's user base means more repositories with scheduled workflows compete
|
|
29
|
+
for the same dispatch queue infrastructure.
|
|
30
|
+
- The midnight UTC window is severely congested; nearby times face similar load.
|
|
31
|
+
- GitHub dispatches schedule events in batches; individual repos have no dispatch priority.
|
|
32
|
+
- Repositories with no activity for 60+ days have their schedules disabled entirely.
|
|
33
|
+
- GitHub's documentation explicitly states schedules are best-effort with no SLA.
|
|
34
|
+
|
|
35
|
+
This is a known platform-capacity limitation, not a bug. No fix or workaround guarantees
|
|
36
|
+
on-time execution from within GitHub's scheduler.
|
|
37
|
+
Reported in actions/runner#4468 (open Jun 2026); discussed in community/196910.
|
|
38
|
+
fix: |
|
|
39
|
+
There is no way to guarantee on-time execution of a scheduled GitHub Actions workflow.
|
|
40
|
+
Mitigation strategies depend on how time-sensitive the job is.
|
|
41
|
+
|
|
42
|
+
Mitigation 1: Schedule away from congestion windows.
|
|
43
|
+
Avoid exact hours and midnight UTC. Use irregular minutes and off-peak hours
|
|
44
|
+
(e.g., 23 14 * * * instead of 0 0 * * *).
|
|
45
|
+
|
|
46
|
+
Mitigation 2: Use an external cron service with workflow_dispatch.
|
|
47
|
+
Remove the on: schedule trigger and replace it with workflow_dispatch. An external
|
|
48
|
+
scheduler (cloud function, server cron, GitHub App) calls the GitHub REST API dispatches
|
|
49
|
+
endpoint on time. The workflow starts immediately because workflow_dispatch jobs are not
|
|
50
|
+
subject to the schedule queue.
|
|
51
|
+
|
|
52
|
+
Mitigation 3: Accept the delay and build tolerance.
|
|
53
|
+
For non-time-critical automations (nightly builds, maintenance tasks), build tolerance
|
|
54
|
+
into downstream systems rather than fighting the delay.
|
|
55
|
+
|
|
56
|
+
Mitigation 4: Self-hosted runners with external scheduling.
|
|
57
|
+
For strict timing requirements, combine self-hosted runners with an external trigger so
|
|
58
|
+
the job bypasses GitHub hosted-runner queuing entirely.
|
|
59
|
+
fix_code:
|
|
60
|
+
- language: yaml
|
|
61
|
+
label: "Use irregular schedule time to avoid peak congestion"
|
|
62
|
+
code: |
|
|
63
|
+
on:
|
|
64
|
+
schedule:
|
|
65
|
+
# Avoid :00, :15, :30, :45 and midnight UTC — use irregular off-peak times
|
|
66
|
+
- cron: '23 14 * * *' # 2:23 PM UTC
|
|
67
|
+
workflow_dispatch: {} # Manual fallback for missed or delayed runs
|
|
68
|
+
- language: yaml
|
|
69
|
+
label: "External cron dispatch via GitHub API (reliable timing)"
|
|
70
|
+
code: |
|
|
71
|
+
# External scheduler (Lambda, cron job, GitHub App) calls:
|
|
72
|
+
# curl -X POST \
|
|
73
|
+
# -H "Authorization: Bearer $GH_TOKEN" \
|
|
74
|
+
# https://api.github.com/repos/OWNER/REPO/actions/workflows/nightly.yml/dispatches \
|
|
75
|
+
# -d '{"ref":"main"}'
|
|
76
|
+
#
|
|
77
|
+
# Your workflow — no on: schedule needed:
|
|
78
|
+
on:
|
|
79
|
+
workflow_dispatch: {}
|
|
80
|
+
|
|
81
|
+
jobs:
|
|
82
|
+
nightly:
|
|
83
|
+
runs-on: ubuntu-latest
|
|
84
|
+
steps:
|
|
85
|
+
- uses: actions/checkout@v4
|
|
86
|
+
- run: ./scripts/nightly-build.sh
|
|
87
|
+
prevention:
|
|
88
|
+
- "Never build SLAs or downstream dependencies that assume a scheduled workflow starts within minutes of its cron time."
|
|
89
|
+
- "Always add workflow_dispatch: alongside schedule: so delayed or missed runs can be manually triggered from the Actions UI."
|
|
90
|
+
- "Use non-round-number cron minutes (17, 23, 41, etc.) to avoid the most congested slots."
|
|
91
|
+
- "For time-critical jobs, use an external scheduler that calls the GitHub API rather than relying on GitHub scheduler queue."
|
|
92
|
+
- "Track repository activity — GitHub disables scheduled workflows on repos inactive for 60+ days."
|
|
93
|
+
docs:
|
|
94
|
+
- url: "https://github.com/actions/runner/issues/4468"
|
|
95
|
+
label: "actions/runner#4468 — Continuously increasing schedule drift (Jun 2026)"
|
|
96
|
+
- url: "https://github.com/orgs/community/discussions/196910"
|
|
97
|
+
label: "Community discussion: schedule drift increasing over time"
|
|
98
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#schedule"
|
|
99
|
+
label: "schedule event — best-effort documentation"
|
|
100
|
+
- url: "https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/disabling-and-enabling-a-workflow"
|
|
101
|
+
label: "Scheduled workflow disabled after 60 days inactivity"
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
id: permissions-auth-028
|
|
2
|
+
title: "checkout persist-credentials:true Default Stores GITHUB_TOKEN in .git/config"
|
|
3
|
+
category: permissions-auth
|
|
4
|
+
severity: warning
|
|
5
|
+
tags:
|
|
6
|
+
- checkout
|
|
7
|
+
- persist-credentials
|
|
8
|
+
- github-token
|
|
9
|
+
- security
|
|
10
|
+
- credential-helper
|
|
11
|
+
patterns:
|
|
12
|
+
- regex: "persist.credentials.*(true|default)|persist-credentials.*not.*false"
|
|
13
|
+
flags: "i"
|
|
14
|
+
- regex: "\\.git.config.*token|credential.*helper.*store.*github"
|
|
15
|
+
flags: "i"
|
|
16
|
+
error_messages:
|
|
17
|
+
- "GITHUB_TOKEN persisted in .git/config by actions/checkout"
|
|
18
|
+
- "Unexpected repository write from a non-commit step"
|
|
19
|
+
root_cause: |
|
|
20
|
+
`actions/checkout` defaults to `persist-credentials: true`. When enabled, the action configures
|
|
21
|
+
a credential helper that stores the GITHUB_TOKEN in the repository local `.git/config`.
|
|
22
|
+
Every repository operation in subsequent steps automatically uses this token for authentication,
|
|
23
|
+
without any explicit credential setup in those steps.
|
|
24
|
+
|
|
25
|
+
Consequences:
|
|
26
|
+
- Any step that invokes the source control client (directly or via tools that do so internally)
|
|
27
|
+
inherits repository write access if the job holds `contents: write` permission.
|
|
28
|
+
- Third-party tools that call the source control client internally (Docker buildx, dependency
|
|
29
|
+
managers reading version tags, build tools) receive unintended repository access.
|
|
30
|
+
- In fork PR workflows (`pull_request_target`), checking out a fork ref with persisted
|
|
31
|
+
credentials gives forked code implicit access to secrets via subsequent repository operations.
|
|
32
|
+
- Unintended repository writes can occur from any step that invokes write-mode operations.
|
|
33
|
+
|
|
34
|
+
The default remains `true` for backward compatibility, but silently increases attack surface
|
|
35
|
+
for every job that does not need post-checkout write access.
|
|
36
|
+
|
|
37
|
+
167 reactions on actions/checkout#485 (open since 2021).
|
|
38
|
+
fix: |
|
|
39
|
+
Set `persist-credentials: false` on all checkout steps that do not require authenticated
|
|
40
|
+
repository operations in later steps. Jobs that do need to write back to the repo should
|
|
41
|
+
use a scoped credential pattern or a dedicated auto-commit action that manages credentials
|
|
42
|
+
explicitly in its own isolated context.
|
|
43
|
+
fix_code:
|
|
44
|
+
- language: yaml
|
|
45
|
+
label: "Recommended: disable persist-credentials for read-only jobs (build, lint, test)"
|
|
46
|
+
code: |
|
|
47
|
+
steps:
|
|
48
|
+
# Token NOT stored in .git/config — safe for read-only jobs
|
|
49
|
+
- uses: actions/checkout@v4
|
|
50
|
+
with:
|
|
51
|
+
persist-credentials: false
|
|
52
|
+
|
|
53
|
+
- name: Build and test
|
|
54
|
+
run: npm ci && npm test
|
|
55
|
+
|
|
56
|
+
- language: yaml
|
|
57
|
+
label: "Scoped: only persist credentials for jobs that explicitly write back to repo"
|
|
58
|
+
code: |
|
|
59
|
+
jobs:
|
|
60
|
+
read-only-analysis:
|
|
61
|
+
runs-on: ubuntu-latest
|
|
62
|
+
steps:
|
|
63
|
+
- uses: actions/checkout@v4
|
|
64
|
+
with:
|
|
65
|
+
persist-credentials: false # no ambient write access
|
|
66
|
+
|
|
67
|
+
write-back:
|
|
68
|
+
runs-on: ubuntu-latest
|
|
69
|
+
permissions:
|
|
70
|
+
contents: write
|
|
71
|
+
steps:
|
|
72
|
+
# persist-credentials: true (default) only where write is explicitly intended
|
|
73
|
+
- uses: actions/checkout@v4
|
|
74
|
+
|
|
75
|
+
- name: Generate and commit output
|
|
76
|
+
uses: stefanzweifel/auto-commit-action@v5
|
|
77
|
+
with:
|
|
78
|
+
commit_message: "chore: auto-generated output"
|
|
79
|
+
prevention:
|
|
80
|
+
- "Default to persist-credentials: false — only omit (use default true) when authenticated repository operations are explicitly needed in later steps."
|
|
81
|
+
- "Audit jobs that rely on the implicit default and also hold contents:write permission."
|
|
82
|
+
- "In pull_request_target workflows, always set persist-credentials: false when checking out fork refs."
|
|
83
|
+
- "Review third-party tools in the job that invoke the source control client internally."
|
|
84
|
+
docs:
|
|
85
|
+
- url: "https://github.com/actions/checkout/issues/485"
|
|
86
|
+
label: "actions/checkout #485 — Remove persist-credentials or change default (167 reactions)"
|
|
87
|
+
- url: "https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions"
|
|
88
|
+
label: "GitHub Security Hardening for GitHub Actions"
|
|
89
|
+
- url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/accessing-contextual-information-about-workflow-runs"
|
|
90
|
+
label: "GitHub Actions — GITHUB_TOKEN permissions"
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
id: permissions-auth-029
|
|
2
|
+
title: "checkout@v6 http.extraheader overrides inline token for cross-repository push"
|
|
3
|
+
category: permissions-auth
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- checkout
|
|
7
|
+
- v6
|
|
8
|
+
- cross-repo
|
|
9
|
+
- extraheader
|
|
10
|
+
- token
|
|
11
|
+
- push
|
|
12
|
+
- persist-credentials
|
|
13
|
+
|
|
14
|
+
patterns:
|
|
15
|
+
- regex: "remote: Permission to .+ denied to github-actions\\[bot\\]"
|
|
16
|
+
flags: "i"
|
|
17
|
+
- regex: "fatal: unable to access '.+': The requested URL returned error: 403"
|
|
18
|
+
flags: "i"
|
|
19
|
+
- regex: "The requested URL returned error: 403"
|
|
20
|
+
flags: "i"
|
|
21
|
+
|
|
22
|
+
error_messages:
|
|
23
|
+
- "remote: Permission to org/other-repo.git denied to github-actions[bot]."
|
|
24
|
+
- "fatal: unable to access 'https://github.com/org/other-repo.git/': The requested URL returned error: 403"
|
|
25
|
+
|
|
26
|
+
root_cause: |
|
|
27
|
+
checkout@v6 changed how credentials are stored compared to v4/v5. Instead of writing the
|
|
28
|
+
GITHUB_TOKEN directly into .git/config, v6 stores credentials in a separate file under
|
|
29
|
+
$RUNNER_TEMP and registers it via a git includeIf directive. This injects an
|
|
30
|
+
http.extraheader = Authorization: Bearer <GITHUB_TOKEN> for ALL https://github.com/ URLs.
|
|
31
|
+
|
|
32
|
+
When a workflow adds a second remote using an inline Personal Access Token (PAT) in the URL
|
|
33
|
+
(e.g., https://PAT@github.com/other-org/other-repo.git), git still sends the checkout-injected
|
|
34
|
+
Authorization header alongside the URL-embedded token. The injected GITHUB_TOKEN header takes
|
|
35
|
+
precedence over the URL-embedded PAT, and since GITHUB_TOKEN only has access to the current
|
|
36
|
+
repository, the push to the other repository is rejected with 403 Forbidden.
|
|
37
|
+
|
|
38
|
+
This regression is specific to workflows that push to repositories other than the one that
|
|
39
|
+
was checked out. Workflows that only interact with the current repository are unaffected.
|
|
40
|
+
|
|
41
|
+
fix: |
|
|
42
|
+
Option 1 (recommended): Set persist-credentials: false in the checkout step. This prevents
|
|
43
|
+
checkout@v6 from injecting the extraheader entirely. Configure authentication explicitly in
|
|
44
|
+
subsequent steps using the token in the remote URL or via GH_TOKEN environment variable.
|
|
45
|
+
|
|
46
|
+
Option 2 (targeted): Explicitly unset the http extraheader before performing cross-repo
|
|
47
|
+
operations. Add a step that clears the auth override for github.com URLs before pushing
|
|
48
|
+
to the other repository.
|
|
49
|
+
|
|
50
|
+
Option 3 (fallback): Pin to checkout@v5 for workflows that require cross-repo push
|
|
51
|
+
until a v6 upstream fix is available (actions/checkout#2424 is open).
|
|
52
|
+
|
|
53
|
+
fix_code:
|
|
54
|
+
- language: yaml
|
|
55
|
+
label: "Option 1: Disable persist-credentials to prevent extraheader injection"
|
|
56
|
+
code: |
|
|
57
|
+
steps:
|
|
58
|
+
- name: Checkout current repo
|
|
59
|
+
uses: actions/checkout@v6
|
|
60
|
+
with:
|
|
61
|
+
persist-credentials: false # No extraheader injected; manual auth required
|
|
62
|
+
|
|
63
|
+
- name: Push tag to target repository
|
|
64
|
+
env:
|
|
65
|
+
TARGET_PAT: ${{ secrets.TARGET_REPO_PAT }}
|
|
66
|
+
run: |
|
|
67
|
+
# Token embedded in remote URL; no competing extraheader present
|
|
68
|
+
TARGET_REMOTE="https://x-access-token:${TARGET_PAT}@github.com/org/target-repo.git"
|
|
69
|
+
git remote add target "${TARGET_REMOTE}"
|
|
70
|
+
git tag -f nightly
|
|
71
|
+
git push -f target nightly
|
|
72
|
+
|
|
73
|
+
- language: yaml
|
|
74
|
+
label: "Option 2: Clear the injected extraheader before cross-repo operations"
|
|
75
|
+
code: |
|
|
76
|
+
steps:
|
|
77
|
+
- uses: actions/checkout@v6
|
|
78
|
+
|
|
79
|
+
# ... build steps ...
|
|
80
|
+
|
|
81
|
+
- name: Unset checkout extraheader for cross-repo push
|
|
82
|
+
run: git config --unset-all "http.https://github.com/.extraheader" || true
|
|
83
|
+
|
|
84
|
+
- name: Push tag to target repository
|
|
85
|
+
env:
|
|
86
|
+
TARGET_PAT: ${{ secrets.TARGET_REPO_PAT }}
|
|
87
|
+
run: |
|
|
88
|
+
git remote add target "https://x-access-token:${TARGET_PAT}@github.com/org/target-repo.git"
|
|
89
|
+
git push -f target nightly
|
|
90
|
+
|
|
91
|
+
prevention:
|
|
92
|
+
- "Use persist-credentials: false on all checkout steps in workflows that interact with multiple repositories."
|
|
93
|
+
- "Avoid embedding tokens directly in remote URLs when checkout@v6 is used without persist-credentials:false."
|
|
94
|
+
- "Test cross-repo operations in a staging branch after upgrading from checkout@v4 or v5 to v6."
|
|
95
|
+
- "Check actions/checkout release notes for credential storage changes when upgrading major versions."
|
|
96
|
+
|
|
97
|
+
docs:
|
|
98
|
+
- url: "https://github.com/actions/checkout/issues/2424"
|
|
99
|
+
label: "actions/checkout#2424: Unable to create tags in another repo with v6"
|
|
100
|
+
- url: "https://github.com/actions/checkout/releases/tag/v6.0.0"
|
|
101
|
+
label: "checkout@v6.0.0 release notes — persist-credentials storage change"
|
|
102
|
+
- url: "https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication"
|
|
103
|
+
label: "GitHub Docs: Automatic token authentication and GITHUB_TOKEN scopes"
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
id: permissions-auth-027
|
|
2
|
+
title: "create-github-app-token token auto-revoked in post step — cannot pass to downstream jobs"
|
|
3
|
+
category: permissions-auth
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- create-github-app-token
|
|
7
|
+
- github-app
|
|
8
|
+
- token-revocation
|
|
9
|
+
- cross-job
|
|
10
|
+
- job-outputs
|
|
11
|
+
- masked-secrets
|
|
12
|
+
patterns:
|
|
13
|
+
- regex: "HttpError.*401.*Bad credentials"
|
|
14
|
+
flags: "i"
|
|
15
|
+
- regex: "Token has been revoked"
|
|
16
|
+
flags: "i"
|
|
17
|
+
- regex: "RequestError.*401"
|
|
18
|
+
flags: "i"
|
|
19
|
+
error_messages:
|
|
20
|
+
- "HttpError: Bad credentials"
|
|
21
|
+
- "Error: 401 Bad credentials"
|
|
22
|
+
- "RequestError: 401 Unauthorized"
|
|
23
|
+
root_cause: |
|
|
24
|
+
actions/create-github-app-token automatically revokes the generated installation token
|
|
25
|
+
in the action's post step (after the job completes). This means a token generated in
|
|
26
|
+
job A and passed as a job output to job B will already be revoked by the time job B
|
|
27
|
+
runs. Additionally, GitHub Actions automatically masks any value that looks like a
|
|
28
|
+
token (matching the ghp_, ghs_, ghu_, github_pat_, etc. prefixes). Masked values
|
|
29
|
+
cannot be passed through job outputs — the runner replaces them with "***" in outputs,
|
|
30
|
+
so the downstream job receives a masked/empty string rather than the actual token value.
|
|
31
|
+
Setting skip-token-revoke: true is necessary but not sufficient: the masking mechanism
|
|
32
|
+
still prevents the token from appearing as a job output.
|
|
33
|
+
fix: |
|
|
34
|
+
Recreate the token in each job that needs it by calling actions/create-github-app-token
|
|
35
|
+
as a step within each dependent job. This is the recommended pattern from the action
|
|
36
|
+
maintainers. Do not attempt to pass the token through job outputs.
|
|
37
|
+
fix_code:
|
|
38
|
+
- language: yaml
|
|
39
|
+
label: "Wrong: token passed through job output (always fails)"
|
|
40
|
+
code: |
|
|
41
|
+
jobs:
|
|
42
|
+
get-token:
|
|
43
|
+
runs-on: ubuntu-latest
|
|
44
|
+
outputs:
|
|
45
|
+
token: ${{ steps.app-token.outputs.token }} # BROKEN: masked, will be ***
|
|
46
|
+
steps:
|
|
47
|
+
- id: app-token
|
|
48
|
+
uses: actions/create-github-app-token@v1
|
|
49
|
+
with:
|
|
50
|
+
app-id: ${{ vars.APP_ID }}
|
|
51
|
+
private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
|
52
|
+
use-token:
|
|
53
|
+
needs: get-token
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
steps:
|
|
56
|
+
- run: echo "token is ${{ needs.get-token.outputs.token }}"
|
|
57
|
+
# Always prints "***" — token unusable
|
|
58
|
+
- language: yaml
|
|
59
|
+
label: "Correct: recreate token in each job that needs it"
|
|
60
|
+
code: |
|
|
61
|
+
jobs:
|
|
62
|
+
build:
|
|
63
|
+
runs-on: ubuntu-latest
|
|
64
|
+
steps:
|
|
65
|
+
- id: app-token
|
|
66
|
+
uses: actions/create-github-app-token@v1
|
|
67
|
+
with:
|
|
68
|
+
app-id: ${{ vars.APP_ID }}
|
|
69
|
+
private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
|
70
|
+
- uses: actions/checkout@v4
|
|
71
|
+
with:
|
|
72
|
+
token: ${{ steps.app-token.outputs.token }}
|
|
73
|
+
deploy:
|
|
74
|
+
runs-on: ubuntu-latest
|
|
75
|
+
steps:
|
|
76
|
+
- id: app-token # recreate — do not reuse from build job
|
|
77
|
+
uses: actions/create-github-app-token@v1
|
|
78
|
+
with:
|
|
79
|
+
app-id: ${{ vars.APP_ID }}
|
|
80
|
+
private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
|
81
|
+
- run: ./deploy.sh
|
|
82
|
+
env:
|
|
83
|
+
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
|
84
|
+
prevention:
|
|
85
|
+
- "Never pass GitHub App tokens through job outputs — the token is masked and will be empty"
|
|
86
|
+
- "Recreate the token in every job that needs it using a dedicated create-github-app-token step"
|
|
87
|
+
- "Store APP_ID as a repository variable (vars.APP_ID) and private key as a secret"
|
|
88
|
+
- "Consider using a GitHub App with fine-grained permissions to minimize the blast radius of token exposure"
|
|
89
|
+
docs:
|
|
90
|
+
- url: "https://github.com/actions/create-github-app-token/issues/66"
|
|
91
|
+
label: "actions/create-github-app-token#66: output token cannot be used across jobs"
|
|
92
|
+
- url: "https://github.com/actions/create-github-app-token#usage"
|
|
93
|
+
label: "actions/create-github-app-token: Usage documentation"
|
|
94
|
+
- url: "https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-to-access-secrets"
|
|
95
|
+
label: "GitHub Docs: Security hardening — intermediate environment for secrets"
|