Autopsy 09 · CI/CD trust chain | Comic Strip 7 The Self Trust Bot

Would Ethosure have caught this?  The Claude Code GitHub Action permission bypass

A malicious GitHub App could trigger Anthropic’s Claude Code Action, exploit a prompt injection through a crafted issue, steal workflow credentials, obtain an OIDC token, and push malicious code to the action’s own source repository. The bypass was one function: <code>checkWritePermissions</code> unconditionally trusted any actor whose identity ended in <code>[bot]</code>. Policy-as-code on CI/CD closes the confused-deputy path even when the agent is the deputy.

Series: Would Ethosure have caught this?
Root cause: CI/CD confused deputy via unchecked bot-identity trust
Coding-ethos policy: ci.actor.bot_identity_not_authoritative

The incident

RyotaK of GMO Flatt Security reported the vulnerability to Anthropic on January 12, 2026. Public disclosure occurred on June 1, 2026 as CVE-2025-66032 with GitHub advisory GHSA-xq4m-mc3c-vvg3, rated CVSS 4.0 at 7.8. The bypass sat in a single function: the checkWritePermissions helper unconditionally trusted any actor whose identity ended in [bot], regardless of the actor’s actual repository permissions. A malicious GitHub App bot could then trigger the agent by opening an issue, use prompt injection through crafted issue content to redirect the agent, harvest workflow credentials, obtain an OIDC token, exchange it for a privileged GitHub App installation token, and push malicious code back to the anthropics/claude-code-action source repository itself. Anthropic patched the primary bypass within four days of the initial report; hardened versions shipped as claude-code-action v1.0.94 and @anthropic-ai/claude-code v1.0.93. Aikido Security later identified at least five Fortune 500 companies running configurations that matched the same vulnerable pattern.

The applicable coding-ethos policy

Policy packModel / provider governance pack (with regulated-enterprise base overlay)
Principleleast-authority-in-the-pipeline
Control familyPolicy-as-code on CI/CD trust chain

Coding-ethos policies are compiled from principle-owned CEL expressions. Every expression in the Ethosure corpus passes three gates: policyvalidate (schema + CEL compile), audit_runtime.py (the fields the expression references are guaranteed present on the runtime event path), and policytest (behavioral fixtures — the policy fires on the positive case and stays quiet on the near miss). The corpus ships 190 policies across 49 principles in six packs, with 422 fixtures and zero failures.

READ  Would Ethosure have caught this? Sysdig’s LLM-driven post-exploitation in four pivots

Walkthrough

  1. The vulnerability is small and instructive. One helper function decided who could push code from a GitHub Actions workflow, and it decided by string-matching the actor’s name. Anything ending in [bot] was treated as write-authorised. That is not authentication. That is a naming convention doing security.
  2. ci.actor.bot_identity_not_authoritative fires on PreToolUse for any CI-triggered agent action whose write authorisation is derived from an actor identity ending in [bot] or matching a naming convention rather than from an explicit permissions check against the target repository. It returns severity block. The policy pushes the check to where it belongs: the repository’s permissions model.
  3. ci.workflow_input.no_untrusted_injection_into_prompt is the second expression. GitHub issue bodies, PR titles, and comment content are attacker-controllable inputs. The expression fires on any agent runtime that ingests GitHub metadata into its prompt without a sanitisation lease recorded in the evidence layer. This is the general form of the prompt-injection surface RyotaK used.
  4. ci.oidc.token_exchange_requires_workload_binding is the third. An OIDC token exchanged for a privileged GitHub App installation token should not succeed unless the workload identity matches the target scope. The expression fires on any token exchange whose destination scope exceeds the workload’s declared authority. This blocks the last step of the chain — push to the action’s own source repository.

Policy YAML

# Pack: model-provider-governance (v2)
# Inherits: regulated-enterprise-base
principles:
  - id: least-authority-in-the-pipeline
    title: Least Authority in the Pipeline
    directive: >-
      CI/CD agents must not derive write authority from identity naming conventions,
      must not ingest untrusted repository metadata into prompts without a sanitisation
      lease, and must not exchange OIDC tokens for scopes beyond the workload's
      declared authority.
    policy:
      expressions:
        - id: ci.actor.bot_identity_not_authoritative
          scope: agent_action
          severity: block
          event: PreToolUse
          principle_ids:
            - least-authority-in-the-pipeline
            - security-by-design
          skill_id: safe-ci-workflow
          when: >
            runtime.kind == "ci_workflow"
            && tool_call.category in ["git_push", "pr_create", "pr_merge",
                                       "release_publish"]
            && actor.identity.matches(".*\\[bot\\]$")
            && !repository.permissions.exists(p,
                 p.principal == actor.identity && p.write == true)
          message: >-
            Bot-suffixed identity is being treated as write-authorised without a matching
            repository permissions entry.
          advice: >-
            Replace the naming-convention check with a lookup against the repository's
            permissions model. Naming conventions are not authentication.

        - id: ci.workflow_input.no_untrusted_injection_into_prompt
          scope: agent_action
          severity: block
          event: PreToolUse
          principle_ids:
            - least-authority-in-the-pipeline
            - radical-visibility
          when: >
            runtime.kind == "ci_workflow"
            && prompt.assembled_from.exists(s,
                 s.source in ["issue_body", "pr_title", "pr_body",
                              "comment_body", "commit_message"]
                 && !s.sanitisation_lease.exists(l, l.state == "active"))
          message: >-
            Agent prompt is being assembled from untrusted repository metadata with no
            recorded sanitisation lease.
          advice: >-
            Route the metadata through the prompt-sanitisation pipeline and record the lease.
            This closes the prompt-injection primitive that CVE-2025-66032 exploited.

        - id: ci.oidc.token_exchange_requires_workload_binding
          scope: agent_action
          severity: block
          event: PreToolUse
          principle_ids:
            - least-authority-in-the-pipeline
            - security-by-design
          when: >
            tool_call.category == "oidc_token_exchange"
            && exchange.target_scope.exceeds(workload.declared_authority)
          message: >-
            OIDC token exchange requests a scope beyond the workload's declared authority.
          advice: >-
            Bind the exchange to the workload's declared scope. A privileged installation token
            for the action's own source repository should never be reachable from the action.

Assurance boundary. Fixtures prove that synthetic positive cases fire and near-misses stay quiet. They do not establish production alert rates. DLP-backed controls depend on upstream detection, shell-command regexes are defense in depth rather than a sandbox boundary, and regulatory tags are engineering aids requiring counsel review.

READ  What is CEL? The "Calculator" That Makes Agent Decisions Deterministic

Where this policy lives in the corpus

The Ethosure importable corpus ships six coding-ethos packs. Each pack composes with the regulated-enterprise base pack, which owns cross-cutting controls for secrets, destructive actions, and human oversight.

  • Regulated-enterprise base pack — secrets, destructive actions, human-in-the-loop, evidence-layer recording.
  • Financial services overlay — OSFI E-23 alignment, FINTRAC-adjacent data handling, transaction-boundary controls.
  • Healthcare & life sciences overlay — PHI handling, model-provenance requirements, evaluation-set contamination checks.
  • Government & critical infrastructure overlay — identity-provenance requirements, review-channel signatures, air-gap enforcement labels.
  • Model & provider governance pack — egress allowlists, evaluation-runtime isolation, cross-runtime coordination detection.
  • AI cost control pack — payload-size and endpoint-visible model checks (preventive), token-total and response-side model facts (detective), external ledger for cumulative budgets.

Published September 3, 2026 · Ethosure · Part of the Autopsy Series.

Subscribe to Ethosure's Newsletter to get monthly updates on AI Governance

We don’t spam! Read our privacy policy for more info.