codfish/semantic-release-action GitHub Action has been compromised - StepSecurity
Customers
Pricing
Resources
Company
Request a Demo<br>Login
Customers
Pricing
Resources
Company
Start Free
Login
Back to Blog
Threat Intel
codfish/semantic-release-action GitHub Action has been compromised
On June 24, 2026, an attacker compromised the codfish/semantic-release-action GitHub repository. At 15:39:06 UTC they force-pushed a malicious commit and repointed several version tags to that commit. As a result, any workflow running against those tags after that time executed the attacker's code inside its GitHub Actions runner.
Rohan Prabhu<br>View LinkedIn
June 24, 2026
Share on X<br>Share on X<br>Share on LinkedIn<br>Share on Facebook<br>Follow our RSS feed
Table of Contents
Loading nav...
Summary<br>On June 24, 2026 at 15:39:06 UTC, an attacker force-pushed a malicious commit to codfish/semantic-release-action and redirected several version tags to point at the malicious commit. Any workflow that ran against one of these tags after that timestamp executed the attacker's payload directly inside the GitHub Actions runner. The payload steals GitHub OIDC tokens, harvests Personal Access Tokens matching known GitHub token patterns, encrypts the collected material with AES-128-GCM, and attempts to propagate a backdoor into other repositories accessible with the stolen credentials.<br>We analyzed the malicious commit and the modified action.yml. The attacker converted the action from a Docker-based runner to a composite action, adding two steps — one to install the Bun runtime via oven-sh/setup-bun and one to execute the payload via bun run, both guarded with if: always() so the payload fires even when prior steps have failed. The malicious index.js payload is approximately 512 KB of heavily obfuscated JavaScript. The C2 exfiltration endpoint remains encoded beyond the current analysis window; this post will be updated as deobfuscation progresses.<br>Background: What Is codfish/semantic-release-action?<br>codfish/semantic-release-action is a GitHub Action that wraps semantic-release, the popular automated versioning and changelog tool. It is widely used by open-source and enterprise projects to automate release workflows — determining the next version number, generating release notes, tagging the repository, and publishing to registries, all triggered from a CI push. The action has been in active use since 2019, has over 100 GitHub stars, and is referenced by thousands of workflows that run on every push to a release branch.<br>In its legitimate form, the action is Docker-based: it builds a container from the repository's Dockerfile and runs node /action/entrypoint.js. The entrypoint is a straightforward wrapper around the semantic-release JavaScript API with no network calls beyond what semantic-release itself requires. The legitimate v5 branch and its GHCR image remain clean and unaffected by this compromise.<br>Affected Tags
Tag<br>Resolves to
v5.0.0, v5<br>5792aba
v4.0.1, v4.0.0, v4<br>5792aba
v3.5.0, v3.4.1, v3.4.0, v3.3.0, v3.2.0, v3.1.1, v3.1.0, v3.0.0, v3<br>5792aba
v2.2.1<br>5792aba
v2<br>bcb6b1d
v2.0.0<br>2845931
v1.9.0, v1<br>98a1e3e
v1.8.0<br>2fc5441
v1.7.0<br>779ea86
v1.6.2<br>e5f263f
v1.6.1<br>83036fb
The Attack: Tag Hijacking<br>Git tags are mutable references. By default, nothing prevents a repository maintainer — or anyone with push access — from repointing an existing tag to a different commit using git push --force. GitHub Actions workflows that reference an action with a mutable tag (uses: codfish/semantic-release-action@v2) resolve the tag at runtime. When the tag moves, every workflow that runs after the move silently executes the new commit's code, with no notification to the downstream workflow author.<br>At 15:39:06 UTC on June 24, 2026, the attacker introduced commit 6b9501e1889cc45c91726729610cf69c2442b8c5 and simultaneously force-updated seven version tags to point at it. The commit modifies two files relative to the last legitimate state: action.yml and index.js.<br>Modified action.yml: Docker → Composite<br>The legitimate action.yml declares a Docker runner:<br># Legitimate action.yml (v2.0.0, commit da160b1)<br>runs:<br>using: docker<br>image: Dockerfile<br>The malicious version replaces this with a composite action containing three steps:<br># Malicious action.yml (commit 6b9501e)<br>runs:<br>using: composite<br>steps:<br>- name: Run semantic-release<br>uses: ./<br># ... legitimate semantic-release step
- name: Setup Bun<br>uses: oven-sh/setup-bun@v2<br>if: always() # ← fires even if semantic-release step failed
- name: Run<br>shell: bash<br>if: always() # ← fires even if prior steps failed<br>run: bun run ${{ github.action_path }}/index.jsThe if: always() guard on both injected steps is deliberate: it ensures the payload runs regardless of whether the semantic-release step succeeds, fails, or is skipped. The attacker also leverages oven-sh/setup-bun — a legitimate third-party action — to install the Bun runtime, choosing Bun over Node.js specifically because Bun lacks the --require hook...