v vanemmerik.ai / SUPPLY-CHAIN
Supply Chain · Watch Monday · 17 August 2026 End-of-day synthesis 4 watches · 45 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 — vm2 — the Node.js sandbox library agent tooling still leans on to run untrusted code — took five new disclosures in one batch, three of them full escapes that bypass its own documented defenses.

No new critical KEV entries and nothing from the RSS feeds flagged an active campaign today — the volume came entirely from GHSA, which had a heavy batch-disclosure day: five advisories against vm2, three against MLflow, three against uniget, and multi-advisory batches for sqlparse, Glances, http4k, Netty, and Etherpad.

The vm2 cluster is the one worth naming: two of the five are full sandbox escapes to RCE that bypass vm2's own prior fixes (a documented Error.cause invariant that was never actually enforced, and an indirect-call trick around a patched proto-mutator block), with CVSS up to 9.9, and the other three chip away at its bufferAllocLimit DoS guard through unwrapped Buffer/TypedArray paths. It rhymes with atomic-agents-stack's MCP-catalog MITM-to-RCE bug and this morning's Socket piece on AI agents widening the supply-chain attack surface — all three are the same shape: code built to run untrusted input safely, and it doesn't. MLflow's unauthenticated SSRF (redirect-following past its own guard, straight to cloud metadata) and uniget's inverted signature-verification logic (the safety check only runs if you set the variable named 'ignore the signature') are separate stories but the same theme: trust boundaries that look enforced in the code but aren't in practice.

→ Operational priority for the night if vm2 executes untrusted code anywhere in your stack — agent tooling especially — stop trusting it as a security boundary and start planning a move to isolated-vm or a VM-level sandbox; none of today's five vm2 fixes changes that calculus.

18:00 ET · First Watch

vm2: sandbox breakout via dangerous host proto mutators — bypasses a previous fix

vm2's fix for an earlier breakout (GHSA-v6mx-mf47-r5wg) blocked direct dangerous-mutator calls, but wrapping the call as an indirect call (`indirectcall.call(indirectcall, dangerousmutator, ...)`) slips past the check and lets sandboxed code walk back to the host proto chain and execute arbitrary commands. A working PoC ships with the advisory. If you run vm2 to execute untrusted code — including inside agent tooling — this is a full escape with no patched release; treat vm2 as unfit for that job and migrate to isolated-vm or a VM-level sandbox.

vm2: missing Error.cause sanitization enables sandbox escape to RCE

vm2's exception handler sanitizes SuppressedError and AggregateError sub-errors but never touches the ES2022 `.cause` property, so sandbox code that catches a host-thrown error whose `.cause` references a powerful host object (like `process`) can walk that reference straight to arbitrary command execution. vm2's own docs explicitly claim `.cause` is sanitized — the implementation doesn't match the claim, which is the same failure mode as the proto-mutator bypass above: documented invariant, unenforced in code. Same remediation: don't rely on vm2 for untrusted-code isolation.

MLflow: unauthenticated full-read SSRF in webhook delivery — redirect and DNS-rebinding bypass

The default MLflow Tracking Server ships with no authentication, and its webhook-test endpoint returns the upstream response to the caller. The SSRF guard added in 3.10.0 validates the initial hostname but never re-validates redirect targets — host a public endpoint that passes the check, then 302 to `169.254.169.254` or `127.0.0.1`, and MLflow follows it and hands back the body. That's an unauthenticated read of cloud metadata (credentials) on any default-configured server. If you run MLflow Tracking without auth in front of it, that's the fix; patching the SSRF guard alone isn't enough given how many default deployments are unauthenticated by design.

uniget CLI: metadata signature verification only runs when you set UNIGET_IGNORE_METADATA_SIGNATURE — inverted by default

The sigstore check on uniget's metadata.json is gated on the wrong side of the condition: it only verifies when `UNIGET_IGNORE_METADATA_SIGNATURE` is set — the variable named 'ignore the signature' is what turns verification on, so a normal, unconfigured run never checks it. metadata.json populates a `Tool.Check` field that gets run through `/bin/bash -c`, the same sink as a prior RCE CVE (2026-45152) that this signature check was added specifically to close. This is a supply-chain integrity control that's been silently off since it shipped; upgrade and confirm the fixed version actually verifies without any env var set.

vm2: NodeVM builtin:['*'] exposes os and dns modules — host reconnaissance and hijack

Embedders who opt into `builtin: ['*']` for convenience get the sandbox full access to `os` (host UID/GID/username/homedir) and `dns` (host network topology, including container/VM interfaces), which is process-wide state, not sandbox state. It's config-gated rather than default-on, but it's the kind of shortcut a busy integration takes; audit any vm2 embedding for a wildcard builtins list and scope it down explicitly.

vm2: bufferAllocLimit cap bypassed by Buffer.concat and Buffer.from(arrayLike)

The `bufferAllocLimit` DoS guard wraps `Buffer.alloc`/`allocUnsafe`/`allocUnsafeSlow` and the deprecated `Buffer(N)` constructor, but `Buffer.concat(list, totalLength)` and `Buffer.from(arrayLike)` with a spoofed `length` reach the same host C++ allocator uncapped — one call allocates unbounded host memory. Third vm2 finding in this batch to show the same pattern: a defense that covers the documented API surface but not the equivalent paths next to it.

vm2: memory-exhaustion DoS via bufferAllocLimit bypass using ArrayBuffer/TypedArray

Same bufferAllocLimit gap as above, different door: ArrayBuffer, SharedArrayBuffer, and TypedArray constructors hit the identical V8/libuv allocation path as Buffer.alloc but were never wrapped by the cap. Between this and GHSA-gmc2-2x9w-cgh9, bufferAllocLimit is bypassable through at least four unwrapped API surfaces — if you configured it as your DoS mitigation, it isn't one right now.

atomic-agents-stack: HTTP MCP catalog accepts cleartext http and spawns catalog-supplied commands (MITM to RCE)

The HTTP MCP server-registry backend accepts plain `http://` catalog URLs and later spawns whatever `command`/`args` the catalog specifies as local stdio subprocesses — no LLM step required. A network MITM on that cleartext connection rewrites the catalog response and gets code execution on the agent host; the policy allowlist that would stop this is opt-in and off by default. The `https` path is sound (verify=True, no redirect-following), so the fix is forcing it: audit any MCP registry config for `http://` catalog URLs and require `https` before this ships.

MLflow: CreateModelVersion source validation skips the READ permission check on the referenced run

CreateModelVersion checks that a source path sits inside a run's artifact directory but never checks whether the caller can actually read that run — so any authenticated user can point a new model version at someone else's run, gain MANAGE permission on the resulting registered model, and pull the victim's artifacts through the model-version artifact endpoint, sidestepping the experiment-level READ gate entirely. A cross-tenant read primitive dressed up as a registry-write API; patch and review who's created model versions against runs they don't own.

uniget CLI: EDITOR command injection via naive shell-syntax split — confirmed working exploit

uniget parses the `EDITOR` env var with a plain `strings.Split(editor, " ")` instead of respecting shell syntax, so `EDITOR="/path/to/wrapper && id && echo"` splits into separate exec arguments and runs attacker commands — the reporter confirmed it with an actual `id` execution. Anything reading EDITOR from environment for hook-editing workflows should be reviewed for the same shape.

http4k: DigestAuthProvider.verify doesn't bind the response to the request URI — replayable across endpoints

The `uri` parameter inside a client's Digest Authorization response was never checked against the actual request URL, breaking the per-request-URL binding the Digest scheme depends on — a captured digest response can be replayed against any other URL in the same realm. Present since DigestAuthProvider was introduced; upgrade http4k-security-digest and treat any previously captured digest responses as compromised.

Etherpad: stored XSS in HTML export via unescaped attribute-pool values

HTML export interpolates plugin-hook-supplied attribute-pool values into `span data-<k>="<v>"` without attribute escaping; a pad editor can plant an arbitrary value via a crafted changeset (only the `author` attribute is validated), and a bundled plugin that reads the hook — ep_font_color and similar — renders it unescaped on export. Anyone exporting Etherpad content to HTML for downstream consumption should treat pad content as untrusted until patched.

Glances: --disable-config-exec hardening never covered on-alert action commands

The shell-operator stripping added for AMP command values in a prior fix (GHSA-3vwc-qwhc-3mj7) was never applied to on-alert action commands, which read from the same config file — so `--disable-config-exec` gives a false sense of coverage while a configured alert action with `&&`/`>`/`|` still executes. Third and fourth Glances advisories in today's batch both share this shape: partial hardening that didn't extend to a sibling code path. If you rely on --disable-config-exec, audit alert-action config specifically.

Glances: action-template sanitizer bypass via nested process cmdline values

The sanitizer added for CVE-2026-32608 only processes top-level string values; a process's `cmdline`, which Glances exposes as a list, is a nested value the sanitizer never touches — giving a second bypass path alongside the cross-field reconstruction already on today's watch (GHSA-qcpp-8x79-hhp3). Same underlying command-injection weakness, two independent bypasses disclosed the same day; still no patched release as of this writing.

sqlparse: ReDoS in dollar-quoted SQL literal lexer via backreference regex

The lexer's dollar-quote-closing regex uses a backreference that goes O(n²) on inputs with many unique unmatched dollar-quote openers, giving sustained CPU exhaustion to anyone who can hand arbitrary SQL text to a sqlparse-based tool. sqlparse sits underneath pgAdmin, Django Debug Toolbar, and assorted SQL formatters — check what user-facing surfaces feed it untrusted SQL.

9Router: unauthenticated SSRF via the OIDC-provider test endpoint

The `/api/auth/oidc/test` endpoint takes a user-controlled `issuerUrl` and makes an outbound request without checking for internal IP ranges, and it's reachable without a session — any network-adjacent attacker can use it to probe or hit internal services. Standard SSRF-via-config-test-endpoint shape; require an authenticated session and block private-range destinations before this one gets chained into something worse.

atomic-agents-stack: parallel helper/delegate batch reserves $0 for unpriced models, bypassing the cost-cap guard

Cost-guardrail math looks up per-model pricing and silently returns 0.0 for anything not in the hardcoded table (self-hosted models, new provider SKUs), which zeroes out the batch reservation that's the only thing stopping a parallel fan-out from blowing past a configured daily cap. Not a code-execution bug, but if you run cost_guardrails with an unlisted model, the cap you think is protecting you isn't. Add unpriced models to the table or fail closed instead of reserving $0.

MLflow: LogInputs endpoint bypasses per-run UPDATE authorization under basic-auth

The LogInputs protobuf handler is simply missing from MLflow's before-request authorization map, so on a basic-auth-protected server any authenticated user can inject dataset records into another user's run — where the equivalent log-metric endpoint correctly returns 403. Classic missing-entry-in-a-map bug; if you run MLflow with the basic-auth app, check for handlers added since this map was last audited.

uniget CLI: path traversal in hook filename handling

Hook filenames are concatenated directly into a file path without stripping `../` sequences, so `uniget hooks edit --type=pre-install "../../../../etc/passwd"` reads arbitrary files (demonstrated against /etc/passwd with EDITOR=cat). Third uniget finding in the same batch; sanitize path input across the hooks subsystem rather than patching one call site.

http4k: DigestAuthProvider silently forces MD5 regardless of configured algorithm

Deployments configured for SHA-256 Digest auth were actually running MD5 under the hood — the algorithm parameter was ignored — inheriting MD5's collision weaknesses without any indication in config that it happened. Paired advisory with the URI-binding bug above; both land in the same fix.

Netty: CorsHandler silently overwrites existing Vary headers, enabling cache poisoning

`CorsHandler#setVaryHeader` calls `.set()` instead of appending, replacing any Vary header the backend application already set — which can make a shared cache serve one origin's cached response to another. Check any Netty deployment behind a CDN or shared cache for Vary-dependent responses that also pass through CorsHandler.

Netty: memory exhaustion in SctpMessageCompletionHandler via unbounded fragment buffering

The fix for a prior CVE capped the number of concurrent incomplete SCTP messages and fragments per message, but never capped the total buffered fragment size, so an unauthenticated sender can still drive an OutOfMemoryError with large fragments. Narrow blast radius — SCTP is not a common transport — but worth a patch cycle if you run anything on it.

Etherpad: weak token RNG, non-constant-time login compare, and plugin path handling — hardening bundle

A grouped fix: author/session/readonly IDs moved from Math.random() to crypto.getRandomValues, OIDC login compare moved to crypto.timingSafeEqual with a uniform failure delay, plus plugin dependency path handling tightened. None individually severe, but the token-RNG fix matters most — Math.random()-derived session tokens are guessable.

Glances: as_dict_secure() leaks credentials embedded in config URL values via unauthenticated API

The function documented to return a sanitized config dict for unauthenticated API access only redacts by key name, never inspects value content — so `public_api`/`public_username`/`public_password`-style URL values that embed credentials pass through unredacted. Same root problem as the day's other Glances findings: defenses written for the field shape attackers used yesterday, not the one next to it.

sqlparse: O(n·depth) grouping cost lets a small payload burn CPU before depth caps trigger

sqlparse's MAX_GROUPING_DEPTH/MAX_GROUPING_TOKENS caps exist to bound parsing work, but the path that reaches those caps is itself O(n·depth) per token group — a 1-2KB deeply nested payload burns multiple CPU-seconds before the cap raises an error. Bounded (it does eventually fail), but cheap for an attacker; same library as the ReDoS above, same day.

sqlparse: quadratic O(n²) DoS in group_comments on comment-only input

A statement made of many repeated single-line comments lexes and groups in quadratic time, reachable through plain `sqlparse.parse()` or `format(strip_comments=True)`. Third sqlparse DoS finding in the batch; if you're patching for the ReDoS above, take this one in the same pass.

sqlparse: Python/PHP code-generation output modes allow SQL-string breakout via unescaped backslashes

sqlparse's documented Python/PHP snippet-generation modes escape quote characters but not pre-existing backslashes first, so crafted SQL can neutralize the escape, terminate the generated string, and inject code into the output. Narrow — only matters if something downstream executes or imports the generated snippet — but rounds out today's four-advisory sqlparse batch.

chrome-devtools-mcp: validatePath() doesn't canonicalize symlinks before enforcing workspace roots

The MCP server's root-boundary check does a textual `path.resolve()` comparison without resolving symlinks first, so a symlink inside the workspace that points outside it walks past the root restriction. Reported through Google's OSS VRP but ruled out of reward scope by project tier — still an open, unfixed issue. If you run this MCP server against untrusted workspaces, don't rely on its root enforcement yet.

pkcs12: Decode functions can accept a PKCS#12 file encoded with the wrong password

Decode, DecodeChain, DecodeTrustStore, and ToPEM fail to reject excessively-short PBMAC1 keys, so a file encoded under a different password than the one supplied can still be accepted — anything using the decode password as an authentication check on untrusted PKCS#12 input can be fooled. Files from trusted sources only are unaffected; if you decode PKCS#12 from anywhere untrusted and rely on the password as auth, patch first.

docx4j: stack overflow via cyclic w:basedOn style chain

PropertyResolver walks the OpenXML style-inheritance chain without cycle detection, so a Word document where Style A is based on Style B and Style B is based on Style A causes unbounded recursion and crashes the process — a classic DoS-by-malicious-document, reachable through common conversion and TOC-generation code paths. Anyone converting or rendering untrusted .docx uploads with docx4j should treat this as an input-validation gap until patched.

13:00 ET · Forenoon Watch

CISA KEV adds Ray-Project Ray code injection — CSRF-reachable RCE on ML training clusters (CVE-2025-62593)

CISA added CVE-2025-62593 to the KEV catalog: a code-injection bug in Ray, the distributed-compute framework widely used for ML training and inference, reachable via cross-site request forgery from Firefox or Safari (CWE-94 + CWE-352). Ray's dashboard/job-submission API has been a repeat RCE target since the 2023 "ShadowRay" campaigns against unauthenticated exposed clusters — this is the same class of exposure, just triggered through a browser instead of a direct API hit. If you run Ray clusters, confirm the dashboard isn't internet-facing and patch per the GHSA advisory before the 2026-08-21 KEV deadline.

conflibot: command injection via crafted PR branch names under pull_request_target

conflibot, a GitHub Action that auto-resolves merge conflicts on PRs, builds git commands by shell string interpolation using the PR's head branch name — attacker-controlled from any fork, no maintainer interaction required. Under the documented pull_request_target setup, a branch name containing backticks or $(...) gets arbitrary command execution with the write-scoped GITHUB_TOKEN and repo secrets in scope, the same exploit-pull_request_target shape that's burned a string of popular Actions this year. If you use wktk/conflibot, upgrade to 1.2.1 or 2.0.0 now and audit recent PR branch names for shell metacharacters.

New API: integer overflow in quota billing yields negative charges — self-crediting, confirmed exploited in the wild

New API — an open-source LLM API gateway/billing proxy — had an integer-overflow bug in its quota math: a crafted request quantity (e.g. n ≈ 1.8×10^19) wraps the int conversion negative, and a negative settlement is a credit, letting any funded low-privilege user inflate their balance with one request. The advisory confirms this was exploited in the wild in July before the emergency patch (rc.18) shipped, and self-registration plus any free-starting-balance feature (check-in rewards, invite bonuses) makes it effectively unauthenticated at scale. If you run new-api with billing enabled, upgrade to rc.18+ now and audit consumption logs for negative quota deltas.

New API: user-list API leaks root access token, enabling admin→root privilege escalation

Second critical in the same new-api disclosure batch: the admin user-list endpoint serializes User.AccessToken in its JSON response, including for the root account, because the field-omit logic only excluded 'password.' Any admin user can pull the root access token from GET /api/user/ and use it against root-only config endpoints — a clean admin-to-root escalation sitting right next to the quota-overflow bug above. Upgrade to rc.7+ and rotate root/admin tokens if untrusted admins had list access before patching.

New API: unauthenticated payment webhooks allow memory/disk DoS via unbounded body reads and full-body logging

Third in the batch: the Stripe/Creem/Waffo webhook endpoints in new-api read and fully logged the request body before verifying the webhook signature, so an unauthenticated attacker could send oversized POSTs to force memory pressure and log-disk growth without forging a payment. Availability-only — no forged transactions — but it's a free, unauthenticated DoS knob on a public endpoint; fixed in rc.11 via a 512KB anonymous-request body-size limit.

Glances: command injection bypass of action-template sanitizer via cross-field shell-operator reconstruction

Glances' action-template sanitizer strips shell operators like && per-field, but an attacker who controls two adjacent stat values — e.g. a process name ending in & and a cmdline starting with & — can reconstruct && across the Mustache-render boundary and get command execution via secure_popen. It's an incomplete fix of CVE-2026-32608 from earlier this year: right defense, wrong stage (sanitizing pre-render fields instead of the rendered command). If you use unescaped {{{ }}} action templates in Glances, that's the pattern to check for now — no patched release yet.

Medplum: improper redirect-URI validation in external auth callback allows authorization-code leakage

Medplum's external-IdP callback accepts any redirect URI that merely starts with a registered one instead of matching exactly, so a forged OAuth state with a redirectUri like http://callback.audit.local.oastify.com/cb leaks the authorization code — and, if the attacker supplies their own PKCE verifier, a full session — to an attacker-controlled host. Medplum is a healthcare data platform, so this is a PHI-exposure path riding on a classic prefix-vs-exact redirect-URI bug. Patch to the fixed @medplum/core release and require exact-match redirect URIs if you run a fork.

deepmerge-ts: stack exhaustion when merging recursive object graphs

deepmerge() and deepmergeInto() crash with RangeError: Maximum call stack size exceeded when both merged objects contain a self-reference at the same key path — there's no cycle detection in the recursive merge. If your service deep-merges attacker-controlled JSON (config payloads, webhook bodies) with deepmerge-ts, that's a one-line DoS; patch to >= 8.0.0 or pretest inputs for cycles before merging.

Glances: REST API CORS credentials guard uses exact-match instead of membership test

Glances' REST API CORS guard checks cors_origins == ["*"] exactly instead of testing membership, so a config like cors_origins=*,https://trusted.example.com silently defeats the credentials-disable protection while Starlette's CORSMiddleware still treats any wildcard entry as allow-all. Any site can then read a logged-in operator's process list — including command-line arguments, which often carry secrets — via a cross-origin fetch; it's the same exact-match-vs-membership bug shape CVE-2026-46608 already fixed in Glances' XML-RPC server, just never ported to the REST path.

Terragrunt: arbitrary file deletion via malicious module manifest

Terragrunt trusted a downloaded module's .terragrunt-module-manifest file for cleanup without checking that listed paths stay inside the module directory, so a malicious module with directory-traversal entries can delete arbitrary files the Terragrunt process can reach — a CI/CD denial-of-service or local-source-loss primitive, not RCE. Pin modules to vetted commit SHAs and upgrade to v1.0.4.

New API: Redis user-quota cache overwrite via PUT /api/user/self allows quota bypass

Fourth in the new-api batch: PUT /api/user/self writes a full stale User snapshot back to Redis, overwriting the Quota field that billing updates via HINCRBY — an authenticated user can race settings updates against relay calls to keep their cached balance artificially high. Fixed in rc.16 by scoping settings updates to specific fields instead of the whole hash.

New API: admin can reset passkeys for same-level or higher-privileged users

Fifth and last in today's new-api batch: the admin passkey-reset endpoint skipped the role-level check other privileged endpoints use, letting a lower-privileged admin strip a same-level or root admin's passkey. Requires existing admin access, so it's privilege-escalation-adjacent rather than a standalone compromise; fixed in rc.7 alongside the root-token-leak fix above.

07:00 ET · Morning Watch

Socket's Feross Aboukhadijeh on how AI agents expand the supply-chain attack surface

Writeup of Feross Aboukhadijeh's AI Council 2026 talk, walking through recent package-compromise campaigns and arguing that AI coding agents widen the supply-chain attack surface by pulling in dependencies and running installer scripts faster than a human reviewer can vet them. No new vulnerability or active campaign here — it's a framing piece, but the underlying argument matches what this watch has been tracking all year: postinstall scripts and typosquats remain the cheapest way into an agent-assisted build. Worth a read if you're evaluating agent-driven dependency management; not an action item on its own.