vLLM: OpenAI auth bypass
Summary
A vulnerability in ASGI web servers and starlette's trust on those web servers enables an authentication bypass of the OpenAI API AuthenticationMiddleware, which was discovered during @x41sec's source code audit.
It allows to use the API without providing the configured VLLM_API_KEY or --api-key.
Details
In https://github.com/vllm-project/vllm/blob/v0.14.0/vllm/entrypoints/openai/api_server.py#L689-L692 the url_path is taken from the URL, which is reconstructed by _starlette_ based on the request scope.
``py
from starlette.datastructures import URL, Headers, MutableHeaders, State
...
url_path = URL(scope=scope).path.removeprefix(root_path)
headers = Headers(scope=scope)
if url_path.startswith("/v1") and not self.verify_token(headers):
response = JSONResponse(content={"error": "Unauthorized"}, status_code=401)
return response(scope, receive, send)
return self.app(scope, receive, send)
`
The request scope includes the request's Host: header and reconstructs the URL as shown below:
`py
f"{scheme}://{host_header}{path}"
`
Neither starlette nor any of the ASGI servers (including uvicorn, which vllm uses) properly filter the Host: header for invalid characters. This allows an attacker to include special URL characters such as / or ? in the Host: header and thereby control the reconstructed URL and it's .path attribute.
FastAPI/starlette's routing uses the HTTP path and does not depend on the parsed url.path attribute, allowing attackers to reach an endpoint via a certain path while providing a different value in the .path`.
Impact
Instances of vllm that use an API Key for the OpenAI API and expose the API to attackers.
Instances behind an RFC-conforming web server (such as nginx) are not affected.
vLLM: Security Check Bypass via assert Statement in Activation Function Loading Allows Arbitrary Code Execution
Summary
An assert-based security check in vLLM's activation function loading allows any unauthenticated attacker to achieve arbitrary code execution on the server by publishing a malicious HuggingFace model, when vLLM runs in Python optimized mode (python -O or PYTHONOPTIMIZE=1).
Details
vLLM uses an assert statement at vllm/model_executor/layers/pooler/activations.py:48 as its sole security control to restrict which activation functions can be loaded from a HuggingFace model's config.json:
``python
vllm/model_executor/layers/pooler/activations.py:35-53
function_name: str | None = None
if (
hasattr(config, "sentence_transformers")
and "activation_fn" in config.sentence_transformers
):
function_name = config.sentence_transformers["activation_fn"]
elif (
hasattr(config, "sbert_ce_default_activation_function")
and config.sbert_ce_default_activation_function is not None
):
function_name = config.sbert_ce_default_activation_function
if function_name is not None:
assert function_name.startswith("torch.nn.modules."), (
"Loading of activation functions is restricted to "
"torch.nn.modules for security reasons"
)
fn = resolve_obj_by_qualname(function_name)()
`
Python's assert statements are stripped at compile time when running in optimized mode (python -O or PYTHONOPTIMIZE=1). When the assert is absent, the attacker-controlled function_name from the model's config.json is passed directly to resolve_obj_by_qualname() — an unrestricted import gadget:
`python
def resolve_obj_by_qualname(qualname: str) -> Any:
module_name, obj_name = qualname.rsplit(".", 1)
module = importlib.import_module(module_name)
return getattr(module, obj_name)
`
This is the same vulnerability class as CVE-2017-1000433 (pysaml2 assert-based auth bypass), flagged by Bandit B101 and Ruff S101, and the reason Django proactively replaced all assert-based security checks (ticket #32508).
Attacker-controlled input sources:
config.sentence_transformers["activation_fn"] (line 40)
config.sbert_ce_default_activation_function (line 45)
Affected call sites — get_act_fn() is called via resolve_classifier_act_fn() from:
vllm/model_executor/layers/pooler/seqwise/poolers.py:122 — SequencePooler
vllm/model_executor/layers/pooler/tokwise/poolers.py:130 — TokenPooler
Broader systemic risk: resolve_obj_by_qualname is called from ~20 locations across the codebase with no validation of its own. Any future caller feeding user-controlled input to it without validation creates the same vulnerability class.
Suggested fix: Replace the assert with an explicit conditional raise:
`python
if not function_name.startswith("torch.nn.modules."):
raise ValueError(
"Loading of activation functions is restricted to "
"torch.nn.modules for security reasons"
)
`
Impact
Arbitrary code execution. A malicious model author publishes a HuggingFace model with a crafted config.json. When a victim loads this model with vLLM running under python -O or PYTHONOPTIMIZE=1, arbitrary code executes during model initialization with the privileges of the vLLM process.
The attack requires:
1. Victim loads a malicious model from HuggingFace (user interaction)
2. vLLM runs under python -O or PYTHONOPTIMIZE=1` (documented in production use)
3. Model uses a cross-encoder architecture (e.g. BERT or RoBERTa with sequence classification)
Coordinated disclosure note: This vulnerability was also reported via huntr.com on April 2, 2026 (https://huntr.com/bounties/dcb05b04-e625-41e7-adbc-bbae0cc2d64c). A GitHub Security Advisory was also filed because it is vLLM's stated preferred disclosure channel per SECURITY.md.
Fix
A fix for this was introduced in this commit: https://github.com/vllm-project/vllm/commit/b3c7ffcab82c2439726f8cb213800f6f38c023d3
LangChain: Path traversal and sandbox escape in LangChain file-search middleware and loaders
Summary
Several LangChain components that resolve filesystem paths or expand search patterns do not consistently confine the resolved path to the intended root directory. Affected behaviors include: a file-search agent middleware that validates a starting directory but not the search pattern or the resolved target of matched files, so glob patterns and symlinks can reach files outside the configured root; prompt- and chain/agent-configuration loaders that accept path fields and resolve them without confining the result to a trusted base or rejecting symlink targets; and path-prefix authorization checks that compare by string prefix without a path-segment boundary, so a sibling path sharing the prefix is accepted. When these components receive path values, search patterns, or workspace contents influenced by an untrusted source — including an LLM acting on untrusted input — the result can be disclosure of files outside the intended boundary. We have no evidence of this behavior being triggered in the wild.
Affected users / systems
You may be affected if you expose an agent with filesystem-search middleware over a directory and accept prompts or retrieved content influenced by untrusted sources; load prompt or chain/agent configuration from untrusted or shared sources; or rely on path-prefix restrictions to confine tool file access. Callers that confine these components to fully trusted inputs and first-party configuration are not affected.
Impact
Confidentiality: disclosure of file contents outside the intended root/sandbox.
Authorization: path-prefix bypass can grant access to sibling resources beyond the intended subtree.
Patches / mitigation
The affected components will canonicalize candidate paths (resolving symlinks) and verify the resolved real path remains within the configured root before reading or returning it; search patterns will be normalized so they cannot escape the root; configuration loaders will confine resolved path fields and reject symlink escapes unless the caller explicitly opts in to dangerous loading; and path-prefix checks will enforce a path-segment boundary. Path validation will be made operating-system-portable.
Compatibility
Callers that already pass only in-root paths, validated configuration, and trusted search inputs see no behavioral change. Callers that intentionally reference external paths can opt in via the existing dangerous-loading flag.
Operational guidance
Confine filesystem-backed agent tools to a dedicated directory and prefer running them sandboxed/containerized; validate path and identifier inputs where untrusted input enters; do not enable dangerous loading for configuration whose origin you do not control.
LangSmith / hosted deployments note
This issue concerns library components executed by agents.
Nuxt dev server vite-node IPC socket is world-connectable on Linux
Impact
When running nuxt dev on Linux (Node.js 20+, outside Docker / StackBlitz), Nuxt's internal vite-node IPC server binds to a Linux abstract-namespace Unix socket (\0nuxt-vite-node-<pid>-<ts>.sock). Abstract sockets have no filesystem inode and therefore no permission bits: any local UID on the host that can read /proc/net/unix can enumerate the socket and connect to it.
The IPC server does not perform any peer-credential or shared-secret check before dispatching requests. The module request type passes its moduleId field straight into Vite's SSR fetchModule(), which is not gated by Vite's HTTP-layer server.fs.allow deny-list. A co-resident unprivileged local user can therefore request paths like /home/<dev>/project/.env?raw or ~/.ssh/id_rsa?raw and read the developer's secrets through the dev server's SSR plugin pipeline. The resolve request type additionally enables filesystem probing.
This affects developers running nuxt dev on shared multi-tenant Linux hosts (lab machines, shared bastions, CI runners shared between jobs without per-job container isolation). It does not affect:
Production builds (nuxt build / nuxt start). The IPC server only runs in development.
macOS or Windows developers.
Docker / StackBlitz environments, which already fall back to a filesystem socket.
Single-user laptops or per-job containerised CI.
Patches
Fixed in nuxt@4.4.7 (commit 1f9f4767) and backported to nuxt@3.21.7 (commit c293bf95).
The fix removes the abstract-namespace branch entirely. The IPC server now always binds to a filesystem Unix socket under the OS temp directory and explicitly chmod 0600s it after listen(), restricting connections to the owning UID. If the chmod fails for any reason, the server closes rather than serve requests on an unrestricted channel.
Workarounds
If you cannot upgrade immediately on an affected host:
Run nuxt dev inside a container or VM with no other tenants. Docker already triggers the filesystem-socket fallback in vulnerable versions and that fallback is unaffected.
Bind the dev process to a single-user namespace (unshare -U, rootless containers).
Restrict /proc/net/unix visibility via hidepid=2 mount options where applicable, though this is partial mitigation only.
References
Affected file: packages/vite/src/plugins/vite-node.ts
CWE-276: Incorrect Default Permissions
Credit
Reported by Anthropic / Claude as part of Anthropic's coordinated vulnerability disclosure programme, reference ANT-2026-MSNKZFAT. Thanks to the Anthropic security team for the report and the detailed reproduction.
Independently reported by @alcls01111 via GitHub's coordinated disclosure flow (GHSA-5gvc-46gq-948j), closed as a duplicate of this advisory.
Nuxt: Route-rule middleware bypass via case-sensitivity mismatch between vue-router and the routeRules matcher
Impact
Nuxt looks up routeRules for the current navigation by calling
getRouteRules({ path: to.path }) from the page-router plugin and the
no-pages router plugin. The compiled routeRules matcher (built on
rou3) performs case-sensitive matching, while vue-router is configured
with its default sensitive: false and matches paths case-insensitively.
The two routers therefore disagree on which rules apply to a given
request path: vue-router still matches the page record for
/Admin/dashboard, but the routeRules lookup for the same path
returns no match. Any appMiddleware declared via routeRules is never
added to the middleware set and never runs, on both SSR and client
navigations. The same path skips other path-keyed route rules in the
same way (ssr, redirect, appLayout, and the prerender / payload
hints used client-side).
For applications using routeRules with appMiddleware as an
authorization gate (a documented pattern), an attacker can flip the case
of any static segment in a protected URL (for example /Admin/dashboard
instead of /admin/dashboard) to render the protected page with the
middleware skipped. The server returns the fully server-rendered page
including any useFetch / useAsyncData results captured during SSR.
This is an instance of CWE-178 (Improper Handling of Case Sensitivity)
leading to CWE-863 (Incorrect Authorization) for apps that treat
appMiddleware as an authorization boundary.
Mitigating factors
Only affects apps that use routeRules.appMiddleware. The more
idiomatic definePageMeta({ middleware }) is bound to the matched
route record and is unaffected.
Nuxt route middleware is documented as an app-layer concern, not a
server-side auth boundary; well-built apps enforce authorization
again at the API / data-fetching layer.
Apps that explicitly set router.options.sensitive = true are not
affected.
Patches
Fixed in nuxt@4.4.7 (commit 07e39cd6) and backported to nuxt@3.21.7 (commit 3f3e3fa7). The fix normalizes the path used for routeRules lookups so it matches vue-router's default case-insensitive semantics.
Workarounds
Until you can upgrade, you can mitigate by either:
1. Setting router.options.sensitive = true so vue-router matches
case-sensitively (this changes route-matching behaviour app-wide).
2. Moving security-critical middleware off routeRules.appMiddleware
and onto definePageMeta({ middleware: [...] }) on the protected
page components, which is bound to the matched record.
3. Enforcing authorization at the API / data-fetching layer (which you
should be doing in any case).
Credit
Reported by Anthropic / Claude through Anthropic's coordinated
vulnerability disclosure process. Reference: ANT-2026-9FSEBYMC.
Nuxt: URL-handling weaknesses in `navigateTo` and `reloadNuxtApp`: SSR open redirect, client-side script execution via the `open` option, and protocol-relative bypass in `reloadNuxtApp`
Summary
Three weaknesses in Nuxt's client-navigation URL handling, all reachable
from documented public APIs (navigateTo and reloadNuxtApp):
1. (CVE-2026-56326) SSR open redirect in navigateTo via path-normalisation bypass.
navigateTo decided whether a target was external by inspecting the raw
input with hasProtocol(..., { acceptRelative: true }). Inputs such as
/..//evil.com, /.//evil.com, /%2e%2e//evil.com, or
/app/..//evil.com slipped past that check because they start with
/, but WHATWG URL parsing then normalised them to the
protocol-relative pathname //evil.com. The normalised value was
written to the Location response header and into the
<meta http-equiv="refresh"> body of the SSR redirect page, so a
victim's browser would resolve the redirect cross-origin to the
attacker's host.
2. (CVE-2026-56698) Client-side script execution via navigateTo({ open: ... }). The
client-side early-open handler called window.open(toPath, ...) without
applying the isScriptProtocol check that gates the normal navigateTo
path. A target of javascript:... (or another script-capable scheme)
passed to navigateTo(url, { open: { ... } }) therefore executed in the
application's origin instead of being rejected.
3. (CVE-2026-56697) Open redirect in reloadNuxtApp via protocol-relative bypass.
reloadNuxtApp({ path }) rejects script-capable protocols by parsing
the path with new URL(path, window.location.href) and checking the
resolved protocol against isScriptProtocol. Protocol-relative paths
such as //evil.com resolve to the current page's protocol (https:),
which passes that check; the value is then assigned to
window.location.href, which the browser treats as a cross-origin
redirect. This is the same protocol-relative bypass family as (1), in
a different sink.
Impact
For (1), the practical risk is phishing or OAuth-code theft against any
Nuxt app that forwards user-controlled input (for example a ?next=
query parameter on a login route) into navigateTo on the server. The
framework documents that navigateTo blocks external hosts unless
external: true is passed, so maintainers commonly rely on it as the
safe path for post-login redirects.
For (2), any app that passes a user-controlled URL into
navigateTo(url, { open: { ... } }) was vulnerable to reflected XSS in
the application's first-party origin.
For (3), any app that forwards user-controlled input into
reloadNuxtApp({ path }) could be redirected cross-origin for phishing
or OAuth-code theft, even on releases that already shipped the
isScriptProtocol guard added by #35115.
Patches
Fixed in nuxt@4.4.7 and backported to nuxt@3.21.7. The three sinks
are addressed by:
Path-normalisation bypass in navigateTo:
4.x: commit 2cce6fb0
3.x: commit 1f2dd5e7
navigateTo({ open }) script-protocol guard:
4.x: #35206 (commit 3394716d)
3.x: commit 62fc32ed
Protocol-relative bypass in reloadNuxtApp:
4.x: commit e447a793
3.x: commit 6497d99d
Workarounds
For (1): validate redirect targets before passing them to navigateTo,
for example reject any input where
new URL(target, 'http://localhost').pathname starts with //, or
only accept a known allow-list of paths.
For (2): reject any user-controlled URL whose protocol is not in an
allow-list (typically just http: and https:) before passing it to
navigateTo({ open: ... }).
For (3): same shape as (1). Reject paths starting with // (or where
new URL(path, window.location.href).host !== window.location.host)
before passing to reloadNuxtApp({ path }).
References
CWE-601: URL Redirection to Untrusted Site ('Open Redirect')
CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
Credits
Reported by Anthropic / Claude as ANT-2026-S08HN6DH through Anthropic's
coordinated vulnerability disclosure programme.
The reloadNuxtApp protocol-relative bypass (sink 3) was independently
reported by @alcls01111 via GitHub's
coordinated disclosure flow (GHSA-w7fp-2cfv-4837), closed as a
duplicate of this advisory.
Starlette: request.form() limits silently ignored for application/x-www-form-urlencoded enable DoS
Summary
request.form() accepts max_fields and max_part_size to bound resource consumption while parsing form data. These limits are enforced for multipart/form-data, but silently ignored for application/x-www-form-urlencoded. An unauthenticated attacker can therefore send a urlencoded body with an arbitrarily large number of fields or an arbitrarily large field, even when the application configured limits it believed would apply.
Details
request.form() dispatches to a different parser depending on the Content-Type. For multipart/form-data the max_files, max_fields, and max_part_size limits are forwarded to the parser, but for application/x-www-form-urlencoded the parser is constructed without them. It has no max_fields or max_part_size parameter to receive them, and it appends every field with no count check and accumulates each field's name and value with no size check. The configured limits are therefore both unreachable and unenforced for url-encoded bodies.
Because the url-encoded parser does its work synchronously between stream reads, the two attack shapes have different effects:
Field count drives CPU and event-loop blocking. A body of ~1,000,000 fields (a sub-10MB payload such as f0=v&f1=v&...) blocks the worker's event loop for several seconds while parsing, during which the worker serves no other request.
Field size drives memory. A single large field value (e.g. a 50MB value) is buffered in full to build the FormData, forcing memory allocation proportional to the request body.
The equivalent multipart/form-data request is correctly rejected with 400 Too many fields / 400 Field exceeded maximum size.
Impact
This Denial of service (DoS) vulnerability affects all applications built with Starlette (or FastAPI) that call request.form() on application/x-www-form-urlencoded requests. A single request with a very large number of fields blocks the event loop for several seconds, and a single request with a very large field forces unbounded memory allocation; in either case, parallel requests can render the service unusable. A reverse proxy that enforces a request body size limit reduces but does not eliminate the exposure, since a sub-10MB body is already enough to block the event loop.
Mitigation
Upgrade to a patched version, which forwards max_fields and max_part_size to the url-encoded parser and enforces them while parsing, raising before the oversized field or excess fields are accumulated. The defaults match multipart/form-data (max_fields=1000, max_part_size=1MB) and can be customized via request.form(max_fields=..., max_part_size=...).
Starlette: SSRF and NTLM credential theft via UNC paths in StaticFiles on Windows
Summary
When serving static files on Windows, StaticFiles resolves the requested path with os.path.realpath. If a UNC path (such as \\attacker.com\share) reaches the resolver, realpath causes the process to open a connection to the remote host over SMB (port 445). This is a server-side request forgery (SSRF) that leaks the service account's NTLMv2 credentials to the attacker-controlled host, which can then be cracked offline or relayed to other hosts.
Details
StaticFiles.lookup_path() joins the requested path onto the served directory and calls os.path.realpath on the result before checking containment with os.path.commonpath. On Windows, a UNC path is absolute, so os.path.join discards the served directory and realpath resolves the bare UNC path, triggering the outbound SMB connection and NTLM authentication before the containment check rejects the path. The HTTP response is a benign 404, but the credential disclosure has already happened. POSIX systems are not affected.
This only affects the default configuration (follow_symlink=False), which uses os.path.realpath. The follow_symlink=True branch uses os.path.abspath, which performs no I/O.
Impact
Applications running on Windows that serve files with StaticFiles (directly, or via a framework built on Starlette such as FastAPI) in the default configuration are affected. StaticFiles is typically unauthenticated, so any client can trigger the SMB connection and leak the service account's NTLMv2 hash. A secondary impact is discovering internal hosts reachable over SMB by timing responses for valid versus invalid addresses.
Mitigation
Applications not running on Windows are not affected. On Windows, serving static files through a dedicated web server (such as nginx or IIS) instead of StaticFiles avoids the issue. Blocking outbound SMB (port 445) from the application host prevents the credential disclosure even if a UNC path is resolved.
Starlette: Arbitrary HTTP method dispatched to `HTTPEndpoint` attributes via `getattr`
Summary
When dispatching a request, HTTPEndpoint selects the handler by lowercasing the HTTP method and looking it up as an attribute with getattr, without restricting the lookup to a known set of HTTP verbs.
When an HTTPEndpoint subclass is registered through Route(...) without an explicit methods= argument, the route does not constrain the method and every method reaches the endpoint. If a non-standard HTTP method whose lowercased name matches an attribute on the endpoint subclass reaches the endpoint, that attribute is invoked as if it were a request handler. An attacker can use this to reach methods that were never meant to be HTTP handlers, such as internal helpers, without the authorization checks applied by the intended public handler.
Details
HTTPEndpoint uses the client-supplied method name to resolve an instance attribute, without validating it against the set of HTTP verbs the endpoint supports. A method such as _DO_DELETE therefore resolves an attribute like _do_delete and invokes it. Non-standard methods are valid RFC 9110 token methods, so an endpoint must not treat the method name as a trusted attribute selector.
Impact
An application is affected when all of the following hold:
It defines an HTTPEndpoint subclass and registers it via Route(...) without an explicit methods= argument.
The subclass defines additional methods whose names match a non-standard HTTP-method token shape and that accept a single request argument and return a response.
This also affects frameworks built on Starlette, like FastAPI.
Mitigation
Register HTTPEndpoint subclasses with an explicit methods= argument on the Route, listing only the HTTP verbs the endpoint supports. The route then rejects any other method with 405 Method Not Allowed before it reaches the endpoint, so non-standard methods cannot resolve an attribute.
SwiftNIO NIOHTTP1: HTTPDecoder accepts unbounded HTTP/1 header blocks, enabling remote DoS
Summary
The HTTPDecoder in NIOHTTP1 enforces no limit on the total size of an HTTP/1 message's header block or on the number of header fields per message. A remote peer can submit an arbitrary number of small, valid headers in a single request and have them all accumulated into the resulting HTTPHeaders value before any application code runs. This can be used to exhaust memory, or — for consumers that subsequently convert headers into swift-http-types' HTTPFields — to crash the process.
Details
HTTPDecoder previously enforced only a single hardcoded parsing limit: 80 KB per individual header field (name + value). There was no cap on the cumulative size of the header block, nor on the number of header fields per message. Because each individual field can remain well below the 80 KB threshold, a peer can submit hundreds of thousands of valid headers in a single request, all of which are appended to the decoded HTTPHeaders without bound.
The headers are then visible to user code through the standard HTTPServerRequestPart.head / HTTPClientResponsePart.head events. Two observed downstream effects:
Hummingbird 2 (and other consumers that bridge HTTPHeaders into swift-http-types' HTTPFields) crashes via a precondition failure inside HTTPFields once the configured field count is exceeded.
Vapor 4 does not crash, but the per-request memory footprint scales linearly with the number of headers received, allowing a single connection to inflate server memory use substantially.
Impact
A single unauthenticated remote peer can trigger a denial of service against any HTTP/1 server (or, in the response direction, any HTTP/1 client) built on NIOHTTP1 — either by crashing the process, depending on the downstream framework, or by driving the process's resident memory to arbitrary sizes.
Patches
This issue is addressed in swift-nio 2.100.0 and later.
The HTTPDecoder now applies three parsing limits with conservative defaults, exposed through the new NIOHTTPDecoderLimitConfiguration type:
| Limit | Default |
| --- | --- |
| maxHeaderFieldSize | 80 KB |
| maxHeaderListSize | 2 MB |
| maxHeaderFieldCount | 256 |
Exceeding any of these limits causes the decoder to fail with HTTPParserError.headerOverflow. The configuration can be supplied directly to HTTPRequestDecoder / HTTPResponseDecoder, or via the decoderConfiguration property on NIOUpgradableHTTPServerPipelineConfiguration and NIOUpgradableHTTPClientPipelineConfiguration.
Users who require larger limits — for example, applications that legitimately exchange very large header blocks — can opt into them explicitly by constructing a custom NIOHTTPDecoderLimitConfiguration.
Workarounds
Users unable to upgrade can mitigate by placing a reverse proxy in front of the service that enforces equivalent limits on request header count and total header size.
Credit
This issue was reported by @Joannis. SwiftNIO thanks @Joannis for the report and the support in landing the fix.
SwiftNIO: Out-of-bounds write via ByteBuffer index and length UInt32 overflow
Summary
A program using swift-nio is vulnerable to a potential out-of-bounds write when attacker-controlled index or length values exceeding UInt32.max are passed to some ByteBuffer methods. This affects all swift-nio versions from 1.0.0 to 2.99.0. It is fixed in 2.100.0 and later releases.
Details
ByteBuffer internally stores indices and capacities as UInt32 values. The internal helper functions _toIndex and _toCapacity, which convert from Int to UInt32, used UInt32(truncatingIfNeeded:). On 64-bit platforms, this silently discards the upper 32 bits of the value rather than trapping on overflow. For example, a value of UInt32.max + 1 (0x100000000) would be truncated to 0.
This truncation can cause safety preconditions to pass when they should fail. Subsequent operations would then use the incorrect truncated value, potentially leading to out-of-bounds memory writes or reads.
The affected ByteBuffer methods that may lead to out-of-bounds writes are:
copyBytes(at:to:length:) — a crafted destination index exceeding UInt32.max could copy bytes to an incorrect offset.
writeWithUnsafeMutableBytes(minimumWritableBytes:) — a crafted minimumWritableBytes exceeding UInt32.max could provide the caller with a buffer pointer of incorrect length, which can easily be subsequently overflowed.
The affected ByteBuffer methods that have logic errors but do neither expose out-of-bounds reads nor out-of-bounds writes:
moveReaderIndex(forwardBy:) / moveWriterIndex(forwardBy:) — a crafted offset exceeding UInt32.max could move indices to incorrect positions, bypassing bounds checks. These indices cannot be out of the bounds of the buffer, so they do not expose access to uninitialized memory or produce wild pointers.
The ByteBuffer(takingOwnershipOf:allocator:) initialiser — passing a buffer larger than UInt32.max bytes could create a ByteBuffer with an incorrect capacity.
Outside of these methods, there are still impacts, but they are simply logical bugs. In these cases applications can be forced to read from or write to unexpected parts of the buffer. This does not cause memory-safety issues, but it can cause logical issues or corruption of outbound packets.
Impact
Exploitation requires an attacker to influence the index, offset, or length parameter of the affected ByteBuffer methods with a value exceeding UInt32.max (approximately 4 GiB). This is a high bar for most applications: attacker-controlled length parameters to ByteBuffer are typically used on the read path, and the above methods are typically not used on the read paths. However, applications that calculate buffer positions arithmetically from untrusted input when attempting to do writes, or that process very large payloads, may be at risk of memory safety issues.
Other applications may encounter logical issues due to reading unexpected bytes, or writing to unexpected parts of the buffer.
When the memory-safety issue is exploitable, the consequences are severe. Because truncatingIfNeeded silently produces an incorrect but valid UInt32 value, subsequent operations may write to or read from memory outside the valid buffer region. In optimised (release) builds where preconditions are not checked, this could lead to out-of-bounds memory writes, potentially corrupting adjacent heap memory.
In debug builds, some of these conditions are caught by assertions, but truncatingIfNeeded occurs before the assertion checks the (already-truncated) value, so even assertions may not reliably catch the issue.
Patches
The issue is fixed by replacing UInt32(truncatingIfNeeded:) with UInt32(_:) in the _toIndex and _toCapacity helper functions. The UInt32(_:) initialiser traps on overflow in both debug and release builds, converting a potential silent memory corruption into a deterministic crash.
One call site in getSlice(at:length:) retains truncatingIfNeeded because prior bounds checks against the non-truncated Int values mathematically guarantee the values fit within UInt32.
Workarounds
Applications can mitigate this issue by validating that all index and length values passed to ByteBuffer methods do not exceed UInt32.max (4,294,967,295). In practice, most applications are not affected because buffer indices are derived from protocol parsing rather than raw untrusted input.
SwiftNIO: CRLF Injection in outbound HTTP request URI via NIOHTTPRequestHeadersValidator
Programs using swift-nio is vulnerable to HTTP request smuggling and HTTP response splitting attacks, caused by insufficient validation of outbound HTTP/1.1 request and response start line components.
This vulnerability affects all swift-nio versions from 2.0.0 to 2.99.0. It is fixed in 2.100.0 and later releases.
This vulnerability is caused by the NIOHTTPRequestHeadersValidator and NIOHTTPResponseHeadersValidator channel handlers only validating header field names and values, while leaving the request URI, request method, and response reason phrase unvalidated. An attacker who can influence the content of these fields — for example by controlling a URL path or a custom HTTP method in a proxy application — can inject CR/LF sequences or other control characters into the HTTP start line. This allows construction of arbitrary additional HTTP requests or responses on the wire, a classic HTTP request smuggling or HTTP response splitting attack.
Exploiting this vulnerability requires the attacker to influence the content of outbound HTTP start line fields. In proxy applications that forward attacker-controlled URIs or methods, this is straightforward. For clients, a malicious server that triggers a redirect to a crafted URL could exploit the URI validation gap. For servers, any client that can cause the server to emit a crafted response reason phrase could exploit the response splitting gap.
In vulnerable applications, where attacker controlled data is supplied to these fields, the attack is low-effort: injecting a CRLF sequence into a URI or reason phrase requires only a single crafted request. Successful exploitation can allow an attacker to smuggle additional HTTP requests past intermediaries or split HTTP responses, potentially bypassing WAFs or poisoning web caches. However, most applications are not vulnerable at all.
The risk can be mitigated by ensuring that all user-controlled input is sanitized before being used in HTTP start line components. However, this mitigation places the burden on application developers and is error-prone.
The issue is fixed by extending NIOHTTPRequestHeadersValidator to validate request URIs against the character set defined in RFC 9112 Section 3.2 and RFC 3986 Section 3, and to validate custom HTTP methods against the token grammar defined in RFC 9110. NIOHTTPResponseHeadersValidator is extended to validate custom response reason phrases against RFC 9112 Section 4. Applications that use these validator channel handlers — which are installed by default when using addHTTPClientHandlers() or addHTTPServerHandlers() — will reject invalid outbound messages with an HTTPParserError.invalidHeaderToken error rather than emitting them to the network.
SwiftNIO is grateful to @kuranikaran and @YLChen-007 for their reporting and assistance with the project's process.
vLLM is vulnerable to an Out-of-Memory (OOM) Denial of Service (DoS) attack due to unbounded frame count processing in the `VideoMediaIO.load_base64()` method
vLLM versions 0.8.0 and later are vulnerable to an Out-of-Memory (OOM) Denial of Service (DoS) attack due to unbounded frame count processing in the VideoMediaIO.load_base64() method. When processing video/jpeg data URLs, the method splits the base64 data string on commas to extract individual JPEG frames without enforcing a frame count limit. An attacker can exploit this by crafting a single API request containing thousands of comma-separated base64-encoded JPEG frames in a data URL, causing the server to decode all frames into memory and crash due to excessive memory consumption. This vulnerability is reachable via the OpenAI-compatible chat completions API and does not require authentication.
vLLM's Artifact Pin Decay allows pinned deployments to load unpinned code, weights, and processors
Summary
vLLM's revision pinning controls do not consistently apply to all artifacts loaded for a model. A deployment that supplies --revision or --code-revision can still load dynamic code, GGUF files, image processors, retrieval side weights, or same-repository subfolder weights/config from an unpinned/default revision.
This is a supply-chain integrity issue for pinned vLLM deployments. Operators can believe they are serving a reviewed model revision while vLLM resolves behavior-affecting nested or sibling artifacts outside that reviewed revision.
Details
The expected invariant is:
> When a vLLM operator supplies a model or code revision pin, every code, config, processor, weight file, side weight, and same-repository subfolder artifact loaded as part of that model should resolve under that pin unless vLLM exposes and enforces a separate explicit pin for that artifact.
Current main was verified affected at commit 3795d7acf431980e62e738493f437ae2a51549da.
Affected source boundaries:
vllm/model_executor/models/registry.py:1045-1051 and :1058-1064
_try_resolve_transformers() passes revision=model_config.revision and trust_remote_code=model_config.trust_remote_code, but omits code_revision=model_config.code_revision for external auto_map dynamic module imports.
vllm/model_executor/model_loader/gguf_loader.py:58-60
The direct-file GGUF form repo/file.gguf calls hf_hub_download(repo_id=repo_id, filename=filename) without passing revision.
vllm/model_executor/models/roberta.py:203-209
BGE-M3 secondary sparse and ColBERT side weights are declared with revision=None.
vllm/model_executor/models/kimi_k25.py:111-114
Kimi-K2.5 calls cached_get_image_processor() without passing model_config.revision.
vllm/model_executor/models/kimi_audio.py:92-95
Kimi-Audio loads Whisper config from the whisper-large-v3 subfolder without a revision argument.
vllm/model_executor/models/kimi_audio.py:425-430
Kimi-Audio declares same-repository whisper-large-v3 secondary weights with revision=None.
vllm/model_executor/model_loader/default_loader.py:287-301
The default loader preserves model_config.revision for the primary source, then consumes model-supplied secondary sources as declared.
The strongest example is Kimi-Audio: the primary moonshotai/Kimi-Audio-7B-Instruct weights preserve the configured model revision, but the same-repository whisper-large-v3 audio tower config/weights do not. A pinned Kimi-Audio deployment can therefore load the Whisper subfolder outside the audited revision.
This report does not claim a trust_remote_code=False bypass, unauthenticated RCE, or real artifact compromise. The issue is improper propagation of explicit artifact pins across supported loader paths.
Impact
Affected users are operators who pin vLLM model deployments to a reviewed Hugging Face revision for safety review, provenance, rollback, or reproducibility. The impact is that the pin does not reliably describe the full set of artifacts vLLM serves. Even when the operator selects an audited revision, vLLM can resolve behavior-affecting secondary artifacts from the repository default branch or another mutable ref.
Depending on the model path, the unpinned artifact can be dynamic model code, a GGUF file, an image processor, retrieval side weights, or the same-repository Kimi-Audio Whisper subfolder weights/config.
This breaks the operational guarantee of a pinned deployment: "serve the exact artifact set I reviewed." A later change to an unpinned secondary artifact can alter model behavior without changing the operator's configured revision, making review, rollback, incident response, and audit records unreliable.
Occurrences
vllm/model_executor/models/kimi_k25.py L111-L114 — Kimi-K2.5 loads its image processor with cached_get_image_processor() but does not pass self.ctx.model_config.revision. The processor can therefore resolve from the default repository revision even when the model deployment is pinned.
vllm/model_executor/models/kimi_audio.py L425-L430 — Kimi-Audio declares same-repository whisper-large-v3 secondary weights with revision=None. A pinned Kimi-Audio deployment can therefore load the Whisper audio tower weights from an unpinned/default revision.
vllm/model_executor/models/kimi_audio.py L92-L95 — Kimi-Audio loads Whisper config from the same repository's whisper-large-v3 subfolder without passing the top-level model revision. The config for this behavior-affecting subcomponent can be resolved outside the audited model revision.
vllm/model_executor/models/registry.py L1058-L1064 — The later dynamic model-class resolution repeats the same pin-decay pattern: it forwards revision and trust_remote_code, but omits code_revision. This means an operator-provided code pin is not enforced at the dynamic module loader boundary.
vllm/model_executor/model_loader/gguf_loader.py L58-L60 — The direct GGUF form repo/file.gguf calls hf_hub_download(repo_id=repo_id, filename=filename) without passing model_config.revision. A deployment that pins the model revision can therefore resolve this GGUF file from the repository default revision.
vllm/model_executor/models/registry.py L1045-L1051 — try_get_class_from_dynamic_module() is called for external auto_map config/model classes with revision=model_config.revision, but without forwarding model_config.code_revision. When --code-revision is set, this dynamic module resolution can still fall back to the default code revision instead of the audited code revision.
vllm/model_executor/models/roberta.py L203-L209 — BgeM3EmbeddingModel creates same-repository secondary sparse/ColBERT weight sources with revision=None. The primary model revision is not propagated to these side weights, so they can be downloaded outside the operator-selected model revision.
Fixes
This was fixed in: https://github.com/vllm-project/vllm/pull/42616
___
Originally filed via huntr: https://huntr.com/bounties/3f1e24c0-87d2-4f6c-a705-820f380879ac.
The vLLM maintainer (Russell Bryant) redirected the report to the private GHSA channel. Offline proof bundle (vllm_artifact_pin_decay_bundle_verify.py + bundle-verification-20260430T143506Z.json) is available upon request.
React Router vulnerable to Denial of Service via reflected user input in single-fetch
A DoS vulnerability exists in the React Router v7 Framework Mode, as well as Remix v2.9.0+ with Single Fetch enabled. In some scenarios the underlying serialization algorithm can become a bottleneck when encoding specific types of data into server responses. Please upgrade to React Router v7.14.0 or later.
> [!NOTE]
> This does not impact your React Router application if you are using Declarative Mode (<BrowserRouter>) or Data Mode (createBrowserRouter/<RouterProvider>).
Allocation of Resources Without Limits or Throttling in Axios
Summary
Axios versions 1.7.0 through 1.15.x did not enforce configured request and response size limits when requests were sent with the fetch adapter. Applications that selected adapter: 'fetch', or ran in environments where axios resolved to the fetch adapter, could receive or send bodies larger than maxContentLength or maxBodyLength despite those limits being explicitly configured.
This can cause resource exhaustion in server-side usage when a malicious or compromised server returns an oversized response, when an attacker can supply a large data: URL, or when an application forwards attacker-controlled request bodies through axios while relying on maxBodyLength as a boundary.
Impact
The impact is availability-only. Affected applications may process, buffer, or transmit data beyond the configured limit, potentially exhausting memory, CPU, or network resources.
This does not affect axios’s default unlimited behaviour by itself: maxContentLength and maxBodyLength default to -1. The vulnerability exists when an application has configured finite limits and expects axios to enforce them.
Server-side runtimes are the primary concern. Browser impact is generally constrained by the browser process and browser fetch behavior, and should not be described as server process exhaustion.
Affected Functionality
Affected functionality includes requests using the built-in fetch adapter with finite maxContentLength or maxBodyLength values.
Relevant configurations include:
adapter: 'fetch'
adapter: ['fetch', ...] when fetch is selected
environments where neither xhr nor http is available and axios falls back to fetch
custom fetch environments configured through env.fetch
Unaffected functionality includes:
Node.js default http adapter enforcement
versions before the fetch adapter was introduced
configurations that do not rely on finite axios size limits
Technical Details
In vulnerable versions, lib/adapters/fetch.js destructured request config without maxContentLength or maxBodyLength. The adapter dispatched fetch() and then materialized the response through text(), arrayBuffer(), blob(), or related resolvers without checking the configured response limit.
The fix in e5540dc added:
maxContentLength and maxBodyLength reads in lib/adapters/fetch.js
upfront data: URL decoded-size checks
outbound body-size checks before dispatch
Content-Length response pre-checks
streaming response enforcement
fallback checks for environments without ReadableStream
regression tests in tests/unit/adapters/fetch.test.js
Proof of Concept of Attack
``js
import http from 'node:http';
import axios from 'axios';
const server = http.createServer((req, res) => {
let received = 0;
req.on('data', chunk => {
received += chunk.length;
});
req.on('end', () => {
res.end(JSON.stringify({ received }));
});
});
await new Promise(resolve => server.listen(0, resolve));
const url = http://127.0.0.1:${server.address().port}/;
await axios.post(url, 'A'.repeat(2 1024 1024), {
adapter: 'fetch',
maxBodyLength: 1024
});
// Vulnerable versions succeed and the server receives 2097152 bytes.
// Fixed versions reject with ERR_BAD_REQUEST.
server.close();
`
Workarounds
Use the Node.js http adapter for server-side requests where finite size limits are security-relevant.
Validate or cap attacker-controlled request bodies before passing them to axios.
Reject or strictly allowlist attacker-controlled URL schemes, especially data:` URLs, before calling axios.
<details>
<summary>Original Report</summary>
Summary
When Axios is used with adapter: 'fetch', configured body/response size limits are not enforced. This allows oversized uploads/downloads (including data: URLs) despite explicit limits, which can lead to memory/resource exhaustion in server-side usage.
Details
maxBodyLength and maxContentLength are not applied in the fetch adapter flow:
lib/adapters/fetch.js (146-160): config destructuring does not include these controls.
lib/adapters/fetch.js (220-234): request is dispatched with fetch() without request-size enforcement.
lib/adapters/fetch.js (267-283): response is materialized via text(), arrayBuffer(), blob(), etc. without response-size checks.
By contrast, the HTTP adapter enforces both limits.
PoC
Environment:
Axios main at commit f7a4ee2
Node v24.2.0
Steps:
1. Start an HTTP server that counts received bytes and echoes {received}.
2. Send 2 MiB with:
adapter: 'fetch'
maxBodyLength: 1024
3. Request a 4 KiB data: URL with:
adapter: 'fetch'
maxContentLength: 16
Expected secure behavior: both requests rejected.
Observed:
Upload: success, server received 2097152
data: response: success, length 4096
Impact
Type: DoS / resource exhaustion due to limit bypass.
Impacted: applications using Axios fetch adapter as a server-side security control boundary for untrusted request/response sizes.
</details>
---
Starlette has missing Host header validation that poisons request.url.path, bypassing path-based security checks
Summary
In affected versions, the HTTP Host request header was not validated before being used to reconstruct request.url. Because the routing algorithm relies on the raw HTTP path while request.url is rebuilt from the Host header, a malformed header could make request.url.path differ from the path that was actually requested. Middleware and endpoints that apply security restrictions based on request.url (rather than the raw scope path) could therefore be bypassed.
Details
When a client requests http://example.com/foo, it sends:
``http
GET /foo HTTP/1.1
Host: example.com
`
Affected versions reconstructed the URL by concatenating http://{host}{path} and re-parsing the result. The Host value is only valid as a uri-host [ ":" port ] per RFC 9112 §3.2, where uri-host follows the restricted host grammar of RFC 3986 §3.2.2. When it contains characters outside that grammar - notably /, ?, or # - those characters move the path/query/fragment boundaries during re-parsing, so the parsed request.url.path no longer matches the path the server actually received. For example:
`http
GET /foo HTTP/1.1
Host: example.com/abc?bar=
`
reconstructs to http://example.com/abc?bar=/foo, whose parsed path is /abc - even though routing used the real path /foo. The router still dispatches to /foo and the endpoint executes, but any middleware or code that reads request.url.path sees /abc, so path-based authorization checks can be bypassed.
Impact
Any application running an affected version that relies on request.url (or request.url.path) for security-sensitive decisions is affected. The most common case is middleware that gates access to certain path prefixes based on request.url.path. Deployments fronted by a proxy or load balancer are mitigated only if that proxy rejects or normalizes the malformed Host header before forwarding and the application does not trust attacker-controlled host headers (e.g. X-Forwarded-Host) elsewhere.
Mitigation
Upgrade to a patched version, which validates the Host header against the grammar of RFC 9112 §3.2 / RFC 3986 §3.2.2 when constructing request.url and falls back to scope["server"]` for malformed values.
React Router vulnerable to DoS via unbounded path expansion in __manifest endpoint
There exists a potential DOS attack vector in React Router Framework Mode applications (as well as Remix v2.10.0 - 2.17.4). Certain requests can be crafted to consume disproportionate resources on the server, resulting in response time degredation and/or service unavailability for end users.
> [!NOTE]
> This does not impact your React Router application if you are using Declarative Mode (<BrowserRouter>) or Data Mode (createBrowserRouter/<RouterProvider>).
React Router's vendored turbo-stream v2 allows arbitrary constructor invocation via TYPE_ERROR deserialization leading to Unauth RCE
When using React Router v7 in Framework Mode, there exists a combination of steps that could potentially allow unauthorized RCE through external requests. This first requires the application code to have an existing prototype pollution vulnerability. This can be leveraged into a 2-step attack in which the second step can trigger unauthorized RCE on the remote server.
> [!NOTE]
> This does not impact your React Router application if you are using Declarative Mode (<BrowserRouter>) or Data Mode (createBrowserRouter/<RouterProvider>).
React Router's same-origin redirect with path starting // causes open redirect via protocol-relative URL reinterpretation
Certain URLs passed to the redirect function can trigger an open redirect to an external domain depending on the level of validation done by the application prior to returning the redirect.
> [!NOTE]
> This does not impact your React Router application if you are using Declarative Mode (<BrowserRouter>)