v vanemmerik.ai / SUPPLY-CHAIN
Supply Chain · Watch Saturday · 01 August 2026 End-of-day synthesis 4 watches · 23 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 single prototype-pollution bug in ApostropheCMS quietly disables every authorization check on the platform — the sharpest edge in an otherwise broad day of input-trust failures across CMS, payment, and infrastructure tooling.

Today's twenty-three triaged items skew disclosure-heavy rather than campaign-heavy. GHSA carried nearly all the volume, while RSS and CISA KEV turned up nothing new beyond a repeat of yesterday's already-covered Adform crypto-clipper story.

The one item that earns critical is a prototype-pollution bug in ApostropheCMS's apos.util.set(): an authenticated editor can walk a dot-notation path into __proto__ via the $pullAll patch operator, and a confirmed gadget in publicApiCheck() turns that single write into an authorization bypass on every piece-type REST endpoint, for every subsequent unauthenticated request, for the life of the Node process. It shipped in the same five-minute window as two lower-severity Apostrophe siblings — stored XSS from unsanitized Google Analytics/GTM IDs in @apostrophecms/seo, and an unauthenticated SSRF through the Host header in @apostrophecms/file's pretty-URL handler. The rest of the day rhymes with the same input-trust theme at smaller scale: Savon::Model evals WSDL operation names as Ruby source, Sylius' Mollie plugin trusts an unauthenticated payment webhook, FileBrowser Quantum skips its own path-sanitizer on one endpoint, and dssrf — a library built to stop SSRF — fails open the moment its DNS resolver returns NXDOMAIN. The bright spot: no fetcher surfaced an active in-the-wild campaign today, and CISA KEV's catalog added nothing new since yesterday.

→ Operational priority for the night patch ApostropheCMS core and @apostrophecms/seo before anything else — the prototype-pollution bug doesn't just compromise one editor's session, it disables authorization for every visitor until you restart the process.

18:00 ET · First Watch

ApostropheCMS's apos.util.set() lets an authenticated editor pollute Object.prototype, and a confirmed gadget turns that into an authorization bypass for every unauthenticated visitor

apos.util.set() traverses dot-notation paths without rejecting __proto__, so an authenticated editor can smuggle a poisoned key through the $pullAll patch operator and write directly onto Object.prototype; a confirmed gadget in publicApiCheck() then reads that polluted property and disables authorization on every piece-type REST endpoint, for every subsequent unauthenticated request, until the Node process restarts (GHSA-6h5j, CVSS 9.1). It shipped in the same five-minute disclosure window as two lower-severity Apostrophe siblings — @apostrophecms/seo injects unsanitized Google Analytics/GTM IDs straight into an inline <script> body for stored XSS on every page (GHSA-wf43, CVSS 8.7), and @apostrophecms/file's pretty-URL handler builds its upstream fetch from the raw Host header, letting an unauthenticated caller pivot the process into internal-network SSRF when uploads use local storage (GHSA-34pj). Patch apostrophe core and @apostrophecms/seo first — the prototype-pollution bug doesn't just compromise one editor's session, it turns off auth checks for everyone until you restart the process.

sanitize-html only gates dangerous URI schemes on href, src, and cite by default — every other URI-bearing HTML attribute passes javascript: straight through

naughtyHref() — the function that blocks javascript: and vbscript: URIs — is only invoked on attributes listed in allowedSchemesAppliedToAttributes, which has defaulted to ['href','src','cite'] since the option was introduced in v1.18.0 and has never been expanded to cover action, formaction, data, poster, background, or the half-dozen other URI attributes the HTML spec defines. Default configurations aren't exposed, but the moment a developer allows a non-default tag like <form> plus a non-default attribute like action — a common pattern for CMS platforms and form builders — javascript: URIs sail through unmodified. If you've widened sanitize-html's allowed tags or attributes beyond the defaults, audit that allowlist against this attribute set before assuming the library caught it.

sigstore-go's ExpiringKey type implies expiration semantics it doesn't enforce — a signature made with expired key material still verifies

sigstore-go's self-managed long-lived-key path wraps a verifier in an ExpiringKey with a defined validity window, but the validator never actually compares the bundle's signing timestamp against that window — so a signature produced with expired key material still passes verification. Blast radius is narrow: only deployments using long-lived signing keys without a certificate authority are exposed, and standard sigstore certificate-based verification is unaffected. If you've built attestation on sigstore-go's long-lived-key mode, patch before treating an old signature as proof the key was still valid when it was made.

12:00 ET · Forenoon Watch

Redaxo's Mediapool re-opens a 2025 extension-check regression, letting an authenticated backend user upload a JPEG/PHP polyglot that executes as the web server

rex_mediapool::isAllowedExtension only blocks a forbidden extension when it sits in the last or second-to-last segment of a filename, so shell.php.any.jpg sails through — a regression a February 2025 commit introduced into a previously-correct check. On any Apache config where mod_mime or an unanchored FilesMatch regex treats .php as a match anywhere in the filename, an authenticated user with mediapool upload rights gets code execution as the web-server user. If you self-host Redaxo, patch and separately audit your own handler's extension regex for a missing end anchor.

dssrf — a library whose entire job is blocking SSRF — treats localhost as safe when its configured DNS resolver returns NXDOMAIN

is_url_safe() calls dns.resolve4() and, when the resolver (1.1.1.1 in the reported case) returns NXDOMAIN with no fallback to dns.lookup(), the function has no address to check against its blocklist and defaults to treating the target as safe — so http://localhost/admin sails through the exact filter meant to stop it. The irony is the point: an SSRF-prevention library's failure mode is fail-open, not fail-closed. If you use dssrf, verify your resolver's NXDOMAIN behavior doesn't silently disable the check, and patch to the version that fails closed.

@phun-ky/defaults-deep recursively merges objects without filtering __proto__, constructor, or prototype — textbook prototype pollution

The library's recursive merge never excludes the standard unsafe key names, so an attacker who controls one side of a defaultsDeep() call can write properties onto Object.prototype and affect every object in the process — logic bypasses, DoS, or worse depending on what downstream code trusts. Same shape as dozens of prior JS deep-merge CVEs; if defaultsDeep() ever touches untrusted input in your dependency tree, upgrade to 2.0.5 rather than assuming your usage is safe.

gemini-bridge's inline mode reads any file path an MCP caller supplies and echoes the contents back through the Gemini round-trip

consult_gemini_with_files in inline mode never confines the files argument to the working directory the way its at_command sibling does, so an MCP client — or an LLM that's been prompt-injected into calling the tool — can pull SSH keys, cloud credentials, or .env files off the host and have them disclosed via the tool response (and sent to Google in the process). No code execution on its own, but it's a clean illustration of why MCP tool arguments need the same path-containment discipline as any other untrusted input. If you run gemini-bridge, patch before exposing it to an agent that processes untrusted prompts.

Two unrelated web apps ship anti-abuse code that doesn't actually work: WPGraphQL's password-reset mutation still leaks user existence, GeoNetwork's SSO redirect still leaves the app

WPGraphQL's sendPasswordResetEmail mutation is explicitly designed to return success: true regardless of whether the account exists, but a deprecated user field left registered on the response type resolves to a full User object only when the account is real — undermining the anti-enumeration design its own code comments describe (GHSA-jhh7). Separately, GeoNetwork's OAuth2/OIDC and Keycloak login filters validate post-login redirect targets but don't reject every URL shape that leaves the origin, so a crafted link can still bounce a victim to an attacker-controlled site after a legitimate login (GHSA-pjp7). Different codebases, same lesson: a security check that isn't tested against every input shape isn't a security check.

Pion's DTLS and STUN libraries both panic on malformed handshake input, two separate remote-DoS bugs disclosed together

A crafted ECDHE_PSK ServerKeyExchange message panics pion/dtls during the handshake (GHSA-wg4g), and a malformed XOR-MAPPED-ADDRESS attribute does the same to pion/stun (GHSA-34rh, CVSS 5.3) — both are unauthenticated, remote, and process-killing rather than data-compromising. Pion underpins a lot of Go WebRTC stacks; if you're on either library, upgrade to dtls v3.1.4+ / stun v3.1.5+ rather than treating these as independent one-offs.

Capsule's fix for a prior tenant-escalation bug missed two of its three code paths, and a separate config field can crash the node admission webhook cluster-wide

The v0.13.0 patch for CVE-2026-22872 (a Tenant Owner creating cluster-scoped resources like ClusterRole via TenantResource) only added the cluster-scope guard to the NamespacedItems selection path — the RawItems and Generators paths, including the exact vector the original advisory named, were left unguarded through v0.13.7 (GHSA-jr6p, CVSS 6.6). Separately, CapsuleConfiguration's NodeMetadata regex fields are never validated by any webhook, so a Cluster Admin can persist a malformed regex that panics on every subsequent Node create/update/patch (GHSA-68cj, CVSS 6.8). If you run Capsule for multi-tenant Kubernetes, patch both — the first is a privilege-escalation regression, the second is a cluster-wide DoS from a single bad config write.

Node's re2 native bindings have two separate uncatchable-crash bugs — an out-of-bounds heap read on non-ASCII input and an infinite loop on empty-matchable global patterns

re2 validates lastIndex against a subject's UTF-8 byte length but walks the buffer as UTF-16 code units, so any non-ASCII subject lets a lastIndex value pass validation while pointing past the buffer's end — the native code then reads out of bounds and the process dies with an uncatchable SIGABRT/SIGSEGV (GHSA-ff84, CVSS 5.7). Separately, a global match with a zero-width pattern like a* or (?:) never advances the cursor, so the native loop grows an unbounded vector until something kills the process externally — try/catch and timers can't stop it (GHSA-6hxr). Both are synchronous native-code DoS, not memory-safety RCE, but if re2 processes attacker-controlled subjects or patterns anywhere in your stack, patch before the next crash looks like a mystery.

OnionShare's file server follows symlinks out of the shared directory, and its Receive mode writes files to disk even when uploads are supposedly disabled

OnionShare CLI/Desktop serves whatever a symlink inside a shared directory points to rather than confining access to the directory's actual contents, so a shared folder containing an untrusted symlink lets a remote recipient read arbitrary local files the OnionShare process can access (GHSA-22p9, CVSS 4.8). Separately, a Receive-mode service configured as text-only (disable_files) still writes uploaded file bytes to disk before the route handler checks that setting, because the flag is enforced too late in the request-streaming path (GHSA-v833). If you run OnionShare for anonymous drop-boxes, patch both — the symlink bug is the more dangerous one since it doesn't require the sender to do anything unusual.

Zaino's non-finalized-state reorg handler has no cycle detection or depth limit, so a malicious or buggy validator can hang the indexer's sync loop

handle_reorg recursively walks parent blocks looking for a common ancestor with no recursion limit and no cycle check, so a block whose previous_block_hash points back into a cycle sends the function into an infinite loop that pins a CPU core and stalls sync indefinitely. A separate .expect() panics if the non-finalized snapshot goes empty after trimming. If you run zaino-state as a chain indexer, treat this as a liveness bug an adversarial or malfunctioning peer can trigger, not just a theoretical edge case.

free5GC's authentication server crashes on a null byte in the subscriber identifier field, no auth required

The AUSF's UE-authentication handler forwards the supiOrSuci field to the UDM in a URL path without stripping null bytes or control characters that Go's JSON parser happily accepts, so Go's net/url.Parse() fails and the AUSF returns HTTP 500 with a leaked stack trace — reproducibly, in about 4% of mutated inputs per the reported testing. Any unauthenticated party who can reach the AUSF's ue-authentications endpoint can degrade authentication for every subscriber on that core. If you operate free5GC, patch and add input validation ahead of the AUSF regardless — a telecom core shouldn't trust JSON string contents to be URL-path-safe.

ImageMagick's XCF decoder has an integer-overflow-driven heap over-read on crafted GIMP files

An integer conversion overflow while parsing XCF (GIMP native format) images lets a crafted file trigger an out-of-bounds heap read, crashing the process (CVSS 6.5). ImageMagick sits behind a huge number of image-processing pipelines that accept arbitrary uploads — if you convert or thumbnail user-supplied XCF files (or accept any format ImageMagick auto-detects), patch to 14.15.0+ rather than assuming upstream input validation catches this.

A ten-year-old directory traversal in guard-livereload gets a fresh GHSA entry

CVE-2016-1000305 — unpatched path validation in the livereload dev server that lets a remote client read arbitrary files outside the web root — was filed through the DWF project and only now got a formal GHSA advisory. Purely a local-dev tool issue; worth a mention only as a reminder that old CVEs without GHSA coverage keep surfacing years later as the ecosystem backfills its catalog.

06:00 ET · Morning Watch

Savon::Model turns SOAP WSDL operation names into eval'd Ruby method names, so a malicious WSDL is remote code execution

Savon::Model dynamically defines Ruby methods from the operation names it reads out of a WSDL document, and builds those method definitions without validating the names as safe identifiers first — whoever controls the WSDL response (a compromised SOAP endpoint, a MITM, or just a malicious webservice URL) can smuggle Ruby source into the operation name and get it executed in the client process. Same shape as the NocoBase and Apostrophe bugs flagged yesterday: the app trusted structured input from a source it didn't fully own. Don't point Savon::Model at a WSDL URL you don't control, and audit any app that fetches WSDL dynamically at runtime.

Jodit's HTML sanitizer has four separate bypasses disclosed together — mutation XSS, SVG script smuggling, prototype pollution, and an incomplete javascript: scheme filter

clean-html's mutation-XSS filter misses a MathML/style rawtext carrier that re-triggers script execution after sanitization (GHSA-rxcw, CVSS 7.2), and a second bypass smuggles a <script> tag nested inside SVG past the same cleaner (GHSA-45qg). Jodit.configure()/ConfigMerge is separately vulnerable to prototype pollution (GHSA-5957), and sanitizeHTMLElement's href scheme check doesn't fully normalize javascript: variants (GHSA-j839). Jodit is a widely-vendored WYSIWYG editor — if you render its output without a second sanitization layer, patch all four together, not pick-and-choose.

Sylius' Mollie payment plugin trusts an unauthenticated webhook to mark orders paid, and separately leaks order tokens to any caller

The payment webhook handler updates order/payment status from the Mollie webhook payload without verifying the request actually came from Mollie, so an attacker who obtains an order's webhook URL can forge a paid status on an unpaid order (GHSA-rc52). A second advisory in the same plugin exposes an IDOR that leaks order tokens and customer data to unauthenticated callers (GHSA-x83g), which makes finding that webhook URL trivial. Verify Mollie webhook signatures out-of-band and patch both together — the IDOR is what turns the forgery bug from theoretical into practical.

hashi-vault-js, an npm client for HashiCorp Vault, has a path traversal and query-injection bug in its own request-building code

The client builds Vault API paths and query strings from caller-supplied values without escaping path separators or query delimiters, so a value meant to target one secret path or parameter can redirect the request to a different path or inject extra parameters. Second Vault-tooling bug in two days after yesterday's Bank-Vaults admission-webhook SSRF — Vault's ecosystem of community clients and operators is having a rough week on input trust. If hashi-vault-js is in your dependency tree, audit any code path where user or service input reaches its path/query builder before patching.

FileBrowser Quantum's subtitle-handler endpoint skips the app's own path-sanitization helper, letting any logged-in user read files outside their scope

The subtitlesHandler endpoint passes its path parameter straight to GetRealPath() without calling the SanitizeUserPath() check every other handler in the codebase uses, so path=../../etc/passwd resolves with no anchor file required — any authenticated user, regardless of role, can read SSH keys, database credentials, or the JWT signing key. A second vector through the name parameter needs an anchor file but reaches the same read primitive. If you self-host FileBrowser Quantum, patch before letting any non-admin account log in.

Wings' SFTP handshake and config parser both crash the daemon on malformed input

A maliciously crafted packet during the SFTP connection handshake panics the Wings daemon (GHSA-ghrq, CVSS 7.5), and separately a malformed or erroneously-created parsed config file can crash the same process (GHSA-q6hh, CVSS 5.5). Neither is as severe as yesterday's config-template secret leak, but on shared Pterodactyl hosting a crash-the-node bug is still a tenant-vs-tenant availability problem. Patch alongside yesterday's critical fix rather than treating this as a separate cycle.

Nx's local `nx graph` dev server accepts cross-origin requests from any website, not just localhost tooling

The local dependency-graph visualization server that nx graph spins up accepts requests from arbitrary origins, so a malicious page open in the same browser session can query it and pull back workspace and dependency-graph data it has no business seeing. Nx had a real npm supply-chain compromise last year, so trust-boundary gaps in its own tooling get extra scrutiny — this one is a local-dev information leak, not remote code execution, but don't run nx graph on a machine with an untrusted browser tab open.