cipher-security 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cipher.js +566 -0
- package/lib/api/billing.js +321 -0
- package/lib/api/compliance.js +693 -0
- package/lib/api/controls.js +1401 -0
- package/lib/api/index.js +49 -0
- package/lib/api/marketplace.js +467 -0
- package/lib/api/openai-proxy.js +383 -0
- package/lib/api/server.js +685 -0
- package/lib/autonomous/feedback-loop.js +554 -0
- package/lib/autonomous/framework.js +512 -0
- package/lib/autonomous/index.js +97 -0
- package/lib/autonomous/leaderboard.js +594 -0
- package/lib/autonomous/modes/architect.js +412 -0
- package/lib/autonomous/modes/blue.js +386 -0
- package/lib/autonomous/modes/incident.js +684 -0
- package/lib/autonomous/modes/privacy.js +369 -0
- package/lib/autonomous/modes/purple.js +294 -0
- package/lib/autonomous/modes/recon.js +250 -0
- package/lib/autonomous/parallel.js +587 -0
- package/lib/autonomous/researcher.js +583 -0
- package/lib/autonomous/runner.js +955 -0
- package/lib/autonomous/scheduler.js +615 -0
- package/lib/autonomous/task-parser.js +127 -0
- package/lib/autonomous/validators/forensic.js +266 -0
- package/lib/autonomous/validators/osint.js +216 -0
- package/lib/autonomous/validators/privacy.js +296 -0
- package/lib/autonomous/validators/purple.js +298 -0
- package/lib/autonomous/validators/sigma.js +248 -0
- package/lib/autonomous/validators/threat-model.js +363 -0
- package/lib/benchmark/agent.js +119 -0
- package/lib/benchmark/baselines.js +43 -0
- package/lib/benchmark/builder.js +143 -0
- package/lib/benchmark/config.js +35 -0
- package/lib/benchmark/coordinator.js +91 -0
- package/lib/benchmark/index.js +20 -0
- package/lib/benchmark/llm.js +58 -0
- package/lib/benchmark/models.js +137 -0
- package/lib/benchmark/reporter.js +103 -0
- package/lib/benchmark/runner.js +103 -0
- package/lib/benchmark/sandbox.js +96 -0
- package/lib/benchmark/scorer.js +32 -0
- package/lib/benchmark/solver.js +166 -0
- package/lib/benchmark/tools.js +62 -0
- package/lib/bot/bot.js +238 -0
- package/lib/brand.js +105 -0
- package/lib/commands.js +100 -0
- package/lib/complexity.js +377 -0
- package/lib/config.js +213 -0
- package/lib/gateway/client.js +309 -0
- package/lib/gateway/commands.js +991 -0
- package/lib/gateway/config-validate.js +109 -0
- package/lib/gateway/gateway.js +367 -0
- package/lib/gateway/index.js +62 -0
- package/lib/gateway/mode.js +309 -0
- package/lib/gateway/plugins.js +222 -0
- package/lib/gateway/prompt.js +214 -0
- package/lib/mcp/server.js +262 -0
- package/lib/memory/compressor.js +425 -0
- package/lib/memory/engine.js +763 -0
- package/lib/memory/evolution.js +668 -0
- package/lib/memory/index.js +58 -0
- package/lib/memory/orchestrator.js +506 -0
- package/lib/memory/retriever.js +515 -0
- package/lib/memory/synthesizer.js +333 -0
- package/lib/pipeline/async-scanner.js +510 -0
- package/lib/pipeline/binary-analysis.js +1043 -0
- package/lib/pipeline/dom-xss-scanner.js +435 -0
- package/lib/pipeline/github-actions.js +792 -0
- package/lib/pipeline/index.js +124 -0
- package/lib/pipeline/osint.js +498 -0
- package/lib/pipeline/sarif.js +373 -0
- package/lib/pipeline/scanner.js +880 -0
- package/lib/pipeline/template-manager.js +525 -0
- package/lib/pipeline/xss-scanner.js +353 -0
- package/lib/setup-wizard.js +288 -0
- package/package.json +31 -0
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
// Copyright (c) 2026 defconxt. All rights reserved.
|
|
2
|
+
// Licensed under AGPL-3.0 — see LICENSE file for details.
|
|
3
|
+
// CIPHER is a trademark of defconxt.
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Runtime DOM XSS scanner using Playwright headless browser.
|
|
7
|
+
*
|
|
8
|
+
* Complements the static XSS scanner (xss-scanner.js) by executing JavaScript
|
|
9
|
+
* in a real browser context to detect XSS that only manifests at runtime.
|
|
10
|
+
*
|
|
11
|
+
* Playwright is an OPTIONAL dependency — scanner gracefully degrades when
|
|
12
|
+
* it's not installed. Do NOT add playwright to package.json.
|
|
13
|
+
*
|
|
14
|
+
* Ported from pipeline/dom_xss_scanner.py (390 LOC Python).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { URL } from 'node:url';
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Payloads
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
/** @type {Array<[string, string, string]>} [label, payload, detection] */
|
|
24
|
+
const XSS_PAYLOADS = Object.freeze([
|
|
25
|
+
['script-alert', '<script>alert("CIPHER-XSS-1")</script>', 'dialog'],
|
|
26
|
+
['img-onerror', '<img src=x onerror=alert("CIPHER-XSS-2")>', 'dialog'],
|
|
27
|
+
['svg-onload', '<svg/onload=alert("CIPHER-XSS-3")>', 'dialog'],
|
|
28
|
+
['body-onload', '<body onload=alert("CIPHER-XSS-4")>', 'dialog'],
|
|
29
|
+
['event-handler', '" onfocus=alert("CIPHER-XSS-5") autofocus="', 'dialog'],
|
|
30
|
+
['javascript-uri', 'javascript:alert("CIPHER-XSS-6")', 'dialog'],
|
|
31
|
+
['template-literal', '${alert("CIPHER-XSS-7")}', 'dialog'],
|
|
32
|
+
['iframe-srcdoc', '<iframe srcdoc="<script>alert(\'CIPHER-XSS-8\')</script>">', 'dialog'],
|
|
33
|
+
]);
|
|
34
|
+
|
|
35
|
+
/** @type {Array<[string, string]>} [label, payload] */
|
|
36
|
+
const URL_PAYLOADS = Object.freeze([
|
|
37
|
+
['reflected-script', '<script>alert("CIPHER-XSS-R1")</script>'],
|
|
38
|
+
['reflected-img', '<img src=x onerror=alert("CIPHER-XSS-R2")>'],
|
|
39
|
+
['reflected-svg', '<svg/onload=alert("CIPHER-XSS-R3")>'],
|
|
40
|
+
['hash-injection', '#<img src=x onerror=alert("CIPHER-XSS-R4")>'],
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
// Common parameter names to test when URL has no existing params
|
|
44
|
+
const COMMON_PARAMS = ['q', 'search', 'query', 'id', 'name', 'page', 'url', 'redirect', 'next', 'callback'];
|
|
45
|
+
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// Data classes
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
class DOMXSSFinding {
|
|
51
|
+
/**
|
|
52
|
+
* @param {object} opts
|
|
53
|
+
* @param {string} opts.url
|
|
54
|
+
* @param {string} opts.vector input | url-param | fragment | form
|
|
55
|
+
* @param {string} opts.inputName
|
|
56
|
+
* @param {string} opts.payload
|
|
57
|
+
* @param {string} opts.payloadLabel
|
|
58
|
+
* @param {boolean} [opts.confirmed=true]
|
|
59
|
+
* @param {string} [opts.reproduction='']
|
|
60
|
+
*/
|
|
61
|
+
constructor(opts) {
|
|
62
|
+
this.url = opts.url;
|
|
63
|
+
this.vector = opts.vector;
|
|
64
|
+
this.inputName = opts.inputName;
|
|
65
|
+
this.payload = opts.payload;
|
|
66
|
+
this.payloadLabel = opts.payloadLabel;
|
|
67
|
+
this.confirmed = opts.confirmed !== false;
|
|
68
|
+
this.reproduction = opts.reproduction ?? '';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
toDict() {
|
|
72
|
+
return {
|
|
73
|
+
url: this.url,
|
|
74
|
+
vector: this.vector,
|
|
75
|
+
input: this.inputName,
|
|
76
|
+
payload_label: this.payloadLabel,
|
|
77
|
+
confirmed: this.confirmed,
|
|
78
|
+
reproduction: this.reproduction,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
class DOMXSSScanResult {
|
|
84
|
+
/**
|
|
85
|
+
* @param {object} opts
|
|
86
|
+
* @param {string} opts.target
|
|
87
|
+
* @param {number} [opts.pagesTested=0]
|
|
88
|
+
* @param {number} [opts.inputsTested=0]
|
|
89
|
+
* @param {number} [opts.paramsTested=0]
|
|
90
|
+
* @param {DOMXSSFinding[]} [opts.findings=[]]
|
|
91
|
+
* @param {string[]} [opts.errors=[]]
|
|
92
|
+
*/
|
|
93
|
+
constructor(opts) {
|
|
94
|
+
this.target = opts.target;
|
|
95
|
+
this.pagesTested = opts.pagesTested ?? 0;
|
|
96
|
+
this.inputsTested = opts.inputsTested ?? 0;
|
|
97
|
+
this.paramsTested = opts.paramsTested ?? 0;
|
|
98
|
+
this.findings = opts.findings ?? [];
|
|
99
|
+
this.errors = opts.errors ?? [];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
toDict() {
|
|
103
|
+
return {
|
|
104
|
+
target: this.target,
|
|
105
|
+
pages_tested: this.pagesTested,
|
|
106
|
+
inputs_tested: this.inputsTested,
|
|
107
|
+
params_tested: this.paramsTested,
|
|
108
|
+
findings: this.findings.map((f) => f.toDict()),
|
|
109
|
+
confirmed_xss: this.findings.length,
|
|
110
|
+
errors: this.errors,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
summary() {
|
|
115
|
+
if (this.findings.length > 0) {
|
|
116
|
+
return (
|
|
117
|
+
`${this.findings.length} confirmed DOM XSS in ${this.target} ` +
|
|
118
|
+
`(${this.inputsTested} inputs, ${this.paramsTested} params tested)`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return `No DOM XSS found in ${this.target} (${this.inputsTested} inputs tested)`;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
// DOMXSSScanner
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
|
|
129
|
+
class DOMXSSScanner {
|
|
130
|
+
/**
|
|
131
|
+
* @param {object} [opts]
|
|
132
|
+
* @param {boolean} [opts.headless=true]
|
|
133
|
+
* @param {number} [opts.timeout=5000]
|
|
134
|
+
*/
|
|
135
|
+
constructor(opts = {}) {
|
|
136
|
+
this._headless = opts.headless !== false;
|
|
137
|
+
this._timeout = opts.timeout ?? 5000;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Check if Playwright is available.
|
|
142
|
+
* @returns {Promise<boolean>}
|
|
143
|
+
*/
|
|
144
|
+
async available() {
|
|
145
|
+
try {
|
|
146
|
+
await import('playwright');
|
|
147
|
+
return true;
|
|
148
|
+
} catch {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Scan a URL for DOM XSS vulnerabilities.
|
|
155
|
+
* @param {string} url
|
|
156
|
+
* @param {object} [opts]
|
|
157
|
+
* @param {boolean} [opts.testParams=true]
|
|
158
|
+
* @param {boolean} [opts.testInputs=true]
|
|
159
|
+
* @param {boolean} [opts.testForms=true]
|
|
160
|
+
* @returns {Promise<DOMXSSScanResult>}
|
|
161
|
+
*/
|
|
162
|
+
async scanUrl(url, opts = {}) {
|
|
163
|
+
const testParams = opts.testParams !== false;
|
|
164
|
+
const testInputs = opts.testInputs !== false;
|
|
165
|
+
const testForms = opts.testForms !== false;
|
|
166
|
+
|
|
167
|
+
let pw;
|
|
168
|
+
try {
|
|
169
|
+
pw = await import('playwright');
|
|
170
|
+
} catch {
|
|
171
|
+
return new DOMXSSScanResult({
|
|
172
|
+
target: url,
|
|
173
|
+
errors: ['Playwright not installed — run: npm install playwright && npx playwright install chromium'],
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const result = new DOMXSSScanResult({ target: url });
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
const browser = await pw.chromium.launch({ headless: this._headless });
|
|
181
|
+
const context = await browser.newContext({
|
|
182
|
+
ignoreHTTPSErrors: true,
|
|
183
|
+
javaScriptEnabled: true,
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
if (testParams) {
|
|
187
|
+
await this._testUrlParams(context, url, result);
|
|
188
|
+
}
|
|
189
|
+
if (testInputs) {
|
|
190
|
+
await this._testInputFields(context, url, result);
|
|
191
|
+
}
|
|
192
|
+
if (testForms) {
|
|
193
|
+
await this._testForms(context, url, result);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
result.pagesTested++;
|
|
197
|
+
await context.close();
|
|
198
|
+
await browser.close();
|
|
199
|
+
} catch (err) {
|
|
200
|
+
result.errors.push(`Scanner error: ${err.message}`);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return result;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Inject payloads into URL query parameters.
|
|
208
|
+
* @private
|
|
209
|
+
*/
|
|
210
|
+
async _testUrlParams(context, url, result) {
|
|
211
|
+
const parsed = new URL(url);
|
|
212
|
+
const params = parsed.searchParams;
|
|
213
|
+
|
|
214
|
+
if ([...params.keys()].length === 0) {
|
|
215
|
+
// Try common parameter names
|
|
216
|
+
for (const paramName of COMMON_PARAMS) {
|
|
217
|
+
for (const [label, payload] of URL_PAYLOADS) {
|
|
218
|
+
result.paramsTested++;
|
|
219
|
+
const injected = new URL(url);
|
|
220
|
+
injected.searchParams.set(paramName, payload);
|
|
221
|
+
const finding = await this._checkUrlXss(
|
|
222
|
+
context, injected.toString(), 'url-param', paramName, label, payload,
|
|
223
|
+
);
|
|
224
|
+
if (finding) {
|
|
225
|
+
result.findings.push(finding);
|
|
226
|
+
return; // One confirmed finding is enough
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
} else {
|
|
231
|
+
for (const paramName of params.keys()) {
|
|
232
|
+
for (const [label, payload] of URL_PAYLOADS) {
|
|
233
|
+
result.paramsTested++;
|
|
234
|
+
const injected = new URL(url);
|
|
235
|
+
injected.searchParams.set(paramName, payload);
|
|
236
|
+
const finding = await this._checkUrlXss(
|
|
237
|
+
context, injected.toString(), 'url-param', paramName, label, payload,
|
|
238
|
+
);
|
|
239
|
+
if (finding) {
|
|
240
|
+
result.findings.push(finding);
|
|
241
|
+
break; // Move to next param
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Test fragment injection
|
|
248
|
+
for (const [label, payload] of URL_PAYLOADS) {
|
|
249
|
+
if (label.startsWith('hash')) {
|
|
250
|
+
result.paramsTested++;
|
|
251
|
+
const injected = `${url}${payload}`;
|
|
252
|
+
const finding = await this._checkUrlXss(
|
|
253
|
+
context, injected, 'fragment', '#', label, payload,
|
|
254
|
+
);
|
|
255
|
+
if (finding) {
|
|
256
|
+
result.findings.push(finding);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Inject payloads into input fields on the page.
|
|
264
|
+
* @private
|
|
265
|
+
*/
|
|
266
|
+
async _testInputFields(context, url, result) {
|
|
267
|
+
const page = await context.newPage();
|
|
268
|
+
const dialogs = [];
|
|
269
|
+
page.on('dialog', async (d) => {
|
|
270
|
+
dialogs.push(d.message());
|
|
271
|
+
await d.accept();
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
try {
|
|
275
|
+
await page.goto(url, { waitUntil: 'networkidle', timeout: this._timeout * 2 });
|
|
276
|
+
} catch (err) {
|
|
277
|
+
result.errors.push(`Navigation failed: ${err.message}`);
|
|
278
|
+
await page.close();
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const inputs = await page.$$(
|
|
283
|
+
"input[type='text'], input[type='search'], input:not([type]), textarea, [contenteditable='true']",
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
for (const inp of inputs) {
|
|
287
|
+
const inputName =
|
|
288
|
+
(await inp.getAttribute('name')) ||
|
|
289
|
+
(await inp.getAttribute('id')) ||
|
|
290
|
+
(await inp.getAttribute('placeholder')) ||
|
|
291
|
+
'unnamed';
|
|
292
|
+
|
|
293
|
+
for (const [label, payload] of XSS_PAYLOADS) {
|
|
294
|
+
result.inputsTested++;
|
|
295
|
+
dialogs.length = 0;
|
|
296
|
+
try {
|
|
297
|
+
await inp.fill('');
|
|
298
|
+
await inp.fill(payload);
|
|
299
|
+
await inp.dispatchEvent('change');
|
|
300
|
+
await inp.dispatchEvent('blur');
|
|
301
|
+
await page.waitForTimeout(500);
|
|
302
|
+
|
|
303
|
+
if (dialogs.some((d) => d.includes('CIPHER-XSS'))) {
|
|
304
|
+
result.findings.push(
|
|
305
|
+
new DOMXSSFinding({
|
|
306
|
+
url,
|
|
307
|
+
vector: 'input',
|
|
308
|
+
inputName,
|
|
309
|
+
payload,
|
|
310
|
+
payloadLabel: label,
|
|
311
|
+
reproduction: `Fill input '${inputName}' with: ${payload}`,
|
|
312
|
+
}),
|
|
313
|
+
);
|
|
314
|
+
break; // One payload per input
|
|
315
|
+
}
|
|
316
|
+
} catch {
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
await page.close();
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Test form submissions with XSS payloads.
|
|
326
|
+
* @private
|
|
327
|
+
*/
|
|
328
|
+
async _testForms(context, url, result) {
|
|
329
|
+
const page = await context.newPage();
|
|
330
|
+
const dialogs = [];
|
|
331
|
+
page.on('dialog', async (d) => {
|
|
332
|
+
dialogs.push(d.message());
|
|
333
|
+
await d.accept();
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
try {
|
|
337
|
+
await page.goto(url, { waitUntil: 'networkidle', timeout: this._timeout * 2 });
|
|
338
|
+
} catch {
|
|
339
|
+
await page.close();
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
const forms = await page.$$('form');
|
|
344
|
+
for (const form of forms) {
|
|
345
|
+
const formInputs = await form.$$(
|
|
346
|
+
"input[type='text'], input:not([type]), textarea",
|
|
347
|
+
);
|
|
348
|
+
if (formInputs.length === 0) continue;
|
|
349
|
+
|
|
350
|
+
const payload = XSS_PAYLOADS[0][1]; // script-alert
|
|
351
|
+
dialogs.length = 0;
|
|
352
|
+
try {
|
|
353
|
+
for (const fi of formInputs) {
|
|
354
|
+
await fi.fill(payload);
|
|
355
|
+
}
|
|
356
|
+
const submitBtn = await form.$("button[type='submit'], input[type='submit']");
|
|
357
|
+
if (submitBtn) {
|
|
358
|
+
await submitBtn.click();
|
|
359
|
+
} else {
|
|
360
|
+
await form.dispatchEvent('submit');
|
|
361
|
+
}
|
|
362
|
+
await page.waitForTimeout(1000);
|
|
363
|
+
|
|
364
|
+
if (dialogs.some((d) => d.includes('CIPHER-XSS'))) {
|
|
365
|
+
const formId =
|
|
366
|
+
(await form.getAttribute('id')) ||
|
|
367
|
+
(await form.getAttribute('action')) ||
|
|
368
|
+
'form';
|
|
369
|
+
result.findings.push(
|
|
370
|
+
new DOMXSSFinding({
|
|
371
|
+
url,
|
|
372
|
+
vector: 'form',
|
|
373
|
+
inputName: formId,
|
|
374
|
+
payload,
|
|
375
|
+
payloadLabel: 'form-submit',
|
|
376
|
+
reproduction: `Submit form '${formId}' with XSS in all text inputs`,
|
|
377
|
+
}),
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
} catch {
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
await page.close();
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Navigate to an injected URL and check for XSS execution.
|
|
389
|
+
* @private
|
|
390
|
+
*/
|
|
391
|
+
async _checkUrlXss(context, url, vector, paramName, label, payload) {
|
|
392
|
+
const page = await context.newPage();
|
|
393
|
+
const dialogs = [];
|
|
394
|
+
page.on('dialog', async (d) => {
|
|
395
|
+
dialogs.push(d.message());
|
|
396
|
+
await d.accept();
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
try {
|
|
400
|
+
await page.goto(url, { waitUntil: 'networkidle', timeout: this._timeout });
|
|
401
|
+
await page.waitForTimeout(500);
|
|
402
|
+
} catch {
|
|
403
|
+
// Navigation errors are expected for some payloads
|
|
404
|
+
} finally {
|
|
405
|
+
await page.close();
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (dialogs.some((d) => d.includes('CIPHER-XSS'))) {
|
|
409
|
+
return new DOMXSSFinding({
|
|
410
|
+
url,
|
|
411
|
+
vector,
|
|
412
|
+
inputName: paramName,
|
|
413
|
+
payload,
|
|
414
|
+
payloadLabel: label,
|
|
415
|
+
reproduction: `Navigate to: ${url}`,
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// ---------------------------------------------------------------------------
|
|
423
|
+
// Exports
|
|
424
|
+
// ---------------------------------------------------------------------------
|
|
425
|
+
|
|
426
|
+
export {
|
|
427
|
+
// Payloads (for testing/inspection)
|
|
428
|
+
XSS_PAYLOADS,
|
|
429
|
+
URL_PAYLOADS,
|
|
430
|
+
// Data classes
|
|
431
|
+
DOMXSSFinding,
|
|
432
|
+
DOMXSSScanResult,
|
|
433
|
+
// Scanner
|
|
434
|
+
DOMXSSScanner,
|
|
435
|
+
};
|