Skip to content

fuzz_ws

Synchronously fuzz a WebSocket frame with WSMessage-typed positions. The schema mirrors resend_ws plus a positions[] list. Each variant performs a fresh TCP (and TLS for wss) dial, an HTTP/1.1 Upgrade dance, sends one frame, and records the response.

This tool is synchronous: every variant runs in-process and the full per-variant result list is returned when finished. There is no concurrency or rate limit -- pace from the client side.

Limits

  • Maximum variants per call: 1000 (cartesian product across all positions)

Each variant is executed sequentially with its own connection, upgrade dance, and Stream row. Per-variant SafetyFilter input gating runs before the upstream send -- blocked variants are recorded with error and the run continues.

Parameters

fuzz_ws inherits every base field from resend_ws (flow_id, target_addr, scheme, path, raw_query, opcode, fin, payload, body_encoding, payload_set, masked, mask, close_code, close_reason, compressed, tls_fingerprint, timeout_ms, tag).

Parameter Type Required Default Description
(base fields) -- -- -- See resend_ws. opcode is required; target_addr + path are required when flow_id is empty
positions array Yes Ordered position list (see below). At least one entry
stop_on_close boolean No false Abort remaining variants once any variant receives a Close frame from upstream

positions

Field Type Required Default Description
path string Yes Typed path: payload or close_reason
payloads string[] Yes List of values to substitute at this path. At least one element
encoding string No "text" "text" or "base64". Applies to every payload

Response

Field Type Description
total_variants integer Total variant count from the cartesian product
completed_variants integer Variants actually executed before completion or stop
stopped_reason string Empty when all variants ran; "stop_on_close: ..." or similar otherwise
variants array Per-variant result rows (see below)
duration_ms integer Total run duration in milliseconds
tag string Echo of the supplied tag (when set)

Each variant row:

Field Type Description
index integer Variant index (zero-based, in execution order)
stream_id string New stream record id
opcode string Opcode of the upstream's terminating frame
fin boolean FIN bit on the result frame
payload_size integer Result-frame payload size in bytes (full payload not stored on the row)
compressed boolean true when the result frame used per-message-deflate
close_code integer RFC 6455 status code on Close frames
close_reason string Close reason text on Close frames
payloads object Map of position path -> decoded payload string
error string Error message when the variant failed
duration_ms integer Per-variant duration

Full result-frame payloads are not stored on the row (a malicious upstream could amplify memory use up to 1000 variants times the per-frame Layer cap). Retrieve them via the query tool keyed by stream_id.

Pipeline placement

Each variant traverses the same self-contained PluginStepPost -> RecordStep pipeline as resend_ws (PluginStepPre is bypassed per RFC-001 §9.3).

Job and result persistence

Synchronous fuzz runs persist a row to fuzz_jobs and one row per variant to fuzz_results, so query (resource = "fuzz_jobs" / "fuzz_results") can list and aggregate variants after the call returns. Per-variant flows carry origin = "fuzz".

Examples

Fuzz the payload of a text frame

// fuzz_ws
{
  "flow_id": "ws-abc-123",
  "opcode": "text",
  "positions": [
    {
      "path": "payload",
      "payloads": [
        "{\"action\":\"ping\"}",
        "{\"action\":\"admin\"}",
        "<script>alert(1)</script>"
      ]
    }
  ]
}

Fuzz a close reason

// fuzz_ws
{
  "flow_id": "ws-abc-123",
  "opcode": "close",
  "close_code": 1000,
  "positions": [
    {"path": "close_reason", "payloads": ["bye", "exit", ""]}
  ],
  "stop_on_close": true
}