Custom XSS templates need guardrails, not just a textarea
Allowing custom payload templates is useful for real testing, but IDs, placeholders, imports, and registry behavior need careful handling.
Custom templates are where an XSS tool becomes useful for real work.
Built-in payloads cover common cases. They do not cover the weird internal widget that decodes Base64, strips spaces, injects into an SVG title, then rehydrates through a frontend framework. At some point, testers need to write their own patterns.
That does not mean the UI should be a raw textarea with a save button.
Placeholder syntax matters
A template should make variable parts explicit:
<img src=x onerror={{callback}}>
That is easier to review than a pile of nearly identical payloads where only alert(1) changes. It also helps prevent accidental edits to the structural part of the payload.
Placeholders need constraints. Empty keys, duplicate names, and unexpected braces should not silently produce broken output. A broken payload is not dangerous in the usual sense, but it wastes time and pollutes saved collections.
IDs are part of the API
Template IDs look like labels until they collide.
If custom templates live in the same registry as built-in templates, a user-defined dom-innerhtml could shadow the built-in version. Maybe that is intentional. Usually it is not. If the UI loads the wrong template after a shared URL or collection import, the report becomes unreliable.
Reserved IDs should stay reserved. Imports should regenerate IDs when needed. The product should prefer explicit duplication over silent replacement.
Imports are untrusted input
Security tools often import JSON because JSON feels harmless. It is still input.
Validate it.
const schema = z.object({
schemaVersion: z.literal(1),
templates: z.array(templateSchema).max(100)
});
That does not solve everything, but it blocks the obvious mess: huge files, missing fields, bad categories, impossible placeholder shapes, and values that break the UI.
Also check file size before calling file.text(). Validating after loading a 200 MB file is not a validation strategy.
Custom does not mean trusted
A custom template may contain dangerous HTML. That is the point. But the builder UI should display it as text, not render it. The playground can render in isolation when the user asks. The template library should not execute anything simply because it is listing saved entries.
This boundary is easy to lose during product polish.
Custom templates are powerful because they encode local knowledge. Treat them like local code snippets: useful, portable, and worth validating before they enter the workflow.