Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
id: permissions-auth-123
title: 'Reusable workflow nested job requests higher permission than caller grants — workflow validation fails'
category: permissions-auth
severity: error
tags:
- reusable-workflow
- permissions
- nested-job
- workflow-validation
- permissions-escalation
patterns:
- regex: 'The nested job .+ is requesting .+, but is only allowed'
flags: 'i'
- regex: 'Error calling workflow .+ The nested job'
flags: 'i'
error_messages:
- "Error calling workflow 'org/repo/.github/workflows/deploy.yml@branch'. The nested job 'build' is requesting 'packages: write', but is only allowed 'packages: read'."
- "The nested job 'deploy' is requesting 'contents: write', but is only allowed 'contents: read'."
root_cause: |
GitHub validates permissions at workflow parse time, not at runtime. If the caller workflow
explicitly restricts a permission (e.g., `packages: read` at the job or workflow level) and
the called reusable workflow's job block requests a higher permission (e.g., `packages: write`),
GitHub emits a validation error and prevents the workflow from running at all.

This validation happens unconditionally — even if the nested job has an `if: false` condition,
a `needs:` dependency that would prevent it from executing, or a concurrency group that would
cancel it. There is no way to guard against this with runtime conditions; the check is purely
structural.

The error arises most commonly when:
1. A caller workflow grants only the minimum permissions it needs (e.g., `packages: read` for
reading images), and calls a reusable workflow that also requires `packages: write` in a
different job for a different purpose.
2. A monorepo shares a reusable workflow across caller workflows with different permission
budgets, and one caller has a more restrictive permission set than the reusable workflow
declares.
fix: |
Choose one of these options:

Option 1 — Elevate the caller permission to match the called workflow's requirement:
Add `packages: write` (or the required permission) to the caller workflow's job-level
or workflow-level `permissions:` block so the nested job can request it.

Option 2 — Remove the explicit restrictive permission from the caller:
If the caller does not explicitly set `packages:` in its `permissions:` block at all,
GitHub defaults to granting whatever the called reusable workflow requests. Removing the
overly restrictive key avoids the parse-time conflict.

Option 3 — Split into separate reusable workflows:
Refactor so that read-only callers call a reusable workflow that only declares read
permissions, and write-capable callers call a separate workflow that declares write.
fix_code:
- language: yaml
label: 'Option 1 — Elevate caller permission to satisfy nested job requirement'
code: |
# Caller workflow — grant the permission the reusable job requires
jobs:
call-deploy:
permissions:
contents: read
packages: write # Required by the nested job in the reusable workflow
uses: ./.github/workflows/deploy.yml
- language: yaml
label: 'Option 2 — Omit the restrictive permission from the caller'
code: |
# Caller workflow — omit "packages:" entirely to let the called
# workflow declare its own permissions without a ceiling conflict.
jobs:
call-deploy:
permissions:
contents: read
# packages: read <-- Remove this line; let the called workflow control it
uses: ./.github/workflows/deploy.yml
prevention:
- 'Audit caller permissions before restricting them: check every reusable workflow you call
for its job-level permission declarations, then ensure none exceed your caller grants.'
- 'Use the minimum-permissions principle at the workflow level, not the job level, when
calling reusable workflows — this prevents unintended ceiling conflicts.'
- 'Add a CI step or linting rule (e.g., actionlint) that validates caller/callee permission
compatibility across your workflow files before merging.'
docs:
- url: 'https://docs.github.com/en/actions/security-for-github-actions/security-guides/automatic-token-authentication#permissions-for-the-github_token'
label: 'GitHub Docs: Permissions for the GITHUB_TOKEN'
- url: 'https://docs.github.com/en/actions/sharing-automations/reusing-workflows#supported-keywords-for-jobs-that-call-a-reusable-workflow'
label: 'GitHub Docs: Supported keywords for jobs that call a reusable workflow'
- url: 'https://github.com/actions/runner/issues/4151'
label: 'actions/runner#4151: needs/if should prevent errors about nested jobs violating permissions'
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
id: runner-environment-494
title: '`dotnet dev-certs https --trust` fails with EventSource ID mismatch on Ubuntu — exit code 4'
category: runner-environment
severity: error
tags:
- dotnet
- ubuntu
- dev-certs
- aspnetcore
- eventsource
- https
- certificate
patterns:
- regex: 'Exception in Command Processing for EventSource Dotnet-dev-certs'
flags: 'i'
- regex: 'Event WslWindowsTrustSucceeded was assigned event ID'
flags: 'i'
- regex: 'There was an error trusting the HTTPS developer certificate'
flags: 'i'
error_messages:
- "[0] ERROR: Exception in Command Processing for EventSource Dotnet-dev-certs: Event WslWindowsTrustSucceeded was assigned event ID 115 but 113 was passed to WriteEvent."
- "There was an error trusting the HTTPS developer certificate. It will be trusted by some clients but not by others."
- "Error: Process completed with exit code 4."
root_cause: |
The `dotnet dev-certs https --trust` command fails on Ubuntu 24.04 runner image versions
starting around 20260217.30.1 with an EventSource schema mismatch. The error message
"Event WslWindowsTrustSucceeded was assigned event ID 115 but 113 was passed to WriteEvent"
indicates that the .NET EventSource schema used to compile `dotnet dev-certs` no longer
matches the runtime EventSource registration in the running .NET host.

This regression was introduced when the Ubuntu 24.04 runner image updated its bundled
.NET SDK to a version where the `Dotnet-dev-certs` EventSource had incompatible event ID
assignments between the tool binary and the runtime. The command exits with code 4
(certificate trust failed), causing any workflow step running
`dotnet dev-certs https --trust` to fail.

On Linux, the `--trust` flag is less meaningful than on Windows or macOS because Linux
does not have a centralized system certificate trust store that .NET can update
non-interactively in a headless CI environment. In most Linux CI configurations,
the certificate is not actually trusted system-wide by this command.
fix: |
Option 1 (recommended for Linux CI) — Skip `dotnet dev-certs https --trust` on Linux:
The `--trust` step does not reliably work in Linux CI environments. Guard the step
with `if: runner.os != 'Linux'`, or remove it entirely from Linux workflows.

Option 2 — Add `continue-on-error: true` if the trust step is not strictly required:
If certificate trust is only needed on Windows or macOS, tolerate the failure:
`continue-on-error: true` on the dev-certs step for Linux runners.

Option 3 — Use ASPNETCORE environment variables to configure HTTPS without needing a
trusted dev certificate in CI.
fix_code:
- language: yaml
label: 'Skip --trust on Linux using OS guard'
code: |
jobs:
integration-test:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

- name: Setup dev certificates (skip --trust on Linux)
run: |
dotnet dev-certs https --clean
# --trust is unreliable in headless Linux CI — skip to avoid exit code 4
if [[ "$RUNNER_OS" != "Linux" ]]; then
dotnet dev-certs https --trust
fi
- language: yaml
label: 'Cross-platform — guard --trust with if condition'
code: |
jobs:
integration-test:
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4

- name: Clean dev certs
run: dotnet dev-certs https --clean

- name: Trust dev certs (Windows and macOS only)
if: runner.os != 'Linux'
run: dotnet dev-certs https --trust
prevention:
- 'Avoid relying on `dotnet dev-certs https --trust` in Linux CI — it does not integrate
with the system trust store and frequently breaks across .NET version upgrades.'
- 'Use OS-conditional steps (if: runner.os != ''Linux'') for certificate trust operations
that are only meaningful on Windows or macOS.'
- 'For ASP.NET Core integration tests on Linux, configure the test server to use HTTP or
provide a pre-trusted certificate via environment variables instead of dev-certs.'
docs:
- url: 'https://github.com/actions/runner-images/issues/13705'
label: 'actions/runner-images#13705: New Ubuntu runner image failing on DOTNET HTTPS dev cert --trust'
- url: 'https://github.com/dotnet/aspnetcore/issues/65391'
label: 'dotnet/aspnetcore#65391: Related upstream EventSource ID regression'
- url: 'https://aka.ms/dev-certs-trust'
label: 'Microsoft Docs: Trust the ASP.NET Core HTTPS development certificate'
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
id: runner-environment-493
title: 'Ubuntu runner toolcache Ruby gem directories world-writable — RubyGems insecure-removal error'
category: runner-environment
severity: error
tags:
- ubuntu
- ruby
- rubygems
- bundler
- toolcache
- permissions
- world-writable
patterns:
- regex: 'is world-writable and does not have the sticky bit set'
flags: 'i'
- regex: 'insecure to remove due to potential vulnerabilities'
flags: 'i'
- regex: 'hostedtoolcache/Ruby/.+/gems/.+ is world-writable'
flags: 'i'
error_messages:
- "/opt/hostedtoolcache/Ruby/4.0.1/x64/lib/ruby/gems/4.0.0/gems/erb-6.0.1 is world-writable and does not have the sticky bit set, making it insecure to remove due to potential vulnerabilities."
- "/opt/hostedtoolcache/Ruby/3.4.2/x64/lib/ruby/gems/3.4.0/gems/bundler-2.6.3 is world-writable and does not have the sticky bit set, making it insecure to remove due to potential vulnerabilities."
root_cause: |
Certain Ubuntu runner image versions (notably 20260201.15.1 on Ubuntu 24.04 and
20260201.24.1 on Ubuntu 22.04) provisioned the Ruby toolcache with 777 (world-writable)
permissions on gem directories under /opt/hostedtoolcache/Ruby/.

RubyGems and Bundler enforce a security policy that refuses to operate on world-writable
gem directories when performing cleanup or removal operations. When these tools detect
that the gem directory lacks the sticky bit and is world-writable, they emit this error
and abort, causing workflows that run gem cleanup, bundle exec, or other gem management
commands to fail.

The root cause is a provisioning script bug in those specific image versions that set
overly permissive directory permissions. The macOS runner toolcache was not affected
(uses 755 permissions). Subsequent image versions fixed the permissions to 755.
fix: |
The preferred fix is to let the runner image auto-update to a version that sets correct
permissions (755 or 775) on the toolcache gem directories.

As a workflow-level workaround, add a step before gem operations to fix permissions:

- name: Fix Ruby toolcache permissions
run: |
find /opt/hostedtoolcache/Ruby -type d -exec chmod o-w {} \;

Alternatively, use the ruby/setup-ruby action which installs Ruby from scratch with
correct permissions rather than relying on the pre-installed toolcache.
fix_code:
- language: yaml
label: 'Workaround — chmod toolcache gem dirs before gem operations'
code: |
jobs:
test:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

- name: Fix Ruby toolcache permissions (workaround for world-writable regression)
run: |
find /opt/hostedtoolcache/Ruby -type d -exec chmod o-w {} \; 2>/dev/null || true

- name: Install dependencies
run: bundle install

- name: Run tests
run: bundle exec rspec
- language: yaml
label: 'Preferred — install Ruby via ruby/setup-ruby instead of using toolcache'
code: |
jobs:
test:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

- uses: ruby/setup-ruby@v1
with:
ruby-version: '3.4'
bundler-cache: true

- name: Run tests
run: bundle exec rspec
prevention:
- 'Prefer using ruby/setup-ruby to install Ruby in CI rather than the pre-installed toolcache
— this ensures consistent permissions regardless of the runner image version.'
- 'Pin your Ruby workflows to the latest available image version or use auto-update policies
to pick up bug fixes like this permissions regression quickly.'
- 'Add a pre-flight check step that verifies Ruby gem directory permissions before running
bundle install in environments that rely on the toolcache.'
docs:
- url: 'https://github.com/actions/runner-images/issues/13647'
label: 'actions/runner-images#13647: Ubuntu-24.04 ships Rubygems directories that are world-writable'
- url: 'https://github.com/ruby/rubygems/issues/9284'
label: 'ruby/rubygems#9284: Tracking issue for world-writable toolcache gem directories'
- url: 'https://github.com/ruby/setup-ruby'
label: 'ruby/setup-ruby: Recommended action for installing Ruby in GitHub Actions'
Loading