module Xssmaze
Overview
Request logging, in place of Kemal's Log-based handler. One line per
request, columns that stay put, and the status code carrying the colour:
14:02:11 200 GET /basic/level1/?query=a 1.2ms
Defined in:
assets.crregistry.cr
request_log.cr
Constant Summary
-
VERSION =
{{ ((read_file("/srv/crystaldoc.info/github-hahwul-xssmaze-v0.4.0/src/../shard.yml")).lines.find(&.starts_with?("version:")).split(":"))[1].strip }} -
Single source of truth: read the version straight from shard.yml at compile time so it can never drift from the released version again.
Class Method Summary
-
.cookie_value(s : String) : String
Same bargain as
.header_value, one layer down, forSet-Cookie. - .freeze!
- .get : Array(Maze)
- .grouped_mazes : Hash(String, Array(Maze))
- .gzip(body : String) : Bytes
-
.header_value(s : String) : String
Strip CR/LF/NUL from a value before it goes into a response header.
-
.html_escape(s : String) : String
Escapes & < > " ' — byte-for-byte what the previous hand-rolled chain of five
gsubs produced, in a single pass instead of five intermediates. -
.push(name : String, url : String, desc : String, method : String = "GET", params : Array(String) = ["query"], vuln : String = "unclassified", sources : Array(String) = [] of String, sinks : Array(String) = [] of String, delivery : Array(String) = [] of String, exploitable : Bool = true, note : String | Nil = nil)
name/url/desc/method/paramsare the original positional contract and must stay put — every existing call site depends on it.
Class Method Detail
Same bargain as .header_value, one layer down, for Set-Cookie.
HTTP::Cookie enforces RFC 6265 4.1.1 and raises IO::Error on any
byte outside 0x20..0x7e plus " , ; \, so the cookie mazes
answered 500 on precisely the payloads they exist to reflect — a
?pref="x" never reached Set-Cookie at all, and rsplit-level4 died
on the very characters a response-splitting test is made of. Dropping
those bytes keeps the value in the header where the lesson is; the body
reflection alongside it stays raw.
Strip CR/LF/NUL from a value before it goes into a response header.
This is NOT the lab going soft on itself. Crystal's HTTP::Headers
raises ArgumentError on a control character, so a payload like
?query=a%0d%0aX-Evil:1 never produced a split response — it produced a
500 and a stack trace, killing the maze's real lesson (the value is
still reflected into the header, which is what header-context tests
need). Sanitizing here keeps the reflection and drops the crash.
Escapes & < > " ' — byte-for-byte what the previous hand-rolled chain of
five gsubs produced, in a single pass instead of five intermediates.
name/url/desc/method/params are the original positional
contract and must stay put — every existing call site depends on it.
Everything after params is structured vulnerability metadata, passed
by keyword; see Maze for the schema. Omitting them leaves the endpoint
"unclassified" rather than silently guessing.