module
Xssmaze::Catalog
Overview
Catalog packages every "static" response served from the maze index: the HTML landing page, /map/* views, /sitemap.xml, /version, /stats, etc. Each entry stores the body, a pre-gzipped copy, and a short ETag, all computed once at boot so the request path is just a hash lookup.
Defined in:
catalog.crConstant Summary
-
MAP_LINKS =
["/map/text", "/map/json", "/map/categories", "/map/markdown", "/map/openapi", "/solutions.json", "/solutions/basic", "/stats", "/payloads", "/random", "/health", "/version"] of ::String -
Endpoints the catalog publishes for tooling. Rendered in the page footer.
/solutions/basicstands in for the per-category answer key (/solutions/ <category>);/solutions.jsonis the whole key keyed by maze name. -
OPENAPI_METHODS =
["get", "put", "post", "delete", "options", "head", "patch", "trace"] of ::String -
Path Item operations OpenAPI 3.0 actually defines. Anything outside this set — HTTP QUERY, for one — is not a valid Path Item field, so it goes into the
x-additional-operationsextension instead of quietly making the whole document invalid for every consumer. -
PAYLOADS_BODY =
{description: "Reference XSS payloads for lab use only.", payloads: [{label: "basic alert", value: "<script>alert(1)</script>"}, {label: "img onerror", value: "<img src=x onerror=alert(1)>"}, {label: "svg onload", value: "<svg onload=alert(1)>"}, {label: "javascript: scheme", value: "javascript:alert(1)"}, {label: "data: html", value: "data:text/html,<script>alert(1)</script>"}, {label: "iframe srcdoc", value: "<iframe srcdoc='<script>alert(1)</script>'>"}, {label: "details ontoggle", value: "<details open ontoggle=alert(1)>"}, {label: "input autofocus", value: "<input autofocus onfocus=alert(1)>"}, {label: "form action javascript", value: "<form action=javascript:alert(1)><button>x</button></form>"}, {label: "polyglot", value: "jaVasCript:/*-/*`/*\\`/*'/*\"/**/(/* */oNcliCk=alert() )//%0D%0A%0D%0A//</stYle/</titLe/</teXtarEa/</scRipt/--!>\\x3csVg/<sVg/oNloAd=alert()//>\\x3e"}]}.to_json -
ROBOTS_BODY =
"User-agent: *\nDisallow: /\n"
Class Method Summary
-
.build_all : Hash(String, Entry)
Build every cached static asset in one shot.
-
.build_categories_json(groups : Hash(String, Array(Maze)), total : Int32) : String
Per-category rollup.
- .build_index_html(groups : Hash(String, Array(Maze)), total : Int32) : String
- .build_map_markdown(mazes : Array(Maze)) : String
- .build_map_text(mazes : Array(Maze)) : String
-
.build_openapi(mazes : Array(Maze)) : String
Minimal OpenAPI 3.0 document so external tooling (Swagger UI, code generators, scanner runners) can ingest the catalog directly.
- .build_sitemap(mazes : Array(Maze)) : String
-
.build_solution_pages : Hash(String, Entry)
One cached Entry per solution category, keyed by category name.
- .build_stats(mazes : Array(Maze), groups : Hash(String, Array(Maze))) : String
-
.render_404(path : String) : String
The 404 body is the one page that varies per request (it echoes the missed path), so it is rendered rather than cached like the entries above.
Class Method Detail
Build every cached static asset in one shot. The key naming maps 1:1 to the route table in server.cr so adding a new catalog view is a matter of: add a builder above, add an Entry here, add a route in the server table.
Per-category rollup. Beyond the raw count, this breaks each category down by vulnerability class and reachability so a benchmark can pick its target set (and exclude controls) without walking every endpoint in /map/json.
Minimal OpenAPI 3.0 document so external tooling (Swagger UI, code generators, scanner runners) can ingest the catalog directly.
One cached Entry per solution category, keyed by category name. Built the
same way as the flat catalog views so /solutions/<category> gets the same
ETag / gzip / 304 treatment — it just needs a dynamic route to select the
category rather than a fixed STATIC_ROUTES row.
The 404 body is the one page that varies per request (it echoes the missed path), so it is rendered rather than cached like the entries above. The path is escaped — this page is chrome, not a maze. It shares the index stylesheet instead of carrying its own, so the two pages can never drift apart.