Merge pull request #4 from TauNeutrino/fix-xss-vulnerability-2050985831484711700
🔒 security: fix XSS vulnerabilities in UI helpers and actions
This commit is contained in:
137
tests/repro_vulnerability.js
Normal file
137
tests/repro_vulnerability.js
Normal file
@@ -0,0 +1,137 @@
|
||||
const fs = require('fs');
|
||||
const vm = require('vm');
|
||||
const path = require('path');
|
||||
|
||||
console.log("=== Running Vulnerability Reproduction Tests ===");
|
||||
|
||||
// Mock DOM
|
||||
const createMockElement = (id = 'mock') => {
|
||||
const el = {
|
||||
id,
|
||||
classList: { add: () => { }, remove: () => { }, contains: () => false },
|
||||
_innerHTML: '',
|
||||
get innerHTML() { return this._innerHTML; },
|
||||
set innerHTML(val) {
|
||||
this._innerHTML = val;
|
||||
// Check for XSS
|
||||
if (val.includes('<img src=x onerror=alert(1)>')) {
|
||||
console.error(`❌ VULNERABILITY DETECTED in ${id}: XSS payload found in innerHTML!`);
|
||||
console.error(`Payload: ${val}`);
|
||||
process.exit(1);
|
||||
}
|
||||
},
|
||||
_textContent: '',
|
||||
get textContent() { return this._textContent; },
|
||||
set textContent(val) { this._textContent = val; },
|
||||
value: '',
|
||||
style: { cssText: '', display: '' },
|
||||
addEventListener: () => { },
|
||||
removeEventListener: () => { },
|
||||
appendChild: (child) => { },
|
||||
removeChild: () => { },
|
||||
querySelector: (sel) => createMockElement(sel),
|
||||
querySelectorAll: () => [createMockElement()],
|
||||
getAttribute: () => '',
|
||||
setAttribute: () => { },
|
||||
remove: () => { },
|
||||
dataset: {}
|
||||
};
|
||||
return el;
|
||||
};
|
||||
|
||||
const sandbox = {
|
||||
console: console,
|
||||
document: {
|
||||
body: createMockElement('body'),
|
||||
createElement: (tag) => createMockElement(tag),
|
||||
getElementById: (id) => createMockElement(id),
|
||||
querySelector: (sel) => createMockElement(sel),
|
||||
},
|
||||
localStorage: {
|
||||
getItem: () => null,
|
||||
setItem: () => { },
|
||||
removeItem: () => { }
|
||||
},
|
||||
fetch: () => Promise.reject(new Error('<img src=x onerror=alert(1)>')),
|
||||
setTimeout: (cb) => cb(),
|
||||
setInterval: () => { },
|
||||
requestAnimationFrame: (cb) => cb(),
|
||||
Date: Date,
|
||||
Notification: { permission: 'denied', requestPermission: () => { } },
|
||||
window: { location: { href: '' } },
|
||||
crypto: { randomUUID: () => '1234' }
|
||||
};
|
||||
|
||||
// Load utils.js (for escapeHtml if needed)
|
||||
const utilsCode = fs.readFileSync(path.join(__dirname, '../src/utils.js'), 'utf8')
|
||||
.replace(/export /g, '')
|
||||
.replace(/import .*? from .*?;/g, '');
|
||||
|
||||
// Load constants.js
|
||||
const constantsCode = fs.readFileSync(path.join(__dirname, '../src/constants.js'), 'utf8')
|
||||
.replace(/export /g, '');
|
||||
|
||||
// Load ui_helpers.js
|
||||
const uiHelpersCode = fs.readFileSync(path.join(__dirname, '../src/ui_helpers.js'), 'utf8')
|
||||
.replace(/export /g, '')
|
||||
.replace(/import .*? from .*?;/g, '');
|
||||
|
||||
// Load actions.js
|
||||
const actionsCode = fs.readFileSync(path.join(__dirname, '../src/actions.js'), 'utf8')
|
||||
.replace(/export /g, '')
|
||||
.replace(/import .*? from .*?;/g, '');
|
||||
|
||||
vm.createContext(sandbox);
|
||||
vm.runInContext(utilsCode, sandbox);
|
||||
vm.runInContext(constantsCode, sandbox);
|
||||
// Mock state
|
||||
vm.runInContext(`
|
||||
var authToken = 'mock-token';
|
||||
var currentUser = 'mock-user';
|
||||
var orderMap = new Map();
|
||||
var userFlags = new Set();
|
||||
var highlightTags = [];
|
||||
var allWeeks = [];
|
||||
var currentWeekNumber = 1;
|
||||
var currentYear = 2024;
|
||||
var displayMode = 'this-week';
|
||||
var langMode = 'de';
|
||||
`, sandbox);
|
||||
vm.runInContext(uiHelpersCode, sandbox);
|
||||
vm.runInContext(actionsCode, sandbox);
|
||||
|
||||
async function runTests() {
|
||||
console.log("Testing openVersionMenu error handling...");
|
||||
try {
|
||||
await sandbox.openVersionMenu();
|
||||
} catch (e) {}
|
||||
|
||||
console.log("Testing showToast...");
|
||||
sandbox.showToast('<img src=x onerror=alert(1)>');
|
||||
|
||||
console.log("Testing showErrorModal...");
|
||||
sandbox.showErrorModal('<img src=x onerror=alert(1)>', 'safe content', '<img src=x onerror=alert(1)>', 'http://example.com');
|
||||
|
||||
console.log("Testing openVersionMenu version list rendering...");
|
||||
// Mock successful fetch but with malicious data
|
||||
sandbox.fetch = () => Promise.resolve({
|
||||
ok: true,
|
||||
json: () => Promise.resolve([
|
||||
{
|
||||
tag: '<img src=x onerror=alert(1)>',
|
||||
name: 'malicious',
|
||||
url: 'javascript:alert(1)',
|
||||
body: 'malicious body'
|
||||
}
|
||||
])
|
||||
});
|
||||
|
||||
await sandbox.openVersionMenu();
|
||||
|
||||
console.log("All tests finished (if you see this, no vulnerability was detected by the check).");
|
||||
}
|
||||
|
||||
runTests().catch(err => {
|
||||
console.error("Test execution failed:", err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user