diff --git a/errors/permissions-auth/reusable-workflow-nested-job-exceeds-caller-permissions.yml b/errors/permissions-auth/reusable-workflow-nested-job-exceeds-caller-permissions.yml new file mode 100644 index 0000000..5cccfa7 --- /dev/null +++ b/errors/permissions-auth/reusable-workflow-nested-job-exceeds-caller-permissions.yml @@ -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' diff --git a/errors/runner-environment/ubuntu-dotnet-dev-certs-trust-eventsource-mismatch.yml b/errors/runner-environment/ubuntu-dotnet-dev-certs-trust-eventsource-mismatch.yml new file mode 100644 index 0000000..ca6c0eb --- /dev/null +++ b/errors/runner-environment/ubuntu-dotnet-dev-certs-trust-eventsource-mismatch.yml @@ -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' diff --git a/errors/runner-environment/ubuntu-toolcache-ruby-gems-world-writable.yml b/errors/runner-environment/ubuntu-toolcache-ruby-gems-world-writable.yml new file mode 100644 index 0000000..7169e48 --- /dev/null +++ b/errors/runner-environment/ubuntu-toolcache-ruby-gems-world-writable.yml @@ -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'