diff --git a/docs/index.html b/docs/index.html
index 9584583a9..6c8002506 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -152,6 +152,7 @@
+
';
+ var sanitized = testSanitizeInput(malicious);
+ assert.equal("", sanitized, "Script tags should be completely removed");
+ });
+
+ test("strips img onerror handlers", function () {
+ var malicious = "
";
+ var sanitized = testSanitizeInput(malicious);
+ assert.equal("", sanitized, "Img tags with onerror should be removed");
+ });
+
+ test("strips all HTML tags and attributes", function () {
+ var malicious = '
Click me
';
+ var sanitized = testSanitizeInput(malicious);
+ assert.equal(
+ "Click me",
+ sanitized,
+ "HTML tags removed but text content preserved",
+ );
+ });
+
+ test("handles empty and null inputs safely", function () {
+ assert.equal("", testSanitizeInput(""));
+ assert.equal("", testSanitizeInput(null));
+ assert.equal("", testSanitizeInput(undefined));
+ });
+ });
+
+ suite("Client-side URL safety", function () {
+ test("blocks javascript: URLs", function () {
+ var malicious = "javascript:alert(1)";
+ var safe = safeUrl(malicious);
+ assert.equal("", safe, "javascript: URLs should be blocked");
+ });
+
+ test("blocks data: URLs", function () {
+ var malicious = "data:text/html,";
+ var safe = safeUrl(malicious);
+ assert.equal("", safe, "data: URLs should be blocked");
+ });
+
+ test("allows legitimate HTTP URLs", function () {
+ var legitimate = "https://www.w3.org/TR/html/";
+ var safe = safeUrl(legitimate);
+ assert.equal(
+ "https://www.w3.org/TR/html/",
+ safe,
+ "Legitimate URLs should be allowed",
+ );
+ });
+
+ test("handles case-insensitive protocol detection", function () {
+ assert.equal(
+ "",
+ safeUrl("JAVASCRIPT:alert(1)"),
+ "Uppercase javascript: should be blocked",
+ );
+ assert.equal(
+ "",
+ safeUrl("JavaScript:alert(1)"),
+ "Mixed case javascript: should be blocked",
+ );
+ assert.equal(
+ "",
+ safeUrl("DATA:text/html,';
+ testEndpoint("search-refs", { q: scriptPayload }, function (err, result) {
+ if (err) return done(err);
+ assert.deepEqual(
+ result,
+ { message: "Missing q parameter" },
+ "Script tags should be sanitized to empty, treated as missing parameter",
+ );
+ done();
+ });
+ });
+
+ test("/reverse-lookup blocks XSS in urls parameter", function (done) {
+ var xssPayload = "
";
+ testEndpoint(
+ "reverse-lookup",
+ { urls: xssPayload },
+ function (err, result) {
+ if (err) return done(err);
+ assert.deepEqual(
+ result,
+ {},
+ "Malicious URL should return empty result",
+ );
+ done();
+ },
+ );
+ });
+
+ test("/reverse-lookup blocks javascript: URLs", function (done) {
+ var jsPayload = "javascript:alert(1)";
+ testEndpoint(
+ "reverse-lookup",
+ { urls: jsPayload },
+ function (err, result) {
+ if (err) return done(err);
+ assert.deepEqual(
+ result,
+ {},
+ "JavaScript URLs should return empty result",
+ );
+ done();
+ },
+ );
+ });
+
+ test("/search-refs preserves legitimate queries", function (done) {
+ testEndpoint("search-refs", { q: "html" }, function (err, result) {
+ if (err) return done(err);
+ assert.ok(
+ typeof result === "object",
+ "Legitimate query should return object",
+ );
+ assert.ok(
+ Object.keys(result).length > 0,
+ "Legitimate query should return results",
+ );
+ done();
+ });
+ });
+
+ test("/reverse-lookup preserves legitimate URLs", function (done) {
+ testEndpoint(
+ "reverse-lookup",
+ { urls: "https://www.w3.org/TR/html/" },
+ function (err, result) {
+ if (err) return done(err);
+ assert.ok(
+ typeof result === "object",
+ "Legitimate URL should return object",
+ );
+ // May be empty if URL not in database, but should not error
+ done();
+ },
+ );
+ });
+ });
+
+ suite("Integration security tests", function () {
+ test("XSS payload completely neutralized through full pipeline", function () {
+ var xssPayload = "
";
+ var serverSanitized = testSanitizeInput(xssPayload);
+ assert.equal(
+ "",
+ serverSanitized,
+ "Server should strip malicious HTML completely",
+ );
+ });
+
+ test("Legitimate content preserved through pipeline", function () {
+ var legitimate = "HTML specification";
+ var serverSanitized = testSanitizeInput(legitimate);
+ assert.equal(
+ "HTML specification",
+ serverSanitized,
+ "Legitimate text preserved",
+ );
+ });
+
+ test("Mixed content handled safely", function () {
+ var mixed = "Normal text more text";
+ var serverSanitized = testSanitizeInput(mixed);
+ assert.equal("Normal text more text", serverSanitized);
+ });
+ });
+
+ suite("Edge case security tests", function () {
+ test("handles nested and encoded XSS attempts", function () {
+ var nested = "<script>alert(1)</script>";
+ var sanitized = testSanitizeInput(nested);
+ // sanitize-html keeps HTML entities as-is when no tags are allowed
+ // This is safe because <script> cannot execute as JavaScript
+ assert.equal(
+ "<script>alert(1)</script>",
+ sanitized,
+ "HTML entities kept safe and non-executable",
+ );
+ });
+
+ test("handles URL with malicious fragments", function () {
+ var maliciousUrl = "https://example.com#";
+ var safe = safeUrl(maliciousUrl);
+ // URL is allowed through since it has legitimate https protocol
+ // The fragment with script tags will be handled by DOMPurify during HTML sanitization
+ assert.ok(
+ safe.includes("example.com"),
+ "Legitimate domain should be preserved",
+ );
+ });
+ });
+});