An issue labeler should be able to misclassify an issue without gaining a way to publish software. On November 11, 2026, Depi disclosed a chain in OpenAI's Codex repository that crossed that boundary. The researchers report steering an agent through issue text, reaching runner secrets through exposed debugging pipes, and using cache poisoning and release-workflow behavior to obtain package-publishing authority.
Depi says OpenAI fixed the core bug. The report does not establish that a malicious Codex package reached users or identify a customer CLI version that resolves this CI chain. November 11 is the disclosure date; it is not an established exploitation or remediation date. General Analysis has not reproduced the attack.
If you run agents in GitHub Actions, trace what an untrusted issue can reach: commands on the runner, files restored by another job, and credentials used to publish. Start with the capabilities the task never needed.
A label choice does not need a shell#
If a workflow only classifies issue text, use a model request without tools and a fixed output catalog. That removes the model's command-execution path while retaining the useful classification task. A malicious issue may still distort the answer, so application code must decide which outputs are acceptable.
OpenAI's current issue-labeling script, inspected on November 12, supplies an empty tools list, disables tool selection, requests enum-constrained structured output, and validates returned labels locally. The workflow separates label suggestion from label application. This is an observable design pattern in the current source, not proof of when or how the entire disclosed chain was repaired.
For a hypothetical repository, keep the responsibilities this narrow:
| Job | Permitted work | Keep outside its authority |
|---|---|---|
| Classify the issue | Read the issue text and return values from a fixed catalog | Shell tools, repository writes, cache access, release credentials |
| Apply approved labels | Validate the catalog values and update the triggering issue | Model-selected repository or issue targets, executable output, release-triggering labels |
| Build and publish | Build an approved revision under the release policy | Triage output as code, untrusted cache contents, issue-controlled workflow definitions |
In this design, the model never chooses the repository, issue number, script path, or permission set used by the second job. Derive those from the trusted event context and reviewed workflow. Exclude any label that starts privileged automation from the classifier's catalog.
Keep output as data throughout the handoff. Quoting a shell variable addresses one parsing problem; it does not make a model-generated command safe to execute. GitHub's secure-use guidance recommends minimum job permissions and avoiding direct interpolation of untrusted values into scripts. A validator that accepts only known labels is a much smaller authority surface than a job that executes the model's final message.
See how your AI systems hold up under real attacks
General Analysis maps AI applications and agents, red teams prompts, retrieval, tools, MCP servers, browser actions, permissions, and business workflows, then turns findings into evidence your team can reproduce and retest.
Check cache access independently of repository permissions#
The disclosed cache path is historical. GitHub introduced read-only default-branch cache tokens for qualifying untrusted triggers on June 26. The rule depends on both the triggering event and the execution/cache context. Do not assume every trigger has identical access.
GitHub then made explicit cache-mode controls generally available on github.com on September 10. These controls are separate from the repository permissions assigned to GITHUB_TOKEN. Explicit write or write-only can override a low-trust event's read-only default.
For a triage workflow that needs no dependency cache, the useful setting is none. The following is a configuration fragment, not a complete workflow: insert the settings into reviewed jobs with their own steps. It deliberately grants no publishing authority.
Code source: illustrative fragment using GitHub's documented workflow and job cache syntax.
permissions: {}
cache-mode: none
jobs:
classify:
runs-on: ubuntu-latest
permissions:
contents: read
cache-mode: none
apply_labels:
needs: classify
runs-on: ubuntu-latest
permissions:
issues: write
cache-mode: none
Check every job override, including calls to reusable workflows. A top-level restriction is insufficient evidence if a job changes it. Remove unused cache steps, but retain the service-level restriction: omitting actions/cache alone does not express a denial of cache access.
Check the cache-step messages even when the job succeeds. GitHub documents that a disallowed cache operation logs a message and continues; a skipped restore becomes a cache miss. If release builds need caching, document which trusted jobs can populate it and which release jobs restore it. A cache key identifies an entry; it does not certify who was authorized to produce its contents.
When the task really needs an agent#
Code review or repair may require file access and commands. Give that workload its own execution review rather than expanding the classifier until it becomes a general agent.
OpenAI's Codex Action security document distinguishes command permissions from the privileges of the Codex process. It recommends narrow permission profiles alongside drop-sudo or a deliberately configured unprivileged user. It also warns that the loaded Codex home configuration remains trusted input. Do not take that configuration from the untrusted checkout being analyzed.
The same document recommends running Codex last in its job because it may leave processes, modified action files, or hooks behind. Put privileged follow-up work on a fresh runner and validate the data it consumes. On self-hosted infrastructure, two job names do not establish fresh hosts or clean storage; the platform owner must supply that isolation.
Review host resources reachable by the workload, including temporary directories, local IPC endpoints, credential brokers, and mounted services. A network restriction does not establish isolation from every local interface. Keep release secrets out of the entire agent runner, not merely out of the environment passed to one child process.
The coding-agent security baseline covers the broader rollout. Our earlier Codex sandbox upgrade guide addresses a different task: verifying workstation CLI and desktop fixes. Its version thresholds should not be reused for this disclosure.
Trace the release authority to its consumer#
Open the package's publishing configuration as well as its workflow YAML. npm trusted publishing binds publishing to a configured repository and workflow, with an optional environment. It replaces long-lived publishing credentials with OIDC-based authentication. That identity check does not decide whether the workflow's inputs or build outputs are trustworthy.
Record the accepted workflow filename, environment, allowed publishing actions, and any remaining token-based publishing route. Then trace the scripts, artifacts, caches, and triggers that can reach that workflow. An approval at the final job is useful only when the reviewer can identify the revision and artifact being approved.
For an exposure review, preserve the relevant workflow revisions, trigger identity, run IDs, resolved action/CLI versions, cache restore/save records, and package publication records. Investigate unexpected release events or changes to publishing configuration against those records. A suspicious issue is a lead; it is not proof that a package was compromised.
Before closing the review, have the platform owner account for every connection from triage to release. The intended handoff is a validated label on the triggering issue. Any additional path through a shared runner, writable cache, or credential needs an owner and a decision to remove or justify it.
Frequently asked questions
- Does this disclosure establish that a malicious Codex package reached users?
No. Depi reports obtaining authority that could have enabled a malicious release and says OpenAI fixed the core bug. Its report does not establish that users received a malicious package, and General Analysis has not reproduced the chain.
- Is read-only GITHUB_TOKEN permission enough to prevent cache poisoning?
Review cache access separately. GitHub Actions has a cache-mode setting with its own service-enforced access. A job-level setting overrides the workflow value, and explicit write access can override the default restriction for low-trust events.
- Does an issue labeler need a coding agent with shell access?
A classifier that chooses from a fixed label catalog can use a model without tools. Validate the result in application code and let a separate, narrowly authorized job apply approved labels. Workflows that inspect or modify code need a separate execution-risk review.

