From 2c302037179301ab5939a02026b1e8f1db52ab81 Mon Sep 17 00:00:00 2001 From: Joya Biswas Date: Sat, 18 Apr 2026 02:00:19 +0600 Subject: [PATCH] feat: add filterXSSWithResult() to track removed tags and attributes (fixes #284) --- lib/index.js | 63 +++++++++++++++++++++++++++++++++++++ test-my-fix.js | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 test-my-fix.js diff --git a/lib/index.js b/lib/index.js index 1861a4d5..799ca34f 100644 --- a/lib/index.js +++ b/lib/index.js @@ -20,8 +20,71 @@ function filterXSS(html, options) { return xss.process(html); } +/** + * Filter XSS and return both the sanitized HTML and a list of removed elements. + * Addresses feature request: https://github.com/leizongmin/js-xss/issues/284 + * + * @param {String} html - dirty HTML string to sanitize + * @param {Object} options - same options as filterXSS() + * @return {Object} { html: String, removed: Array } + * + * Each item in `removed` is one of: + * { type: "tag", tag: "script", html: "

Hello

'); +console.log(" Input :", '

Hello

'); +console.log(" Output :", r1.html); +console.log(" Removed:", JSON.stringify(r1.removed, null, 2)); +check("removed array is not empty", r1.removed.length > 0); +check("script tag was recorded", r1.removed.some(function(r) { return r.tag === "script"; })); +check("

tag was NOT recorded (it is allowed)", !r1.removed.some(function(r) { return r.tag === "p"; })); + +console.log(); + +// ---- TEST 2 ---- +// onclick is NOT in the whitelist for → should be recorded as removed +console.log("TEST 2: Dangerous attribute gets recorded"); +var r2 = xss.filterXSSWithResult('click me'); +console.log(" Input :", 'click me'); +console.log(" Output :", r2.html); +console.log(" Removed:", JSON.stringify(r2.removed, null, 2)); +check("removed array has 1 item (only onclick)", r2.removed.length === 1); +check("onclick attr was recorded", r2.removed.some(function(r) { return r.attr === "onclick"; })); +check("href was NOT recorded (it is allowed)", !r2.removed.some(function(r) { return r.attr === "href"; })); + +console.log(); + +// ---- TEST 3 ---- +// Completely clean HTML → nothing should be recorded +console.log("TEST 3: Clean HTML produces empty removed array"); +var r3 = xss.filterXSSWithResult("

This is safe

"); +console.log(" Input :", "

This is safe

"); +console.log(" Output :", r3.html); +console.log(" Removed:", JSON.stringify(r3.removed)); +check("removed array is empty", r3.removed.length === 0); +check("html output is unchanged", r3.html === "

This is safe

"); + +console.log(); + +// ---- TEST 4 ---- +// Both a bad tag AND a bad attribute in one string +console.log("TEST 4: Mixed dangerous input — both tag and attr recorded"); +var r4 = xss.filterXSSWithResult('x'); +console.log(" Removed:", JSON.stringify(r4.removed, null, 2)); +check("script tag recorded", r4.removed.some(function(r) { return r.tag === "script" && r.type === "tag"; })); +check("onclick attr recorded", r4.removed.some(function(r) { return r.attr === "onclick" && r.type === "attr"; })); + +console.log(); + +// ---- SUMMARY ---- +console.log("========================================"); +console.log(" Results:", passed, "passed,", failed, "failed"); +if (failed === 0) { + console.log(" 🎉 ALL TESTS PASSED!"); +} else { + console.log(" ⚠️ SOME TESTS FAILED - check your code"); +} +console.log("========================================"); \ No newline at end of file