Back to Blog
high SEVERITY8 min read

How Silent Form Limit Bypasses Happen in Starlette and How to Fix Them

CVE-2026-54283 is a high-severity Denial of Service vulnerability in Starlette where form size limits set on `request.form()` were silently ignored for `application/x-www-form-urlencoded` content, allowing attackers to submit arbitrarily large payloads that could exhaust server resources. The fix upgrades Starlette from version 0.49.1 to 0.50.0, where the form parser correctly enforces configured limits for both multipart and URL-encoded content types. This change was applied to `agent/sandbox/u

O
By Orbis AppSec
Published August 28, 2026Reviewed August 28, 2026

Answer Summary

CVE-2026-54283 is a high-severity Denial of Service (DoS) vulnerability in the Starlette Python web framework (CWE-770: Allocation of Resources Without Limits or Throttling). In versions prior to 0.50.0, size limits passed to `request.form()` were silently ignored when the request used `application/x-www-form-urlencoded` encoding, meaning an attacker could send unbounded form payloads to exhaust server memory or CPU. The fix is to upgrade Starlette from 0.49.1 to 0.50.0, which correctly enforces the configured limits for all form content types.

Vulnerability at a Glance

cweCWE-770 (Allocation of Resources Without Limits or Throttling)
fixUpgrade Starlette from 0.49.1 to 0.50.0 to enforce limits for all form content types
riskAttackers can send arbitrarily large URL-encoded form payloads to exhaust server resources
languagePython
root causeStarlette's form parser applied size limits only to multipart forms, not application/x-www-form-urlencoded
vulnerabilityDenial of Service via silently ignored form size limits

How Silent Form Limit Bypasses Happen in Starlette and How to Fix Them


Vulnerability at a Glance

Field Detail
Vulnerability Denial of Service via silently ignored form size limits
CWE CWE-770 — Allocation of Resources Without Limits or Throttling
Language Python
Framework Starlette
Risk Unbounded memory consumption from URL-encoded form payloads
Root Cause Form size limits not enforced for application/x-www-form-urlencoded
Fix Upgrade Starlette from 0.49.10.50.0

Introduction

The agent/sandbox/uv.lock file in this repository pins Starlette at version 0.49.1 — a version that contains a subtle but dangerous flaw in how it processes HTML form submissions. When application code calls request.form() with size-limiting parameters, developers reasonably expect those limits to be enforced. In Starlette 0.49.1, however, those limits are silently ignored for the application/x-www-form-urlencoded content type, the default encoding used by virtually every HTML <form> element on the web.

This is the kind of vulnerability that's particularly insidious: the code looks safe, the developer believes it's protected, but the protection simply isn't there. An attacker who knows this can bypass what appears to be a hardened endpoint and submit payloads large enough to exhaust server memory or CPU — a classic Denial of Service attack requiring nothing more than an HTTP client.


The Vulnerability Explained

What request.form() Is Supposed to Do

Starlette's request.form() is an async method that parses incoming form data from HTTP request bodies. It supports two content types:

  • multipart/form-data — used for file uploads
  • application/x-www-form-urlencoded — the default for standard HTML forms

Developers can pass limit parameters to cap how much data is processed:

# Developer's intent: limit form fields to prevent resource exhaustion
form_data = await request.form(
    max_fields=100,
    max_files=10,
    max_part_size=1_000_000  # 1 MB per part
)

This looks correct. The problem is that in Starlette 0.49.1, these limits were only wired into the multipart parser. When the incoming Content-Type header was application/x-www-form-urlencoded, the parser read the entire body without checking any of those configured limits.

The Silent Failure

The vulnerability is not a crash or an exception — it's silence. The limits are accepted as valid arguments, no warning is raised, no error is thrown, and the developer gets no feedback that their carefully configured guardrails are doing nothing. This is what makes CVE-2026-54283 especially dangerous: it creates a false sense of security.

A vulnerable endpoint might look like this:

from starlette.applications import Starlette
from starlette.routing import Route
from starlette.responses import JSONResponse

async def submit_form(request):
    # In Starlette 0.49.1, max_fields is ignored for
    # application/x-www-form-urlencoded — limits are silently dropped
    form = await request.form(max_fields=50, max_part_size=64_000)
    return JSONResponse({"status": "ok"})

app = Starlette(routes=[Route("/submit", submit_form, methods=["POST"])])

How an Attacker Exploits This

An attacker doesn't need authentication, special knowledge of the application, or sophisticated tooling. They only need to know the endpoint accepts form submissions. A simple attack looks like this:

# Generate a massive URL-encoded form payload and POST it
python3 -c "
import urllib.parse, sys
# Create a payload with thousands of fields and large values
payload = '&'.join(f'field_{i}=' + 'A' * 10000 for i in range(10000))
print(payload)
" | curl -s -X POST \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-binary @- \
  https://target-app.example.com/submit

Because Starlette 0.49.1 ignores max_fields and max_part_size for URL-encoded bodies, this payload — roughly 100 MB of form data — is read entirely into memory and parsed. Repeat this across multiple concurrent connections and the server's memory is exhausted, taking down the application for legitimate users.

Real-World Impact for This Repository

The vulnerable dependency lives in agent/sandbox/uv.lock, which governs the Python environment for the agent's sandbox component. If this component exposes any HTTP endpoints that process form submissions (a common pattern in agent frameworks that accept configuration, commands, or data via web interfaces), those endpoints are exposed to this resource exhaustion attack without any of the configured limits providing protection.


The Fix

What Changed

The fix is a targeted dependency upgrade in agent/sandbox/uv.lock, bumping Starlette from 0.49.1 to 0.50.0:

 [[package]]
 name = "starlette"
-version = "0.49.1"
+version = "0.50.0"
 source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
 dependencies = [
     { name = "anyio" },
 ]
-sdist = { url = "...starlette-0.49.1.tar.gz", hash = "sha256:481a43b71e24ed8c43b11ea02f5353d77840e01480881b8cb5a26b8cae64a8cb" }
+sdist = { url = "...starlette-0.50.0.tar.gz", hash = "sha256:a2a17b22203254bcbc2e1f926d2d55f3f9497f769416b3190768befe598fa3ca" }
 wheels = [
-    { url = "...starlette-0.49.1-py3-none-any.whl", hash = "sha256:d92ce9f07e4a3caa3ac13a79523bd18e3bc0042bb8ff2d759a8e7dd0e1859875" }
+    { url = "...starlette-0.50.0-py3-none-any.whl", hash = "sha256:9e5391843ec9b6e472eed1365a78c8098cfceb7a74bfd4d6b1c0c0095efb3bca" }
 ]

What Starlette 0.50.0 Actually Fixes

In version 0.50.0, Starlette's form parsing logic was updated so that limits passed to request.form() are enforced for both multipart/form-data and application/x-www-form-urlencoded content types. The URL-encoded form parser now respects max_fields and max_part_size, raising appropriate errors when payloads exceed configured thresholds rather than silently consuming them.

After the upgrade, the same developer code that was previously unprotected:

form = await request.form(max_fields=50, max_part_size=64_000)

...now actually enforces those limits regardless of whether the client sends multipart/form-data or application/x-www-form-urlencoded. An oversized URL-encoded payload will be rejected before it's fully read into memory.

Why Only the Lock File Changed

The change is confined to agent/sandbox/uv.lock because uv.lock is the authoritative record of exactly which package versions are installed in the sandbox environment. Updating this file is the correct and complete way to deploy the patched version — no application code changes are required because the fix lives entirely within the Starlette library itself.


Prevention & Best Practices

1. Don't Rely Solely on Application-Level Limits

Even with the fix in place, defense in depth is valuable. Configure your reverse proxy (nginx, Caddy, AWS ALB) to enforce a maximum request body size before traffic reaches your Python application:

# nginx: reject bodies larger than 10MB at the proxy layer
client_max_body_size 10M;

This catches oversized requests before Starlette ever sees them, regardless of which version you're running.

2. Pin Dependencies and Audit Them Regularly

The vulnerability was detectable precisely because uv.lock pins exact versions. Use lock files for all Python projects and integrate a scanner like Trivy, pip-audit, or Safety into your CI pipeline:

# Scan your lock file for known CVEs
trivy fs --scanners vuln agent/sandbox/uv.lock

# Or use pip-audit
pip-audit -r requirements.txt

3. Test Your Limits, Don't Just Set Them

When you configure resource limits in your framework, write tests that verify those limits are actually enforced:

import pytest
from starlette.testclient import TestClient

def test_form_size_limit_enforced():
    """Verify that max_fields is actually enforced for URL-encoded forms."""
    client = TestClient(app)
    # Generate a payload exceeding the configured limit
    oversized = "&".join(f"field_{i}=value" for i in range(200))
    response = client.post(
        "/submit",
        content=oversized,
        headers={"Content-Type": "application/x-www-form-urlencoded"},
    )
    # Should be rejected, not processed
    assert response.status_code in (400, 413, 422)

This kind of test would have caught the silent bypass in 0.49.1 immediately.

4. Follow OWASP Guidance on Resource Limits

The OWASP API Security Top 10 lists "Unrestricted Resource Consumption" (API4:2023) as a top concern. Key recommendations include:

  • Enforce maximum payload sizes at multiple layers (proxy, framework, application)
  • Set timeouts on all I/O operations, including form parsing
  • Monitor for anomalous request sizes in your logging/observability stack

5. Understand CWE-770

This vulnerability maps to CWE-770: Allocation of Resources Without Limits or Throttling. Whenever you're writing code that reads user-supplied data into memory — forms, file uploads, JSON bodies, streaming data — always ask: what is the worst-case allocation if a malicious actor controls this input?


Key Takeaways

  • Silent failures are the most dangerous kind: Starlette 0.49.1 accepted max_fields and max_part_size parameters without error but ignored them for URL-encoded forms — giving developers false confidence their endpoints were protected.
  • application/x-www-form-urlencoded is the default form encoding: This is not an exotic edge case. Standard HTML <form> elements use this content type by default, meaning the vulnerable code path was the most common one.
  • Lock files enable precise vulnerability detection: Because agent/sandbox/uv.lock pins the exact version 0.49.1, Trivy could definitively identify the vulnerable package and flag it for remediation.
  • The fix requires no application code changes: Upgrading to Starlette 0.50.0 is sufficient — the limit enforcement logic lives in the library, so existing calls to request.form() with limit parameters automatically gain correct behavior.
  • Always validate that resource limits work with a test: Setting a limit is not the same as enforcing a limit. A simple integration test that sends an oversized payload would have caught this bypass immediately.

How Orbis AppSec Detected This

  • Source: Incoming HTTP request body with Content-Type: application/x-www-form-urlencoded, containing an attacker-controlled payload of arbitrary size
  • Sink: request.form() in Starlette's URL-encoded form parser (starlette/formparsers.py), which allocated memory proportional to the full request body size without enforcing the max_fields or max_part_size limits passed by the caller
  • Missing control: Enforcement of developer-configured size limits for the application/x-www-form-urlencoded code path — limits were checked only in the multipart parser branch
  • CWE: CWE-770 — Allocation of Resources Without Limits or Throttling
  • Fix: Upgraded Starlette from 0.49.1 to 0.50.0 in agent/sandbox/uv.lock, where the URL-encoded form parser correctly enforces all configured limits

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

CVE-2026-54283 is a reminder that security controls are only valuable if they're actually enforced. In Starlette 0.49.1, the request.form() API accepted resource limit parameters in good faith but silently discarded them for URL-encoded form bodies — the most common form encoding on the web. Any endpoint that relied on those limits for protection against resource exhaustion was, in practice, unprotected.

The fix is straightforward: upgrade to Starlette 0.50.0, where limits are enforced uniformly across all form content types. But the broader lesson is architectural: resource limits should be tested, not just configured; they should be layered (proxy + framework + application); and dependency scanners should be part of every CI pipeline so that vulnerabilities like this are caught before they reach production.


References

Frequently Asked Questions

What is a silent limit bypass in Starlette's form parser?

It's when a developer sets size limits on request.form() expecting them to be enforced, but the framework ignores them for certain content types, leaving the application exposed to oversized payloads.

How do you prevent form-based DoS in Python Starlette applications?

Use Starlette 0.50.0 or later, which enforces form size limits for both multipart and application/x-www-form-urlencoded content types. Also consider adding reverse-proxy level request body size limits as a defense-in-depth measure.

What CWE is this form limit bypass vulnerability?

CWE-770: Allocation of Resources Without Limits or Throttling, because the application allocates memory to parse form data without enforcing the developer-configured bounds.

Is setting max_fields or max_files on request.form() enough to prevent DoS in older Starlette versions?

No. In Starlette versions before 0.50.0, those limits were silently ignored for application/x-www-form-urlencoded requests, giving developers a false sense of security.

Can static analysis detect this form limit bypass vulnerability?

Yes. Trivy flagged this exact pattern (CVE-2026-54283) by identifying the vulnerable version of Starlette in the uv.lock dependency file, which is how it was caught in this repository.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #18947

Related Articles

critical

How CORS Misconfiguration happens in Node.js with Hono and how to fix it

CVE-2026-54290 is a HIGH severity CORS misconfiguration in the Hono web framework where the CORS middleware incorrectly reflects any `Origin` header back to the client — including credentials — when the `origin` option defaults to a wildcard. Upgrading `hono` from `4.12.16` to `4.12.34` in `package-lock.json` and pinning the version via `overrides` in `package.json` closes the vulnerability. Left unpatched, this flaw could allow malicious cross-origin sites to make credentialed requests and read

high

How Denial of Service via Infinite Loop happens in Node.js and how to fix it

A critical Denial of Service vulnerability (CVE-2026-67213) in the nanoid package allowed attackers to trigger infinite loops during random ID generation. This fix upgrades nanoid from version 3.3.11 to 3.3.18 using npm overrides, eliminating the infinite loop condition in the customAlphabet function that could crash Node.js applications.

high

How package_managers.pnpm.pnpm-missing-minimum-release-age.pnpm-minimum-release-age happens in pnpm workspaces and how to fix it

A pnpm workspace configuration was missing the `minimumReleaseAge` setting, allowing freshly published (and potentially malicious) package versions to be installed immediately. The fix adds a 7-day quarantine period along with `blockExoticSubdeps` and `trustPolicy: no-downgrade` to harden the supply chain against package takeover attacks.

critical

How WebSocket Protocol Handler Vulnerabilities happen in Node.js Dependencies and how to fix it

A critical vulnerability (CVE-2026-54466) was discovered in websocket-driver version 0.7.4, a WebSocket protocol handler used in the dependency tree. The vulnerability allowed attackers to exploit flaws in WebSocket frame parsing, potentially leading to denial of service or protocol-level attacks. The fix upgraded websocket-driver to version 0.7.5, which patches the protocol handling vulnerabilities and hardens input validation for untrusted WebSocket frames.

critical

How Server-Side Request Forgery (SSRF) Happens in Node.js fetch Tools and How to Fix It

A critical Server-Side Request Forgery (SSRF) vulnerability in `plugins/tools/fetch.js` allowed attackers to access internal resources and cloud metadata endpoints by passing arbitrary URLs to the fetch command. The fix adds hostname resolution and private IP range validation before executing any HTTP requests, preventing attackers from targeting internal infrastructure.

high

How IP Address Parsing Inconsistencies Cause SSRF and Trust-Boundary Bypass in Node.js Applications

The `ip-address` library version 10.2.0 contained a critical parsing inconsistency where the `Address4` decoder interpreted leading-zero octets as decimal numbers, while most DNS resolvers and network systems interpreted them as octal. This mismatch allowed attackers to bypass IP-based access controls and SSRF filters. Upgrading to version 10.3.1 fixes this vulnerability by aligning the library's parsing behavior with standard resolver behavior.