@htekdev/actions-debugger 1.0.22 → 1.0.24

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 (61) hide show
  1. package/errors/caching-artifacts/artifact-minimum-retention-one-day.yml +153 -0
  2. package/errors/caching-artifacts/cache-api-propagation-delay-post-save.yml +128 -0
  3. package/errors/caching-artifacts/cache-backend-internal-error-skipped.yml +75 -0
  4. package/errors/caching-artifacts/cache-hit-step-id-case-sensitive-mismatch.yml +95 -0
  5. package/errors/caching-artifacts/cache-save-post-step-skipped-on-failure.yml +114 -0
  6. package/errors/concurrency-timing/deploy-pages-in-progress-deployment-wedged.yml +70 -0
  7. package/errors/concurrency-timing/deployment-review-timeout-expired.yml +88 -0
  8. package/errors/concurrency-timing/job-concurrency-scope-per-run-not-global.yml +81 -0
  9. package/errors/concurrency-timing/merge-queue-concurrency-cancel-blocks-all.yml +86 -0
  10. package/errors/concurrency-timing/reusable-workflow-github-workflow-context-cancel.yml +124 -0
  11. package/errors/concurrency-timing/runner-scale-set-jobs-never-start.yml +123 -0
  12. package/errors/concurrency-timing/runner-temp-dir-race-concurrent-workers.yml +90 -0
  13. package/errors/known-unsolved/artifact-download-url-unauthenticated-404.yml +98 -0
  14. package/errors/known-unsolved/checkout-v6-credentials-docker-run-manual.yml +105 -0
  15. package/errors/known-unsolved/concurrency-groups-repo-scoped-only.yml +138 -0
  16. package/errors/known-unsolved/matrix-256-job-limit.yml +142 -0
  17. package/errors/known-unsolved/merge-group-paths-filter-not-supported.yml +137 -0
  18. package/errors/known-unsolved/no-job-allow-failure.yml +73 -0
  19. package/errors/known-unsolved/reusable-secrets-inherit-not-deep-forwarded.yml +113 -0
  20. package/errors/known-unsolved/schedule-cron-hours-long-queue-drift.yml +101 -0
  21. package/errors/permissions-auth/checkout-persist-credentials-token-write.yml +90 -0
  22. package/errors/permissions-auth/create-github-app-token-cross-job-token-revoked.yml +95 -0
  23. package/errors/permissions-auth/github-token-contents-write-missing-git-push.yml +117 -0
  24. package/errors/permissions-auth/org-actions-policy-blocks-unapproved-action.yml +106 -0
  25. package/errors/runner-environment/codeql-action-v2-deprecated.yml +110 -0
  26. package/errors/runner-environment/macos-26-openssl-3-system-library-breaking.yml +114 -0
  27. package/errors/runner-environment/macos-26-ruby-34-default-upgrade.yml +114 -0
  28. package/errors/runner-environment/macos-26-xcode-default-265-pin-required.yml +99 -0
  29. package/errors/runner-environment/macos-latest-label-switches-to-macos26.yml +127 -0
  30. package/errors/runner-environment/node20-removed-toolcache-default-node22.yml +104 -0
  31. package/errors/runner-environment/org-runner-group-dispatch-null.yml +102 -0
  32. package/errors/runner-environment/powershell-74-76-threadjob-module-rename.yml +124 -0
  33. package/errors/runner-environment/self-hosted-runner-not-found.yml +134 -0
  34. package/errors/runner-environment/self-hosted-runner-selinux-service-exec-failure.yml +116 -0
  35. package/errors/runner-environment/service-container-no-healthcheck.yml +158 -0
  36. package/errors/runner-environment/setup-node-v5-corepack-pnpm-not-found.yml +101 -0
  37. package/errors/runner-environment/setup-node-yarn-not-installed-self-hosted.yml +76 -0
  38. package/errors/runner-environment/setup-python-externally-managed-env-error.yml +95 -0
  39. package/errors/runner-environment/windows-2019-runner-retired-june2025.yml +118 -0
  40. package/errors/runner-environment/windows-2022-docker-daemon-not-started.yml +108 -0
  41. package/errors/silent-failures/cache-hit-output-string-not-boolean.yml +96 -0
  42. package/errors/silent-failures/checkout-lfs-pointer-not-content.yml +105 -0
  43. package/errors/silent-failures/reusable-workflow-output-skipped-contains-secret.yml +115 -0
  44. package/errors/silent-failures/setup-node-silent-download-exit-zero.yml +105 -0
  45. package/errors/silent-failures/setup-python-truncated-manifest-silent-exit.yml +111 -0
  46. package/errors/silent-failures/undefined-env-expression-empty-string-silent.yml +115 -0
  47. package/errors/silent-failures/windows-powershell-github-output-bash-syntax.yml +118 -0
  48. package/errors/triggers/fork-pr-first-time-contributor-approval-required.yml +142 -0
  49. package/errors/triggers/on-push-branches-glob-star-no-slash-match.yml +78 -0
  50. package/errors/triggers/pull-request-target-env-protection-default-branch-eval.yml +117 -0
  51. package/errors/triggers/required-status-check-renamed-never-passes.yml +87 -0
  52. package/errors/triggers/schedule-cron-self-hosted-runner-not-triggered.yml +107 -0
  53. package/errors/triggers/workflow-run-checkout-uses-default-branch.yml +114 -0
  54. package/errors/yaml-syntax/composite-action-run-shell-missing.yml +90 -0
  55. package/errors/yaml-syntax/composite-action-secrets-context-unavailable.yml +99 -0
  56. package/errors/yaml-syntax/github-script-octokit-renamed-to-github.yml +130 -0
  57. package/errors/yaml-syntax/labeler-v5-config-format-breaking.yml +67 -0
  58. package/errors/yaml-syntax/reusable-workflow-nesting-depth-exceeded.yml +113 -0
  59. package/errors/yaml-syntax/runs-on-expression-array-syntax-error.yml +121 -0
  60. package/errors/yaml-syntax/setup-go-matrix-version-float-coercion.yml +69 -0
  61. package/package.json +1 -1
@@ -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,113 @@
1
+ id: known-unsolved-021
2
+ title: "secrets: inherit Does Not Cascade to Second-Level Nested Reusable Workflows"
3
+ category: known-unsolved
4
+ severity: silent-failure
5
+ tags:
6
+ - reusable-workflow
7
+ - secrets
8
+ - secrets-inherit
9
+ - nesting
10
+ - silent-failure
11
+
12
+ patterns:
13
+ - regex: "secret .+ is required, but not provided"
14
+ flags: "i"
15
+ - regex: "secrets\\.\\w+ is not a valid secret key"
16
+ flags: "i"
17
+
18
+ error_messages:
19
+ - "Secret GIT_PAT is required, but not provided"
20
+ - "Required secret 'MY_SECRET' was not provided by the caller"
21
+
22
+ root_cause: |
23
+ `secrets: inherit` in a caller workflow passes ALL repository and organization
24
+ secrets to the directly-called (first-level) reusable workflow. However, if that
25
+ reusable workflow then calls ANOTHER reusable workflow (second level), those
26
+ inherited secrets are NOT automatically forwarded to the second level.
27
+
28
+ Each `workflow_call` boundary is a discrete secrets scope. `secrets: inherit`
29
+ only covers one hop. If you have:
30
+
31
+ root.yml → (secrets: inherit) → level1.yml → level2.yml
32
+
33
+ then `level2.yml` receives an empty secrets context unless `level1.yml`
34
+ explicitly passes them via its own `secrets:` block.
35
+
36
+ This is a platform design decision, not a bug. GitHub has confirmed that
37
+ `secrets: inherit` does not cascade through multiple `workflow_call` layers.
38
+ There is no current mechanism for automatic multi-level secret propagation.
39
+
40
+ Community reports: actions/runner#2709 (open, labeled bug, 8 👍, 2023–2025),
41
+ community/discussions/23107.
42
+
43
+ fix: |
44
+ Explicitly declare and pass secrets at EVERY reusable workflow boundary.
45
+
46
+ For level1.yml to forward secrets to level2.yml, level1.yml must:
47
+ 1. Declare secrets in its on.workflow_call.secrets block.
48
+ 2. Pass those secrets explicitly in the jobs.*.secrets block when calling level2.yml.
49
+
50
+ Alternatively, use `secrets: inherit` at EACH level — but the calling workflow
51
+ (level1.yml) itself must also explicitly declare the secrets it expects so
52
+ they are available in its scope to pass down.
53
+
54
+ fix_code:
55
+ - language: yaml
56
+ label: "root.yml — caller uses secrets: inherit for first hop"
57
+ code: |
58
+ jobs:
59
+ call-level1:
60
+ uses: ./.github/workflows/level1.yml
61
+ secrets: inherit # passes ALL secrets to level1.yml only
62
+
63
+ - language: yaml
64
+ label: "level1.yml — must declare AND re-forward secrets to level2.yml"
65
+ code: |
66
+ on:
67
+ workflow_call:
68
+ secrets:
69
+ MY_SECRET:
70
+ required: true
71
+ DEPLOY_TOKEN:
72
+ required: false
73
+
74
+ jobs:
75
+ call-level2:
76
+ uses: ./.github/workflows/level2.yml
77
+ secrets:
78
+ MY_SECRET: ${{ secrets.MY_SECRET }} # explicit re-forward
79
+ DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }} # explicit re-forward
80
+
81
+ - language: yaml
82
+ label: "level2.yml — must declare secrets it expects"
83
+ code: |
84
+ on:
85
+ workflow_call:
86
+ secrets:
87
+ MY_SECRET:
88
+ required: true
89
+ DEPLOY_TOKEN:
90
+ required: false
91
+
92
+ jobs:
93
+ use-secrets:
94
+ runs-on: ubuntu-latest
95
+ steps:
96
+ - name: Use secret
97
+ run: echo "token length = ${#MY_SECRET}"
98
+ env:
99
+ MY_SECRET: ${{ secrets.MY_SECRET }}
100
+
101
+ prevention:
102
+ - "Audit every workflow_call boundary in nested reusable workflows — each hop must explicitly forward secrets."
103
+ - "Document required secrets at every level in workflow comments to avoid silent empty-string failures."
104
+ - "Use composite actions instead of nested reusable workflows when secrets do not need to cross job boundaries — composite actions share the parent job's secrets context automatically."
105
+ - "Test nested workflows by echoing a non-sensitive derived value (e.g., secret length) at each level to confirm propagation."
106
+
107
+ docs:
108
+ - url: "https://docs.github.com/en/actions/using-workflows/reusing-workflows#passing-secrets-to-nested-workflows"
109
+ label: "GitHub Docs — Passing secrets to nested workflows"
110
+ - url: "https://github.com/actions/runner/issues/2709"
111
+ label: "actions/runner#2709 — Passing secrets to reusable workflow does not work (open, 2023)"
112
+ - url: "https://github.com/orgs/community/discussions/23107"
113
+ label: "community/discussions#23107 — Inheriting secrets in reusable workflows"
@@ -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,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"
@@ -0,0 +1,117 @@
1
+ id: permissions-auth-025
2
+ title: "GITHUB_TOKEN Missing contents:write — git push Returns 403 Write Access Not Granted"
3
+ category: permissions-auth
4
+ severity: error
5
+ tags:
6
+ - github-token
7
+ - contents-write
8
+ - git-push
9
+ - 403
10
+ - permissions-block
11
+ - auto-commit
12
+ - write-access
13
+ patterns:
14
+ - regex: "remote: Write access to repository not granted"
15
+ flags: "i"
16
+ - regex: "error: failed to push some refs"
17
+ flags: "i"
18
+ - regex: "refusing to allow.*GitHub Actions.*to create or update workflow"
19
+ flags: "i"
20
+ - regex: "HttpError: Resource not accessible by integration"
21
+ flags: "i"
22
+ error_messages:
23
+ - "remote: Write access to repository not granted."
24
+ - "error: failed to push some refs to 'https://github.com/owner/repo.git'"
25
+ - "fatal: unable to access 'https://github.com/owner/repo.git/': The requested URL returned error: 403"
26
+ - "Resource not accessible by integration"
27
+ root_cause: |
28
+ The GITHUB_TOKEN is scoped by the `permissions:` block on the workflow or job. When any
29
+ `permissions:` block is present, all unspecified permissions are set to `none` (not to their
30
+ defaults). If `contents: write` is not explicitly granted, any `git push`, `git commit`, or
31
+ REST API call that writes to the repository will be rejected with HTTP 403 "Write access to
32
+ repository not granted."
33
+
34
+ Three common situations:
35
+
36
+ 1. **Minimal permissions block** — the workflow declares `permissions: read-all` or lists
37
+ specific permissions (e.g., `pull-requests: write`) but omits `contents: write`. Any
38
+ subsequent git push fails with 403.
39
+
40
+ 2. **Inherited restrictive org policy** — the organization sets the default token permission
41
+ to "read repository contents" (Settings → Actions → General → Workflow permissions). Without
42
+ an explicit `contents: write` in the workflow, the token cannot push.
43
+
44
+ 3. **Fine-grained token in workflow context** — a PAT or GitHub App token is used for checkout
45
+ but the GITHUB_TOKEN (still the effective token for `git push`) lacks write access.
46
+
47
+ This is distinct from `permissions-auth-017` (empty `permissions: {}` block removing
48
+ `contents: read` and breaking checkout). This pattern specifically affects git write operations
49
+ and REST API calls that create or update repository content.
50
+ fix: |
51
+ Add `contents: write` to the permissions block on the job or workflow where the push occurs.
52
+ Grant the narrowest scope needed — prefer job-level `permissions:` over workflow-level to
53
+ limit the blast radius.
54
+
55
+ If the org policy sets the default to "read-only", every workflow that writes to the repo
56
+ must explicitly declare `contents: write`.
57
+ fix_code:
58
+ - language: yaml
59
+ label: "Add contents:write at the job level (preferred — narrowest scope)"
60
+ code: |
61
+ jobs:
62
+ auto-commit:
63
+ runs-on: ubuntu-latest
64
+ permissions:
65
+ contents: write # ← required for git push / creating commits via API
66
+ steps:
67
+ - uses: actions/checkout@v4
68
+ - name: Bump version and push
69
+ run: |
70
+ git config user.name "github-actions[bot]"
71
+ git config user.email "github-actions[bot]@users.noreply.github.com"
72
+ npm version patch --no-git-tag-version
73
+ git add package.json
74
+ git commit -m "chore: bump version [skip ci]"
75
+ git push
76
+ - language: yaml
77
+ label: "Org default is read-only — always set explicit contents:write for push workflows"
78
+ code: |
79
+ # When Settings → Actions → General → Workflow permissions = "Read repository contents and packages"
80
+ # EVERY workflow that pushes must declare contents: write:
81
+
82
+ permissions:
83
+ contents: write # required for git push, release creation, branch creation
84
+ pull-requests: write # only if the workflow also comments on PRs
85
+
86
+ jobs:
87
+ release:
88
+ runs-on: ubuntu-latest
89
+ steps:
90
+ - uses: actions/checkout@v4
91
+ - name: Create release
92
+ run: gh release create "${{ github.ref_name }}" --generate-notes
93
+ env:
94
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
95
+ - language: yaml
96
+ label: "Debug: print effective token permissions before failing"
97
+ code: |
98
+ - name: Check token permissions
99
+ run: |
100
+ gh api /repos/${{ github.repository }} --jq '.permissions'
101
+ gh auth status
102
+ env:
103
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
104
+ prevention:
105
+ - "Always add `contents: write` to any job that does `git push`, `git tag`, creates releases, or writes files via the GitHub API."
106
+ - "Set the organization default to 'Read and write' only when necessary; otherwise document that all push workflows must declare `contents: write`."
107
+ - "Use job-level `permissions:` blocks instead of workflow-level to minimize token scope."
108
+ - "Add a permissions comment near the `permissions:` block listing what each grant is needed for, so future maintainers don't accidentally remove it."
109
+ docs:
110
+ - url: "https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/controlling-permissions-for-github_token"
111
+ label: "GitHub Docs: Controlling permissions for GITHUB_TOKEN"
112
+ - url: "https://stackoverflow.com/questions/79437803/git-commit-in-github-actions-workflow-failing-write-access-to-repository-not"
113
+ label: "SO#79437803 — git commit failing: Write access to repository not granted, 403"
114
+ - url: "https://stackoverflow.com/questions/79471500/github-actions-authentication-failed-for-pushing-to-repository"
115
+ label: "SO#79471500 — Authentication failed for pushing to repository"
116
+ - url: "https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository#setting-the-permissions-of-the-github_token-for-your-repository"
117
+ label: "GitHub Docs: Setting GITHUB_TOKEN default permissions"