@htekdev/actions-debugger 1.0.124 → 1.0.126
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/caching-artifacts-073.yml +100 -0
- package/errors/caching-artifacts/caching-artifacts-074.yml +117 -0
- package/errors/concurrency-timing/concurrency-timing-059.yml +146 -0
- package/errors/concurrency-timing/concurrency-timing-060.yml +144 -0
- package/errors/known-unsolved/known-unsolved-071.yml +122 -0
- package/errors/known-unsolved/known-unsolved-072.yml +143 -0
- package/errors/known-unsolved/known-unsolved-073.yml +172 -0
- package/errors/permissions-auth/permissions-auth-071.yml +144 -0
- package/errors/permissions-auth/permissions-auth-072.yml +112 -0
- package/errors/permissions-auth/permissions-auth-073.yml +127 -0
- package/errors/permissions-auth/permissions-auth-074.yml +106 -0
- package/errors/permissions-auth/permissions-auth-075.yml +137 -0
- package/errors/runner-environment/runner-environment-227.yml +106 -0
- package/errors/runner-environment/runner-environment-228.yml +117 -0
- package/errors/runner-environment/runner-environment-229.yml +119 -0
- package/errors/runner-environment/runner-environment-230.yml +129 -0
- package/errors/runner-environment/runner-environment-231.yml +90 -0
- package/errors/runner-environment/runner-environment-232.yml +131 -0
- package/errors/runner-environment/runner-environment-233.yml +90 -0
- package/errors/runner-environment/runner-environment-234.yml +114 -0
- package/errors/runner-environment/runner-environment-235.yml +151 -0
- package/errors/silent-failures/silent-failures-112.yml +97 -0
- package/errors/silent-failures/silent-failures-113.yml +110 -0
- package/errors/silent-failures/silent-failures-114.yml +116 -0
- package/errors/silent-failures/silent-failures-115.yml +130 -0
- package/errors/silent-failures/silent-failures-116.yml +117 -0
- package/errors/silent-failures/silent-failures-117.yml +137 -0
- package/errors/silent-failures/silent-failures-118.yml +156 -0
- package/errors/triggers/triggers-072.yml +150 -0
- package/errors/yaml-syntax/yaml-syntax-075.yml +128 -0
- package/errors/yaml-syntax/yaml-syntax-076.yml +107 -0
- package/package.json +1 -1
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
id: permissions-auth-073
|
|
2
|
+
title: 'GraphQL "refusing to allow a GitHub App to create or update workflow without `workflows` permission" — misleading error, actual fix is actions: write or App workflow scope'
|
|
3
|
+
category: permissions-auth
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- GitHub-App
|
|
7
|
+
- workflows
|
|
8
|
+
- actions-write
|
|
9
|
+
- GraphQL
|
|
10
|
+
- pull-request
|
|
11
|
+
- misleading-error
|
|
12
|
+
- OAuth-scope
|
|
13
|
+
- enablePullRequestAutoMerge
|
|
14
|
+
patterns:
|
|
15
|
+
- regex: "refusing to allow a GitHub App to create or update workflow .+ without .workflows. permission"
|
|
16
|
+
flags: 'i'
|
|
17
|
+
- regex: 'GraphQL.+Pull request.+workflow.+without.+workflows.*permission'
|
|
18
|
+
flags: 'i'
|
|
19
|
+
error_messages:
|
|
20
|
+
- "GraphQL: Pull request refusing to allow a GitHub App to create or update workflow `.github/workflows/release.yml` without `workflows` permission (enablePullRequestAutoMerge)"
|
|
21
|
+
- "refusing to allow a GitHub App to create or update workflow `.github/workflows/ci.yml` without `workflows` permission"
|
|
22
|
+
root_cause: |
|
|
23
|
+
When a GitHub App or the `GITHUB_TOKEN` attempts to enable auto-merge,
|
|
24
|
+
create a pull request that modifies workflow files, or perform certain
|
|
25
|
+
pull-request GraphQL mutations (`enablePullRequestAutoMerge`,
|
|
26
|
+
`createPullRequest`, etc.) on a branch that touches `.github/workflows/`,
|
|
27
|
+
GitHub's API returns a misleading error:
|
|
28
|
+
|
|
29
|
+
`GraphQL: Pull request refusing to allow a GitHub App to create or update
|
|
30
|
+
workflow '...' without 'workflows' permission`
|
|
31
|
+
|
|
32
|
+
The error message references `workflows` as if it were a value you can set
|
|
33
|
+
in the `permissions:` block of a GitHub Actions workflow. **It is not.**
|
|
34
|
+
The word `workflows` here refers to the **GitHub App OAuth scope** (which
|
|
35
|
+
grants the App permission to edit workflow files), not the `actions: write`
|
|
36
|
+
or any standard GITHUB_TOKEN permission key.
|
|
37
|
+
|
|
38
|
+
There are two distinct situations that produce this error:
|
|
39
|
+
|
|
40
|
+
1. **GitHub App missing the Workflows permission:** If your GitHub App does
|
|
41
|
+
not have the "Workflows" repository permission (a GitHub App installation
|
|
42
|
+
permission distinct from all the `permissions:` block keys), any operation
|
|
43
|
+
that modifies `.github/workflows/` through the App will be rejected with
|
|
44
|
+
this message.
|
|
45
|
+
|
|
46
|
+
2. **GITHUB_TOKEN used for auto-merge enabling on workflow-touching PRs:**
|
|
47
|
+
When a workflow uses `gh pr merge --auto` (or the GraphQL
|
|
48
|
+
`enablePullRequestAutoMerge` mutation) on a PR that modifies workflow
|
|
49
|
+
files, the GITHUB_TOKEN may lack the necessary scope even with
|
|
50
|
+
`contents: write`, because enabling auto-merge on workflow-modifying PRs
|
|
51
|
+
requires the workflows OAuth scope which GITHUB_TOKEN cannot hold.
|
|
52
|
+
|
|
53
|
+
The error message's reference to `workflows` is confusing because it is not
|
|
54
|
+
a recognized key in the YAML `permissions:` block. Developers who try to
|
|
55
|
+
add `permissions: workflows: write` get a YAML validation error ("Unknown
|
|
56
|
+
property 'workflows'"), leading to frustration when neither the permissions
|
|
57
|
+
block nor the token can be directly granted this scope.
|
|
58
|
+
fix: |
|
|
59
|
+
**For GitHub Apps:**
|
|
60
|
+
Go to your GitHub App's settings → Permissions & events → Repository
|
|
61
|
+
permissions → and enable **"Workflows" permission** (set to Read & Write).
|
|
62
|
+
Then re-install the app on the repository/organization so the new permission
|
|
63
|
+
takes effect.
|
|
64
|
+
|
|
65
|
+
**For GITHUB_TOKEN on auto-merge of workflow-touching PRs:**
|
|
66
|
+
The GITHUB_TOKEN cannot hold the `workflows` OAuth scope. You must use a
|
|
67
|
+
Personal Access Token (classic, with the `workflow` scope) or a GitHub App
|
|
68
|
+
with the Workflows permission. Store either as a repository secret and use it
|
|
69
|
+
instead of `GITHUB_TOKEN` for the auto-merge step:
|
|
70
|
+
|
|
71
|
+
```yaml
|
|
72
|
+
- name: Enable auto-merge
|
|
73
|
+
env:
|
|
74
|
+
GH_TOKEN: ${{ secrets.MY_PAT_WITH_WORKFLOW_SCOPE }}
|
|
75
|
+
run: gh pr merge --auto --squash ${{ github.event.pull_request.number }}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**For `actions: write` confusion:**
|
|
79
|
+
Adding `actions: write` to the `permissions:` block does NOT fix this error.
|
|
80
|
+
`actions: write` governs artifacts, caches, and workflow runs — NOT the
|
|
81
|
+
ability to create or modify workflow files.
|
|
82
|
+
fix_code:
|
|
83
|
+
- language: yaml
|
|
84
|
+
label: 'Use a PAT with the workflow scope instead of GITHUB_TOKEN for auto-merge on workflow-touching PRs'
|
|
85
|
+
code: |
|
|
86
|
+
jobs:
|
|
87
|
+
auto-merge:
|
|
88
|
+
runs-on: ubuntu-latest
|
|
89
|
+
steps:
|
|
90
|
+
- name: Enable auto-merge
|
|
91
|
+
# GITHUB_TOKEN cannot hold the 'workflow' OAuth scope.
|
|
92
|
+
# Use a PAT (classic) with the 'workflow' scope stored as a secret.
|
|
93
|
+
env:
|
|
94
|
+
GH_TOKEN: ${{ secrets.PAT_WITH_WORKFLOW_SCOPE }}
|
|
95
|
+
run: |
|
|
96
|
+
gh pr merge --auto --squash "${{ github.event.pull_request.number }}"
|
|
97
|
+
- language: yaml
|
|
98
|
+
label: 'Use create-github-app-token with Workflows permission to create PRs that modify workflows'
|
|
99
|
+
code: |
|
|
100
|
+
jobs:
|
|
101
|
+
create-update-pr:
|
|
102
|
+
runs-on: ubuntu-latest
|
|
103
|
+
steps:
|
|
104
|
+
- name: Generate App token (App must have Workflows read+write permission)
|
|
105
|
+
id: app-token
|
|
106
|
+
uses: actions/create-github-app-token@v1
|
|
107
|
+
with:
|
|
108
|
+
app-id: ${{ vars.MY_APP_ID }}
|
|
109
|
+
private-key: ${{ secrets.MY_APP_PRIVATE_KEY }}
|
|
110
|
+
|
|
111
|
+
- name: Create PR modifying a workflow file
|
|
112
|
+
env:
|
|
113
|
+
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
|
114
|
+
run: |
|
|
115
|
+
gh pr create --title "Update workflow" --body "..." --base main
|
|
116
|
+
prevention:
|
|
117
|
+
- 'When your GitHub App or automation needs to create PRs that touch `.github/workflows/`, verify the App installation has the "Workflows" repository permission enabled (App settings → Permissions → Workflows: Read & Write).'
|
|
118
|
+
- 'Do NOT attempt `permissions: workflows: write` in your workflow YAML — "workflows" is not a valid permission key and will cause a YAML validation error; it is an App-level OAuth scope, not a token permission.'
|
|
119
|
+
- 'For auto-merge automation on PRs that may modify workflow files, use a classic PAT with the `workflow` scope or a GitHub App with Workflows permission rather than GITHUB_TOKEN.'
|
|
120
|
+
- 'When debugging this error, the key question is: "Does the token/App have the `workflows` OAuth scope?" — not "Does the job have the right permissions: block?"'
|
|
121
|
+
docs:
|
|
122
|
+
- url: 'https://github.com/cli/cli/issues/11493'
|
|
123
|
+
label: 'cli/cli#11493 — Misleading GraphQL error: "without workflows permission" when using enablePullRequestAutoMerge'
|
|
124
|
+
- url: 'https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/choosing-permissions-for-a-github-app'
|
|
125
|
+
label: 'GitHub Docs — Choosing permissions for a GitHub App (Workflows permission)'
|
|
126
|
+
- url: 'https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens'
|
|
127
|
+
label: 'GitHub Docs — Classic PAT workflow scope for modifying workflow files'
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
id: permissions-auth-074
|
|
2
|
+
title: 'Composer leaks new-format GITHUB_TOKEN (ghs_APPID_JWT) to stderr in CI logs — CVE-2026-45793 / GHSA-f9f8-rm49-7jv2'
|
|
3
|
+
category: permissions-auth
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- composer
|
|
7
|
+
- GITHUB_TOKEN
|
|
8
|
+
- token-disclosure
|
|
9
|
+
- ghs-token
|
|
10
|
+
- php
|
|
11
|
+
- setup-php
|
|
12
|
+
- CVE-2026-45793
|
|
13
|
+
- security
|
|
14
|
+
- token-format-rollout
|
|
15
|
+
patterns:
|
|
16
|
+
- regex: 'Your github oauth token for .+ contains invalid characters'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
- regex: 'UnexpectedValueException.+github oauth token.+invalid characters'
|
|
19
|
+
flags: 'i'
|
|
20
|
+
- regex: 'contains invalid characters: "ghs_'
|
|
21
|
+
flags: 'i'
|
|
22
|
+
error_messages:
|
|
23
|
+
- 'Your github oauth token for github.com contains invalid characters: "ghs_AppID_eyJhbGc..."'
|
|
24
|
+
- '[InvalidArgumentException] Your github oauth token for github.com contains invalid characters: "ghs_..."'
|
|
25
|
+
root_cause: |
|
|
26
|
+
Starting April 27, 2026, GitHub began rolling out a new stateless token format for
|
|
27
|
+
GITHUB_TOKEN and GitHub App installation tokens. The new format is `ghs_<AppID>_<JWT>`,
|
|
28
|
+
approximately 520 characters long, and uses base64url encoding which includes hyphens (`-`)
|
|
29
|
+
and underscores.
|
|
30
|
+
|
|
31
|
+
Composer versions >=2.3.0 <2.9.8 (and >=1.0 <1.10.28, >=2.0.0 <2.2.28) validate GitHub OAuth
|
|
32
|
+
tokens with the regex `^[.A-Za-z0-9_]+$`. This regex does not permit hyphens. The new
|
|
33
|
+
`ghs_APPID_JWT` token format routinely contains hyphens (base64url RFC 4648 §5 uses `-` and `_`
|
|
34
|
+
as URL-safe replacements for `+` and `/`).
|
|
35
|
+
|
|
36
|
+
When the token fails this validation, Composer throws an `UnexpectedValueException` that
|
|
37
|
+
interpolates the raw token verbatim into the error message:
|
|
38
|
+
|
|
39
|
+
`Your github oauth token for github.com contains invalid characters: "ghs_<full-token>"`
|
|
40
|
+
|
|
41
|
+
Symfony Console then prints this exception to stderr. GitHub Actions' built-in secret masker
|
|
42
|
+
matches registered values as exact substrings, but Symfony Console may wrap the message, embed
|
|
43
|
+
it in framing text (`In BaseIO.php line N:`), or interleave ANSI control sequences. As a result,
|
|
44
|
+
the masker does not redact the token, and the plaintext credential reaches the CI log.
|
|
45
|
+
|
|
46
|
+
This affects any PHP workflow that uses Composer with GITHUB_TOKEN registered as a GitHub OAuth
|
|
47
|
+
credential. Many widely-used Actions — including `shivammathur/setup-php` — automatically
|
|
48
|
+
register `GITHUB_TOKEN` into Composer's global `auth.json`, so the leak triggers without
|
|
49
|
+
any explicit user configuration.
|
|
50
|
+
|
|
51
|
+
CVSS score: 7.5 (High). CVE-2026-45793 / GHSA-f9f8-rm49-7jv2.
|
|
52
|
+
fix: |
|
|
53
|
+
**Primary fix: Upgrade Composer to a patched version.**
|
|
54
|
+
- Composer 2.x (mainline): upgrade to >=2.9.8
|
|
55
|
+
- Composer 2.2.x (LTS): upgrade to >=2.2.28
|
|
56
|
+
- Composer 1.x: upgrade to >=1.10.28
|
|
57
|
+
|
|
58
|
+
The patched versions update the token validation regex to accept hyphens, preventing the
|
|
59
|
+
disclosure.
|
|
60
|
+
|
|
61
|
+
**Secondary workaround (if upgrading is not immediately possible):**
|
|
62
|
+
Explicitly unset the GitHub token from Composer's auth config before running Composer, then
|
|
63
|
+
set it via a token-safe mechanism, or use a classic `ghp_` PAT which uses the old format
|
|
64
|
+
unaffected by this regression:
|
|
65
|
+
|
|
66
|
+
```yaml
|
|
67
|
+
- name: Remove GITHUB_TOKEN from Composer auth (workaround)
|
|
68
|
+
run: composer config --global --unset github-oauth.github.com
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**If using `shivammathur/setup-php`:** The action pins a Composer version; ensure you're on
|
|
72
|
+
a version that ships Composer >=2.9.8.
|
|
73
|
+
fix_code:
|
|
74
|
+
- language: yaml
|
|
75
|
+
label: 'Pin Composer to patched version in GitHub Actions workflow'
|
|
76
|
+
code: |
|
|
77
|
+
- name: Install dependencies
|
|
78
|
+
run: |
|
|
79
|
+
# Ensure Composer is on a patched version before running install.
|
|
80
|
+
# Composer 2.9.8+ fixes CVE-2026-45793 (GITHUB_TOKEN disclosure with new ghs_ format)
|
|
81
|
+
composer self-update --2 2.9.8
|
|
82
|
+
composer install --no-interaction --prefer-dist
|
|
83
|
+
- language: yaml
|
|
84
|
+
label: 'Use shivammathur/setup-php with Composer version pinned to patched release'
|
|
85
|
+
code: |
|
|
86
|
+
- name: Set up PHP and Composer
|
|
87
|
+
uses: shivammathur/setup-php@v2
|
|
88
|
+
with:
|
|
89
|
+
php-version: '8.3'
|
|
90
|
+
tools: composer:2.9.8 # Pin to patched version fixing CVE-2026-45793
|
|
91
|
+
env:
|
|
92
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
93
|
+
prevention:
|
|
94
|
+
- 'Always pin Composer to a specific patched version in CI (e.g., `composer:2.9.8` in setup-php tools). Do not rely on "latest" which may ship a vulnerable version during the rollout window.'
|
|
95
|
+
- 'Audit your workflow logs for lines matching `contains invalid characters` — these indicate a credential has been disclosed to the log.'
|
|
96
|
+
- 'Be aware that `shivammathur/setup-php` auto-registers `GITHUB_TOKEN` into Composer auth by default; upgrading to a patched Composer is the only reliable mitigation.'
|
|
97
|
+
- 'For repositories that do not need Composer to make authenticated GitHub API calls, you can disable auto-registration: `COMPOSER_AUTH: ''{}''` in the env block prevents auto-injection.'
|
|
98
|
+
docs:
|
|
99
|
+
- url: 'https://github.com/advisories/GHSA-f9f8-rm49-7jv2'
|
|
100
|
+
label: 'GHSA-f9f8-rm49-7jv2 — Composer leaks GITHUB_TOKEN in CI logs (CVE-2026-45793)'
|
|
101
|
+
- url: 'https://github.com/composer/composer/security/advisories/GHSA-f9f8-rm49-7jv2'
|
|
102
|
+
label: 'composer/composer security advisory — GHSA-f9f8-rm49-7jv2 details and affected versions'
|
|
103
|
+
- url: 'https://github.blog/changelog/2026-04-24-notice-about-upcoming-new-format-for-github-app-installation-tokens/'
|
|
104
|
+
label: 'GitHub Changelog — Notice about new format for GitHub App installation tokens (April 2026)'
|
|
105
|
+
- url: 'https://github.com/composer/composer/issues/12849'
|
|
106
|
+
label: 'composer/composer#12849 — New format for GitHub Tokens (upstream issue report)'
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
id: permissions-auth-075
|
|
2
|
+
title: 'OIDC token missing `repository_custom_property_*` claim — custom property must be defined at org level, not just repo level'
|
|
3
|
+
category: permissions-auth
|
|
4
|
+
severity: silent-failure
|
|
5
|
+
tags:
|
|
6
|
+
- OIDC
|
|
7
|
+
- custom-properties
|
|
8
|
+
- trust-policy
|
|
9
|
+
- AWS
|
|
10
|
+
- Azure
|
|
11
|
+
- claim-missing
|
|
12
|
+
- org-level
|
|
13
|
+
- AccessDenied
|
|
14
|
+
- cloud-auth
|
|
15
|
+
patterns:
|
|
16
|
+
- regex: 'repository_custom_property'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
- regex: '(?:AccessDenied|is not authorized).+sts:AssumeRoleWithWebIdentity'
|
|
19
|
+
flags: 'i'
|
|
20
|
+
- regex: 'OpenIDConnectTokenVerificationFailed|sub.*claim.*condition.*failed|conditions.*not.*met'
|
|
21
|
+
flags: 'i'
|
|
22
|
+
error_messages:
|
|
23
|
+
- 'Error: User: arn:aws:sts::ACCOUNT:assumed-role/... is not authorized to perform: sts:AssumeRoleWithWebIdentity'
|
|
24
|
+
- 'Could not assume role with OIDC: Not authorized to perform sts:AssumeRoleWithWebIdentity'
|
|
25
|
+
- 'ClientAssertionCredential authentication failed: AADSTS70021'
|
|
26
|
+
- 'Error: No federated identity credential with issuer https://token.actions.githubusercontent.com matches'
|
|
27
|
+
root_cause: |
|
|
28
|
+
GitHub Actions OIDC tokens gained support for `repository_custom_property_*` claims in
|
|
29
|
+
March 2026 (GA April 2, 2026 changelog). These claims allow trust policies on AWS, Azure,
|
|
30
|
+
and other cloud providers to filter access based on how a repository is classified within
|
|
31
|
+
an organization (e.g., `repository_custom_property_environment: production`).
|
|
32
|
+
|
|
33
|
+
However, there is a critical prerequisite that is not prominently documented:
|
|
34
|
+
**custom property definitions must be created at the organization level** — not just at the
|
|
35
|
+
repository level — for the corresponding claims to appear in OIDC tokens.
|
|
36
|
+
|
|
37
|
+
Specifically:
|
|
38
|
+
1. The custom property must be defined in the organization's custom properties schema
|
|
39
|
+
(Settings → Custom properties at the org level).
|
|
40
|
+
2. The property must be set/assigned a value for the repository in question.
|
|
41
|
+
|
|
42
|
+
When a property is created only at the **repository level** (via repo-specific custom
|
|
43
|
+
properties without an org-level schema entry), or when the property exists at the org
|
|
44
|
+
level but has no value set for the specific repository, the `repository_custom_property_*`
|
|
45
|
+
claim is simply **absent** from the OIDC token rather than being set to null or an empty
|
|
46
|
+
string.
|
|
47
|
+
|
|
48
|
+
Trust policies that include a condition requiring a specific `repository_custom_property_*`
|
|
49
|
+
claim evaluate to no-match and deny authentication. The cloud provider returns a generic
|
|
50
|
+
`AccessDenied` / `is not authorized` error with no indication that the claim is missing.
|
|
51
|
+
The workflow author sees a cloud authentication failure with no clear link to the OIDC
|
|
52
|
+
token contents.
|
|
53
|
+
|
|
54
|
+
This is a silent configuration error: the workflow appears to run normally until the
|
|
55
|
+
authentication step fails, and there is no warning during workflow setup or YAML validation
|
|
56
|
+
that the property is misconfigured.
|
|
57
|
+
fix: |
|
|
58
|
+
**Step 1: Verify the OIDC token contents.**
|
|
59
|
+
Add a debug step to print the OIDC token claims before the cloud auth step:
|
|
60
|
+
|
|
61
|
+
```yaml
|
|
62
|
+
- name: Debug OIDC claims
|
|
63
|
+
run: |
|
|
64
|
+
TOKEN=$(curl -sS -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
|
|
65
|
+
"$ACTIONS_ID_TOKEN_REQUEST_URL" | jq -r '.value')
|
|
66
|
+
echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq .
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
If `repository_custom_property_*` claims are absent, the property is not defined at the
|
|
70
|
+
org level or has no value for this repository.
|
|
71
|
+
|
|
72
|
+
**Step 2: Define the custom property at the organization level.**
|
|
73
|
+
Navigate to the GitHub organization → Settings → Custom properties and create the property
|
|
74
|
+
definition there. Repository-level-only properties do NOT produce OIDC claims.
|
|
75
|
+
|
|
76
|
+
**Step 3: Assign a value to the property for the target repository.**
|
|
77
|
+
After creating the org-level property definition, explicitly set the value for each
|
|
78
|
+
repository that needs the claim by going to the repository's Settings → Custom properties
|
|
79
|
+
or using the REST API.
|
|
80
|
+
|
|
81
|
+
**If you cannot use org-level properties:** Fall back to other OIDC sub-claim conditions
|
|
82
|
+
such as `repository`, `ref`, `environment`, or `job_workflow_ref` which are always present
|
|
83
|
+
in Actions OIDC tokens.
|
|
84
|
+
fix_code:
|
|
85
|
+
- language: yaml
|
|
86
|
+
label: 'Add OIDC debug step to inspect claims before cloud auth'
|
|
87
|
+
code: |
|
|
88
|
+
jobs:
|
|
89
|
+
deploy:
|
|
90
|
+
runs-on: ubuntu-latest
|
|
91
|
+
permissions:
|
|
92
|
+
id-token: write
|
|
93
|
+
contents: read
|
|
94
|
+
steps:
|
|
95
|
+
- name: Debug OIDC token claims (remove after debugging)
|
|
96
|
+
run: |
|
|
97
|
+
TOKEN=$(curl -sS \
|
|
98
|
+
-H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
|
|
99
|
+
"$ACTIONS_ID_TOKEN_REQUEST_URL" | jq -r '.value')
|
|
100
|
+
# Print decoded payload (base64url decode the second segment)
|
|
101
|
+
echo "$TOKEN" | cut -d. -f2 | \
|
|
102
|
+
python3 -c "import sys,base64,json; data=sys.stdin.read().strip(); \
|
|
103
|
+
padded=data+'=='*((-len(data))%4); \
|
|
104
|
+
print(json.dumps(json.loads(base64.urlsafe_b64decode(padded)), indent=2))"
|
|
105
|
+
|
|
106
|
+
- name: Configure AWS credentials via OIDC
|
|
107
|
+
uses: aws-actions/configure-aws-credentials@v4
|
|
108
|
+
with:
|
|
109
|
+
role-to-assume: arn:aws:iam::ACCOUNT:role/my-role
|
|
110
|
+
aws-region: us-east-1
|
|
111
|
+
- language: yaml
|
|
112
|
+
label: 'AWS trust policy condition using repository_custom_property_* (requires org-level property)'
|
|
113
|
+
code: |
|
|
114
|
+
# AWS IAM trust policy condition requiring a custom property claim.
|
|
115
|
+
# PREREQUISITE: The property must be defined at org level AND set on the repository.
|
|
116
|
+
# If the claim is absent, AWS returns AccessDenied with no indication of missing claim.
|
|
117
|
+
{
|
|
118
|
+
"Condition": {
|
|
119
|
+
"StringEquals": {
|
|
120
|
+
"token.actions.githubusercontent.com:repository_custom_property_environment": "production"
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
prevention:
|
|
125
|
+
- 'Always create custom property definitions at the **organization level** (Org Settings → Custom properties), not just at the repository level. Repo-level-only properties do NOT appear in OIDC tokens.'
|
|
126
|
+
- 'After creating an org-level property, explicitly set a value for each target repository. A property defined but not assigned a value for a repo is also absent from the OIDC token.'
|
|
127
|
+
- 'Before deploying trust policies that use `repository_custom_property_*` conditions, verify the claim is present in the OIDC token by printing decoded token claims in a debug step.'
|
|
128
|
+
- 'When trust policy conditions reference `repository_custom_property_*`, always have a fallback monitoring alert for `AccessDenied` / `is not authorized` errors to catch misconfigured or unset properties quickly.'
|
|
129
|
+
docs:
|
|
130
|
+
- url: 'https://github.com/github/docs/issues/43779'
|
|
131
|
+
label: 'github/docs#43779 — custom properties docs gap: org-level definition required for OIDC claims'
|
|
132
|
+
- url: 'https://docs.github.com/en/actions/security-for-github-actions/security-hardening-your-deployments/about-security-hardening-with-openid-connect#defining-trust-conditions-on-cloud-roles-using-oidc-claims'
|
|
133
|
+
label: 'GitHub Docs — About security hardening with OIDC: defining trust conditions using claims'
|
|
134
|
+
- url: 'https://github.blog/changelog/2026-04-02-github-actions-early-april-2026-updates/'
|
|
135
|
+
label: 'GitHub Changelog — April 2026: Actions OIDC tokens now support repository custom properties'
|
|
136
|
+
- url: 'https://docs.github.com/en/organizations/managing-organization-settings/managing-custom-properties-for-repositories-in-your-organization'
|
|
137
|
+
label: 'GitHub Docs — Managing custom properties for repositories in your organization'
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
id: runner-environment-227
|
|
2
|
+
title: 'Bash script handler unquoted path breaks job hooks and run steps in directories with spaces'
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- bash
|
|
7
|
+
- hooks
|
|
8
|
+
- spaces
|
|
9
|
+
- path
|
|
10
|
+
- job-hooks
|
|
11
|
+
- self-hosted
|
|
12
|
+
- script-handler
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: 'bash: .+: No such file or directory'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
- regex: 'ACTIONS_RUNNER_HOOK_JOB_STARTED.+No such file'
|
|
17
|
+
flags: 'i'
|
|
18
|
+
- regex: 'bash.*--noprofile.*--norc.*-e.*-o pipefail'
|
|
19
|
+
flags: 'i'
|
|
20
|
+
error_messages:
|
|
21
|
+
- 'bash: /Volumes/My: No such file or directory'
|
|
22
|
+
- 'bash: Shared Files/hook.sh: No such file or directory'
|
|
23
|
+
- 'Error: Process completed with exit code 127.'
|
|
24
|
+
root_cause: |
|
|
25
|
+
In the GitHub Actions runner source code, the default argument template for the bash
|
|
26
|
+
shell handler (`ScriptHandlerHelpers.cs`) does not quote the script path placeholder:
|
|
27
|
+
|
|
28
|
+
_defaultArguments["bash"] = "--noprofile --norc -e -o pipefail {0}";
|
|
29
|
+
|
|
30
|
+
When `{0}` is replaced with a path containing spaces — such as
|
|
31
|
+
`/Volumes/My Shared Files/hook.sh` — the resulting command becomes:
|
|
32
|
+
|
|
33
|
+
bash --noprofile --norc -e -o pipefail /Volumes/My Shared Files/hook.sh
|
|
34
|
+
|
|
35
|
+
Bash treats this as three separate arguments: `/Volumes/My`, `Shared`, and
|
|
36
|
+
`Files/hook.sh`. The first token is not a valid path, so bash exits with
|
|
37
|
+
"No such file or directory" and exit code 127.
|
|
38
|
+
|
|
39
|
+
By contrast, the PowerShell and cmd templates DO quote the path:
|
|
40
|
+
_defaultArguments["pwsh"] = "-command \"& '{0}'\"";
|
|
41
|
+
_defaultArguments["powershell"] = "-command \". '{0}'\"";
|
|
42
|
+
_defaultArguments["cmd"] = "/D /E:ON /V:OFF /S /C \"CALL \"{0}\"\"";
|
|
43
|
+
|
|
44
|
+
Only bash and sh are affected. The affected scenarios include:
|
|
45
|
+
- Job hooks (ACTIONS_RUNNER_HOOK_JOB_STARTED, ACTIONS_RUNNER_HOOK_JOB_COMPLETED)
|
|
46
|
+
when the hook script resides in a directory whose path contains spaces — a common
|
|
47
|
+
case on macOS with Tart VMs that mount shared directories at
|
|
48
|
+
`/Volumes/My Shared Files/`.
|
|
49
|
+
- `run:` steps where the runner's _work directory path contains spaces (less common
|
|
50
|
+
but possible on custom self-hosted runner installations).
|
|
51
|
+
|
|
52
|
+
The bug affects all released runner versions (no version introduced it — the
|
|
53
|
+
template has always been unquoted). A fix was proposed in the issue but had not
|
|
54
|
+
shipped as of the issue filing date.
|
|
55
|
+
fix: |
|
|
56
|
+
Workaround: avoid spaces in the path to hook scripts and runner working directories.
|
|
57
|
+
|
|
58
|
+
1. Move hook scripts to a path with no spaces (e.g., `/opt/runner-hooks/hook.sh`
|
|
59
|
+
instead of `/Volumes/My Shared Files/hooks/hook.sh`).
|
|
60
|
+
Set ACTIONS_RUNNER_HOOK_JOB_STARTED=/opt/runner-hooks/job-started.sh in the
|
|
61
|
+
runner environment (`.env` file or system environment).
|
|
62
|
+
|
|
63
|
+
2. On macOS with Tart VMs, use a symlink from a space-free path to the shared
|
|
64
|
+
volume's hook script:
|
|
65
|
+
ln -s "/Volumes/My Shared Files/hook.sh" /opt/hooks/job-started.sh
|
|
66
|
+
Set the env var to the symlink path.
|
|
67
|
+
|
|
68
|
+
3. Wrap the script invocation in a no-space wrapper script that calls the real path.
|
|
69
|
+
|
|
70
|
+
There is no supported way to override the bash argument template at the user level.
|
|
71
|
+
The permanent fix requires a runner source code change (quoting `{0}`).
|
|
72
|
+
fix_code:
|
|
73
|
+
- language: bash
|
|
74
|
+
label: 'Create a space-free symlink to the actual hook script'
|
|
75
|
+
code: |
|
|
76
|
+
# On macOS: create a symlink from a no-space path to the hook in the shared volume
|
|
77
|
+
mkdir -p /opt/runner-hooks
|
|
78
|
+
ln -sf "/Volumes/My Shared Files/hooks/job-started.sh" /opt/runner-hooks/job-started.sh
|
|
79
|
+
ln -sf "/Volumes/My Shared Files/hooks/job-completed.sh" /opt/runner-hooks/job-completed.sh
|
|
80
|
+
|
|
81
|
+
# In the runner's .env file (located in the runner install directory):
|
|
82
|
+
# ACTIONS_RUNNER_HOOK_JOB_STARTED=/opt/runner-hooks/job-started.sh
|
|
83
|
+
# ACTIONS_RUNNER_HOOK_JOB_COMPLETED=/opt/runner-hooks/job-completed.sh
|
|
84
|
+
|
|
85
|
+
- language: bash
|
|
86
|
+
label: 'Move hook scripts to a space-free directory at runner setup time'
|
|
87
|
+
code: |
|
|
88
|
+
# Preferred: install hook scripts in a path with no spaces from the start
|
|
89
|
+
sudo mkdir -p /usr/local/runner-hooks
|
|
90
|
+
sudo cp ./hooks/job-started.sh /usr/local/runner-hooks/
|
|
91
|
+
sudo chmod +x /usr/local/runner-hooks/job-started.sh
|
|
92
|
+
|
|
93
|
+
# In .env:
|
|
94
|
+
# ACTIONS_RUNNER_HOOK_JOB_STARTED=/usr/local/runner-hooks/job-started.sh
|
|
95
|
+
prevention:
|
|
96
|
+
- 'Always install runner hook scripts under paths with no spaces — use /opt/, /usr/local/, or /home/<user>/ prefixes.'
|
|
97
|
+
- 'On macOS Tart VM hosts, avoid mounting shared directories with spaces in the volume name; use snake_case or hyphenated names (e.g., My_Shared_Files).'
|
|
98
|
+
- 'After configuring ACTIONS_RUNNER_HOOK_JOB_STARTED, test the hook manually by running bash --noprofile --norc -e -o pipefail <path> to catch path issues before the runner is live.'
|
|
99
|
+
- 'If the runner work directory path contains spaces, reconfigure the runner with a clean no-space _work path.'
|
|
100
|
+
docs:
|
|
101
|
+
- url: 'https://github.com/actions/runner/issues/4404'
|
|
102
|
+
label: 'actions/runner#4404 — Bash script handler does not quote the script path'
|
|
103
|
+
- url: 'https://docs.github.com/en/actions/hosting-your-own-runners/managing-self-hosted-runners/running-scripts-before-or-after-a-job'
|
|
104
|
+
label: 'GitHub Docs — Running scripts before or after a job (job hooks)'
|
|
105
|
+
- url: 'https://github.com/actions/runner/blob/main/src/Runner.Worker/Handlers/ScriptHandlerHelpers.cs'
|
|
106
|
+
label: 'actions/runner source — ScriptHandlerHelpers.cs (unquoted bash template)'
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
id: runner-environment-228
|
|
2
|
+
title: 'setup-node@v6 cache detection fails when .yarnrc.yml contains approvedGitRepositories (yarn 4.14+)'
|
|
3
|
+
category: runner-environment
|
|
4
|
+
severity: error
|
|
5
|
+
tags:
|
|
6
|
+
- setup-node
|
|
7
|
+
- yarn
|
|
8
|
+
- cache
|
|
9
|
+
- yarnrc
|
|
10
|
+
- approvedGitRepositories
|
|
11
|
+
- yarn-4
|
|
12
|
+
- cache-detection
|
|
13
|
+
patterns:
|
|
14
|
+
- regex: 'Unrecognized or legacy configuration settings found: approvedGitRepositories'
|
|
15
|
+
flags: 'i'
|
|
16
|
+
- regex: "The 'yarn config get cacheFolder' command failed with exit code"
|
|
17
|
+
flags: 'i'
|
|
18
|
+
- regex: 'yarn config get cacheFolder.*exit code: 1'
|
|
19
|
+
flags: 'i'
|
|
20
|
+
error_messages:
|
|
21
|
+
- "Usage Error: Unrecognized or legacy configuration settings found: approvedGitRepositories - run \"yarn config -v\" to see the list of settings supported in Yarn"
|
|
22
|
+
- "Error: The 'yarn config get cacheFolder' command failed with exit code: 1"
|
|
23
|
+
root_cause: |
|
|
24
|
+
Yarn 4.14 introduced the `approvedGitRepositories` security setting in `.yarnrc.yml`.
|
|
25
|
+
This key enforces an allowlist of Git repository URLs that yarn is permitted to fetch
|
|
26
|
+
packages from, blocking unapproved source URLs with:
|
|
27
|
+
|
|
28
|
+
"Request to '<url>' has been blocked because it doesn't match any of the
|
|
29
|
+
patterns in 'approvedGitRepositories'"
|
|
30
|
+
|
|
31
|
+
However, any `.yarnrc.yml` key that is unrecognized or deprecated by the currently
|
|
32
|
+
installed version of yarn causes yarn to abort ALL config commands with:
|
|
33
|
+
|
|
34
|
+
"Usage Error: Unrecognized or legacy configuration settings found: approvedGitRepositories"
|
|
35
|
+
|
|
36
|
+
The `actions/setup-node@v6` action detects the yarn cache folder path by executing
|
|
37
|
+
`yarn config get cacheFolder` early in the action — before any Node.js version is
|
|
38
|
+
installed and before yarn itself is updated. If the runner's bundled yarn version is
|
|
39
|
+
older than 4.14, it does not recognize `approvedGitRepositories` and aborts.
|
|
40
|
+
|
|
41
|
+
The action catches the non-zero exit code and surfaces the error:
|
|
42
|
+
"Error: The 'yarn config get cacheFolder' command failed with exit code: 1"
|
|
43
|
+
|
|
44
|
+
This failure prevents setup-node from resolving the yarn cache path, breaking the
|
|
45
|
+
entire step. Users frequently observe this when:
|
|
46
|
+
- Upgrading to yarn 4.14+ and adding `approvedGitRepositories` to `.yarnrc.yml`
|
|
47
|
+
- Running on hosted runners where the system yarn version is older than 4.14
|
|
48
|
+
- Running on self-hosted runners with a frozen yarn version
|
|
49
|
+
|
|
50
|
+
The root issue is that yarn's unrecognized-key validation is global — it aborts even
|
|
51
|
+
read-only config queries when any single key is unrecognized, even if that key is not
|
|
52
|
+
related to the query.
|
|
53
|
+
fix: |
|
|
54
|
+
Option 1 — Remove cache:yarn from setup-node (safest immediate fix).
|
|
55
|
+
Set cache: '' or omit the cache: input entirely. Manage yarn caching with a separate
|
|
56
|
+
actions/cache step pointed directly at the yarn cache directory.
|
|
57
|
+
|
|
58
|
+
Option 2 — Pin the yarn version in the runner environment to match .yarnrc.yml.
|
|
59
|
+
Ensure the yarn version on the runner is >= 4.14.0 so it recognizes
|
|
60
|
+
approvedGitRepositories before setup-node calls yarn config get.
|
|
61
|
+
|
|
62
|
+
Option 3 — Upgrade setup-node to a version that handles this gracefully.
|
|
63
|
+
Track actions/setup-node#1534 for a fix that makes cache folder detection resilient
|
|
64
|
+
to yarn config validation errors.
|
|
65
|
+
|
|
66
|
+
Option 4 — Use a separate cache step instead of setup-node's built-in cache.
|
|
67
|
+
This avoids the setup-node yarn version probe entirely.
|
|
68
|
+
fix_code:
|
|
69
|
+
- language: yaml
|
|
70
|
+
label: 'Remove cache:yarn from setup-node and use a standalone cache step'
|
|
71
|
+
code: |
|
|
72
|
+
- uses: actions/setup-node@v4
|
|
73
|
+
with:
|
|
74
|
+
node-version: 20
|
|
75
|
+
# Do NOT set cache: yarn — it triggers yarn config get cacheFolder
|
|
76
|
+
# which fails when approvedGitRepositories is in .yarnrc.yml
|
|
77
|
+
|
|
78
|
+
# Manage yarn cache manually
|
|
79
|
+
- name: Get yarn cache directory
|
|
80
|
+
id: yarn-cache-dir
|
|
81
|
+
run: echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT
|
|
82
|
+
|
|
83
|
+
- uses: actions/cache@v4
|
|
84
|
+
with:
|
|
85
|
+
path: ${{ steps.yarn-cache-dir.outputs.dir }}
|
|
86
|
+
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
|
|
87
|
+
restore-keys: |
|
|
88
|
+
${{ runner.os }}-yarn-
|
|
89
|
+
|
|
90
|
+
- run: yarn install --immutable
|
|
91
|
+
|
|
92
|
+
- language: yaml
|
|
93
|
+
label: 'Pin yarn version to 4.14+ before setup-node runs'
|
|
94
|
+
code: |
|
|
95
|
+
- name: Enable corepack with matching yarn version
|
|
96
|
+
run: |
|
|
97
|
+
corepack enable
|
|
98
|
+
corepack prepare yarn@4.14.1 --activate
|
|
99
|
+
|
|
100
|
+
- uses: actions/setup-node@v4
|
|
101
|
+
with:
|
|
102
|
+
node-version: 20
|
|
103
|
+
cache: yarn # Now safe — yarn 4.14+ recognizes approvedGitRepositories
|
|
104
|
+
prevention:
|
|
105
|
+
- 'After adding any new key to .yarnrc.yml, verify it is recognized by running yarn config -v locally and confirming the key appears in the supported list.'
|
|
106
|
+
- 'When using setup-node cache:yarn with yarn 4+, pin the yarn version via packageManager in package.json or via corepack before the setup-node step.'
|
|
107
|
+
- 'Monitor actions/setup-node release notes for fixes to yarn cache detection resilience (issue #1534).'
|
|
108
|
+
- 'If .yarnrc.yml uses security features added in a recent yarn release, document the minimum required yarn version in your repo README and CI setup guide.'
|
|
109
|
+
docs:
|
|
110
|
+
- url: 'https://github.com/actions/setup-node/issues/1534'
|
|
111
|
+
label: 'actions/setup-node#1534 — Problem with yarn v4.14 config approvedGitRepositories'
|
|
112
|
+
- url: 'https://github.com/yarnpkg/berry/issues/7108'
|
|
113
|
+
label: 'yarnpkg/berry#7108 — approvedGitRepositories config key tracking issue'
|
|
114
|
+
- url: 'https://yarnpkg.com/configuration/yarnrc#approvedGitRepositories'
|
|
115
|
+
label: 'Yarn docs — approvedGitRepositories configuration'
|
|
116
|
+
- url: 'https://github.com/actions/setup-node/blob/main/docs/advanced-usage.md#caching-packages-data'
|
|
117
|
+
label: 'setup-node — Advanced usage: caching packages data'
|