@htekdev/actions-debugger 1.0.124 → 1.0.125

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 (28) hide show
  1. package/errors/caching-artifacts/caching-artifacts-073.yml +100 -0
  2. package/errors/caching-artifacts/caching-artifacts-074.yml +117 -0
  3. package/errors/known-unsolved/known-unsolved-071.yml +122 -0
  4. package/errors/known-unsolved/known-unsolved-072.yml +143 -0
  5. package/errors/permissions-auth/permissions-auth-071.yml +144 -0
  6. package/errors/permissions-auth/permissions-auth-072.yml +112 -0
  7. package/errors/permissions-auth/permissions-auth-073.yml +127 -0
  8. package/errors/permissions-auth/permissions-auth-074.yml +106 -0
  9. package/errors/permissions-auth/permissions-auth-075.yml +137 -0
  10. package/errors/runner-environment/runner-environment-227.yml +106 -0
  11. package/errors/runner-environment/runner-environment-228.yml +117 -0
  12. package/errors/runner-environment/runner-environment-229.yml +119 -0
  13. package/errors/runner-environment/runner-environment-230.yml +129 -0
  14. package/errors/runner-environment/runner-environment-231.yml +90 -0
  15. package/errors/runner-environment/runner-environment-232.yml +131 -0
  16. package/errors/runner-environment/runner-environment-233.yml +90 -0
  17. package/errors/runner-environment/runner-environment-234.yml +114 -0
  18. package/errors/runner-environment/runner-environment-235.yml +151 -0
  19. package/errors/silent-failures/silent-failures-112.yml +97 -0
  20. package/errors/silent-failures/silent-failures-113.yml +110 -0
  21. package/errors/silent-failures/silent-failures-114.yml +116 -0
  22. package/errors/silent-failures/silent-failures-115.yml +130 -0
  23. package/errors/silent-failures/silent-failures-116.yml +117 -0
  24. package/errors/silent-failures/silent-failures-117.yml +137 -0
  25. package/errors/silent-failures/silent-failures-118.yml +156 -0
  26. package/errors/yaml-syntax/yaml-syntax-075.yml +128 -0
  27. package/errors/yaml-syntax/yaml-syntax-076.yml +107 -0
  28. package/package.json +1 -1
@@ -0,0 +1,100 @@
1
+ id: caching-artifacts-073
2
+ title: 'actions/upload-artifact and runner blob uploads stall or fail with Bad Request through HTTPS_PROXY — BlobClient missing proxy transport'
3
+ category: caching-artifacts
4
+ severity: error
5
+ tags:
6
+ - upload-artifact
7
+ - proxy
8
+ - https-proxy
9
+ - azure-blob
10
+ - self-hosted
11
+ - blob-client
12
+ - bad-request
13
+ - no-proxy
14
+ patterns:
15
+ - regex: 'Beginning upload of artifact content to blob storage'
16
+ flags: 'i'
17
+ - regex: '^Error: Bad Request$'
18
+ flags: 'im'
19
+ - regex: 'CONNECT.*blob\.core\.windows\.net.*200.*ALLOWED'
20
+ flags: 'i'
21
+ - regex: 'latency=\d+\.\d+s.*stall|stall.*blob.*proxy'
22
+ flags: 'i'
23
+ error_messages:
24
+ - 'Beginning upload of artifact content to blob storage'
25
+ - 'Error: Bad Request'
26
+ - 'CONNECT productionresultssa*.blob.core.windows.net:443 → status=200 (ALLOWED)'
27
+ root_cause: |
28
+ When a self-hosted runner is behind an HTTPS forward proxy (HTTPS_PROXY / https_proxy env var),
29
+ artifact uploads (actions/upload-artifact) and runner-internal uploads (step summaries, job logs,
30
+ diagnostics) stall or fail with "Error: Bad Request".
31
+
32
+ Root cause — two layers, same problem:
33
+
34
+ 1. @actions/artifact (TypeScript, used by upload-artifact@v4-v7) creates a BlobClient from
35
+ @azure/storage-blob with only the authenticated URL:
36
+ new BlobClient(authenticatedUploadURL)
37
+ No StoragePipelineOptions with proxy configuration are passed. The Azure SDK builds its own
38
+ HTTP pipeline without proxy transport, so even when HTTPS_PROXY is set in the environment, the
39
+ SDK does not correctly route the CONNECT tunnel.
40
+
41
+ 2. The runner's .NET ResultsHttpClient (used for step summaries, logs, diagnostics) also creates a
42
+ BlobClient without proxy transport options (runner#4351).
43
+
44
+ Proxy logs show:
45
+ - CONNECT tunnel to *.blob.core.windows.net:443 succeeds (HTTP 200 ALLOWED).
46
+ - Only ~17 KB of the payload is transmitted.
47
+ - The connection stalls for ~75 seconds and returns a "Bad Request" response.
48
+ - curl / Python / .NET HttpClient all upload successfully to the same endpoint in <1 second.
49
+
50
+ The upload step logs show the artifact upload starting but no "Artifact successfully finalized" line,
51
+ followed immediately by "Error: Bad Request". The step fails with no further detail.
52
+ fix: |
53
+ Add the Azure Blob Storage hostname to NO_PROXY to bypass the HTTPS proxy for all blob traffic.
54
+ The upload URL already contains a time-limited SAS token for authentication, so bypassing the
55
+ proxy for this destination does not weaken security in most configurations.
56
+
57
+ Set NO_PROXY (and no_proxy for case-insensitive tools) to include .blob.core.windows.net.
58
+
59
+ This can be set:
60
+ - In the runner's .env file (persists across all jobs on the runner)
61
+ - As job-level env: in the workflow (overrides only for that job)
62
+ fix_code:
63
+ - language: bash
64
+ label: 'Persist NO_PROXY in the runner service .env file'
65
+ code: |
66
+ # Append to the runner .env file (path varies by install location)
67
+ echo 'NO_PROXY=.blob.core.windows.net' >> /home/runner/actions-runner/.env
68
+ echo 'no_proxy=.blob.core.windows.net' >> /home/runner/actions-runner/.env
69
+ # Restart the runner service to pick up the change
70
+ sudo systemctl restart actions.runner.*.service
71
+
72
+ - language: yaml
73
+ label: 'Set NO_PROXY per workflow job to bypass proxy for artifact uploads'
74
+ code: |
75
+ jobs:
76
+ build:
77
+ runs-on: [self-hosted]
78
+ env:
79
+ # Bypass HTTPS proxy for Azure Blob Storage — prevents BlobClient stall
80
+ NO_PROXY: '.blob.core.windows.net'
81
+ no_proxy: '.blob.core.windows.net'
82
+ steps:
83
+ - uses: actions/checkout@v4
84
+ - run: ./build.sh
85
+ - uses: actions/upload-artifact@v6
86
+ with:
87
+ name: build-output
88
+ path: ./dist/
89
+ prevention:
90
+ - 'Always test artifact uploads when deploying self-hosted runners behind an HTTPS forward proxy before putting runners into production.'
91
+ - 'Set NO_PROXY=.blob.core.windows.net in the runner environment before rolling out proxy-configured runners.'
92
+ - 'Watch proxy logs for 75-second CONNECT stalls to *.blob.core.windows.net as the signature for this issue.'
93
+ - 'Runner-internal uploads (step summaries, job logs) are also affected — verify both artifact and job-summary visibility when testing.'
94
+ docs:
95
+ - url: 'https://github.com/actions/toolkit/issues/2377'
96
+ label: 'actions/toolkit#2377 — @actions/artifact BlobClient missing proxy transport (open)'
97
+ - url: 'https://github.com/actions/runner/issues/4351'
98
+ label: 'actions/runner#4351 — runner Azure Blob uploads stall through HTTPS proxy (open)'
99
+ - url: 'https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/about-self-hosted-runners#communication-between-self-hosted-runners-and-github'
100
+ label: 'GitHub Docs — Self-hosted runner network communication requirements'
@@ -0,0 +1,117 @@
1
+ id: caching-artifacts-074
2
+ title: 'actions/cache restore silently treats 429 rate limit as cache miss — no retry, full rebuild forced'
3
+ category: caching-artifacts
4
+ severity: silent-failure
5
+ tags:
6
+ - cache
7
+ - rate-limit
8
+ - 429
9
+ - restore
10
+ - cache-miss
11
+ - rebuild
12
+ - no-retry
13
+ - matrix
14
+ patterns:
15
+ - regex: "You've hit a rate limit"
16
+ flags: 'i'
17
+ - regex: 'Failed to restore.*Rate limited.*429'
18
+ flags: 'i'
19
+ - regex: 'Failed to GetCacheEntryDownloadURL.*rate limit exceeded'
20
+ flags: 'i'
21
+ - regex: 'Too Many Requests.*rate limit exceeded'
22
+ flags: 'i'
23
+ error_messages:
24
+ - "Warning: You've hit a rate limit, your rate limit will reset in 18 seconds"
25
+ - "Warning: Failed to restore: Failed to GetCacheEntryDownloadURL: Rate limited: Failed request: (429) Too Many Requests: rate limit exceeded"
26
+ - "Cache not found for input keys: ..."
27
+ root_cause: |
28
+ When the GitHub Actions Cache Service rate-limits a restore request with HTTP 429, the cache
29
+ action (v4/v5) emits a warning and immediately falls back to "Cache not found" — treating the
30
+ rate limit as a permanent cache miss rather than a transient error worth retrying.
31
+
32
+ The cache action does not:
33
+ - Retry the restore after the rate-limit reset period (reported in the warning, typically 10-60s).
34
+ - Fail the step with a hard error so the developer is alerted to an infrastructure issue.
35
+ - Implement any exponential backoff on the restore path.
36
+
37
+ Downstream steps see only "Cache not found for input keys: ..." and proceed to rebuild
38
+ dependencies from scratch, as if the cache had never been saved. The real cause — a transient
39
+ 429 from the cache service — is easily missed because it appears only as a "Warning:" line
40
+ mid-step, several lines before the final "Cache not found" output.
41
+
42
+ This is most disruptive in large parallel matrix workflows where many concurrent jobs all hit
43
+ the cache service simultaneously. The cache service rate-limits the burst, every affected job
44
+ sees a cache miss, and the entire matrix rebuilds from scratch. CI time can multiply by 5-10x
45
+ with no clear indication in the job summary that the rebuilds were avoidable.
46
+ fix: |
47
+ While the cache action does not yet implement automatic retry on 429, you can reduce the impact:
48
+
49
+ 1. Limit max-parallel on matrix strategies to reduce simultaneous cache restore bursts.
50
+
51
+ 2. Use restore-keys as a fallback: even if the exact-key restore is rate-limited, a prefix
52
+ match restore-keys request may succeed (different cache entry, different cache service shard).
53
+
54
+ 3. Stagger cache-heavy workflows using concurrency groups or needs: dependencies so they don't
55
+ all restore caches at the same second.
56
+
57
+ 4. Upgrade to the latest cache action — retry logic for 429 is a tracked improvement in
58
+ actions/cache#1758.
59
+ fix_code:
60
+ - language: yaml
61
+ label: 'Use restore-keys as a fallback to reduce full rebuilds on 429 rate limit'
62
+ code: |
63
+ - uses: actions/cache@v4
64
+ id: cache
65
+ with:
66
+ path: ~/.npm
67
+ key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
68
+ # Fallback: match any npm cache for this OS — may succeed even when exact key is rate-limited
69
+ restore-keys: |
70
+ ${{ runner.os }}-npm-
71
+
72
+ - language: yaml
73
+ label: 'Limit matrix parallelism to reduce simultaneous cache restore bursts'
74
+ code: |
75
+ jobs:
76
+ build:
77
+ strategy:
78
+ matrix:
79
+ target: [linux-x64, linux-arm64, windows-x64, macos-x64, macos-arm64]
80
+ # Limit concurrent cache restores — burst of 2 is much less likely to
81
+ # trigger 429 than a burst of 5 hitting the cache service simultaneously.
82
+ max-parallel: 2
83
+ runs-on: ubuntu-latest
84
+ steps:
85
+ - uses: actions/cache@v4
86
+ with:
87
+ path: ~/.cache
88
+ key: ${{ matrix.target }}-deps-${{ hashFiles('**/Cargo.lock') }}
89
+ restore-keys: |
90
+ ${{ matrix.target }}-deps-
91
+
92
+ - language: yaml
93
+ label: 'Stagger cache-restore-heavy jobs using concurrency groups'
94
+ code: |
95
+ jobs:
96
+ restore-cache:
97
+ concurrency:
98
+ group: cache-restore-${{ github.ref }}
99
+ cancel-in-progress: false # queue, not cancel
100
+ runs-on: ubuntu-latest
101
+ steps:
102
+ - uses: actions/cache@v4
103
+ with:
104
+ path: ~/.gradle/caches
105
+ key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
106
+ prevention:
107
+ - 'Never run more than ~8 simultaneous cache restore operations in the same repository — the cache service rate limit is per repository.'
108
+ - 'Always include restore-keys as a fallback so partial cache hits reduce rebuild cost when the exact key is rate-limited.'
109
+ - 'Watch for the "Warning: You''ve hit a rate limit" log line when investigating unexpectedly slow CI builds.'
110
+ - 'Treat "Cache not found" as potentially a transient 429, not necessarily a first-run or key-miss, especially in high-parallelism workflows.'
111
+ docs:
112
+ - url: 'https://github.com/actions/cache/issues/1758'
113
+ label: 'actions/cache#1758 — Handle rate limit with retry instead of silent cache miss (open)'
114
+ - url: 'https://github.com/actions/cache#inputs'
115
+ label: 'actions/cache — restore-keys documentation'
116
+ - url: 'https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows'
117
+ label: 'GitHub Docs — Caching dependencies to speed up workflows'
@@ -0,0 +1,122 @@
1
+ id: known-unsolved-071
2
+ title: 'Actions cache is repository-scoped — cannot be shared across repositories in the same organization'
3
+ category: known-unsolved
4
+ severity: limitation
5
+ tags:
6
+ - cache
7
+ - cross-repo
8
+ - organization
9
+ - scope
10
+ - monorepo
11
+ - limitation
12
+ - cache-isolation
13
+ patterns:
14
+ - regex: 'Cache not found for input keys.*(?:cross-repo|other.repo|shared.cache)'
15
+ flags: 'i'
16
+ - regex: 'No cache found.*(?:cross-repo|other.repo|shared)'
17
+ flags: 'i'
18
+ error_messages:
19
+ - 'Cache not found for input keys: ...'
20
+ - 'No cache found'
21
+ root_cause: |
22
+ The GitHub Actions cache service scopes all cache entries to the repository where they
23
+ were created. There is no mechanism to share a cache entry between two different
24
+ repositories, even within the same organization.
25
+
26
+ Cache access rules per the GitHub documentation:
27
+ - A workflow can restore caches created in the current branch, the default branch (main),
28
+ or (for pull requests) the base branch including base branches of forks.
29
+ - "Cross-branch" access is supported within the same repository.
30
+ - There is NO "cross-repository" access — a cache created in `org/repo-a` is
31
+ completely invisible to workflows running in `org/repo-b`.
32
+
33
+ This affects teams who:
34
+ - Manage related repositories that share build toolchains (e.g., a shared Go module cache
35
+ across a dozen microservices).
36
+ - Have split monorepos where a common library is built separately and cached.
37
+ - Want to cache a slow Docker layer in one repo and reuse it in a deployment repo.
38
+
39
+ The underlying reason is cache isolation as a security boundary: allowing cross-repo
40
+ cache access could leak build artifacts or credentials stored in the cache between
41
+ unrelated repositories.
42
+
43
+ There is no current GitHub Actions native solution for cross-repo cache sharing. The
44
+ GitHub roadmap has not publicly committed to this feature as of 2026.
45
+ fix: |
46
+ There is no built-in fix. Workarounds depend on your use case:
47
+
48
+ 1. Publish shared artifacts to a package registry (GitHub Packages, npm, PyPI, Docker Hub).
49
+ Instead of caching, version-tag the shared artifact and consume it as a dependency.
50
+ This is the recommended approach for shared libraries and Docker base images.
51
+
52
+ 2. Use a self-hosted runner with a shared filesystem. The runner's local disk or a
53
+ network share can act as a cross-repo cache. Use the `path:` input of actions/cache
54
+ pointing to a shared mount. Cache hits and misses are managed manually via key files.
55
+
56
+ 3. Use a third-party caching backend (S3, GCS, Azure Blob, Artifactory) for build
57
+ artifacts that must be shared. Upload/download via CLI in workflow steps.
58
+
59
+ 4. Consolidate the related repositories into a single repository (monorepo).
60
+ All workflows within the same repo can share cache entries.
61
+ fix_code:
62
+ - language: yaml
63
+ label: 'Publish shared Docker base image to GHCR instead of caching across repos'
64
+ code: |
65
+ # repo-a: builds and publishes the shared base image
66
+ jobs:
67
+ publish-base:
68
+ runs-on: ubuntu-latest
69
+ permissions:
70
+ packages: write
71
+ steps:
72
+ - uses: actions/checkout@v4
73
+ - uses: docker/login-action@v3
74
+ with:
75
+ registry: ghcr.io
76
+ username: ${{ github.actor }}
77
+ password: ${{ secrets.GITHUB_TOKEN }}
78
+ - uses: docker/build-push-action@v6
79
+ with:
80
+ context: ./base-image
81
+ push: true
82
+ tags: ghcr.io/${{ github.repository_owner }}/shared-base:latest
83
+
84
+ # repo-b: pulls the published image instead of relying on cache
85
+ jobs:
86
+ build:
87
+ runs-on: ubuntu-latest
88
+ container:
89
+ image: ghcr.io/myorg/shared-base:latest
90
+ credentials:
91
+ username: ${{ github.actor }}
92
+ password: ${{ secrets.GITHUB_TOKEN }}
93
+ steps:
94
+ - uses: actions/checkout@v4
95
+ - run: ./build.sh
96
+
97
+ - language: yaml
98
+ label: 'Self-hosted runner shared-path cache as a cross-repo workaround'
99
+ code: |
100
+ # Both repo-a and repo-b workflows — same self-hosted runner, shared disk at /opt/shared-cache
101
+ jobs:
102
+ build:
103
+ runs-on: [self-hosted, linux, shared-cache]
104
+ steps:
105
+ - uses: actions/checkout@v4
106
+ - uses: actions/cache@v4
107
+ with:
108
+ path: /opt/shared-cache/gradle
109
+ # Key is independent of the repo — deliberately shared
110
+ key: gradle-${{ hashFiles('**/*.gradle*') }}
111
+ # NOTE: This bypasses GitHub's repo-scope isolation.
112
+ # Ensure the self-hosted runner pool is trusted and isolated.
113
+ - run: ./gradlew build
114
+ prevention:
115
+ - 'Design shared build artifacts as versioned dependencies (packages) from the start — avoids cross-repo cache needs entirely.'
116
+ - 'For Docker base images shared across repos, publish to a registry and reference by digest, not by mutable tags.'
117
+ - 'When adopting self-hosted runners for cross-repo cache sharing, audit what secrets and artifacts are accessible to all jobs that share the runner to avoid cross-contamination.'
118
+ docs:
119
+ - url: 'https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/caching-dependencies-to-speed-up-workflows#restrictions-for-accessing-a-cache'
120
+ label: 'GitHub Docs — Cache access restrictions and scoping'
121
+ - url: 'https://github.com/actions/cache/blob/main/tips-and-workarounds.md'
122
+ label: 'actions/cache — Tips and workarounds (cross-branch, cross-OS, but not cross-repo)'
@@ -0,0 +1,143 @@
1
+ id: known-unsolved-072
2
+ title: 'No parallel steps within a single job — all steps execute sequentially'
3
+ category: known-unsolved
4
+ severity: limitation
5
+ tags:
6
+ - parallel-steps
7
+ - sequential
8
+ - performance
9
+ - job-structure
10
+ - limitation
11
+ - roadmap
12
+ patterns:
13
+ - regex: 'parallel.*steps.*not.*support|steps.*run.*sequentially'
14
+ flags: 'i'
15
+ error_messages:
16
+ - 'steps run sequentially — no native parallel step execution within a single job'
17
+ root_cause: |
18
+ In GitHub Actions, all steps within a single job execute sequentially in the order they
19
+ are defined. There is no native syntax to declare that two or more steps within the same
20
+ job should run concurrently.
21
+
22
+ This means:
23
+ - A job that runs `npm install`, `eslint`, and `jest` must run them one after another,
24
+ even if `eslint` and `jest` are completely independent and could run simultaneously.
25
+ - A job that runs two independent API calls, file downloads, or build targets must wait
26
+ for each to complete before starting the next.
27
+ - The only way to achieve true parallelism in GitHub Actions is to split work across
28
+ multiple jobs with `needs:` dependencies — but this requires each job to set up its
29
+ own runner, check out the repository, restore caches, and install dependencies,
30
+ adding significant overhead for short tasks.
31
+
32
+ This is a long-standing community request. GitHub added it to the public roadmap as
33
+ "Parallel Steps in GitHub Actions" (github/roadmap#1191, GA milestone, 2025).
34
+
35
+ Common workarounds add latency (multiple jobs) or complexity (background processes).
36
+ fix: |
37
+ There is no built-in fix. Workarounds:
38
+
39
+ 1. Split parallel work into separate jobs using `needs:` and a matrix strategy.
40
+ Each job adds runner setup overhead (~15-30s), so this is most effective for
41
+ tasks that take minutes, not seconds.
42
+
43
+ 2. Run steps as background shell processes and wait for them with `wait` (bash only).
44
+ This works for independent shell commands that don't need to write to GITHUB_OUTPUT,
45
+ GITHUB_ENV, or produce step outputs — those mechanisms are not safe for concurrent use.
46
+
47
+ 3. Use `make -j N` or `./gradlew --parallel` or similar build-tool parallelism within
48
+ a single shell step. This parallelizes work inside one run: step without needing
49
+ multiple GitHub Actions steps.
50
+
51
+ 4. Run a Docker Compose or docker run --detach to start background services, then
52
+ use a final step to check results.
53
+ fix_code:
54
+ - language: yaml
55
+ label: 'Run independent checks in parallel via separate jobs (preferred for long tasks)'
56
+ code: |
57
+ jobs:
58
+ lint:
59
+ runs-on: ubuntu-latest
60
+ steps:
61
+ - uses: actions/checkout@v4
62
+ - uses: actions/setup-node@v4
63
+ with:
64
+ node-version: 22
65
+ cache: npm
66
+ - run: npm ci
67
+ - run: npm run lint
68
+
69
+ test:
70
+ runs-on: ubuntu-latest
71
+ steps:
72
+ - uses: actions/checkout@v4
73
+ - uses: actions/setup-node@v4
74
+ with:
75
+ node-version: 22
76
+ cache: npm
77
+ - run: npm ci
78
+ - run: npm test
79
+
80
+ type-check:
81
+ runs-on: ubuntu-latest
82
+ steps:
83
+ - uses: actions/checkout@v4
84
+ - uses: actions/setup-node@v4
85
+ with:
86
+ node-version: 22
87
+ cache: npm
88
+ - run: npm ci
89
+ - run: npm run typecheck
90
+
91
+ # Final gate job waits for all parallel checks
92
+ ci-complete:
93
+ needs: [lint, test, type-check]
94
+ runs-on: ubuntu-latest
95
+ steps:
96
+ - run: echo "All checks passed"
97
+
98
+ - language: bash
99
+ label: 'Background shell processes for independent shell commands (same job)'
100
+ code: |
101
+ # In a single run: step, launch parallel shell processes and wait for all
102
+ # WARNING: This pattern does not work with GITHUB_OUTPUT/GITHUB_ENV/GITHUB_STEP_SUMMARY
103
+ # from the background processes — race conditions corrupt the append-mode files.
104
+ # Use only for commands that write to their own output files.
105
+
106
+ ./fetch-data-source-1.sh > /tmp/source1.json &
107
+ PID1=$!
108
+
109
+ ./fetch-data-source-2.sh > /tmp/source2.json &
110
+ PID2=$!
111
+
112
+ wait $PID1 || { echo "Source 1 fetch failed"; exit 1; }
113
+ wait $PID2 || { echo "Source 2 fetch failed"; exit 1; }
114
+
115
+ echo "Both sources fetched in parallel"
116
+ ./merge-sources.py /tmp/source1.json /tmp/source2.json
117
+
118
+ - language: yaml
119
+ label: 'Use build tool parallelism inside a single step'
120
+ code: |
121
+ jobs:
122
+ build:
123
+ runs-on: ubuntu-latest
124
+ steps:
125
+ - uses: actions/checkout@v4
126
+ - uses: actions/setup-java@v4
127
+ with:
128
+ java-version: 21
129
+ distribution: temurin
130
+ # Gradle parallel project execution — all subprojects build concurrently
131
+ - run: ./gradlew build --parallel --max-workers 4
132
+ prevention:
133
+ - 'Design CI pipelines with parallel jobs from the start — split lint, test, and build into independent jobs to maximize parallelism today.'
134
+ - 'Use a shared cache with `actions/cache` to minimize the overhead of repeated `npm ci` / `pip install` across parallel jobs.'
135
+ - 'Track github/roadmap#1191 for the native parallel steps feature — once released, sequential-step bottlenecks can be eliminated without the multi-job overhead.'
136
+ - 'For build-tool tasks, always prefer build-native parallelism (`make -j`, `--parallel`, `cargo build --jobs`) over workflow-level workarounds.'
137
+ docs:
138
+ - url: 'https://github.com/github/roadmap/issues/1191'
139
+ label: 'github/roadmap#1191 — Parallel Steps in GitHub Actions (GA milestone, open 2025)'
140
+ - url: 'https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idsteps'
141
+ label: 'GitHub Docs — jobs.<job_id>.steps (sequential execution model)'
142
+ - url: 'https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs'
143
+ label: 'GitHub Docs — Using a matrix for parallel jobs as a workaround'
@@ -0,0 +1,144 @@
1
+ id: permissions-auth-071
2
+ title: 'OIDC token not available in pull_request events from forks — id-token: write cannot be granted'
3
+ category: permissions-auth
4
+ severity: error
5
+ tags:
6
+ - oidc
7
+ - fork
8
+ - pull-request
9
+ - id-token
10
+ - attestation
11
+ - aws
12
+ - azure
13
+ - workload-identity
14
+ patterns:
15
+ - regex: 'Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable'
16
+ flags: 'i'
17
+ - regex: 'Could not fetch an OIDC token.*id-token.*write'
18
+ flags: 'i'
19
+ - regex: 'Error: Action failed with error: Error: Error message: Unable to get ACTIONS_ID_TOKEN_REQUEST_URL'
20
+ flags: 'i'
21
+ error_messages:
22
+ - 'Error: Error message: Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable'
23
+ - 'Could not fetch an OIDC token. Did you remember to add `id-token: write` to your workflow permissions?'
24
+ - 'Error: Action failed with error: Error: Error message: Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable'
25
+ root_cause: |
26
+ GitHub Actions workflows triggered by `pull_request` events from forked repositories cannot
27
+ mint OIDC tokens, even when `id-token: write` is explicitly set in the workflow `permissions:` block.
28
+
29
+ The restriction is enforced by GitHub's security model for fork pull requests:
30
+ - Fork PRs run with the permissions of the fork, not the base repository.
31
+ - GitHub documentation states: "You can use the permissions key to add and remove read
32
+ permissions for forked repositories, but typically you can't grant write access."
33
+ - The `id-token: write` scope is a WRITE permission. The ACTIONS_ID_TOKEN_REQUEST_URL
34
+ environment variable is only set when the runtime has write-level token access.
35
+ - When the variable is absent, any action or toolkit call to `core.getIDToken()` fails with
36
+ "Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable".
37
+
38
+ This commonly impacts:
39
+ - Cloud provider OIDC authentication (AWS configure-aws-credentials, Azure login, GCP auth)
40
+ - actions/attest-build-provenance and actions/attest for build attestation on PRs
41
+ - Any action that relies on `@actions/core` `getIDToken()` for OIDC federation
42
+
43
+ The restriction exists to prevent malicious fork contributors from using OIDC tokens to
44
+ authenticate against production cloud accounts or push malicious artifacts.
45
+ fix: |
46
+ Option 1 — Use `pull_request_target` instead of `pull_request` (CAUTION required).
47
+ `pull_request_target` runs in the context of the base repository and CAN mint OIDC tokens.
48
+ However, it executes in the base repo's context with full access to base repo secrets —
49
+ this is a significant security risk if the workflow checks out or executes fork code without
50
+ proper isolation. Read all security guides before using this trigger.
51
+
52
+ Option 2 — Split the workflow into two parts.
53
+ Use `pull_request` for the build/test phase (no OIDC). Use a separate workflow triggered
54
+ by `workflow_run` or manual approval to perform OIDC-protected operations (attestation, deploy)
55
+ after verifying the PR code is safe.
56
+
57
+ Option 3 — Use a GitHub App token with explicit repository permissions for operations
58
+ that do not strictly require OIDC (e.g., package publishing). This avoids the OIDC
59
+ restriction entirely.
60
+
61
+ Option 4 — Only run OIDC-dependent steps on `push` events to protected branches (post-merge),
62
+ not on `pull_request` events from forks. Guard OIDC steps with:
63
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false
64
+ fix_code:
65
+ - language: yaml
66
+ label: 'Guard OIDC steps — skip for fork PRs, run only for same-repo PRs or push'
67
+ code: |
68
+ jobs:
69
+ build-and-attest:
70
+ runs-on: ubuntu-latest
71
+ permissions:
72
+ id-token: write
73
+ contents: read
74
+ attestations: write
75
+ steps:
76
+ - uses: actions/checkout@v4
77
+
78
+ - name: Build artifact
79
+ run: ./build.sh
80
+
81
+ # OIDC-dependent steps: skip if fork PR (id-token write not available)
82
+ - name: Attest build provenance
83
+ if: >
84
+ github.event_name != 'pull_request' ||
85
+ github.event.pull_request.head.repo.full_name == github.repository
86
+ uses: actions/attest-build-provenance@v2
87
+ with:
88
+ subject-path: './dist/app'
89
+
90
+ - language: yaml
91
+ label: 'Split workflow: use workflow_run to run OIDC steps after fork PR merges'
92
+ code: |
93
+ # Workflow 1: runs on pull_request — builds and uploads artifact (no OIDC)
94
+ on:
95
+ pull_request:
96
+ jobs:
97
+ build:
98
+ runs-on: ubuntu-latest
99
+ steps:
100
+ - uses: actions/checkout@v4
101
+ - run: ./build.sh
102
+ - uses: actions/upload-artifact@v4
103
+ with:
104
+ name: build-output
105
+ path: ./dist/
106
+
107
+ ---
108
+
109
+ # Workflow 2: runs after workflow 1 completes — performs OIDC operations
110
+ on:
111
+ workflow_run:
112
+ workflows: ['Build']
113
+ types: [completed]
114
+ jobs:
115
+ attest:
116
+ # Only runs on base-repo push events completing after a merge
117
+ if: github.event.workflow_run.conclusion == 'success'
118
+ runs-on: ubuntu-latest
119
+ permissions:
120
+ id-token: write
121
+ attestations: write
122
+ steps:
123
+ - uses: actions/download-artifact@v4
124
+ with:
125
+ name: build-output
126
+ run-id: ${{ github.event.workflow_run.id }}
127
+ github-token: ${{ secrets.GITHUB_TOKEN }}
128
+ - uses: actions/attest-build-provenance@v2
129
+ with:
130
+ subject-path: './dist/app'
131
+ prevention:
132
+ - 'Never assume `id-token: write` is sufficient — fork PRs override write permissions to read-only regardless of the workflow `permissions:` block.'
133
+ - 'Add an explicit `if:` guard on every OIDC-dependent step to skip it for fork PRs: `if: github.event.pull_request.head.repo.fork == false`.'
134
+ - 'Use `pull_request_target` only after thoroughly reading the security hardening guide — it runs in base repo context and is vulnerable to pwn requests if fork code is checked out.'
135
+ - 'For attestation workflows, run attestation as a post-merge step on `push` to the default branch, not on PRs from forks.'
136
+ docs:
137
+ - url: 'https://github.com/actions/attest-build-provenance/issues/99'
138
+ label: 'actions/attest-build-provenance#99 — OIDC unavailable in fork PR workflows (open)'
139
+ - url: 'https://github.com/aws-actions/configure-aws-credentials/issues/373'
140
+ label: 'aws-actions/configure-aws-credentials#373 — OIDC fails for pull request from fork'
141
+ - url: 'https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#permissions-for-the-github_token'
142
+ label: 'GitHub Docs — GITHUB_TOKEN permissions for forked repos'
143
+ - url: 'https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#using-pull_request_target-safely'
144
+ label: 'GitHub Docs — Security hardening for pull_request_target'