v vanemmerik.ai / SUPPLY-CHAIN
Supply Chain · Watch Monday · 20 July 2026 End-of-day synthesis 4 watches · 93 items

From the watchtower — what crossed the wire today.

A four-times-a-day standing watch on the open-source supply chain. Each pass pulls newly disclosed CVEs, freshly catalogued KEV adds, and active attacks reported in the wild — then ranks them by severity for the day.

The story of the day — A same-day GHSA wave that ran from 6pm to past 9pm ET eventually reached 90 advisories, overshadowing SleeperGem's quiet RubyGems hijack — headlined by a critical node-tar bug reachable through every npm install, a Composer bug that lets a malicious transitive dependency write files outside vendor/, and a second, equally large round of Pillow, Axios, and .NET disclosures that landed after First Watch had already gone to print.

Today started quiet: SleeperGem's dormant-maintainer-account hijack on RubyGems was the only confirmed attack in six hours of monitoring, and the day looked headed for a light Monday.

Then, starting around 6pm ET, GitHub published 50 new advisories across a dozen ecosystems in a two-hour window, three of them critical. The sharpest supply-chain angle belongs to Composer, where a crafted transitive-package name can make Composer write files outside vendor/ during install, and to node-tar, whose decompression path can be driven into memory exhaustion by any tarball with no size cap — node-tar sits underneath npm, yarn, and pnpm's own extraction, so this reaches the install pipeline itself, not just direct importers. LightRAG picked up two criticals in the same window (a hardcoded default token secret and a CORS-wildcard-plus-credentials bug), and Mistune, Pillow, and Tornado each had their own multi-advisory clusters — nine, six, and three respectively — suggesting a coordinated audit push landed across several maintainers' desks at once.

Late addendum at 21:00 ET: the same wave hadn't actually stopped — it kept publishing for another ninety minutes past First Watch's snapshot, bringing tonight's GHSA total to 90. The second half added a duplicate-sized Pillow cluster (seven more heap-corruption and DoS bugs, mostly in image decoders and filters), seven more axios advisories (proxy and prototype-pollution edge cases, none RCE), nine Microsoft .NET CVEs across two NuGet packages (standard monthly patch-cycle noise), three more File Browser bugs, three more Guzzle cookie/header issues, and single entries for protobufjs and webpack-dev-server. None of it topped high severity, and nothing here is a confirmed active exploit — the ceiling for the day stays at the four criticals already on the board (SleeperGem, the two LightRAG bugs, and node-tar).

→ Operational priority for the night patch node-tar and Composer first given their reach into build and install tooling, then Pillow given it's now a thirteen-advisory cluster spanning loaders, filters, and encoders, then work down the rest of the high-severity list by internet exposure — Directus, Cloudreve, and changedetection.io's bugs are all reachable pre-auth if you expose them directly.

21:00 ET · Last Watch

Microsoft Security Advisory CVE-2026-47302 – .NET Denial of Service Vulnerability

Microsoft's July .NET security batch landed via the NuGet/GHSA sync after First Watch's snapshot, headlined by four System.Security.Cryptography.Xml CVEs in XML signature/canonicalization handling — three denial-of-service (this one, CVE-2026-50525, CVE-2026-50648) and one security-feature-bypass (CVE-2026-47304). Standard monthly patch-cycle disclosures with no reported active exploitation; roll into the normal .NET update cadence.

Microsoft Security Advisory CVE-2026-50524 – .NET Denial of Service Vulnerability

Separately, Microsoft.NetCore.App.Runtime.linux-arm picked up five CVEs in the same sync — three DoS (this one, CVE-2026-50651, CVE-2026-57108), one security-feature-bypass (CVE-2026-50528), and one spoofing bug (CVE-2026-50659) in the ARM Linux .NET runtime. Same monthly-patch shape as the Cryptography.Xml batch; no exploitation reported.

Pillow: Heap out-of-bounds write in ImageFilter.RankFilter via integer overflow in ImagingExpand

A second wave of Pillow advisories landed after First Watch's synthesis — seven more, on top of the six already flagged tonight, bringing the day's Pillow total to thirteen. Sharpest is a heap out-of-bounds write in ImageFilter.RankFilter: im.filter(ImageFilter.MedianFilter(4294967295)) overflows an internal size calculation in ImagingExpand and writes past the allocated buffer. If you run Pillow's rank/median filters on any attacker-influenced size parameter, patch this one first.

Pillow: Decompression Bomb DoS via PdfParser.PdfStream.decode()

PdfParser.PdfStream.decode() calls zlib.decompress() using the PDF stream's own declared Length as a buffer-size hint rather than a hard cap, so a crafted PDF can decompression-bomb the process — same missing-bomb-check gap as the loader bugs First Watch already flagged, different code path.

Axios Node HTTP adapter can use an inherited proxy after interceptor config cloning

Axios's Node HTTP adapter can end up routing a request through an attacker-controlled proxy: if Object.prototype.proxy is already polluted by another bug, and a request interceptor replaces the merged (null-prototype-protected) config with a plain object, the polluted proxy value leaks back in. Needs a prototype-pollution primitive elsewhere in the app to reach, but it's the sharpest of seven axios advisories tonight.

File Browser: Colliding username normalization gives two users the same home directory

When self-registration and automatic home-directory creation are both enabled, File Browser's username-cleaning function can normalize two different usernames to the same home directory, giving one user read/write access to another's files. Third and fourth File Browser advisories today on top of the two symlink bugs First Watch already flagged — needs Signup=true and CreateUserDir=true to be reachable.

Astro: composable astro/hono pipeline bypasses security.checkOrigin when middleware() is absent or misordered

Astro's composable astro/hono pipeline only installs the security.checkOrigin origin check via the middleware() primitive — a pipeline that mounts actions() or pages() before or without middleware() dispatches to user code with no origin check at all. If you're on the composable pipeline, verify middleware() runs first or you have a silent origin-check bypass in production.

@astrojs/rss: XML Injection via Unescaped RSS Feed Fields

@astrojs/rss interpolates source.title and enclosure.type directly into XML template strings without escaping, so attacker-controlled feed field values can inject arbitrary XML elements into the generated RSS output.

Axios form serializer maxDepth bypass via {} metatoken

Axios's form serializer still has an incomplete maxDepth bypass: a top-level object key ending in {} gets JSON.stringify()'d before the depth guard inspects nested structure — a follow-on to the already-patched GHSA-62hf-57xw-28j9.

Axios: Nested axios option objects can consume polluted prototype values

Nested option objects like auth or paramsSerializer are cloned as ordinary (non-null-prototype) objects even though axios protects the top-level merged config, so a pre-existing Object.prototype pollution can still leak into nested config fields.

Axios: HTTP/2 streamed uploads bypass maxBodyLength

HTTP/2 streamed uploads bypass maxBodyLength enforcement — buffered bodies are still checked, but streamed bodies over h2 aren't, letting an unbounded upload through a configured cap.

Axios: Fetch adapter ReadableStream uploads bypass maxBodyLength

Same maxBodyLength gap on the fetch adapter: live ReadableStream bodies whose size can't be predicted before dispatch aren't checked against the limit, relevant to edge-runtime deployments using adapter: "fetch".

Axios: NO_PROXY bypass for 0.0.0.0 local addresses in axios

NO_PROXY rules don't treat 0.0.0.0 as a local address, so a request to http://0.0.0.0:<port>/ can get routed through a configured proxy instead of bypassing it as NO_PROXY=localhost,127.0.0.1 intends. Seven axios advisories in tonight's batch, all DoS/config-bypass shaped and none RCE; one upgrade closes all of them.

File Browser: Archive builder turns backslash filenames into path traversal (zip-slip)

The fix for a prior zip-slip advisory (CVE-2026-54093) replaced backslashes with forward slashes in archive filenames — but on Linux, backslash is a legal filename character, so that fix manufactures a traversal sequence out of a filename that wasn't one. A textbook case of an escaping fix introducing the bug it was meant to close.

Guzzle: Unbounded response cookies risk denial of service

Guzzle's CookieJar accepts unlimited Set-Cookie headers per response with no per-cookie or total-header-length cap, so a malicious or compromised server can grow the client's Cookie header without bound on subsequent requests — a resource-exhaustion DoS driven entirely by server-side response headers.

Guzzle: URI fragments disclosed in redirect Referer headers

Second Guzzle cookie/header bug tonight: when the optional referer redirect setting is enabled, RedirectMiddleware can copy the URI fragment (the part after #, normally client-local only) into a generated Referer header, leaking it to the server.

Guzzle: Host-only cookie scope is not preserved

Third: CookieJar doesn't distinguish a cookie set with no Domain attribute from one with an empty Domain — both are supposed to be host-only, but Guzzle stores the request host either way, which can widen a cookie's intended scope depending on how the response was crafted.

protobufjs: Denial of Service via infinite loop in .proto option parsing

protobufjs's option-name parser advances through schema tokens looking for = without checking for end-of-input, so a crafted .proto schema that opens an option declaration and ends prematurely loops forever — reachable via parse(), Root.load(), or Root.loadSync() on any untrusted schema.

protobufjs: Text Format string map parsing can mutate returned map object prototype

Second protobufjs bug: the Text Format extension's string-map parser used ordinary property assignment, so a map entry keyed __proto__ mutates the returned object's prototype instead of creating a map entry — binary decode and the other input paths aren't affected, only the optional Text Format extension.

webpack-dev-server vulnerable to denial of service via a malformed Host or Origin header

Any unauthenticated peer that can reach a running webpack-dev-server can crash it by sending a malformed Host header over plain HTTP or a malformed Origin header on a WebSocket upgrade to /ws — an uncaught exception in the host-validation path kills the process. Dev-only tool, but plenty of dev servers get left reachable on shared networks.

webpack-dev-server vulnerable to cross-site request forgery via internal developer endpoints

Second: the internal /webpack-dev-server/open-editor and /invalidate endpoints perform state-changing actions on any GET request without origin verification, so any site a developer visits while the dev server is running can trigger them cross-origin — CSRF against your own editor-open and rebuild endpoints.

File Browser: Share API exposes the password hash and bypass token

Lowest severity of the File Browser cluster: the Share API returns the full bcrypt password_hash and bypass token for a user's own shares in the JSON response, with no field filtering.

body-parser vulnerable to denial of service when invalid limit value silently disables size enforcement

body-parser silently disables its own size-limit enforcement when the configured limit value fails to parse (NaN or an unparseable string) — bytes.parse() returns null and the check is skipped entirely, so an app leaning on limit as its only size guard accepts unbounded request bodies. Worth a config audit if your limit value can ever come from an empty or malformed environment variable.

18:00 ET

Exposed delivery server reveals an AI-assisted phishing toolkit behind a WebDAV infostealer campaign

Rapid7 found an exposed delivery server holding a full AI-assisted phishing toolkit — 1,048 files of lure templates, filename-spoofing tests, droppers, and builder notes — with one campaign chain already live against Windows users in Mexico via a spoofed government ID-lookup site delivering an infostealer over WebDAV. Not a package-registry compromise, but the same WebDAV-as-delivery-channel and infostealer payload shape this watch has flagged in commodity campaigns all month; filed as context for anyone tracking initial-access infrastructure rather than as a direct supply-chain hit.

HollowGraph malware uses Microsoft Graph calendar entries for covert C2

HollowGraph abuses the calendar feature of compromised Microsoft 365 mailboxes as a covert command-and-control channel, blending C2 traffic into legitimate Graph API calls. No package-registry angle, but it's the same 'live off trusted infrastructure' pattern this watch keeps seeing in supply-chain campaigns — GitHub Releases as droppers, npm as a payload host, now M365 calendars as a C2 channel; filed as context for the technique.

18:00 ET

LightRAG: CORS Wildcard + Credentials Enables Any-Origin Credentialed Requests

LightRAG also serves Access-Control-Allow-Origin: * alongside Access-Control-Allow-Credentials: true — a combination browsers are supposed to refuse, but which some LightRAG deployments apparently rely on, letting any origin make authenticated requests on a logged-in user's behalf. Paired with the hardcoded-secret bypass above, this is a rough night for LightRAG operators — patch both together.

LightRAG is Vulnerable to Authentication Bypass: hardcoded DEFAULT_TOKEN_SECRET and public /auth-status defeat LIGHTRAG_API_KEY protection

LightRAG ships a hardcoded default value for its token-signing secret, and the /auth-status endpoint is reachable without authentication — together they let anyone forge a valid session regardless of the operator's configured LIGHTRAG_API_KEY. If you run LightRAG anywhere reachable from an untrusted network, rotate to a non-default secret immediately; the default alone is the whole vulnerability.

node-tar: Decompression/parse DoS via unlimited input

node-tar's gzip decompression path buffers the full decompressed stream before parsing headers, so a crafted tarball with no size cap can exhaust memory during extraction — and node-tar is the extractor npm, yarn, and pnpm all shell out to, so this reaches the install pipeline of virtually every JS project, not just direct importers. Three sibling PAX-parsing bugs (negative-size infinite loop, NUL-byte crash, numeric path-type confusion) landed in the same batch tonight; patch to the fixed release rather than picking off one CVE.

Tornado: Quadratic DoS via Repeated Header Coalescing

The second Tornado quadratic-time DoS bug tonight comes via repeated header coalescing — same O(n²) parsing shape as the multipart bug above, different code path.

Tornado: Quadratic DoS via Crafted Multipart Parameters

Tornado has two independent quadratic-time DoS bugs tonight, this one via crafted multipart form parameters, driving parsing into O(n²) time. A third Tornado advisory (header injection/XSS via the reason argument) rounds out the batch below — if Tornado sits in front of anything internet-facing, this is a three-CVE patch, not a one-liner.

vLLM denial of service via prompt embeds on M-RoPE models

vLLM crashes or hangs when serving prompt embeddings against M-RoPE (multimodal rotary position embedding) models, a DoS reachable by anyone able to submit a request to an exposed inference endpoint. No data exposure, but a free way to take an endpoint down until patched.

Composer: Arbitrary file write outside vendor via malicious transitive package name

A package name containing path-traversal segments can make Composer write installed files outside the vendor/ directory during install — a malicious transitive dependency several levels deep can plant files anywhere the Composer process can write, no direct dependency required. This is the same shape as npm's dependency-confusion and PyPI typosquatting incidents: the trust boundary is the whole dependency tree, not just composer.json's direct requires. Upgrade Composer and diff any auto-generated vendor paths that look unfamiliar.

brace-expansion: DoS via exponential-time expansion of consecutive non-expanding {} groups

brace-expansion — embedded in minimatch and, transitively, a huge share of the npm ecosystem's glob-matching tooling — has an exponential-time expansion bug for consecutive non-expanding {} groups. A single crafted glob pattern in a CI config, build script, or user input can hang a build; prioritize this for reach alone, given how deep in the dependency tree it sits.

@better-auth/sso: SSO provider may allow registration for any org member without a checking their role

@better-auth/sso's SSO-provider registration flow doesn't check the joining user's role, so any existing org member — not just admins — can register a new SSO provider for the organization. That's a privilege-escalation path into identity infrastructure; audit who's registered SSO providers in any org using this package before patching, in case it's already been used.

Pillow `GdImageFile._open()`: image dimensions accepted without `_decompression_bomb_check()`

Four different Pillow image/font loaders — GdImageFile, BdfFontFile, FontFile.compile, and PcfFontFile — each construct an Image.new() or equivalent without calling _decompression_bomb_check(), so each format bypasses Pillow's own bomb protection independently. This one covers GdImageFile; same root cause and fix pattern across all four. If you process user-uploaded images in any of these formats, patch now rather than waiting to see which loader an attacker picks.

Cloudreve: OAuth access tokens bypass scope enforcement due to missing client_id claim

Cloudreve's OAuth token validation doesn't check the client_id claim, so a token issued to one OAuth client can be replayed against a different client's scope — effectively bypassing per-application scope enforcement. Second Cloudreve advisory tonight; audit any deployment with multiple registered OAuth clients.

File Browser: Out-of-scope file deletion by a Create-only scoped user via symlink-following RemoveAll in upload failure-cleanup

A user scoped to Create-only access can delete files outside their scope by exploiting the same symlink-following behavior in the upload failure-cleanup path's RemoveAll call. Higher severity than the write bug above because deletion is more damaging than creation; fix both symlink-following gaps in the same patch.

js-yaml: YAML merge-key chains can force quadratic CPU consumption

Chained `<<: *anchor` YAML merge keys make js-yaml's parser do quadratic work relative to input size, turning a small crafted YAML document into a CPU-exhausting parse. A second advisory below covers the identical merge-key behavior at medium severity — likely the same fix filed twice; patch once.

Mistune plugins/formatting: quadratic-time parsing on long runs of `~~x~~`, `==x==`, and `^^x^^` markers (strikethrough / mark / insert)

Long runs of `~~strikethrough~~`, `==mark==`, or `^^insert^^` markers drive Mistune's formatting parser into quadratic time — one of three ReDoS-style parsing bugs in tonight's batch, alongside emphasis-pair and reference-link parsing below. A single crafted document is enough to peg a worker thread rendering user-submitted markdown.

Directus: SSRF Protection Bypass via 0.0.0.0 in File Import

Directus's SSRF protection for remote file imports blocks known-internal hostnames but not the literal 0.0.0.0, which most operating systems route to localhost — an attacker can import a 'remote' file from 0.0.0.0 and reach services bound to the local host. Second Directus advisory tonight; both are boundary checks that covered the common case and missed an edge one.

Directus: Authorization-dependent response served from unsegmented cache key

Directus caches responses by a key that doesn't include the requesting user's authorization context, so one user's permission-gated response can be served back to a different, lower-privileged user from cache. Classic cache-poisoning-by-omission — any authorization-sensitive endpoint behind Directus's cache layer needs the fix or a cache bypass until patched.

shell-quote: Quadratic-complexity Denial of Service in `parse()` (CWE-407)

shell-quote's parse() has quadratic time complexity on certain crafted inputs, so a modest string can peg CPU during shell-argument parsing. It's a small, widely-depended package — check whether it's in your dependency tree via any CLI-wrapping tool before assuming it doesn't apply.

Socket.IO: Engine.IO Polling Transport Connection Exhaustion

Socket.IO's Engine.IO polling transport doesn't bound how many long-polling connections a single client can hold open, so a client can exhaust the server's connection pool with a modest number of requests. Any Socket.IO deployment relying on polling (rather than pure WebSocket) is exposed; rate-limit per-IP polling connections until patched.

node-tar: Negative tar entry size causes infinite loop in archive replace

A tar entry with a negative declared size sends node-tar's archive-replace path into an infinite loop instead of erroring out — second of four PAX-parsing bugs disclosed against node-tar tonight. Same fix train as the decompression DoS above; one upgrade closes all four.

Astro: Authorization Bypass via Decode Iteration Limit and Rewrite Path Canonicalization Mismatch

Astro's prior fix for a middleware-encoding auth bypass added a 10-iteration cap on URL decoding, but when the cap is hit it returns the partially-decoded path instead of rejecting the request — encode a path 11 levels deep and middleware sees /%61dmin while the router resolves it to /admin, the exact bypass shape Astro patched before. If pathname-based middleware guards any route in an app using Astro rewrites, re-verify against this after upgrading; the fix needs to reject over-decoded paths outright, not just decode further.

Axios: Excessive recursion in formDataToJSON can cause denial of service

axios — arguably the most-depended HTTP client in the npm ecosystem — picked up three advisories tonight; this one is unbounded recursion in formDataToJSON causing a DoS on crafted form data. Two more axios bugs (a second recursion DoS and a prototype-pollution auth-header injection) round out the trio below — none are close to an RCE, but axios's install base means even a medium-severity bug here reaches an enormous number of applications.

Tornado vulnerable to Header Injection and XSS via reason argument

Tornado's HTTP reason argument on responses isn't sanitized, allowing header injection and XSS if the reason string is ever attacker-influenced. Third and last of tonight's Tornado trio.

changedetection.io is vulnerable to unauthenticated static path traversal

changedetection.io serves static files without validating the requested path stays inside its intended directory, letting an unauthenticated request read arbitrary files off the host. If you self-host changedetection.io and expose it to the internet — a common setup for this tool — patch before anything else on tonight's list; it needs no special access.

Astro: Reflected XSS via unescaped View Transition animation properties

Separately, Astro's View Transitions API passes animation property values into rendered HTML without escaping, giving reflected XSS if those properties are ever attacker-influenced. Unrelated root cause to the auth-bypass regression above — different subsystem, same release.

Pillow: WindowsViewer.get_command() OS command injection via unescaped shell path

Pillow's WindowsViewer.get_command() builds a shell command from an unescaped path, so displaying an image whose path an attacker controls can inject shell commands on Windows. Narrower blast radius than the loader bugs above — needs Image.show() on a Windows host with an attacker-influenced path — but worth closing in the same upgrade.

File Browser: ScopedFs follows a dangling symlink on write, letting a scoped user create files outside their scope

File Browser's ScopedFs follows a dangling symlink when writing, letting a scoped (restricted) user create files outside their assigned scope by pre-planting a symlink that points elsewhere. Root cause for both this and the deletion bug below is the same: symlink-following isn't disabled at the scoped filesystem boundary.

js-yaml: Quadratic-complexity (O(n^2)) DoS via !!omap tag in YAML11_SCHEMA

Separately, a crafted !!omap tag under js-yaml's YAML11_SCHEMA drives its own quadratic-complexity parse — same class of bug as the merge-key issue but a different code path. js-yaml sits underneath a huge share of the npm config-parsing ecosystem; if you parse untrusted YAML anywhere, the patch is worth it even though none of the three go past DoS.

Mistune: XSS via percent-encoded javascript URI bypass in safe_url()

Mistune's safe_url() link sanitizer can be bypassed with a percent-encoded javascript: URI, reintroducing stored XSS in rendered markdown links. Second of tonight's nine-advisory Mistune batch.

Mistune: Arbitrary File Read via Include directive path traversal

Mistune's `.. include::` directive doesn't sanitize the target path, so markdown that reaches Mistune's renderer can read arbitrary files off the host filesystem and inline their contents into rendered output. The sharpest of nine Mistune advisories published together tonight — if you render user-submitted markdown with the directives extension enabled, patch this one first.

Mistune toc / TableOfContents directive: heading IDs use predictable `toc_N` numbering with no slugification, allowing collision with attacker-controlled `id="toc_N"` content

Mistune's table-of-contents directive numbers heading IDs as toc_N without slugifying user content, so attacker-controlled markdown can craft an id="toc_N" element that collides with and hijacks a generated anchor. Lowest severity of tonight's Mistune batch, but the same pattern: user-controlled markdown reaching output without adequate isolation.

Mistune renderers/html.safe_url: HARMFUL_PROTOCOLS list misses legacy and chained schemes that historically chain to `javascript:` execution

The same safe_url() protocol denylist also misses several legacy and chained URI schemes that historically resolve to javascript: execution in real browsers — a second, broader bypass alongside the percent-encoding bug above. Both point at denylist-based sanitization as the wrong approach; allowlist schemes instead of blocking known-bad ones.

Guzzle: Proxy-Authorization headers can be sent to origin servers

Guzzle also forwards the Proxy-Authorization header to the origin server, not just the proxy, when a request is proxied — leaking proxy credentials to whatever server sits on the other end of the tunnel. Audit any Guzzle client configured with an authenticated proxy for credential exposure in access logs.

node-tar: Uncaught Exception DoS via NUL byte in PAX path/linkpath records

A NUL byte embedded in a PAX path or linkpath record throws an uncaught exception during extraction — the fourth and last of tonight's node-tar batch. All four share one fix; treat anything that extracts tarballs at build or install time as needing it, not just direct `tar` importers.

node-tar: Process crash via PAX numeric path type confusion

A PAX header that declares a numeric value where node-tar expects a path string crashes the parser process. Third of tonight's four node-tar advisories, same root cause: untrusted PAX headers processed without type-checking.

Composer: Path traversal in package bin field lets dependencies chmod arbitrary host files

Composer's bin field processing doesn't reject `..` segments, so a malicious package can make Composer chmod a file outside its own install directory world-readable/executable. Confidentiality impact only, no RCE, but it can expose an SSH key or .env file that was deliberately locked to 0600 — same install-time trust-boundary problem as the arbitrary-file-write bug above.

Guzzle: Cookie Disclosure and Injection via IP-Address Domains

Guzzle's cookie jar applies ordinary subdomain suffix-matching to cookies scoped to IP-address domains, so a cookie set for 192.168.0.1 will also be sent to a look-alike host like evil.192.168.0.1. Exploitation needs some influence over name resolution, realistic on shared CI runners, containers, or split-horizon internal networks — don't reuse one CookieJar across trust boundaries.

06:00 ET · Morning Watch

SleeperGem's three payloads named: git_credential_manager impersonation plus two hijacked dormant gems

Follow-up reporting names all three SleeperGem packages: git_credential_manager (v2.8.0–2.8.3, published July 18) impersonates Microsoft's real Git Credential Manager, while Dendreo (dormant since 2020) and fastlane-plugin-run_tests_firebase_testlab (dormant since 2019) are legitimate gems whose hijacked maintainer accounts got the malicious updates. Each release is a loader that fingerprints CI environments and no-ops there, but drops a native daemon with persistence on developer machines — and git_credential_manager was added as a dependency to five packages including the other two, so the payload can reach victims who never installed it directly. Grep Gemfile.lock for git_credential_manager, Dendreo, or fastlane-plugin-run_tests_firebase_testlab at the flagged versions, and treat any transitive pull of git_credential_manager as a compromise indicator even if you never added it yourself.