blob: 5c84a5b4dbe706d9427abf6d7ce2a52ddb637c28 [file]
[
{
"id": "COMP-ABSOLUTE-FORM",
"description": "Absolute-form request-target — server should accept per RFC",
"category": "Compliance",
"rfcReference": "RFC 9112 §3.2.2",
"rfcLevel": "Should",
"expected": "2xx preferred; 400/close/timeout = warn",
"verdict": "Pass",
"statusCode": 200,
"rawRequest": "GET http://127.0.0.1:<PORT>/ HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 2\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nOK"
},
{
"id": "COMP-ACCEPT-NONSENSE",
"description": "Unrecognized Accept value — server may return 406 or default representation",
"category": "Compliance",
"rfcReference": "RFC 9110 §12.5.1",
"rfcLevel": "Should",
"expected": "406 or 2xx",
"verdict": "Warn",
"statusCode": 200,
"rawRequest": "GET / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nAccept: application/x-nonsense\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 2\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nOK",
"behavioralNote": "Server ignored unrecognized Accept — returned default representation"
},
{
"id": "COMP-ASTERISK-WITH-GET",
"description": "Asterisk-form (*) request-target with GET must be rejected",
"category": "Compliance",
"rfcReference": "RFC 9112 §3.2.4",
"rfcLevel": "Must",
"expected": "400, close, or timeout = warn",
"verdict": "Fail",
"statusCode": 500,
"rawRequest": "GET * HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n",
"rawResponse": "HTTP/1.1 500 Internal Server Error\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 21\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nInternal Server Error"
},
{
"id": "COMP-BASELINE",
"description": "Valid GET request — confirms server is reachable",
"category": "Compliance",
"rfcLevel": "NotApplicable",
"expected": "2xx",
"verdict": "Pass",
"statusCode": 200,
"rawRequest": "GET / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 2\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nOK"
},
{
"id": "COMP-CHUNKED-BODY",
"description": "Valid single-chunk POST must be accepted",
"category": "Compliance",
"rfcReference": "RFC 9112 §7.1",
"rfcLevel": "Must",
"expected": "2xx + echo",
"verdict": "Pass",
"statusCode": 200,
"rawRequest": "POST / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n0\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ntransfer-encoding: chunked\r\ndate: <DATE>\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\n5\r\nhello\r\n0\r\n\r\n",
"behavioralNote": "Echoed correctly"
},
{
"id": "COMP-CHUNKED-EMPTY",
"description": "Zero-length chunked body (just terminator) must be accepted",
"category": "Compliance",
"rfcReference": "RFC 9112 §7.1",
"rfcLevel": "Must",
"expected": "2xx or close",
"verdict": "Pass",
"statusCode": 200,
"rawRequest": "POST / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ntransfer-encoding: chunked\r\ndate: <DATE>\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\n0\r\n\r\n"
},
{
"id": "COMP-CHUNKED-EXTENSION",
"description": "Chunk extension (valid per RFC) — server should accept or may reject",
"category": "Compliance",
"rfcReference": "RFC 9112 §7.1.1",
"rfcLevel": "Must",
"expected": "2xx preferred; 400 warns",
"verdict": "Pass",
"statusCode": 200,
"rawRequest": "POST / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nTransfer-Encoding: chunked\r\n\r\n5;ext=value\r\nhello\r\n0\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ntransfer-encoding: chunked\r\ndate: <DATE>\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\n5\r\nhello\r\n0\r\n\r\n",
"behavioralNote": "Echoed correctly"
},
{
"id": "COMP-CHUNKED-HEX-UPPERCASE",
"description": "Chunk size with uppercase hex (A = 10) should be accepted",
"category": "Compliance",
"rfcReference": "RFC 9112 §7.1",
"rfcLevel": "Must",
"expected": "2xx + echo",
"verdict": "Pass",
"statusCode": 200,
"rawRequest": "POST / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nTransfer-Encoding: chunked\r\n\r\nA\r\nhelloworld\r\n0\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ntransfer-encoding: chunked\r\ndate: <DATE>\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nA\r\nhelloworld\r\n0\r\n\r\n",
"behavioralNote": "Echoed correctly"
},
{
"id": "COMP-CHUNKED-MULTI",
"description": "Valid multi-chunk POST must be accepted",
"category": "Compliance",
"rfcReference": "RFC 9112 §7.1",
"rfcLevel": "Must",
"expected": "2xx + echo",
"verdict": "Pass",
"statusCode": 200,
"rawRequest": "POST / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n6\r\n world\r\n0\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ntransfer-encoding: chunked\r\ndate: <DATE>\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\n5\r\nhello\r\n6\r\n world\r\n0\r\n\r\n",
"behavioralNote": "Echoed correctly"
},
{
"id": "COMP-CHUNKED-NO-FINAL",
"description": "Chunked body without zero terminator — incomplete transfer",
"category": "Compliance",
"rfcReference": "RFC 9112 §7.1",
"rfcLevel": "Must",
"expected": "400/close/timeout",
"verdict": "Fail",
"statusCode": 200,
"rawRequest": "POST / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ntransfer-encoding: chunked\r\ndate: <DATE>\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\n"
},
{
"id": "COMP-CHUNKED-TRAILER-VALID",
"description": "Valid chunked body with trailer field should be accepted",
"category": "Compliance",
"rfcReference": "RFC 9112 §7.1.2",
"rfcLevel": "Must",
"expected": "2xx + echo",
"verdict": "Fail",
"statusCode": 200,
"rawRequest": "POST / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n0\r\nX-Checksum: abc\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ntransfer-encoding: chunked\r\ndate: <DATE>\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\n",
"behavioralNote": "Empty body — server did not echo"
},
{
"id": "COMP-CONNECTION-CLOSE",
"description": "Server must close connection after responding to Connection: close",
"category": "Compliance",
"rfcReference": "RFC 9112 §9.3",
"rfcLevel": "Must",
"expected": "2xx + close",
"verdict": "Pass",
"statusCode": 200,
"rawRequest": "GET / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nConnection: close\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nconnection: close\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 2\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nOK"
},
{
"id": "COMP-CONTENT-TYPE",
"description": "Response with content should include Content-Type header",
"category": "Compliance",
"rfcReference": "RFC 9110 §8.3",
"rfcLevel": "Should",
"expected": "2xx with Content-Type",
"verdict": "Pass",
"statusCode": 200,
"rawRequest": "GET / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 2\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nOK"
},
{
"id": "COMP-DATE-FORMAT",
"description": "Date header should use IMF-fixdate format",
"category": "Compliance",
"rfcReference": "RFC 9110 §5.6.7",
"rfcLevel": "Should",
"expected": "IMF-fixdate format",
"verdict": "Pass",
"statusCode": 200,
"rawRequest": "GET / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 2\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nOK",
"behavioralNote": "IMF-fixdate: <DATE>"
},
{
"id": "COMP-DATE-HEADER",
"description": "Origin server must include Date header in responses",
"category": "Compliance",
"rfcReference": "RFC 9110 §6.6.1",
"rfcLevel": "Must",
"expected": "2xx with Date header",
"verdict": "Pass",
"statusCode": 200,
"rawRequest": "GET / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 2\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nOK"
},
{
"id": "COMP-DUPLICATE-CT",
"description": "Duplicate Content-Type headers with different values",
"category": "Compliance",
"rfcReference": "RFC 9110 §5.3",
"rfcLevel": "Should",
"expected": "400 or 2xx",
"verdict": "Warn",
"statusCode": 200,
"rawRequest": "POST / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nContent-Length: 5\r\nContent-Type: text/plain\r\nContent-Type: text/html\r\n\r\nhello",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ntransfer-encoding: chunked\r\ndate: <DATE>\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\n5\r\nhello\r\n0\r\n\r\n"
},
{
"id": "COMP-DUPLICATE-HOST-SAME",
"description": "Duplicate Host headers with identical values must be rejected",
"category": "Compliance",
"rfcReference": "RFC 9112 §3.2",
"rfcLevel": "Must",
"expected": "400",
"verdict": "Fail",
"statusCode": 200,
"rawRequest": "GET / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nHost: 127.0.0.1:<PORT>\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 2\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nOK"
},
{
"id": "COMP-EXPECT-UNKNOWN",
"description": "Unknown Expect value should be rejected with 417",
"category": "Compliance",
"rfcReference": "RFC 9110 §10.1.1",
"rfcLevel": "May",
"expected": "417 or 2xx",
"verdict": "Warn",
"statusCode": 200,
"rawRequest": "GET / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nExpect: 200-ok\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 2\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nOK"
},
{
"id": "COMP-GET-WITH-CL-BODY",
"description": "GET with Content-Length and body — semantically unusual",
"category": "Compliance",
"rfcReference": "RFC 9110 §9.3.1",
"rfcLevel": "May",
"expected": "400 or 2xx",
"verdict": "Warn",
"statusCode": 200,
"rawRequest": "GET / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nContent-Length: 5\r\n\r\nhello",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 2\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nOK"
},
{
"id": "COMP-HEAD-NO-BODY",
"description": "HEAD response must not contain a message body",
"category": "Compliance",
"rfcReference": "RFC 9110 §9.3.2",
"rfcLevel": "Must",
"expected": "2xx with no body",
"verdict": "Fail",
"statusCode": 404,
"rawRequest": "HEAD / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n",
"rawResponse": "HTTP/1.1 404 Not Found\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 9\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\n"
},
{
"id": "COMP-HOST-EMPTY-VALUE",
"description": "Empty Host header value must be rejected",
"category": "Compliance",
"rfcReference": "RFC 9112 §3.2",
"rfcLevel": "Must",
"expected": "400 or close",
"verdict": "Fail",
"statusCode": 200,
"rawRequest": "GET / HTTP/1.1\r\nHost: \r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 2\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nOK"
},
{
"id": "COMP-HOST-WITH-PATH",
"description": "Host header with path component must be rejected",
"category": "Compliance",
"rfcReference": "RFC 9112 §3.2",
"rfcLevel": "Must",
"expected": "400 or close",
"verdict": "Fail",
"statusCode": 404,
"rawRequest": "GET / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>/path\r\n\r\n",
"rawResponse": "HTTP/1.1 404 Not Found\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 9\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nNot Found"
},
{
"id": "COMP-HOST-WITH-USERINFO",
"description": "Host header with userinfo (user@host) must be rejected",
"category": "Compliance",
"rfcReference": "RFC 9112 §3.2",
"rfcLevel": "Must",
"expected": "400 or close",
"verdict": "Fail",
"statusCode": 200,
"rawRequest": "GET / HTTP/1.1\r\nHost: user@127.0.0.1:<PORT>\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 2\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nOK"
},
{
"id": "COMP-HTTP10-DEFAULT-CLOSE",
"description": "HTTP/1.0 without keep-alive — server should close connection after response",
"category": "Compliance",
"rfcReference": "RFC 9112 §9.3",
"rfcLevel": "Should",
"expected": "2xx + close",
"verdict": "Pass",
"statusCode": 200,
"rawRequest": "GET / HTTP/1.0\r\nHost: 127.0.0.1:<PORT>\r\n\r\n",
"rawResponse": "HTTP/1.0 200 OK\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 2\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nOK"
},
{
"id": "COMP-HTTP10-NO-HOST",
"description": "HTTP/1.0 without Host header — valid per HTTP/1.0",
"category": "Compliance",
"rfcReference": "RFC 9112 §3.2",
"rfcLevel": "May",
"expected": "200 or 400",
"verdict": "Warn",
"statusCode": 200,
"rawRequest": "GET / HTTP/1.0\r\n\r\n",
"rawResponse": "HTTP/1.0 200 OK\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 2\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nOK"
},
{
"id": "COMP-HTTP12-VERSION",
"description": "HTTP/1.2 — higher minor version should be accepted as HTTP/1.x compatible",
"category": "Compliance",
"rfcReference": "RFC 9112 §2.3",
"rfcLevel": "May",
"expected": "200 or 505",
"verdict": "Pass",
"rawRequest": "GET / HTTP/1.2\r\nHost: 127.0.0.1:<PORT>\r\n\r\n"
},
{
"id": "COMP-LEADING-CRLF",
"description": "Leading CRLF before request-line — server may ignore per RFC",
"category": "Compliance",
"rfcReference": "RFC 9112 §2.2",
"rfcLevel": "Should",
"expected": "400 or 2xx; close/timeout = warn",
"verdict": "Warn",
"rawRequest": "\r\n\r\nGET / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n"
},
{
"id": "COMP-LONG-URL-OK",
"description": "Server should accept request-lines of at least 8000 octets",
"category": "Compliance",
"rfcReference": "RFC 9112 §3",
"rfcLevel": "Should",
"expected": "not 414; close/timeout = warn",
"verdict": "Pass",
"statusCode": 404,
"rawRequest": "GET /aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n",
"rawResponse": "HTTP/1.1 404 Not Found\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 9\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nNot Found"
},
{
"id": "COMP-METHOD-CASE",
"description": "Lowercase method 'get' — methods are case-sensitive per RFC",
"category": "Compliance",
"rfcReference": "RFC 9110 §9.1",
"rfcLevel": "Should",
"expected": "400/405/501 or 2xx; close/timeout = warn",
"verdict": "Fail",
"statusCode": 404,
"rawRequest": "get / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n",
"rawResponse": "HTTP/1.1 404 Not Found\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 9\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nNot Found"
},
{
"id": "COMP-METHOD-CONNECT",
"description": "CONNECT to an origin server must be rejected",
"category": "Compliance",
"rfcReference": "RFC 9110 §9.3.6",
"rfcLevel": "Should",
"expected": "400/405/501 or close",
"verdict": "Pass",
"statusCode": 400,
"rawRequest": "CONNECT example.com:443 HTTP/1.1\r\nHost: example.com:443\r\n\r\n",
"rawResponse": "HTTP/1.1 400 Bad Request\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 11\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nBad Request"
},
{
"id": "COMP-METHOD-TRACE",
"description": "TRACE request — should be disabled in production",
"category": "Compliance",
"rfcReference": "RFC 9110 §9.3.8",
"rfcLevel": "Should",
"expected": "405/501 or 2xx",
"verdict": "Fail",
"statusCode": 404,
"rawRequest": "TRACE / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n",
"rawResponse": "HTTP/1.1 404 Not Found\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 9\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nNot Found"
},
{
"id": "COMP-NO-1XX-HTTP10",
"description": "Server must not send 1xx responses to an HTTP/1.0 client",
"category": "Compliance",
"rfcReference": "RFC 9110 §15.2",
"rfcLevel": "Must",
"expected": "non-1xx response",
"verdict": "Pass",
"rawRequest": "POST / HTTP/1.0\r\nHost: 127.0.0.1:<PORT>\r\nExpect: 100-continue\r\nContent-Length: 5\r\n\r\nhello"
},
{
"id": "COMP-OPTIONS-ALLOW",
"description": "OPTIONS response should include Allow header listing supported methods",
"category": "Compliance",
"rfcReference": "RFC 9110 §9.3.7",
"rfcLevel": "Should",
"expected": "2xx with Allow header, or 405",
"verdict": "Fail",
"statusCode": 404,
"rawRequest": "OPTIONS / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n",
"rawResponse": "HTTP/1.1 404 Not Found\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 9\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nNot Found"
},
{
"id": "COMP-OPTIONS-STAR",
"description": "OPTIONS * is the only valid asterisk-form request",
"category": "Compliance",
"rfcReference": "RFC 9112 §3.2.4",
"rfcLevel": "Must",
"expected": "2xx or 405; close/timeout = warn",
"verdict": "Fail",
"statusCode": 500,
"rawRequest": "OPTIONS * HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n",
"rawResponse": "HTTP/1.1 500 Internal Server Error\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 21\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nInternal Server Error"
},
{
"id": "COMP-POST-CL-BODY",
"description": "POST with Content-Length and matching body must be accepted",
"category": "Compliance",
"rfcReference": "RFC 9112 §6.2",
"rfcLevel": "Must",
"expected": "2xx + echo",
"verdict": "Pass",
"statusCode": 200,
"rawRequest": "POST / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nContent-Length: 5\r\n\r\nhello",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ntransfer-encoding: chunked\r\ndate: <DATE>\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\n5\r\nhello\r\n0\r\n\r\n",
"behavioralNote": "Echoed correctly"
},
{
"id": "COMP-POST-CL-UNDERSEND",
"description": "POST with Content-Length: 10 but only 5 bytes sent — incomplete body",
"category": "Compliance",
"rfcReference": "RFC 9112 §6.2",
"rfcLevel": "Must",
"expected": "400/close/timeout",
"verdict": "Fail",
"statusCode": 200,
"rawRequest": "POST / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nContent-Length: 10\r\n\r\nhello",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ntransfer-encoding: chunked\r\ndate: <DATE>\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\n"
},
{
"id": "COMP-POST-CL-ZERO",
"description": "POST with Content-Length: 0 and no body must be accepted",
"category": "Compliance",
"rfcReference": "RFC 9112 §6.2",
"rfcLevel": "Must",
"expected": "2xx or close",
"verdict": "Pass",
"statusCode": 200,
"rawRequest": "POST / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nContent-Length: 0\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ntransfer-encoding: chunked\r\ndate: <DATE>\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\n0\r\n\r\n"
},
{
"id": "COMP-POST-NO-CL-NO-TE",
"description": "POST with neither Content-Length nor Transfer-Encoding — implicit zero-length body",
"category": "Compliance",
"rfcReference": "RFC 9112 §6.3",
"rfcLevel": "Must",
"expected": "2xx or close",
"verdict": "Pass",
"statusCode": 200,
"rawRequest": "POST / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ntransfer-encoding: chunked\r\ndate: <DATE>\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\n0\r\n\r\n"
},
{
"id": "COMP-POST-UNSUPPORTED-CT",
"description": "POST with unrecognized Content-Type — server may return 415",
"category": "Compliance",
"rfcReference": "RFC 9110 §15.5.16",
"rfcLevel": "May",
"expected": "415 or 2xx",
"verdict": "Pass",
"statusCode": 200,
"rawRequest": "POST / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nContent-Length: 5\r\nContent-Type: application/x-nonsense\r\n\r\nhello",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ntransfer-encoding: chunked\r\ndate: <DATE>\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\n5\r\nhello\r\n0\r\n\r\n",
"behavioralNote": "Server accepted unknown Content-Type"
},
{
"id": "COMP-RANGE-INVALID",
"description": "Invalid Range header syntax should be ignored",
"category": "Compliance",
"rfcReference": "RFC 9110 §14.2",
"rfcLevel": "May",
"expected": "200 or 416",
"verdict": "Pass",
"statusCode": 200,
"rawRequest": "GET / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nRange: bytes=abc-xyz\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 2\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nOK"
},
{
"id": "COMP-RANGE-POST",
"description": "Range header on POST must be ignored — Range only applies to GET",
"category": "Compliance",
"rfcReference": "RFC 9110 §14.2",
"rfcLevel": "Must",
"expected": "2xx (Range ignored)",
"verdict": "Pass",
"statusCode": 200,
"rawRequest": "POST / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nContent-Length: 5\r\nRange: bytes=0-10\r\n\r\nhello",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ntransfer-encoding: chunked\r\ndate: <DATE>\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\n5\r\nhello\r\n0\r\n\r\n"
},
{
"id": "COMP-REQUEST-LINE-TAB",
"description": "Tab as request-line delimiter — SHOULD reject but MAY parse on whitespace",
"category": "Compliance",
"rfcReference": "RFC 9112 §3",
"rfcLevel": "Should",
"expected": "400 or 2xx; close/timeout = warn",
"verdict": "Warn",
"rawRequest": "GET\t/ HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n"
},
{
"id": "COMP-SPACE-IN-TARGET",
"description": "Whitespace inside request-target is invalid",
"category": "Compliance",
"rfcReference": "RFC 9112 §3.2",
"rfcLevel": "Must",
"expected": "400, close, or timeout = warn",
"verdict": "Warn",
"rawRequest": "GET /pa th HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n"
},
{
"id": "COMP-TRACE-SENSITIVE",
"description": "TRACE should exclude sensitive headers from echoed response",
"category": "Compliance",
"rfcReference": "RFC 9110 §9.3.8",
"rfcLevel": "Should",
"expected": "405/501, or 200 without Auth",
"verdict": "Fail",
"statusCode": 404,
"rawRequest": "TRACE / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nAuthorization: Bearer secret-token-123\r\n\r\n",
"rawResponse": "HTTP/1.1 404 Not Found\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 9\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nNot Found"
},
{
"id": "COMP-TRACE-WITH-BODY",
"description": "TRACE with Content-Length body should be rejected",
"category": "Compliance",
"rfcReference": "RFC 9110 §9.3.8",
"rfcLevel": "Should",
"expected": "400/405 or 200",
"verdict": "Fail",
"statusCode": 404,
"rawRequest": "TRACE / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nContent-Length: 5\r\n\r\nhello",
"rawResponse": "HTTP/1.1 404 Not Found\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 9\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nNot Found"
},
{
"id": "COMP-UNKNOWN-METHOD",
"description": "Unrecognized method should be rejected with 501 or 405",
"category": "Compliance",
"rfcReference": "RFC 9110 §9.1",
"rfcLevel": "Should",
"expected": "501/405/400 or close",
"verdict": "Fail",
"statusCode": 404,
"rawRequest": "FOOBAR / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n",
"rawResponse": "HTTP/1.1 404 Not Found\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 9\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nNot Found"
},
{
"id": "COMP-UNKNOWN-TE-501",
"description": "Unknown Transfer-Encoding without CL should be rejected with 501",
"category": "Compliance",
"rfcReference": "RFC 9112 §6.1",
"rfcLevel": "Must",
"expected": "400/501 or close",
"verdict": "Pass",
"rawRequest": "POST / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nTransfer-Encoding: gzip\r\n\r\n"
},
{
"id": "COMP-VERSION-CASE",
"description": "HTTP version is case-sensitive — lowercase 'http' must be rejected",
"category": "Compliance",
"rfcReference": "RFC 9112 §2.3",
"rfcLevel": "Must",
"expected": "400, close, or timeout = warn",
"verdict": "Warn",
"rawRequest": "GET / http/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n"
},
{
"id": "COMP-VERSION-LEADING-ZEROS",
"description": "HTTP/01.01 — leading zeros in version digits are invalid",
"category": "Compliance",
"rfcReference": "RFC 9112 §2.3",
"rfcLevel": "Must",
"expected": "400, close, or timeout = warn",
"verdict": "Warn",
"rawRequest": "GET / HTTP/01.01\r\nHost: 127.0.0.1:<PORT>\r\n\r\n"
},
{
"id": "COMP-VERSION-MISSING-MINOR",
"description": "HTTP/1 with no minor version digit is invalid",
"category": "Compliance",
"rfcReference": "RFC 9112 §2.3",
"rfcLevel": "Must",
"expected": "400, close, or timeout = warn",
"verdict": "Warn",
"rawRequest": "GET / HTTP/1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n"
},
{
"id": "COMP-VERSION-WHITESPACE",
"description": "HTTP/ 1.1 — whitespace inside version token is invalid",
"category": "Compliance",
"rfcReference": "RFC 9112 §2.3",
"rfcLevel": "Must",
"expected": "400, close, or timeout = warn",
"verdict": "Warn",
"rawRequest": "GET / HTTP/ 1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n"
},
{
"id": "COMP-WHITESPACE-BEFORE-HEADERS",
"description": "Whitespace before first header line must be rejected",
"category": "Compliance",
"rfcReference": "RFC 9112 §2.2",
"rfcLevel": "Must",
"expected": "400 or close",
"verdict": "Pass",
"rawRequest": "GET / HTTP/1.1\r\n \r\nHost: 127.0.0.1:<PORT>\r\n\r\n"
},
{
"id": "RFC9110-5.4-DUPLICATE-HOST",
"description": "Duplicate Host headers with different values must be rejected",
"category": "Compliance",
"rfcReference": "RFC 9112 §3.2",
"rfcLevel": "Must",
"expected": "400",
"verdict": "Fail",
"statusCode": 200,
"rawRequest": "GET / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nHost: other.example.com\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 2\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nOK"
},
{
"id": "RFC9110-5.6.2-SP-BEFORE-COLON",
"description": "Whitespace between header name and colon must be rejected",
"category": "Compliance",
"rfcReference": "RFC 9112 §5",
"rfcLevel": "Must",
"expected": "400",
"verdict": "Fail",
"rawRequest": "GET / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nX-Test : value\r\n\r\n"
},
{
"id": "RFC9112-2.2-BARE-LF-HEADER",
"description": "Bare LF in header should be rejected, but MAY be accepted",
"category": "Compliance",
"rfcReference": "RFC 9112 §2.2",
"rfcLevel": "May",
"expected": "400 or close (pass), 2xx (warn)",
"verdict": "Warn",
"statusCode": 200,
"rawRequest": "GET / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\nX-Test: value\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 2\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nOK"
},
{
"id": "RFC9112-2.2-BARE-LF-REQUEST-LINE",
"description": "Bare LF in request line should be rejected, but MAY be accepted",
"category": "Compliance",
"rfcReference": "RFC 9112 §2.2",
"rfcLevel": "May",
"expected": "400 or close (pass), 2xx (warn)",
"verdict": "Warn",
"statusCode": 200,
"rawRequest": "GET / HTTP/1.1\nHost: 127.0.0.1:<PORT>\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 2\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nOK"
},
{
"id": "RFC9112-2.3-HTTP09-REQUEST",
"description": "HTTP/0.9 request (no version) must be rejected",
"category": "Compliance",
"rfcReference": "RFC 9112 §2.3",
"rfcLevel": "Should",
"expected": "400/close/timeout",
"verdict": "Pass",
"rawRequest": "GET /\r\n"
},
{
"id": "RFC9112-2.3-INVALID-VERSION",
"description": "Invalid HTTP version must be rejected",
"category": "Compliance",
"rfcReference": "RFC 9112 §2.3",
"rfcLevel": "Should",
"expected": "400/505, close, or timeout = warn",
"verdict": "Warn",
"rawRequest": "GET / HTTP/9.9\r\nHost: 127.0.0.1:<PORT>\r\n\r\n"
},
{
"id": "RFC9112-3-CR-ONLY-LINE-ENDING",
"description": "CR without LF as line ending must be rejected",
"category": "Compliance",
"rfcReference": "RFC 9112 §2.2",
"rfcLevel": "Must",
"expected": "400, close, or timeout = warn",
"verdict": "Warn",
"rawRequest": "GET / HTTP/1.1\rHost: 127.0.0.1:<PORT>\r\n\r\n"
},
{
"id": "RFC9112-3-MISSING-TARGET",
"description": "Request line with no target (space but no path) must be rejected",
"category": "Compliance",
"rfcReference": "RFC 9112 §3",
"rfcLevel": "Must",
"expected": "400, close, or timeout = warn",
"verdict": "Warn",
"rawRequest": "GET HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n"
},
{
"id": "RFC9112-3-MULTI-SP-REQUEST-LINE",
"description": "Multiple spaces between request-line components — SHOULD reject but MAY parse leniently",
"category": "Compliance",
"rfcReference": "RFC 9112 §3",
"rfcLevel": "Should",
"expected": "400 or 2xx; close/timeout = warn",
"verdict": "Warn",
"rawRequest": "GET / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n"
},
{
"id": "RFC9112-3.2-FRAGMENT-IN-TARGET",
"description": "Fragment (#) in request-target — not part of origin-form grammar",
"category": "Compliance",
"rfcReference": "RFC 9112 §3.2",
"rfcLevel": "Should",
"expected": "400 or 2xx; 404 = warn",
"verdict": "Pass",
"statusCode": 400,
"rawRequest": "GET /path#frag HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n\r\n",
"rawResponse": "HTTP/1.1 400 Bad Request\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 11\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nBad Request"
},
{
"id": "RFC9112-5-EMPTY-HEADER-NAME",
"description": "Empty header name (leading colon) must be rejected",
"category": "Compliance",
"rfcReference": "RFC 9112 §5",
"rfcLevel": "Must",
"expected": "400 or close",
"verdict": "Pass",
"rawRequest": "GET / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\n: empty-name\r\n\r\n"
},
{
"id": "RFC9112-5-HEADER-NO-COLON",
"description": "Header line without colon must be rejected",
"category": "Compliance",
"rfcReference": "RFC 9112 §5",
"rfcLevel": "Must",
"expected": "400 or close",
"verdict": "Pass",
"rawRequest": "GET / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nNoColonHere\r\n\r\n"
},
{
"id": "RFC9112-5-INVALID-HEADER-NAME",
"description": "Header name with invalid characters (brackets) must be rejected",
"category": "Compliance",
"rfcReference": "RFC 9112 §5",
"rfcLevel": "Must",
"expected": "400 or close",
"verdict": "Pass",
"rawRequest": "GET / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nBad[Name: value\r\n\r\n"
},
{
"id": "RFC9112-5.1-OBS-FOLD",
"description": "Obs-fold (line folding) in headers should be rejected",
"category": "Compliance",
"rfcReference": "RFC 9112 §5.1",
"rfcLevel": "Must",
"expected": "400",
"verdict": "Fail",
"statusCode": 200,
"rawRequest": "GET / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nX-Test: value\r\n continued\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 2\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nOK"
},
{
"id": "RFC9112-6.1-CL-NON-NUMERIC",
"description": "Non-numeric Content-Length must be rejected",
"category": "Compliance",
"rfcReference": "RFC 9112 §6.1",
"rfcLevel": "Must",
"expected": "400 or close",
"verdict": "Pass",
"rawRequest": "POST / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nContent-Length: abc\r\n\r\n"
},
{
"id": "RFC9112-6.1-CL-PLUS-SIGN",
"description": "Content-Length with plus sign must be rejected",
"category": "Compliance",
"rfcReference": "RFC 9112 §6.1",
"rfcLevel": "Must",
"expected": "400 or close",
"verdict": "Pass",
"rawRequest": "POST / HTTP/1.1\r\nHost: 127.0.0.1:<PORT>\r\nContent-Length: +5\r\n\r\nhello"
},
{
"id": "RFC9112-7.1-MISSING-HOST",
"description": "Request without Host header must be rejected with 400",
"category": "Compliance",
"rfcReference": "RFC 9112 §3.2",
"rfcLevel": "Must",
"expected": "400",
"verdict": "Fail",
"statusCode": 200,
"rawRequest": "GET / HTTP/1.1\r\n\r\n",
"rawResponse": "HTTP/1.1 200 OK\r\nx-powered-by: Dart with package:shelf\r\ndate: <DATE>\r\ncontent-length: 2\r\nx-frame-options: SAMEORIGIN\r\ncontent-type: text/plain; charset=utf-8\r\nx-xss-protection: 1; mode=block\r\nx-content-type-options: nosniff\r\n\r\nOK"
}
]