xss
xss, also known as js-xss, sanitizes an untrusted HTML string with a tag-and-attribute allowlist. Unknown tags are escaped by default, disallowed attributes are removed, allowed href and src values pass through a protocol check, and allowed style attributes can be filtered by the bundled cssfilter dependency. It runs in Node, ships a prebuilt browser file and CLI, and supports custom hooks when the default policy does not match an application's HTML subset.
xss is compact and configurable enough for a deliberately small HTML policy, but its defaults are not a substitute for defining that policy. New browser-heavy projects should compare DOMPurify first, and no project should copy permissive custom hooks without security tests.
Use it if
- You deliberately accept a limited HTML subset from users and need the same string-based sanitizer in Node and a browser bundle
- Your policy is naturally expressed as allowed tags and attributes, with optional hooks for custom elements or data attributes
- You want unsupported markup escaped for display rather than silently discarded
- You need a reusable FilterXSS instance or a small CLI for processing stored HTML files
- You do not need user-authored HTML: render plain text through your framework's normal escaped text path instead of introducing an HTML sanitizer and dangerously injecting markup
- You want a sanitizer driven by the browser's DOM parser and maintained primarily for browser XSS defense: DOMPurify is the more focused choice, while xss contains its own character-level tag and attribute parser
- Your URL policy must reject data images, FTP, telephone links, or relative URLs: the default safeAttrValue explicitly accepts data:image/, ftp://, tel:, ./, ../, root-relative paths, and fragments, so you must replace or wrap it
- You plan to allow style while setting css: false: the README says that disables style-content filtering, transferring the full CSS safety burden to your own policy
- You need the repository README's filterXSSWithResult API today: it exists on the current GitHub branch, but the published 1.0.15 tarball does not export it or declare it in the shipped types
Setup reality
npm install xss gives CommonJS code, bundled TypeScript declarations, a command-line binary, commander, and cssfilter. There are no peer dependencies or native builds, but installation is not the important security decision. Start by writing the exact HTML policy your product needs. Passing whiteList or its allowList alias replaces the default policy rather than extending it; clone xss.getDefaultWhiteList() first if you mean to add a tag. Unknown tags are escaped, not removed, unless stripIgnoreTag is true. Even then their text remains, so stripIgnoreTagBody must name script or other elements whose contents should also disappear. Allowed href and src values use the library's own protocol policy, which accepts more than http and https, including data:image/ and FTP. If those are outside your threat model, provide a tested safeAttrValue wrapper or remove the attributes. The default allowlist does not permit style, but adding style activates cssfilter unless css is false. Hook return values are raw output fragments: onIgnoreTag and onIgnoreTagAttr examples use escapeAttrValue because returning unescaped input can recreate the exact injection path the sanitizer was meant to close. Sanitize at the final HTML rendering boundary, keep the original separately if edits are needed, and avoid repeatedly sanitizing already transformed markup. The README's browser example uses a RawGit URL and explicitly says not to use it in production; bundle the package yourself. Finally, pin and test the shipped npm version against your own hostile corpus because the repository README currently describes an unreleased export not present in 1.0.15.
Patterns
Sanitize with the default allowlistsanitize-default-html
const xss = require('xss');
const clean = xss('<p>Hello <strong>friend</strong></p><script>alert(1)</script>');Unknown tags are escaped by default, so the script text remains visible inside escaped markup. Use stripIgnoreTagBody when the contents must disappear too.
Allow only a small formatting subsetdefine-minimal-allowlist
const clean = xss(untrustedHtml, {
allowList: {
p: [],
strong: [],
em: [],
a: ['href', 'title'],
},
});allowList and whiteList are aliases. Supplying either replaces all defaults, which is useful for a deny-by-default policy.
Extend the shipped default policyextend-default-allowlist
const allowList = xss.getDefaultWhiteList();
allowList.mark = [...(allowList.mark || []), 'class'];
const clean = xss(untrustedHtml, { allowList });getDefaultWhiteList returns a fresh object. Mutating the exported xss.whiteList directly can change behavior elsewhere in the same process.
Reuse one configured sanitizerreuse-filter-instance
const filter = new xss.FilterXSS({
allowList: { p: [], code: ['class'], pre: [] },
stripIgnoreTag: true,
});
const first = filter.process(commentA);
const second = filter.process(commentB);FilterXSS avoids rebuilding options for repeated calls. stripIgnoreTag removes disallowed tags but keeps their text content.
Remove dangerous elements and their contentsremove-script-bodies
const clean = xss(untrustedHtml, {
allowList: { p: [], strong: [], em: [] },
stripIgnoreTag: true,
stripIgnoreTagBody: ['script', 'style', 'iframe'],
});stripIgnoreTagBody applies only to tags that are not allowed. List the bodies explicitly; stripIgnoreTag alone leaves their inner text.
Keep text while dropping markupextract-plain-text
const plain = xss(untrustedHtml, {
allowList: {},
stripIgnoreTag: true,
stripIgnoreTagBody: ['script', 'style'],
});This is HTML-to-text-like filtering, not full text extraction. Entities and whitespace follow xss's parser behavior rather than a rendered DOM's textContent.
Allow data attributes with escaped valuesallow-data-attributes
const clean = xss(untrustedHtml, {
onIgnoreTagAttr(tag, name, value) {
if (tag === 'div' && name.startsWith('data-')) {
return `${name}="${xss.escapeAttrValue(value)}"`;
}
},
});A hook's returned string is inserted into output. Escaping the value is essential, and applications should also constrain acceptable attribute names.
Apply a stricter link URL policyrestrict-link-protocols
const strict = new xss.FilterXSS({
allowList: { a: ['href', 'title'] },
safeAttrValue(tag, name, value, cssFilter) {
if (tag === 'a' && name === 'href') {
const normalized = xss.friendlyAttrValue(value).trim();
if (!/^https:\/\//i.test(normalized)) return '';
}
return xss.safeAttrValue(tag, name, value, cssFilter);
},
});The default URL policy also accepts http, mailto, tel, FTP, data images, relative paths, and fragments. This wrapper narrows links to HTTPS.
Allow a small CSS property setfilter-inline-styles
const filter = new xss.FilterXSS({
allowList: { span: ['style'] },
css: {
whiteList: {
color: true,
'font-weight': /^(normal|bold|[1-9]00)$/i,
},
},
});Style is not in the default HTML allowlist. Do not set css: false for untrusted style values unless another trusted CSS sanitizer runs first.
Record rejected tag namesobserve-disallowed-tags
const rejected = new Set();
const clean = xss(untrustedHtml, {
onIgnoreTag(tag) {
rejected.add(tag);
// Return undefined to keep the default escaped output.
},
});
console.log([...rejected]);Returning undefined preserves default handling. Returning the original html argument from this hook would reinsert the disallowed tag unsanitized.
Emit single-quoted attribute valuesuse-single-quoted-attributes
const clean = xss('<a href="https://example.com">link</a>', {
singleQuotedAttributeValue: true,
});This changes output syntax, not the allowlist or URL policy. It was added in 1.0.15.
Process an HTML file from the command linesanitize-file-cli
npx xss -i untrusted.html -o sanitized.htmlThe CLI uses the package defaults. For an application-specific allowlist and automated security tests, call the JavaScript API instead.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| dompurify | npm | Use it for DOM-based sanitization in browsers or on a server with a carefully maintained DOM implementation |
| sanitize-html | npm | Use it for a server-oriented HTML parser with detailed tag, attribute, transform, and URL-scheme policies |
| rehype-sanitize | npm | Use it when untrusted content already flows through the unified or rehype HTML syntax-tree pipeline |