v vanemmerik.ai / SUPPLY-CHAIN
Supply Chain · Watch Thursday · 17 September 2026 End-of-day synthesis 4 watches · 75 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 — One stolen Cloudflare key weaponized Brevo's own widgets against 100,000+ customer sites, while Grav CMS absorbed a single-day, twenty-one-CVE disclosure spanning zip-upload RCE to leaked 2FA secrets.

Today's watch is one of the year's largest single-day batches — 75 items across three passes — but two threads carry it: one active in-the-wild campaign, one vendor absorbing what reads like a professional audit dropped all at once.

Brevo confirmed a hardcoded, full-permission Cloudflare API key let attackers run a malicious edge Worker for five and a half hours on September 14, turning the company's own forms script, chat widget, and SDK loader into a dropper for 100,000+ customer sites — ClickFix lures for every visitor, a backdoored WordPress plugin for logged-in admins. Grav CMS absorbed the other half of the day: twenty-one CVEs since this morning, from a callable-validation gap that turns a crafted zip upload into RCE to three separate ways the "sandboxed" Twig layer leaks the admin password hash and TOTP secret.

The bright spot: none of today's Grav bugs have a confirmed in-the-wild exploit yet, and CISA KEV added nothing new since yesterday's three entries. Operational priority for the night: if you or your customers embed any Brevo script — forms, chat widget, or SDK loader — check WordPress installs for the "Web Media Optimizer" plugin and block yelahaye[.]surf, boiseno[.]club, and cdn10.sendibt1[.]com before anything else on this list.

18:00 ET · First Watch

Brevo — stolen Cloudflare API key turns its embedded widgets into a live malware dropper for 100,000+ customer sites

A long-lived Cloudflare API key with full account permissions was hardcoded in Brevo's application source; attackers used it to stand up a malicious Cloudflare Worker that rewrote content at the CDN edge for ~5.5 hours on September 14, hitting brevo.com itself plus the Brevo forms script, Conversations widget, and SDK loader that 100,000+ customer sites embed. Visitors got a fake Cloudflare verification page into a ClickFix clipboard-paste lure, and on WordPress sites the script fingerprinted logged-in admins and pushed a backdoored plugin ("Web Media Optimizer", staged from cdn10.sendibt1[.]com) that persists after the Worker is gone. This is the polyfill.io shape again — compromise one shared script, inherit every site that embeds it — except the blast radius here runs through a marketing/CDP vendor, not a CDN; if you or your customers embed Brevo's forms, chat widget, or SDK loader, check WordPress installs for the Web Media Optimizer plugin and block yelahaye[.]surf, boiseno[.]club, and cdn10.sendibt1[.]com now.

Grav — blueprint dynamic-data bare-function branch is denylist-gated and omits error_log, giving arbitrary file write

Grav's blueprint dynamic-data evaluator denylists dangerous PHP functions when a callable is given as a single string, but `error_log()` isn't on that list and its third argument writes attacker-controlled content to an attacker-controlled path — an authenticated admin-panel user can drop a webshell anywhere the PHP process can write. Fifth Grav critical/high disclosure in this batch alone (part of a ~21-advisory single-day drop); no patched version yet, so pull the plugin/admin panel offline for untrusted editors until one ships.

Grav — detectXss() misses an event-handler attribute after an unpaired quote in an unquoted attribute value, giving stored XSS

An unquoted HTML attribute value containing a single stray quote desyncs Grav's `detectXss()` tokenizer from the browser's own attribute parser, so a trailing `onerror=` (or similar) handler is invisible to the filter but still executes in the victim's browser. Same root cause as the invalid-UTF-8 and quoted-attribute bypasses disclosed alongside it today — `detectXss()` is a regex gate that Grav is now shipping four separate parser-desync bypasses for in one batch. No fix yet; anyone who lets non-admin users write page content on Grav is exposed until one lands.

CakePHP — multiple FunctionsBuilder methods vulnerable to SQL injection

Several methods on CakePHP's query `FunctionsBuilder` build SQL function calls by concatenating caller-supplied arguments instead of parameterizing them, so any code path that passes user input into those builder methods is injectable regardless of how careful the surrounding query looks. Audit for `FunctionsBuilder` calls fed by request data — this is the kind of bug that hides inside an otherwise-parameterized query. Check the advisory for the exact patched version and upgrade.

Chamilo LMS — CStudio upload flow allows unauthenticated remote code execution

Chamilo's CStudio course-authoring upload flow accepts and processes files without authentication, giving an unauthenticated attacker a direct path to code execution on the server. If you run Chamilo LMS with CStudio reachable from the internet, treat this as an active-exploitation risk today, not a someday-patch item — take the upload endpoint offline or restrict it to trusted networks until a fix lands.

Grav — remote code execution via crafted .zip upload through a callable-validation gap

Grav's plugin-settings callable check only recognizes a dangerous routine when it's written as one string; naming it as a two-part array (component + method) skips both the denylist and the component allowlist entirely, so a logged-in user can point that field at Grav's own plugin-install unzip routine and land an uploaded PHP file straight into the public webroot. Uploads aren't content-inspected, so getting the payload in is trivial — zip a `.php` file, upload it as ordinary media, then chain the settings-save trick to trigger extraction. Any authenticated user achieving RCE (not just admins) makes this the most severe of today's Grav batch even at GHSA's "high" rating; there's no patched version, so restrict media uploads and plugin-settings writes to fully trusted accounts now.

Grav — missing admin.super guard lets an admin.users operator escalate to full super-admin

The core-group blueprint's access-control field isn't gated behind `admin.super` the way it should be, so an account scoped only to `admin.users` can edit its own group membership into the super-admin group with no further privilege check. Straightforward horizontal-to-vertical privilege escalation for any multi-admin Grav install; no fix yet, so audit who currently holds `admin.users` and treat that as equivalent to super-admin until patched.

Grav — unauthenticated path traversal in the plugin-asset-map static file server

`plugin-asset-map.php`'s static-asset endpoint resolves a caller-supplied filename against the assets directory without a directory-boundary check, so an unauthenticated request with `../` sequences reads arbitrary files off disk with the web server's privileges. No auth required, no patched version yet — if you run this endpoint publicly, front it with a reverse-proxy rule that strips `../` until Grav ships a fix.

Grav — path traversal in MediaUploadTrait::deleteFile() allows arbitrary file deletion

The same missing-boundary-check pattern as the asset-map traversal, this time on the delete path: `MediaUploadTrait::deleteFile()` doesn't confirm the resolved path stays inside the media directory, so a crafted filename with `../` deletes files anywhere the PHP process can write. Requires the media-upload permission rather than being fully unauthenticated, but combined with the RCE-via-zip bug above, an attacker with that permission has both write and delete primitives outside the intended sandbox. No patched version yet.

Grav — media_directory() Twig function allows filesystem path traversal and file content disclosure from sandboxed page content

The `media_directory()` Twig function is exposed inside Grav's supposedly-sandboxed page-content templates but doesn't constrain the directory argument, so a lower-privileged content author can walk it outside the media tree and read arbitrary files. Third bug in today's batch where the Twig content sandbox turns out not to be a sandbox for a specific function; treat every whitelisted Twig helper as a potential traversal primitive until Grav audits the full list. No patched version yet.

Grav — UserInterface offsetGet/offsetExists allow-listed in the Twig sandbox leak hashed_password and 2FA secrets to editor-authored content

Grav's Twig sandbox allow-lists `UserInterface::offsetGet()`/`offsetExists()` for use inside page templates, but those methods return raw user-object fields including the password hash and TOTP secret with no field-level restriction — any account that can write page content can render `{{ user['hashed_password'] }}` and exfiltrate credentials for offline cracking or a live 2FA bypass. Pairs directly with the two Grav secret-leak bugs disclosed alongside it (config_denied_paths and the system/site/theme Twig variables); if any non-trusted user can edit pages, rotate admin passwords and 2FA secrets once this lands. No patched version yet.

Grav — config_denied_paths default list omits `system`, exposing secrets like system.cache.redis.password via the Twig sandbox

When `config_access` is enabled for content authors, Grav's `config_denied_paths` blocklist is supposed to keep sensitive config namespaces out of reach — but the default list never includes `system`, so `system.cache.redis.password` and any other credential stored under that namespace is readable straight from sandboxed Twig. A one-line default-config gap with real credential exposure; add `system` to `config_denied_paths` yourself now rather than waiting for the patch.

Grav — the system, site, and theme Twig variables bypass the content sandbox entirely and are never covered by config_denied_paths

Even with `config_denied_paths` fixed and fully populated, the `system`, `site`, and `theme` Twig variables are wired outside the sandbox's enforcement path altogether, so sandboxed page content can read them regardless of any denylist. This is the third and most structural of today's three Grav Twig-secret-leak advisories — the denylist approach doesn't work if some variables never pass through it; disable `twig_content.process_enabled` for any site where page authors aren't fully trusted until Grav redesigns the sandbox boundary.

Grav — path traversal in ImageMedium::watermark() enables arbitrary file disclosure via publicly-cached images

The watermark image path passed to `ImageMedium::watermark()` isn't validated against the media directory boundary, and because Grav publicly caches the processed output, an attacker can traverse to an arbitrary file and have Grav serve its contents back as a cached "image" at a public URL — no direct file read needed, just a crafted watermark request. No patched version yet; disable watermarking or restrict who can trigger it until one ships.

Grav — incomplete callable validation in blueprint dynamic fields allows arbitrary static method invocation and file disclosure

A second, broader gap in the same blueprint dynamic-field callable check that enabled today's zip-upload RCE: validation still misses static-method call syntax in enough cases to let an authenticated user invoke arbitrary static methods across the codebase, which chains into file disclosure even where the RCE-specific fix has landed. If you patch only GHSA-r94f (the zip RCE), this sibling gap likely survives — treat the whole callable-validation subsystem as needing a rewrite, not a patch.

CoreDNS — DoH/DoQ/gRPC transports bypass the UPDATE-rejection policy enforced on UDP/TCP

CoreDNS's rejection of DNS UPDATE messages is wired into the classic UDP/TCP listeners but not into the newer DoH, DoQ, and gRPC transports, so a server exposing those transports accepts UPDATE requests it was configured to reject — a policy-enforcement gap introduced by adding new transports without threading the same guard through. Confirm which transports you expose and that UPDATE rejection applies to all of them, not just the ones CoreDNS has supported longest.

CoreDNS — unauthenticated memory exhaustion in custom transports

A CoreDNS custom-transport plugin path allows an unauthenticated remote caller to force unbounded memory allocation, giving a straightforward DoS against any deployment using the affected transport. Second CoreDNS advisory in today's batch; check which transport plugins you've enabled and prioritize the patch for internet-facing resolvers.

react/http — a malformed HTTP chunked body can lead to denial-of-service and peg the CPU

react/http's chunked-transfer-encoding parser mishandles a malformed body in a way that spins the CPU rather than rejecting the request cleanly, so a single crafted request can peg a worker's CPU and degrade or deny service for everyone sharing it. Unauthenticated by nature of being an HTTP-layer parser bug — patch before this shows up as an unexplained CPU spike in production.

HAPI FHIR — SHCParser DEFLATE infinite loop causes denial of service

HAPI FHIR's SMART Health Card parser (`SHCParser`) can enter an infinite loop when decompressing a crafted DEFLATE stream, hanging the thread that processes it. Paired with a second, related DEFLATE bug in the same parser disclosed today (unbounded decompression) — if you accept SMART Health Cards (vaccine/health credential QR payloads) from untrusted sources, both need the same fix.

HAPI FHIR — SHCParser unbounded DEFLATE decompression causes denial of service

The same `SHCParser` decompresses a caller-supplied DEFLATE stream with no output-size cap, so a small compressed SMART Health Card payload can expand into a memory-exhausting decompression bomb — the classic zip-bomb shape applied to health-credential QR codes. Fix both this and the sibling infinite-loop bug in the same parser together; they're the same untrusted-input trust boundary.

djust — a template binding inherits a context safety grant it never earned, enabling XSS

djust's template-binding resolution lets a value inherit an "already safe, don't escape" flag from an unrelated part of the rendering context instead of only from its own declared source, so attacker-controlled data can ride along on another binding's trust and render unescaped. First of two djust template-layer XSS advisories today; both point at the same class of trust-propagation bug in the binding resolver.

djust — six template-layer defects emit attacker-controlled markup unescaped

Six separate escaping gaps across djust's template layer independently let attacker-controlled markup reach the DOM unescaped — not one bug repeated, six distinct sinks. If you use djust for user-influenced templates, this is worth a full audit pass rather than a single patch-and-move-on; check the advisory for which template constructs are affected.

Steeltoe.Discovery.Consul — malformed 'secure' metadata field aborts service instance lookup

A Consul service-instance entry with a malformed `secure` metadata value throws unhandled during Steeltoe's discovery lookup instead of being skipped or defaulted, so one bad registration in the Consul catalog can break service discovery for everything depending on that lookup. If Consul registration is writable by less-trusted services, this is a discovery-layer DoS, not just a data-quality bug.

Steeltoe.Discovery.Eureka — malformed enum/bool/timestamp field aborts the entire registry fetch

Same failure shape as the Consul advisory disclosed alongside it, but for Eureka: a single malformed enum, boolean, or timestamp field anywhere in the registry response aborts parsing of the whole fetch rather than just that one instance. One bad Eureka registration can take down discovery for every consumer polling that registry — check both Steeltoe.Discovery advisories together if you run either.

Jupyter Server — 5xx request logging leaks token-bearing Referer header values

Jupyter Server logs the full `Referer` header on 5xx responses, and Jupyter's own UI puts the auth token in the URL, which browsers copy into `Referer` on subsequent requests — so a server error during normal use writes a live auth token straight into server logs. Anyone with log access (including log aggregation pipelines and their retention policies) inherits token-level access to the notebook server. Rotate tokens if you've been running an affected version with centralized logging.

libp2p — PeerStore accepts attacker-signed PeerRecords for a victim peer ID and stores certified attacker addresses

libp2p's PeerStore doesn't verify that a PeerRecord's signature actually corresponds to the peer ID it's filed under strongly enough, letting an attacker submit a record signed with their own key but labeled with a victim's peer ID; the store then treats attacker-controlled addresses as certified for that victim. Downstream consumers that trust PeerStore-certified addresses for routing or connection can be redirected to attacker infrastructure while believing they're reaching the legitimate peer. Pairs with a second libp2p signature-trust bug (Gossipsub StrictSign) disclosed the same day — same underlying pattern of insufficient signer/subject binding.

libp2p — Gossipsub StrictSign mode accepts attacker-signed messages as a victim RSA peer ID

Gossipsub's StrictSign verification mode is supposed to guarantee a message's claimed origin matches its cryptographic signer, but for RSA peer IDs specifically the check can be satisfied by an attacker's own valid signature over a message claiming a victim's peer ID as sender. Same signer/subject-binding gap as today's PeerStore advisory, in a different libp2p subsystem — if you rely on StrictSign for message provenance in a pubsub application, both need patching together.

SSH.NET — ScpClient allows server-side remote code execution via default SCP path handling

SSH.NET's `ScpClient` builds the remote-side SCP command from a path without sufficiently neutralizing shell metacharacters under its default handling, so a client connecting to (or accepting a path from) a malicious or compromised SCP server can have that path turn into command execution on the client's own side. Check the advisory for the exact attack direction and patched version — this is one of the less common "client gets pwned by the server" SSH bug shapes.

Fulgur — non-painting replaced elements amplify to thousands of blank PDF pages, causing denial of service

Fulgur's PDF layout engine can be coaxed by non-painting replaced elements (e.g. hidden iframes/objects) into generating thousands of blank pages for a single small input document, exhausting memory or disk on the rendering service. Pairs with a second Fulgur DoS disclosed the same day via CSS-driven page slicing — both are amplification bugs in the same PDF-generation pipeline, so treat them as one fix rather than two.

Fulgur — unbounded page slicing from attacker-controlled CSS height causes denial of service

An attacker-controlled CSS height value drives Fulgur's page-slicing logic with no upper bound, so a crafted stylesheet forces the renderer to slice a document into an unbounded number of pages. Same amplification-to-DoS family as the non-painting-elements bug disclosed alongside it — if you render user-supplied HTML/CSS to PDF with Fulgur, cap both input dimensions and output page count at the application layer until upstream ships a fix.

oras-go — arbitrary file write outside file.Store root via symlink-chain bypass in tar extraction

`file.Store`'s tar extraction (used by `pushDir`) follows a chain of symlinks inside the archive to write outside the intended store root, defeating the path-containment check that's supposed to keep extracted content sandboxed. Pulling an OCI artifact from an untrusted or compromised registry can now write files anywhere the process has permissions — treat registry pulls the same way you'd treat any other untrusted-archive-extraction trust boundary. Upgrade oras-go.

AsyncHttpClient — unbounded HTTP/1.1 response decompression enables a decompression-bomb denial of service

AsyncHttpClient decompresses HTTP/1.1 responses with no cap on the inflated output size, so a small compressed response from a malicious or compromised server expands into a memory-exhausting bomb on the client side. The most severe of today's three AsyncHttpClient advisories (the other two are credential-leak bugs) — if you fetch from any server you don't fully control, this one matters most.

Grav — a single invalid UTF-8 byte disables every rule in Security::detectXss(), bypassing the page-content XSS safety gate

One malformed UTF-8 byte anywhere in the input makes Grav's regex-based `detectXss()` fail closed to "no XSS found" instead of failing safe, so prepending garbage bytes to a payload defeats the entire filter at once rather than needing a payload-specific bypass. Cheapest of today's four `detectXss()` bypasses to exploit — it's a one-byte prefix, not a crafted markup trick. No fix yet.

Grav — stored XSS via Markdown audio/video media <source> URL

Grav's Markdown media-embed handling for audio/video doesn't sanitize the `<source>` URL it generates, so a crafted media reference persists a stored XSS payload for anyone who views the rendered page. Third `detectXss()`-adjacent Grav bypass in today's batch; no patched version yet.

Grav — stored XSS via quoted-attribute bypass in detectXss

A crafted quoted-attribute value slips past `detectXss()`'s pattern matching the same way the unquoted-attribute and invalid-UTF-8 variants do — fourth and last of today's `detectXss()` bypass batch. At this point the function's regex approach itself is the finding, not any individual payload shape; no fix yet.

oras-go — blind SSRF via unvalidated Link header URL in pagination

oras-go follows a `Link` header URL during registry pagination without validating scheme or destination, so a malicious or compromised registry can redirect the client's next paginated request to an internal address. Blind (no response body returned to the attacker), but still useful for internal network probing and confirming service reachability behind status-code timing. Upgrade and, if you can, pin registry pagination to expected hosts.

Soup Sieve — polynomial-time ReDoS in the IDENTIFIER/VALUE selector sub-patterns

Soup Sieve's CSS-selector parser has an O(n²) worst case in the regex sub-patterns matching identifiers and attribute values, so a crafted selector string (not the HTML being parsed — the selector itself) causes disproportionate CPU time. Relevant if selector strings are ever derived from user input rather than hardcoded by your own code. Paired with a second Soup Sieve ReDoS disclosed the same day in the whitespace-trimming regex.

Soup Sieve — polynomial-time ReDoS in the whitespace/comment trimming regex, triggers on valid selectors

Notably, this one triggers on ordinary well-formed selectors, not just adversarial input — the whitespace/comment-trimming regex (`RE_WS_END`) has quadratic behavior on certain valid selector strings, so performance can degrade even without a deliberately crafted attack string as selector length grows. Worth a look even if you don't accept user-supplied selectors, since it can surface as an unexplained slowdown on legitimate input.

@platejs/core — HTML deserialization can trigger browser behavior during parsing

Deserializing untrusted HTML into @platejs/core's editor state can trigger active browser behavior (e.g. resource loads or handler execution) during the parse step itself, before any application-level sanitization runs. If your editor accepts pasted or imported HTML from untrusted sources, check the advisory for which deserialization entry points are affected and whether pre-sanitization closes the gap.

OpenTelemetry-Go — log gRPC exporter ignores environment TLS certs, bypassing mTLS/pinning

The OpenTelemetry-Go log exporter's gRPC transport doesn't honor TLS certificates configured via environment variables, silently falling back to a weaker default trust configuration instead of the mTLS or certificate-pinning setup an operator intended. Fails open rather than erroring, which is the dangerous direction for a security-relevant config gap — verify your collector's actual negotiated TLS configuration rather than trusting the environment variables you set.

Steeltoe — header-forwarded client cert lacks proof of private-key possession

Steeltoe accepts a client certificate supplied via a forwarded header (the standard reverse-proxy mTLS-termination pattern) without any check that the presenter actually holds the corresponding private key, so anyone who can set that header — a misconfigured internal proxy, or a request that reaches the app directly — can impersonate any certificate identity by value alone. Confirm your proxy strips and re-sets this header rather than passing through an inbound one, and that the app isn't reachable except through that proxy.

Steeltoe.Management.Endpoint — HttpExchanges URI masking leaks query-string secrets

The actuator-style HttpExchanges endpoint's URI-masking logic doesn't strip sensitive query-string parameters (API keys, tokens passed as query params) before exposing recorded request URIs, so anything that leaked into a URL is visible to anyone who can read that management endpoint. Audit whether your app ever puts secrets in query strings — this endpoint will have logged them regardless of the masking feature's intent.

Svelte devalue — denial of service via malformed input

Svelte's `devalue` serialization library can be driven into pathological resource consumption by malformed input during deserialization, giving a DoS vector anywhere devalue payloads cross a trust boundary (SSR hydration data from an untrusted source, for instance). Check the advisory for the specific input shape and whether your app ever deserializes devalue payloads that didn't originate from your own server.

Kestra — unauthenticated management/actuator endpoints exposed on port 8081 bypass API basic-auth

Kestra's `/env` and `/loggers` management endpoints listen on a separate port (8081) that isn't covered by the basic-auth configured for the main API, so an operator who locked down the API port can still be leaking environment variables and runtime logger configuration on the management port without realizing it. Firewall port 8081 to trusted networks only — the fix for the auth gap doesn't help if the port itself stays exposed.

vLLM — request-selected PyNvVideoCodec GPU decode bypasses static VRAM reservation

When a request selects the PyNvVideoCodec GPU decode path, vLLM's static VRAM reservation accounting doesn't account for that path's allocations, so a caller can drive GPU memory usage past the limit the operator configured — a resource-exhaustion vector distinct from the CPU-side audio decompression-bomb bug disclosed on vLLM this morning. Same lesson as that one: a safety guard wired into one code path doesn't automatically cover a newer sibling path.

AsyncHttpClient — re-sends client-wide realm credentials to a cross-origin redirect target

AsyncHttpClient follows redirects and re-sends the realm credentials configured for the original request even when the redirect target is a different origin, leaking those credentials to whatever host a server-controlled or MITM'd redirect points at. If you configure client-wide auth and follow redirects, an attacker who can influence the redirect destination gets your credentials for free.

AsyncHttpClient — sends origin credentials to the proxy on the plaintext CONNECT request

Origin-destined credentials go out on the initial plaintext `CONNECT` request to a proxy before the TLS tunnel is established, so anyone positioned to see that CONNECT request (the proxy operator, or anyone on the path to it) sees the credentials in the clear. Third AsyncHttpClient advisory in today's batch alongside the cross-origin-redirect credential leak and the unbounded-decompression DoS — worth a single upgrade pass covering all three.

Grav — non-constant-time nonce comparison in Utils::verifyNonce() used for CSRF protection

Grav's CSRF nonce check uses a standard `==`/`===` string comparison instead of a constant-time compare, opening a timing side-channel that could in theory help an attacker recover a valid nonce byte-by-byte. Low practical severity — reliably exploiting timing side-channels over a network is hard — but a clean, cheap fix (swap in `hash_equals()`); rated low by GHSA and rolled into today's Grav backlog rather than a standalone priority.

Grav — origin validation bypass in Uri::referrer() and Pages::referrerRoute() via unanchored prefix match

Grav's referrer-based origin check uses an unanchored prefix match, so `https://trusted.example.com.attacker.net` passes a validation meant to require exactly `trusted.example.com`. Low-severity on its own — referrer headers are client-controlled and weak as a security boundary to begin with — but worth knowing if any custom plugin leans on `referrerRoute()` for access decisions.

AsyncHttpClient — doesn't verify SCRAM and Digest mutual-authentication responses

AsyncHttpClient accepts SCRAM and Digest auth exchanges without verifying the server's own mutual-authentication proof, so the client can't actually confirm it's talking to the legitimate server rather than a MITM that knows the shared secret's public parameters. Rated low by GHSA and the least urgent of today's three AsyncHttpClient findings, but worth closing in the same upgrade pass as the decompression-bomb and credential-leak bugs.

OpenTelemetry-Go — exporter config logging may leak endpoint URLs in info logs

OpenTelemetry-Go's exporter logs its own configuration at info level, including endpoint URLs that may embed credentials or otherwise-sensitive routing details, into logs that are often shipped somewhere with broader read access than the app itself. Low severity but a good prompt to check whether any of your OTel endpoint URLs carry embedded auth — if they do, that's now sitting in your log pipeline.

12:00 ET · Forenoon Watch

Marten — SQL injection via unescaped string literals in the LINQ provider

Marten's LINQ provider string-interpolates a caller-controlled value — most commonly a `Dictionary<,>` indexer key in `Where(x => x.Attributes[key] == v)`, a common EAV ‘filter by attribute name’ pattern — straight into a quoted SQL literal with no escaping or parameterization. A confirmed PoC turns a single quote in that key into cross-tenant authorization bypass and blind data exfiltration, and where Npgsql permits `;`-batched statements, data modification too. Six separate sinks are affected; upgrade past 9.12.0 and audit any EAV-style filter you build from user input.

Vendure — external-authentication account takeover via unverified email linking

Vendure links an external-auth login (OAuth/social/SSO) to a pre-existing customer account by email address alone, with no requirement that the identity provider verified that email — if your `AuthenticationStrategy` forwards an unverified address, an attacker who registers it with the external provider inherits the victim's existing account with no interaction required. Only deployments with a custom external strategy are affected; native email/password-only stores are safe. First of four Vendure advisories in today's batch — upgrade @vendure/core to 3.7.0 and confirm any custom strategy sets `verified: true` only for provider-confirmed emails.

Vendure — unauthenticated ReDoS via the Shop API regex filter (SQLite backends)

Vendure's Shop API exposes a `regex` filter evaluated as a synchronous SQLite user-defined function inside the Node event loop — a catastrophically backtracking pattern blocks the entire event loop with zero authentication required. Only SQLite-backed deployments are affected (typically dev/test; confirm you're not running SQLite in production); Postgres/MySQL are safe. Second of four Vendure advisories today; upgrade past 3.6.4.

Vendure — stored XSS in the Admin Dashboard via unsafe innerHTML stripping

The Admin Dashboard “strips HTML” from entity descriptions by assigning them to `innerHTML` and reading back `textContent` — but `innerHTML` still executes active markup before anything is read back, so `<img src=x onerror=...>` fires in the browser of whoever views the list. A lower-privileged admin can plant a payload that compromises a higher-privileged admin's session. Third of four Vendure advisories today; upgrade @vendure/dashboard past 3.6.4.

Tina — broken access control lets any TinaCloud user authorize against any self-hosted site

`@tinacms/auth`'s `isAuthorized()` checks a bearer token against whatever TinaCloud app id the caller names in the request, never against the site's own configured app id — so any TinaCloud user can mint a token for their own free app and use it to authorize against someone else's self-hosted site with zero victim interaction. Grants read/upload/delete on the victim's media bucket, and full GraphQL read/write/delete when the default `TinaCloudBackendAuthProvider` is in use. This is pre-auth account-boundary confusion, not a theoretical misconfiguration — patch `@tinacms/auth` and `next-tinacms-azure` now.

PocketBase — unhandled panic in internal worker goroutines can kill the server

PocketBase's panic-recovery middleware only covers the request-handling path; a panic in an internal worker/child goroutine was uncaught and could terminate the whole server process. Fixed by wrapping every internal worker function in a new `routine.SafeWrap` helper. Upgrade to 0.39.7 (or 0.22.48 if pinned pre-0.23).

Wire — unauthenticated decoder crash via 32-bit length overflow (incomplete prior fix)

Wire's fast-path protobuf reader (`ByteArrayProtoReader32`) reads an attacker-controlled varint length into a signed 32-bit int and only rejects negative values — a length like 2147483647 passes that check, then `pos + length` overflows into a negative limit its own bounds guard doesn't catch, and malformed input throws an unchecked exception instead of a clean decode error. Sibling of the already-patched CVE-2026-45799 (different overflow, same family); if you handle untrusted protobuf with Wire, check you're catching decode exceptions broadly, not just `IOException`. Upgrade past 6.4.4, or to 7.0.0-alpha04+.

RabbitMQ Java client — frame-size cap defeated by a server-supplied frameMax=0

`Math.min(maxInboundMessageBodySize, frameMax)` with a server-supplied `frameMax=0` (AMQP's ‘unlimited’ sentinel) evaluates to 0, and the downstream `framePayloadLimit(0)` treats 0 as ‘no limit’ and returns `Integer.MAX_VALUE` — so a malicious or MITM'd broker completely defeats the client's 64MB frame cap, and a single frame with a ~500MB size field triggers an OOM allocation before any content check runs. A sentinel-value collision: 0 means two different things to two different functions in the same call chain. Upgrade `amqp-client` to 5.34.0+.

@cyclonedx/cyclonedx-npm — Windows shell injection via --workspace argument

`cyclonedx-npm`'s `--workspace` argument reaches a Windows-only shell-execution fallback with no sanitization, so a crafted workspace name breaks out into arbitrary OS command execution with the invoking user's privileges. Windows-only; Linux/macOS CI runners are unaffected. If you generate SBOMs on Windows build agents and the workspace name is ever influenced by external input — a PR branch name, say — upgrade to 6.0.0 before your next run.

Redocly CLI — path traversal via the split command

The `split` command writes files derived from an OpenAPI/AsyncAPI description without constraining the output path, so a malicious spec can plant or overwrite files outside the chosen output directory — constrained (YAML/JSON content, method-derived filenames) rather than a free write primitive, but still a real risk if you run `split` against specs you didn't author. Grep component names and `x-codeSamples` `lang` values for a literal `../` to detect an attempt. Upgrade to 1.34.17 or 2.33.2+.

Grav — decompression-bomb size cap bypassed by a forged ZIP central-directory size

Grav 2.0.1's decompression-bomb cap sums the ZIP central directory's declared uncompressed size and rejects archives over 1GiB before extracting — but that declared size is attacker-forgeable and is never checked against the real inflated stream, so an archive claiming 1 byte per entry sails through the cap while `extractTo()` writes the actual, much larger content. Fourth Grav advisory in today's batch, and it's an incomplete fix of the earlier decompression-bomb mitigation — the entry-count and nesting-depth caps still hold; only the size dimension is defeated. No patched version yet.

LiteLLM Proxy — SSRF via api_base smuggled inside user_config

LiteLLM Proxy's request-body guard blocks `api_base`/`base_url` at the top level but never inspects the same keys nested inside `user_config`, so an authenticated caller can smuggle an `api_base` there and redirect the proxy's outbound request to an arbitrary host. Requires a valid virtual key, so this is caller-to-internal-network pivoting, not unauthenticated SSRF. Upgrade to 1.83.9+.

oRPC — Vary header injection in the CORS plugin

oRPC's CORS plugin copied the request's `Vary` header straight onto the response instead of treating it as server-controlled, letting a client pollute cache-key behavior on any shared cache or CDN in front of an oRPC server. Real-world impact is limited to deployments that key shared caches on `Vary`. Upgrade `@orpc/server` to 1.14.8.

Nuxt OG Image — unauthenticated SSRF via fonts[].path, reaches cloud metadata endpoints

`nuxt-og-image` decodes a `fonts[].path` URL from an unauthenticated endpoint and passes it straight into a server-side `fetch()` with no scheme, loopback, or RFC1918 filtering — under the module's documented defaults, anyone who can reach the site can make it fetch cloud metadata endpoints (AWS IMDS, GCE/Azure, kubelet) or internal admin panels. The response itself is blind, but a status-code side channel (500 vs 200) is enough to enumerate live internal services and confirm IMDSv1 reachability. Upgrade past 6.7.0, or set `security.strict = true` in the meantime.

AsyncSSH — event-loop freeze via a peer-supplied maximum packet size of 0

A peer that sends a channel ‘maximum packet size’ of 0 wedges AsyncSSH's synchronous send-buffer flush into an infinite loop with no `await`, freezing the entire asyncio event loop — a malicious server can wedge a client, and one malicious authenticated channel can freeze an AsyncSSH server for every current and future connection. RFC 4254 leaves size=0 undefined; AsyncSSH stored it verbatim instead of rejecting it. Upgrade past 2.23.1.

Sanic — chunked-trailer request smuggling executes a hidden second request

Sanic's HTTP/1.1 parser doesn't fully consume the chunked trailer after the terminating `0\r\n`, so leftover attacker-controlled bytes get reinterpreted as the start of a second, fully-routed request on the same keep-alive connection — verified with a working PoC that produces two real HTTP responses from one TCP send. Classic request-smuggling shape; if Sanic sits behind a proxy or cache that reuses backend connections, this is a smuggling primitive with consequences beyond the single backend. Upgrade to 24.12.1 or 25.12.1.

Vendure — Shop API list queries leak non-public entities when filterOperator is OR

Vendure's Shop API OR-combines its mandatory ‘only show public entities’ guard with the caller-supplied filter when `filterOperator: OR` is set, instead of always AND-combining it — so a crafted filter surfaces disabled products and private collections/facets with no authentication needed. Fourth and last Vendure advisory today; the guard is now unconditionally AND-combined regardless of caller input. Upgrade @vendure/core past 3.6.5.

06:00 ET · Morning Watch

vLLM — unauthenticated audio decompression-bomb DoS on the chat-completions endpoint

vLLM's audio decode-duration guard is wired into the speech-to-text endpoint only; the chat-completions inline-audio path (`input_audio` content parts) calls the same decoder with no limit, so an unauthenticated caller can submit a few-KB compressed clip that expands to multiple GB of float32 PCM and OOMs the worker. It's the third sibling in this media subsystem — image and video decode paths had the same unbounded-expansion bug already — so the fix pattern (thread the duration/size guard into every entry point, not just the one it was designed for) is the thing to check across your own media-handling code. A fix already landed upstream (PR #45908); update if you serve audio-capable models behind chat completions.

OpenFGA — ListUsers can return a user that a `but not` exclusion should have blocked

When an OpenFGA authorization model intersects a `but not` exclusion granted through a type-bound wildcard with a second relation that grants the same user via a concrete tuple, `ListUsers` incorrectly includes the excluded user in its output. Anything that treats `ListUsers` as authoritative for enumeration or enforcement — not just display — can end up granting access to a user the model was written to block. Upgrade to OpenFGA v1.18.1 or later.

Grav — stored CSS injection via Markdown image resize() bypasses the existing media sanitizer

Grav's media sanitizer blocks CSS injection through the direct `style()`/`attribute()` image actions, but the adjacent `resize()` action writes caller-controlled width/height straight into `styleAttributes` without checking for semicolons or colons, so a crafted `?resize=100;position:fixed;...` payload breaks out and injects arbitrary declarations. A lower-privileged content editor can persist a full-viewport overlay that renders when a higher-privileged reviewer or admin previews the page — UI-redress risk, no JS execution required. First of three Grav advisories in today's batch; no patched version yet.

Grav — Twig string concatenation lets stored XSS bypass the blueprint validator entirely

Grav's XSS validator scans raw page content before Twig renders it, so it sees the harmless expression `{{ "on" ~ "error" }}` and passes it — but Twig's `~` concatenation operator reassembles the string at render time into `onerror=alert(...)`, and the same trick reconstructs blocked tags (`<script>`) and protocols (`javascript:`). Any user with page-write access can plant stored XSS that fires for every visitor once `twig_content.process_enabled` is on, which bypasses all four of the validator's regex checks at once. Second Grav advisory in today's batch; disable Twig content processing if you don't need it, and no fixed version exists yet.

Grav — zip bomb via Direct Install's unZip() has no size, entry, or depth limits

Grav's Direct Install tool extracts uploaded ZIPs with `ZipArchive::extractTo()` and no cap on uncompressed size, entry count, or directory depth, and the cleanup routine that deletes a failed extraction has no depth limit either — a deeply nested archive crashes the cleanup with a PHP stack-overflow and leaves the bomb material on disk. Requires `admin.super`, so it's an insider/compromised-admin path rather than remote-anonymous, but it's the third Grav fix in today's batch — all land in the same release once one ships, so treat it as one upgrade rather than three separate ones. No patched version yet.

China-linked FamousSparrow deploys new SparroWocky backdoor against Latin American government targets

FamousSparrow, a China-linked espionage cluster, is running a new backdoor dubbed SparroWocky against government organizations in Latin America. Not a software-supply-chain vector itself, but the same custom-implant tradecraft that shows up downstream once an initial-access broker or trojanized package hands off to a state-aligned operator — worth knowing if you track APT tooling reuse.

Kaspersky ties three separate threat clusters to backdoor, ransomware, and wiper activity against Russian enterprises

Kaspersky attributes concurrent campaigns against Russian enterprises to three distinct clusters — NightEagle (APT-Q-95), Hacking Cat, and Toy Ghouls — spanning backdoors, ransomware, and wipers, with NightEagle notably using new persistence and lateral-movement techniques. General threat-intel rather than a supply-chain compromise, but the persistence tradecraft is the kind that resurfaces in post-compromise stages of package-based intrusions.