Security Audit Report: Java-WebSocket (TooTallNate)
Project: Java-WebSocket
Version: 1.6.1-SNAPSHOT
Repository: https://github.com/TooTallNate/Java-WebSocket
Audit Date: 2026-08-01
Auditor: SuperAudit
Executive Summary
Three-stage automated scanning (init → bizlogic → scan) plus deep manual analysis of the Java-WebSocket library core identified 1 confirmed CVE-worthy vulnerability with successful PoC validation. The automated scanner found 52 candidates (34 bizlogic + 18 traditional), but all were false positives on example code. Manual analysis of the protocol core (org/java_websocket/*) revealed the real security issue.
| ID |
Title |
Severity |
CVE-Worthy |
PoC Validated |
| VULN-1 |
Incomplete Buffer Limit Check in Fragmentation Handling (Draft_6455) |
High |
Yes |
Yes |
Automated Scan Results
Bizlogic Stage (31 flows, 34 findings)
- Flows extracted: 31 WebSocket handler flows (after adding WebSocket support to scanner)
- Findings: 34 (5 critical, 18 medium, 11 low)
- Assessment: All 34 findings target
src/main/example/*.java — demo applications that intentionally lack authentication. 0 CVE-worthy.
Traditional Scan Stage (18 raw candidates)
- Candidates: 17 path_traversal + 1 authz
- Assessment: All path_traversal candidates are
new FileInputStream(KEYSTORE) with hardcoded constant paths in SSL example code. 0 CVE-worthy.
Scanner Enhancement
- WebSocket framework support added to
plugins/bizlogic/java.py:
_extract_websocket_from_file() — detects WebSocketServer/WebSocketClient/WebSocketAdapter extends and WebSocketHandler implements
_match_brace_block() — brace-matching for multi-class file handling
VULN-1: Incomplete Buffer Limit Check in Fragmentation Handling (Draft_6455)
Classification
- CWE: CWE-770 (Allocation of Resources Without Limits or Throttling)
- Severity: High
- Attack Vector: Remote, unauthenticated
- Impact: Denial of Service via unbounded memory allocation
Description
This is an incomplete implementation of the buffer limit check in the fragmentation handling logic of Draft_6455. When a user configures maxFrameSize to limit accumulated message size, intermediate CONTINUOUS frames bypass this limit entirely, allowing unbounded memory allocation regardless of the configured threshold.
Root Cause
The WebSocket protocol (RFC 6455) allows messages to be fragmented across multiple frames: one initial frame (TEXT or BINARY) followed by zero or more CONTINUOUS frames, ending with a FIN frame. Draft_6455 implements a checkBufferLimit() method intended to enforce the configured maxFrameSize on the accumulated message payload. However, this check is only invoked at two points:
- First frame —
processFrameIsNotFin() (line 1045)
- Last frame —
processFrameIsFin() (line 1007)
For all intermediate CONTINUOUS frames, the code path flows through processFrameContinuousAndNonFin() (lines 928–948), which calls addToBufferList() without calling checkBufferLimit():
// Draft_6455.java, lines 928-948
private void processFrameContinuousAndNonFin(WebSocketImpl webSocketImpl, Framedata frame,
Opcode curop) throws InvalidDataException {
if (curop != Opcode.CONTINUOUS) {
processFrameIsNotFin(frame); // ✓ calls checkBufferLimit()
} else if (frame.isFin()) {
processFrameIsFin(webSocketImpl, frame); // ✓ calls checkBufferLimit()
} else if (currentContinuousFrame == null) {
throw new InvalidDataException(...);
}
// ⚠️ For intermediate CONTINUOUS frames (curop == CONTINUOUS, !isFin, currentContinuousFrame != null):
if (curop == Opcode.CONTINUOUS && currentContinuousFrame != null) {
addToBufferList(frame.getPayloadData()); // NO checkBufferLimit()!
}
}
The addToBufferList() method (line 1089) simply appends the payload to a synchronized list with no accumulated size check:
private void addToBufferList(ByteBuffer payloadData) {
byteBufferList.add(payloadData);
}
The checkBufferLimit() method (lines 1101–1108) does exist and would correctly enforce the limit, but it is never reached for intermediate frames:
private void checkBufferLimit() throws LimitExedeedException {
if (currentContinuousFrame == null) return;
long totalSize = getBufferSize(); // sums all entries in byteBufferList
if (totalSize > maxFrameSize) {
throw new LimitExedeedException(...);
}
}
Attack Scenario
An attacker can exploit this by sending a sequence of fragmented WebSocket frames where each individual frame is within the maxFrameSize per-frame limit enforced by translateSingleFrame(), but the accumulated total across CONTINUOUS frames far exceeds the configured threshold:
- Attacker completes WebSocket handshake with target server
- Sends a non-FIN TEXT frame (starts fragmented message, triggers
checkBufferLimit() — passes because payload < maxFrameSize)
- Sends N large non-FIN CONTINUOUS frames (each passes per-frame
maxFrameSize check, but checkBufferLimit() is never called on accumulated buffer)
- Never sends FIN frame → the final
checkBufferLimit() in processFrameIsFin() never triggers
- Server memory grows unbounded with each CONTINUOUS frame → OOM / DoS
The maxFrameSize configuration gives users a false sense of protection: the limit is enforced on individual frames and on the first/last frames of a fragmented message, but not on the accumulated total during the fragmentation process.
PoC Validation
Server: PoCVuln1Server.java — minimal WebSocket server with Draft_6455 configured with maxFrameSize = 1048576 (1 MB)
Client: poc_vuln1_continuous_flood.py — raw TCP client crafting WebSocket frames
Configuration:
- Server
maxFrameSize = 1,048,576 bytes (1 MB)
- Each CONTINUOUS frame payload = 100,000 bytes (~97 KB, well under 1 MB per-frame limit)
- 110 CONTINUOUS frames sent + 1 initial TEXT frame = 111 frames total
Execution output:
========================================================================
PoC: CONTINUOUS Frame Memory Exhaustion (Draft_6455)
Target: Java-WebSocket 1.6.1-SNAPSHOT
========================================================================
[*] Server configured maxFrameSize = 1,048,576 bytes (1024 KB)
[*] Each CONTINUOUS frame payload = 100,000 bytes (97 KB)
[*] Number of CONTINUOUS frames = 110
[*] Planned accumulated total = 11,100,000 bytes (10.6 MB)
[*] Ratio: 10.6x the configured limit
[+] Handshake successful
[+] Sending initial non-FIN TEXT frame (100,000 bytes)
[+] Sent 10/110 | accumulated = 1.0 MB | 1.0x limit | EXCEEDS LIMIT
[+] Sent 20/110 | accumulated = 2.0 MB | 2.0x limit | EXCEEDS LIMIT
[+] Sent 30/110 | accumulated = 3.0 MB | 3.0x limit | EXCEEDS LIMIT
[+] Sent 40/110 | accumulated = 3.9 MB | 3.9x limit | EXCEEDS LIMIT
[+] Sent 50/110 | accumulated = 4.9 MB | 4.9x limit | EXCEEDS LIMIT
[+] Sent 60/110 | accumulated = 5.8 MB | 5.8x limit | EXCEEDS LIMIT
[+] Sent 70/110 | accumulated = 6.8 MB | 6.8x limit | EXCEEDS LIMIT
[+] Sent 80/110 | accumulated = 7.7 MB | 7.7x limit | EXCEEDS LIMIT
[+] Sent 90/110 | accumulated = 8.7 MB | 8.7x limit | EXCEEDS LIMIT
[+] Sent 100/110 | accumulated = 9.6 MB | 9.6x limit | EXCEEDS LIMIT
[+] Sent 110/110 | accumulated = 10.6 MB | 10.6x limit | EXCEEDS LIMIT
========================================================================
[!] RESULT: 111 frames sent, 10.6 MB accumulated
[!] Configured maxFrameSize: 1024 KB (1,048,576 bytes)
[!] Accumulated total: 10.6 MB (11,100,000 bytes)
[!] Exceeded limit by: 10.6x
[!] checkBufferLimit() was NEVER called for intermediate CONTINUOUS frames
[!] FIN frame never sent -> final checkBufferLimit() never triggered
========================================================================
Result: With maxFrameSize explicitly configured to 1 MB, the server accepted all 111 frames accumulating 10.6 MB (10.6× the configured limit) without triggering any size enforcement. The connection remained open and functional throughout. The checkBufferLimit() method was never invoked for any of the 110 intermediate CONTINUOUS frames.
Recommended Fix
Add checkBufferLimit() call after addToBufferList() for intermediate CONTINUOUS frames:
// In processFrameContinuousAndNonFin(), after line 947:
if (curop == Opcode.CONTINUOUS && currentContinuousFrame != null) {
addToBufferList(frame.getPayloadData());
checkBufferLimit(); // ADD THIS LINE — enforce accumulated size on every frame
}
This ensures that the configured maxFrameSize is enforced on the accumulated buffer after every CONTINUOUS frame, not just the first and last.
Conclusion
The Java-WebSocket library contains a confirmed High-severity vulnerability (VULN-1) — an incomplete implementation of the buffer limit check in the fragmentation handling logic of Draft_6455. When a user configures maxFrameSize to limit accumulated message size, intermediate CONTINUOUS frames bypass this limit entirely, allowing unbounded memory allocation regardless of the configured threshold. The PoC demonstrated this by sending 111 frames accumulating 10.6 MB against a server with maxFrameSize explicitly set to 1 MB — exceeding the limit by 10.6× without triggering any enforcement.
The automated scanner correctly identified the example code as low-risk but missed the library core vulnerability because the issue is in the protocol state machine logic (fragmentation handling), not in traditional taint flows. This highlights the importance of manual protocol-level analysis for security-critical libraries.
poc_vuln1_continuous_flood.py
PoCVuln1Server.java
Security Audit Report: Java-WebSocket (TooTallNate)
Project: Java-WebSocket
Version: 1.6.1-SNAPSHOT
Repository: https://github.com/TooTallNate/Java-WebSocket
Audit Date: 2026-08-01
Auditor: SuperAudit
Executive Summary
Three-stage automated scanning (init → bizlogic → scan) plus deep manual analysis of the Java-WebSocket library core identified 1 confirmed CVE-worthy vulnerability with successful PoC validation. The automated scanner found 52 candidates (34 bizlogic + 18 traditional), but all were false positives on example code. Manual analysis of the protocol core (
org/java_websocket/*) revealed the real security issue.Automated Scan Results
Bizlogic Stage (31 flows, 34 findings)
src/main/example/*.java— demo applications that intentionally lack authentication. 0 CVE-worthy.Traditional Scan Stage (18 raw candidates)
new FileInputStream(KEYSTORE)with hardcoded constant paths in SSL example code. 0 CVE-worthy.Scanner Enhancement
plugins/bizlogic/java.py:_extract_websocket_from_file()— detectsWebSocketServer/WebSocketClient/WebSocketAdapterextends andWebSocketHandlerimplements_match_brace_block()— brace-matching for multi-class file handlingVULN-1: Incomplete Buffer Limit Check in Fragmentation Handling (Draft_6455)
Classification
Description
This is an incomplete implementation of the buffer limit check in the fragmentation handling logic of
Draft_6455. When a user configuresmaxFrameSizeto limit accumulated message size, intermediate CONTINUOUS frames bypass this limit entirely, allowing unbounded memory allocation regardless of the configured threshold.Root Cause
The WebSocket protocol (RFC 6455) allows messages to be fragmented across multiple frames: one initial frame (TEXT or BINARY) followed by zero or more CONTINUOUS frames, ending with a FIN frame.
Draft_6455implements acheckBufferLimit()method intended to enforce the configuredmaxFrameSizeon the accumulated message payload. However, this check is only invoked at two points:processFrameIsNotFin()(line 1045)processFrameIsFin()(line 1007)For all intermediate CONTINUOUS frames, the code path flows through
processFrameContinuousAndNonFin()(lines 928–948), which callsaddToBufferList()without callingcheckBufferLimit():The
addToBufferList()method (line 1089) simply appends the payload to a synchronized list with no accumulated size check:The
checkBufferLimit()method (lines 1101–1108) does exist and would correctly enforce the limit, but it is never reached for intermediate frames:Attack Scenario
An attacker can exploit this by sending a sequence of fragmented WebSocket frames where each individual frame is within the
maxFrameSizeper-frame limit enforced bytranslateSingleFrame(), but the accumulated total across CONTINUOUS frames far exceeds the configured threshold:checkBufferLimit()— passes because payload < maxFrameSize)maxFrameSizecheck, butcheckBufferLimit()is never called on accumulated buffer)checkBufferLimit()inprocessFrameIsFin()never triggersThe
maxFrameSizeconfiguration gives users a false sense of protection: the limit is enforced on individual frames and on the first/last frames of a fragmented message, but not on the accumulated total during the fragmentation process.PoC Validation
Server:
PoCVuln1Server.java— minimal WebSocket server withDraft_6455configured withmaxFrameSize = 1048576(1 MB)Client:
poc_vuln1_continuous_flood.py— raw TCP client crafting WebSocket framesConfiguration:
maxFrameSize= 1,048,576 bytes (1 MB)Execution output:
Result: With
maxFrameSizeexplicitly configured to 1 MB, the server accepted all 111 frames accumulating 10.6 MB (10.6× the configured limit) without triggering any size enforcement. The connection remained open and functional throughout. ThecheckBufferLimit()method was never invoked for any of the 110 intermediate CONTINUOUS frames.Recommended Fix
Add
checkBufferLimit()call afteraddToBufferList()for intermediate CONTINUOUS frames:This ensures that the configured
maxFrameSizeis enforced on the accumulated buffer after every CONTINUOUS frame, not just the first and last.Conclusion
The Java-WebSocket library contains a confirmed High-severity vulnerability (VULN-1) — an incomplete implementation of the buffer limit check in the fragmentation handling logic of
Draft_6455. When a user configuresmaxFrameSizeto limit accumulated message size, intermediate CONTINUOUS frames bypass this limit entirely, allowing unbounded memory allocation regardless of the configured threshold. The PoC demonstrated this by sending 111 frames accumulating 10.6 MB against a server withmaxFrameSizeexplicitly set to 1 MB — exceeding the limit by 10.6× without triggering any enforcement.The automated scanner correctly identified the example code as low-risk but missed the library core vulnerability because the issue is in the protocol state machine logic (fragmentation handling), not in traditional taint flows. This highlights the importance of manual protocol-level analysis for security-critical libraries.
poc_vuln1_continuous_flood.py
PoCVuln1Server.java