ReleaseLock learn

What 2026's GitHub supply-chain incidents show about unlocked releases

Published November 1, 2026 · Sources checked November 1, 2026 (PT)

Two 2026 incidents took different routes toward the same prize: access that can change a project's releases. In one, the OVHcloud CLI, release files were swapped. In the other, a campaign Radware found, attackers tried to steal CI secrets through GitHub issues and comments, and Radware has not confirmed any swap. Either way, immutable releases stop the last step. For what each lock level means, see Can this GitHub release be swapped?

Short answer

Anyone who gets credentials that can write to a repository can replace the files on a release, unless the release is immutable. A stolen maintainer account is one way to get those credentials. A workflow that runs text from issues, pull requests or comments as code is another possible way. Locking releases, cutting token permissions and handling outside text safely each break a different step.

Path A: a stolen account replaces release files (OVHcloud CLI, October 19, 2026)

According to OVHcloud's advisory GHSA-862f-55f5-vmvw, a compromised OVHcloud GitHub developer account was used to replace existing release assets on ovh/ovhcloud-cli versions v0.11.0 through v0.15.0 with malicious binaries. The window ran from 14:47 to 16:21 UTC+2 (5:47 to 7:21 AM PT), and OVHcloud puts the malware's availability at 1 hour 33 minutes, with about 250 downloads. Source code and the build chain were not affected.

OVHcloud named the root cause: the repository was not configured to ensure release immutability. It fixed that setting on every repository and published a clean v0.16.0 with a signed tag. The customer report in issue #300 shows that checksums.txt was replaced along with the archives, and that the problem was caught because a CI job had pinned one archive's SHA-256 from earlier. The first learn page covers this incident in more detail.

Path B: issue and comment text injected into build jobs (Radware campaign, September 1, 2026)

What happened

Radware researchers, in their write-up and in an interview with The Register published October 29, 2026, describe 83 malicious issues, pull requests and comments across 61 public repositories, all posted on September 1, 2026. 77 of them went up in about 43 minutes, between 11:26 and 12:10 UTC (4:26 to 5:10 AM PT), and six more came in a second wave. They came from 74 GitHub accounts, many with real development history, which means many were likely compromised. Each item carried a shell command, often hidden inside an HTML comment so it did not show on the rendered page. The command sat in the title in 31 cases, in the body in 17, and in a comment in 35.

How the injection works

Some workflows run automatically when someone opens an issue, opens a pull request or posts a comment. If the workflow builds a shell line like ISSUE_TITLE="${{ github.event.issue.title }}", GitHub fills in the title before the shell runs. A $() command inside the title then runs as code. GitHub's docs on script injections describe exactly this risk and list titles, bodies and comments as untrusted input.

In this campaign, the injected command gathered the job's environment variables, where secrets and tokens often live, and tried to send them to a server the attacker controlled. Radware notes the attacker needed no write access to start. Opening an issue or a pull request can be enough, and the workflow fires within seconds, whether or not anyone ever merges or answers the item.

The unsafe pattern pastes the text into the script:

# Unsafe: the title becomes part of the shell script
on:
  issues:
    types: [opened]
jobs:
  notify:
    runs-on: ubuntu-latest
    steps:
      - run: |
          echo "New issue: ${{ github.event.issue.title }}"

The safer pattern, from GitHub's secure use reference, passes the text through an environment variable and quotes it:

# Safer: the title stays data, not code
    steps:
      - env:
          TITLE: ${{ github.event.issue.title }}
        run: |
          echo "New issue: $TITLE"

What Radware did and did not confirm

Confirmed: run logs showed the malicious command executed inside GitHub Actions in two cases.

Not established: Radware says it did not establish that the attacker obtained repository write permissions, modified source code or compromised downstream software. Its supply-chain scenario describes possible consequences, not outcomes it verified.

The researchers told maintainers of every targeted project. The Register reports most of the repositories were closed or fixed, but the researchers could not confirm all of them. Coverage named well-known projects as targets. Being targeted is not the same as being compromised, and no named project has been shown to have lost access or had a release changed.

From stolen token to swapped release: the possible path

Here is why this matters for releases. If a leaked secret or GITHUB_TOKEN had permission to write repository contents, then it could be used to upload or replace files on a release that is not immutable. That would be the same end state as Path A: same version name, different bytes. Radware's Gabi Nakibly told The Register the team's guess is that the final aim was a supply-chain attack using the repo's credentials. That is a suspected aim, not a confirmed result.

Which control stops which step

Attack stepPathControl that breaks it
Outside text reaches a shell stepBPass untrusted values through an env: variable. Never put ${{ github.event.* }} straight into run:. (script injections, secure use)
Injected command reads secretsBKeep secrets and write tokens out of jobs triggered by issues, comments and pull requests. Separate those jobs from privileged ones. Avoid pull_request_target and workflow_run with untrusted content. (secure use)
Stolen token can write to the repoA, BGive GITHUB_TOKEN the least access it needs, and grant write access per job only when needed. (secure use, GITHUB_TOKEN docs)
Release files replaced after publishA, BImmutable releases: assets and tag are locked once published, and GitHub generates a release attestation. (immutable releases)
Downloader gets swapped bytesA, BPin a hash you got earlier or from another channel. Check with gh release verify and gh release verify-asset. Look up the tool's lock level on ReleaseLock. (verify docs, issue #300)

One limit to keep in mind: immutable releases do not stop secret theft or a poisoned build before publish. They stop files from being replaced after the fact.

What maintainers should do this week

Radware's advice, as reported by The Register, points the same way: give CI the least access it needs, keep workflows triggered by outside events apart from jobs with elevated permissions, treat outside content as untrusted, and limit the secrets a workflow can see. GitHub Security Lab's guide to untrusted input goes deeper.

What downloaders should do

This is not a rare gap. In independent research published October 24, 2026, Xianpeng Shen checked the latest release of 242 popular Go and Rust CLI projects and found that for 179 of them, the files could be replaced and a downloader would have no way to tell. Those numbers are his, not ReleaseLock's.

Incident log

We add new incidents here as they are reported, newest first.

Related on ReleaseLock

Quick answers

Can a GitHub issue or PR comment run code in a project's Actions workflow?

Yes, if the workflow pastes issue, pull request or comment text straight into a shell step. GitHub fills in expressions like ${{ github.event.issue.title }} before the shell runs, so a $() command in the title becomes code. GitHub's docs say to treat that text as untrusted and pass it through an environment variable instead. In a campaign Radware found on September 1, 2026, attackers posted 83 such items across 61 public repositories to try to steal CI secrets.

Did the September 2026 GitHub issue campaign swap any releases?

No swap has been confirmed. Radware found evidence the injected command ran in two cases, but said it did not establish that the attacker got repository write access, changed source code or compromised downstream software. The risk is the path: a stolen token with write access to repository contents could replace files on a release that is not immutable.

What actually stops a GitHub release swap?

Immutable releases. Once an immutable release is published, GitHub blocks changes to its assets and blocks moving or deleting its tag. That stops after-the-fact replacement whether the attacker used a stolen account, as in the OVHcloud CLI incident on October 19, 2026, or a stolen CI token. Downloaders can add a second layer by pinning hashes and checking a tool's lock level on ReleaseLock.

Do immutable releases stop secret theft or a poisoned build?

No. Immutable releases lock a release after it is published. They do not stop someone from stealing secrets from a workflow, and they do not prove the build itself was clean. They stop the files and tag of a published release from being changed later.

Is the tool you install every day still swappable?

Look it up in the directory, or add the free scan step to your workflows.

Search the directory

Sources