v vanemmerik.ai / SUPPLY-CHAIN
Supply Chain · Watch Monday · 03 August 2026 End-of-day synthesis 4 watches · 33 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 three-month-old, still-live RAT campaign targeting Alibaba's internal npm tooling surfaced the same day two dozen disclosures landed across Python, PHP, JavaScript, and Rust — three of them the same host-parsing bug independently rediscovered in Guzzle, ip-address, and fast-uri.

Socket disclosed a three-month-old, still-live npm campaign that splits a RAT loader across 18 packages to target developers inside Alibaba Group business units — the kind of low-download, high-precision attack that doesn't show up as scanner noise. It landed the same day CISA added an N-able N-central auth bypass to KEV and roughly two dozen new GHSA disclosures hit Python, PHP, JavaScript, and Rust ecosystems at once.

Three of those disclosures are the same bug rediscovered independently in three different libraries: Guzzle's noncanonical-host bypass, ip-address's octal-vs-decimal IP disagreement, and fast-uri's backslash-authority confusion all reduce to a validator and a network client parsing the same string differently, and an attacker picks the spelling that fools the check. Five more undici advisories landed in the same batch, mostly cache-key and Cache-Control parsing gaps that let one user's cached response leak to another. Sequelize also picked up a CVSS 9.8 SQL injection, though it's scoped to Oracle-dialect deployments only. On the defensive side, GitPython and AIOHTTP both shipped coordinated multi-advisory fixes from their own internal sweeps rather than patching one bug at a time — GitPython specifically audited all 14 of its unguarded git-option call sites after finding two exploitable ones.

→ Operational priority for the night if you or any contractor touches @ali-scoped npm packages or Alibaba Aone tooling, treat that environment as compromised and rotate npm, GitHub, and cloud secrets from a clean machine before doing anything else.

18:00 ET · First Watch

18 npm packages split a cross-platform RAT loader across a shared dependency chain to target developers inside Alibaba Group

Socket traced a three-month-old, still-live campaign in which no single npm package looks malicious on its own — the loader logic is split across a lure package, a middle-layer dependency, and a "rule engine" package that uses a classic Node.js `vm` sandbox escape (`items.constructor.constructor` → `Function` → `process`) to fetch and run a remote payload. The final stage, `aone-cli`, is a targeted RAT with DingTalk-based lateral movement aimed squarely at Alibaba's internal Aone tooling; commits are timestamped UTC+0800 and the C2 infrastructure has stayed live undetected for three months. If your org or contractors touch @ali-scoped packages, treat any environment that installed `lib-mtop`, `smart-config-manager`, `aone-kit`, or the other 15 IOC'd packages as compromised and rotate npm/GitHub/cloud secrets from a clean machine, not the infected host.

CISA adds an N-able N-central auth-bypass to KEV — the result of an incomplete patch for an earlier bypass in the same product

CVE-2026-18577 is an authentication bypass via an alternate path or channel in N-able N-central, and CISA's own notes flag it as the result of an incomplete fix for the earlier CVE-2026-18556. N-central is an RMM platform with privileged reach across every endpoint it manages, so a bypass here is a foothold into every client environment the instance touches. Confirm you're on the 2026.3 HF1 release; the KEV due date is August 6.

Sequelize's Oracle-dialect string escaper skips quote-escaping for values starting with TO_TIMESTAMP or TO_DATE

Sequelize's Oracle-dialect escape function returns a string unescaped whenever it starts with `TO_TIMESTAMP` or `TO_DATE` — confirmed on 6.37.3 with a working PoC, CVSS 9.8. Only Oracle-dialect deployments are exposed, but if that's you, any code path that reflects user input through those two functions is a direct SQL injection. Patch and audit call sites that build `TO_TIMESTAMP`/`TO_DATE` expressions from untrusted strings.

python-cryptography's chain-builder can be forced into exponential backtracking by duplicate self-signed intermediates

A certificate chain containing duplicate copies of a self-signed intermediate forces python-cryptography's path-builder into exponential-time backtracking — depth limits stop unbounded recursion but a crafted chain still took over 5 seconds to reject in testing. Anything that validates attacker-supplied certificate chains (mTLS endpoints, cert-pinning services) should rate-limit or timeout chain validation on untrusted input until patched.

cryptography's PKCS#7 EnvelopedData decryption leaks a Bleichenbacher oracle through distinguishable errors and timing

Same library, a companion disclosure: `pkcs7_decrypt_der`/`_pem`/`_smime` reported the outcome of an RSA decryption in several distinguishable ways — including the exact recovered length — and the same distinction leaked through timing. That's a textbook Bleichenbacher oracle for any app that decrypts attacker-supplied EnvelopedData and reflects the result. Patch, and stop distinguishing decryption-failure modes in whatever response you send back.

Guzzle hands libcurl a raw URI while sending a separately-computed Host header, so an SSRF check on the string doesn't match what the transport connects to

Guzzle's cURL and stream handlers let libcurl parse and normalize the request authority itself — percent-decoding, IDNA mapping, and octal/hex numeric-IP forms — while the application's own host allowlist or denylist evaluated the URI as plain text. A host like `http://127.0.0.%31/` reads as invalid to `filter_var()` but libcurl decodes and connects to `127.0.0.1` regardless. Classic SSRF-by-parser-divergence; if you validate hosts before handing a URI to Guzzle, resolve and check the actual address, not the string.

AIOHTTP's C HTTP response parser does an out-of-bounds heap read while formatting an error for a malformed chunked response

A malformed chunked response — from an attacker-controlled server or just a broken one — triggers an out-of-bounds heap read in AIOHTTP's C parser's own error-message path, a DoS against any client. Set `AIOHTTP_NO_EXTENSIONS=1` to force the unaffected pure-Python parser if you can't upgrade immediately.

GitPython's option-injection guard is applied per call site, not centrally — a sweep of 14 unguarded sites found two exploitable ones

GitPython blocks dangerous git options via `check_unsafe_options()`, but that guard is opt-in per method rather than enforced centrally. A researcher swept 14 call sites that forward `**kwargs` without it and found two exploitable ones: `IndexFile.checkout()` and `TagReference.create()`, both giving arbitrary file overwrite and read. If you call GitPython with any caller-influenced kwargs, audit for the same missing-guard shape before trusting the library's own boundary.

ip-address's Address4 decodes a leading-zero octet as decimal while the WHATWG URL parser, inet_aton, and getaddrinfo decode it as octal

`new Address4('012.0.0.1')` reports `correctForm()` of `12.0.0.1` and `isPrivate()` of `false`, but `fetch('http://012.0.0.1/')` actually connects to `10.0.0.1` — the library and the network stack disagree about which host the string names. Any SSRF or trust-boundary check built on this library's classification is checking the wrong address; same bug class as today's Guzzle host-bypass, different library.

undici's cache interceptor mishandles malformed private Cache-Control directives two ways: shared-cache disclosure and a parser crash

A response with a malformed qualified `private` directive (`private=""` or `private=","`) gets incorrectly stored in undici's default shared cache and later served to a different caller — cross-user disclosure. A second, related input crashes the parser outright. Anything running undici's shared cache in front of multiple users should patch this one first among today's five undici advisories.

fast-uri requires a literal // to recognize a URI authority, but Node's native URL parser also treats backslash as an authority introducer

fast-uri only recognizes `//` as the authority marker, folding any `\\`, `/\`, or `\/` variant into the path with no authority extracted at all. Node's native WHATWG `URL` — used by `fetch`, undici, and Node's `http`/`https` clients — treats backslash as interchangeable with slash for special schemes, so the two parsers can extract different hosts from the same string. If you validate a URL with fast-uri before handing it to `fetch()`, you're validating a host that isn't the one the request actually goes to.

A crafted Socket.IO packet can declare an arbitrarily large attachment count and the server buffers all of it waiting for the rest to arrive

A specially crafted Socket.IO packet makes the server expect a large number of binary attachments and buffer them while waiting — straightforward, no-auth-required memory-exhaustion DoS against any Socket.IO server (2.x, 3.x, and 4.x all affected pre-patch). Patch to the fixed version for your major line.

python-cryptography's verifier accepts a wildcard SAN even when the constraining intermediate only permits an exact name

Third from the same python-cryptography batch: if a constrained intermediate CA permits only `foo.example.com`, a leaf with a wildcard SAN of `*.example.com` still verifies — a permittedSubtrees escape. If you rely on name constraints to scope a private or constrained CA, this is a real boundary bypass; patch python-cryptography.

Guzzle's cookie-domain matcher doesn't recognize hex/octal IP literals the way libcurl does, so a cookie keeps subdomain scope it shouldn't

Companion bug to the host-bypass above: `SetCookie::matchesDomain()` only excludes IP literals it recognizes as IPs, and forms like `0x7f000001` slip through as plain text, closing the loop on a prior fix (GHSA-g446, CVE-2026-59883) that only handled the WHATWG-parser spelling. Same upgrade covers both Guzzle issues.

AIOHTTP's WebSocket client decompresses RSV1-flagged frames even when permessage-deflate was never negotiated

Third in the batch: the client honors the RSV1 compression bit regardless of whether it actually negotiated `permessage-deflate`, so a malicious peer can force decompression the caller explicitly opted out of — a CPU/memory-amplification lever if paired with a compression-bomb payload.

Hono's default CORS middleware parses the attacker-controlled Access-Control-Request-Headers with a quadratic-time regex

When `allowHeaders` isn't configured — the default — Hono's CORS middleware reflects and parses the preflight `Access-Control-Request-Headers` value with a regex that's quadratic in input length; a single request with a long whitespace run burns seconds of CPU. Cheap DoS lever against any Hono service running default CORS config; patch or set `allowHeaders` explicitly.

GitPython's Commit.count() forwards kwargs into git rev-list with no guard at all, and --output truncates the target file first

Second finding from the same GitPython sweep: `Commit.count()` has no `check_unsafe_options` guard whatsoever, so `count(output='/some/path')` reaches `git rev-list --output=<path>`, which opens and truncates that file to zero bytes before revision parsing even starts. Same audit and same patch as the checkout/tag issue above covers it.

GitPython's Repo.archive() denylist blocks --exec but misses --add-file/--add-virtual-file, which read arbitrary files into the output archive

Third from the sweep: `Repo.archive()`'s guard runs and blocks the obvious options (`--exec`), but the denylist it consults is incomplete — `--add-file` and `--add-virtual-file` still let arbitrary files get read into the generated archive. A present guard isn't automatically a complete one; patch GitPython for all three findings at once.

ip-address's special-use classifiers all short-circuit to false when the parsed address carries a shorter CIDR suffix than the reference range

Second ip-address bug: every classifier (`isPrivate`, `isLoopback`, `isLinkLocal`, etc.) is built on `isInSubnet`, which returns false whenever the input's own mask is shorter than the range it's being checked against — so appending `/0` to any address silences the library's SSRF/trust-boundary checks entirely.

ip-address's Address6 classifies by the IPv6 wrapper rather than the embedded IPv4 address, hiding loopback and link-local literals

Third ip-address bug in the same disclosure batch: IPv4-mapped (`::ffff:0:0/96`) and NAT64 (`64:ff9b::/96`) literals like `::ffff:127.0.0.1` or `::ffff:169.254.169.254` — the cloud-metadata address — all report false for `isLoopback`/`isLinkLocal`/`isMulticast`. Three independent classification failures in one library in one day; patch ip-address once and re-audit anywhere it gates an SSRF or trust boundary.

Whitespace around the = in a qualified Cache-Control directive drops the directive or corrupts the parsed field name

`no-cache ="authorization"` (space before `=`) or `no-cache= "authorization"` (space after) either gets dropped entirely or stored with literal quote characters in the field name, so the qualification never applies downstream and the response gets shared-cached anyway — cross-user disclosure of whatever the directive was meant to exclude.

undici's setCookie has two attribute-injection paths via unsanitized domain and unparsed fields

`validateCookieDomain` doesn't reject semicolons the way `validateCookiePath` does, so a domain value like `example.com; SameSite=None` lands verbatim; separately, the `unparsed` field loop only checks for an `=` sign and doesn't sanitize the value, letting entries like `X-Custom=val; HttpOnly` inject an attribute the caller never set.

undici's retry interceptor can deliver a response whose body length doesn't match the Content-Length header the app sees

Last of today's five undici advisories: `interceptors.retry()` can hand back a response with a stale `Content-Length` after a retry or partial-response resume, which desyncs anything proxying or forwarding that response downstream — gateway and proxy apps using retry() are the ones exposed. Worth one coordinated undici bump tonight rather than patching piecemeal.

Fake installers for the Xeno Roblox script executor push infostealer and RAT malware to players

Consumer-facing malware distribution rather than a registry-ecosystem attack, but the same fake-tool-download pattern behind this month's corepack-impersonation campaign: attackers bank on people searching for a tool and grabbing the first installer that looks right instead of the real one. No engineering action needed; the pattern is worth recognizing since it's the cheapest lever this threat class keeps reusing.

14:00 ET · Forenoon Watch

brace-expansion's maxLength fix only bounds the final combine step; two unbounded upstream arrays still let a small pattern OOM-crash or stall the process

The `maxLength` cap added for CVE-2026-14257 only bounds the final `combine()` step; two arrays built upstream of it — comma-alternative concatenation and padded-sequence generation — are still unbounded, so a ~25KB brace pattern triggers an uncatchable out-of-memory crash and a ~400KB pattern can stall the event loop for over two minutes. try/catch around expand() does not help, and every major line (1.x through 5.x below the patched cutoffs) is affected. Upgrade to the patched release for your line, and if you can't yet, don't pass untrusted input into glob/brace-expansion at all — a small maxLength alone was already shown insufficient.

Angular's i18n attribute-translation path lets i18n-onclick/i18n-onerror smuggle event handlers past the compiler's own XSS guard

Angular blocks direct bindings to event-handler attributes like onclick and onerror, but the i18n metadata-collection path never applied that same check to the i18n-on* translation variants, so translated templates can carry an executable handler straight past the compiler's guard. Affects @angular/core and @angular/compiler across 19.x–22.x; fixed in 19.2.25+/20.3.27+/21.2.19+/22.0.1+. If your templates use any i18n-* attributes, upgrade and re-audit translated markup for injected handlers.

@angular/platform-server's DOM emulation library fails to escape content it serializes raw inside iframe/noembed/noframes/noscript during SSR

@angular/platform-server's DOM emulation dependency (domino) doesn't escape content when it falls back to serializing it raw inside <iframe>, <noembed>, <noframes>, or <noscript> during server-side rendering, so attacker-controlled content rendered into any of those tags can break out as script on the client. It's the classic fallback-serialization gap — the one path nobody audits because it's not the main render path. Patch to platform-server 19.2.25+/20.3.27+/21.2.19+/22.0.7+ if you SSR user-controlled content anywhere near those four tags.

HttpTransferCache's SSR-to-hydration cache key doesn't fully disambiguate requests, so one request's cached response can be replayed into a different request's hydration

HttpTransferCache caches HTTP responses made during SSR for reuse at client hydration, but its cache key doesn't fully disambiguate distinct requests, so two different calls can collide and one request's cached response gets replayed into another's hydration. That's cross-request state poisoning if any of the cached data is user- or session-specific — quiet until it leaks one user's response into someone else's page. Patch @angular/common and check whether anything you SSR-cache carries per-user or per-session data.

russh's server-side channel callbacks can be invoked without a channel actually being open

Channel-scoped server callbacks in russh can be triggered without an open channel, letting a client reach channel-handler logic that's supposed to be gated by the connection's own state machine. The advisory is thin on exploitation detail, so treat the blast radius as unknown for now — worth checking if you run russh as an SSH server (not just a client). Watch the advisory for the patched version and follow-up detail before deciding how urgent this is for you.

PostCSS's traversal guard for sourceMappingURL only runs when the from option is set, so calling process() without it reopens the arbitrary-file-read bug the prior fix closed

The fix for GHSA-6g55 added a path-traversal guard to PostCSS's sourceMappingURL handling, but that guard only runs when the `from` option is set; call postcss().process() without `from` and attacker-controlled CSS with a crafted sourceMappingURL comment can read arbitrary .map files via absolute paths or ../ traversal, leaking their sources and sourcesContent into the generated map. Hits anything that runs PostCSS on untrusted CSS without passing from — online CSS playgrounds, minify-as-a-service, string-input build steps. Pass from explicitly or upgrade past 8.5.22, and check whether you expose result.map anywhere.

06:00 ET · Morning Watch

Three Diffusers bugs let a crafted model repo silently execute code despite trust_remote_code=False

Zafran Security found three ways to bypass Diffusers' trust_remote_code safeguard in DiffusionPipeline.from_pretrained — the gate check landed in the wrong function, so a model repo pulled from the Hub can run arbitrary code even when the caller passes trust_remote_code=False or omits it entirely. Tracked as CVE-2026-44513 (CVSS 8.8), it's the same trust-boundary failure as pickle-based model deserialization attacks, except here the developer explicitly opted out of remote code execution and got it anyway. Pin diffusers>=0.38.0 and audit any pipeline that loads third-party Hub repos — the safeguard you thought you had wasn't actually enforced.