mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-09 19:45:13 +02:00
Dynamic (#77)
This commit is contained in:
parent
55247b7fcd
commit
991c84a1eb
1464 changed files with 225448 additions and 1985 deletions
24
tests/dynamic_fixtures/javascript/async_function/benign.js
Normal file
24
tests/dynamic_fixtures/javascript/async_function/benign.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
// Phase 13 — bare async function, benign control.
|
||||
//
|
||||
// execFile (no shell) via util.promisify(execFile). Payload never reaches a
|
||||
// shell; stderr silenced so payload bytes do not leak via the inner process'
|
||||
// error message.
|
||||
|
||||
'use strict';
|
||||
const { execFile } = require('child_process');
|
||||
const { promisify } = require('util');
|
||||
const execFileP = promisify(execFile);
|
||||
|
||||
async function runPing(host) {
|
||||
process.stdout.write('__NYX_SINK_HIT__\n');
|
||||
try {
|
||||
const { stdout } = await execFileP('true', [host], {
|
||||
timeout: 5000,
|
||||
});
|
||||
return stdout;
|
||||
} catch (_e) {
|
||||
return 'err';
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { runPing };
|
||||
25
tests/dynamic_fixtures/javascript/async_function/vuln.js
Normal file
25
tests/dynamic_fixtures/javascript/async_function/vuln.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// Phase 13 — bare async function, vulnerable.
|
||||
//
|
||||
// Stdlib-only. Async function awaits `child_process.exec` via util.promisify
|
||||
// so the harness's `await _entry.runPing(payload)` resolves before the
|
||||
// process exits.
|
||||
|
||||
'use strict';
|
||||
const { exec } = require('child_process');
|
||||
const { promisify } = require('util');
|
||||
const execP = promisify(exec);
|
||||
|
||||
async function runPing(host) {
|
||||
process.stdout.write('__NYX_SINK_HIT__\n');
|
||||
try {
|
||||
const { stdout } = await execP('echo hello ' + host, { timeout: 5000 });
|
||||
process.stdout.write(stdout);
|
||||
return stdout;
|
||||
} catch (e) {
|
||||
const out = (e.stdout || '') + (e.stderr || '');
|
||||
process.stdout.write(out);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { runPing };
|
||||
19
tests/dynamic_fixtures/javascript/browser_event/benign.js
Normal file
19
tests/dynamic_fixtures/javascript/browser_event/benign.js
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// Phase 13 — browser-side event handler, benign control.
|
||||
//
|
||||
// Uses `textContent` so the payload's `<script>` tag is HTML-escaped before
|
||||
// serialisation; the XSS oracle marker cannot appear in stdout because
|
||||
// `<` becomes `<`.
|
||||
|
||||
'use strict';
|
||||
// nyx-shape: browser-event
|
||||
|
||||
function clickHandler(payload) {
|
||||
process.stdout.write('__NYX_SINK_HIT__\n');
|
||||
const el = document.getElementById('out');
|
||||
if (el) {
|
||||
el.textContent = String(payload);
|
||||
}
|
||||
return el ? el.textContent : '';
|
||||
}
|
||||
|
||||
module.exports = { clickHandler };
|
||||
12
tests/dynamic_fixtures/javascript/browser_event/package-lock.json
generated
Normal file
12
tests/dynamic_fixtures/javascript/browser_event/package-lock.json
generated
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "nyx-harness-jsdom",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "nyx-harness-jsdom",
|
||||
"version": "0.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "nyx-harness-jsdom",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"jsdom": "^24.1.1"
|
||||
}
|
||||
}
|
||||
21
tests/dynamic_fixtures/javascript/browser_event/vuln.js
Normal file
21
tests/dynamic_fixtures/javascript/browser_event/vuln.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// Phase 13 — browser-side event handler, vulnerable.
|
||||
//
|
||||
// Harness spins up jsdom (js_shared::emit_browser_event), assigns
|
||||
// `globalThis.document`, then calls `clickHandler(payload)`. The handler
|
||||
// writes payload into innerHTML — the XSS oracle's `<script>NYX_XSS_CONFIRMED
|
||||
// </script>` payload appears in the serialised DOM the harness mirrors to
|
||||
// stdout.
|
||||
|
||||
'use strict';
|
||||
// nyx-shape: browser-event
|
||||
|
||||
function clickHandler(payload) {
|
||||
process.stdout.write('__NYX_SINK_HIT__\n');
|
||||
const el = document.getElementById('out');
|
||||
if (el) {
|
||||
el.innerHTML = String(payload);
|
||||
}
|
||||
return el ? el.innerHTML : '';
|
||||
}
|
||||
|
||||
module.exports = { clickHandler };
|
||||
20
tests/dynamic_fixtures/javascript/commonjs_export/benign.js
Normal file
20
tests/dynamic_fixtures/javascript/commonjs_export/benign.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
// Phase 13 — CommonJS export, benign control.
|
||||
|
||||
'use strict';
|
||||
const { execFileSync } = require('child_process');
|
||||
|
||||
function runPing(host) {
|
||||
process.stdout.write('__NYX_SINK_HIT__\n');
|
||||
try {
|
||||
execFileSync('true', [host], {
|
||||
encoding: 'utf8',
|
||||
timeout: 5000,
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
});
|
||||
return 'ok';
|
||||
} catch (_e) {
|
||||
return 'err';
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { runPing };
|
||||
21
tests/dynamic_fixtures/javascript/commonjs_export/vuln.js
Normal file
21
tests/dynamic_fixtures/javascript/commonjs_export/vuln.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// Phase 13 — CommonJS export, vulnerable.
|
||||
//
|
||||
// Synchronous `execSync` with shell:true via string concat. Stdlib only.
|
||||
|
||||
'use strict';
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
function runPing(host) {
|
||||
process.stdout.write('__NYX_SINK_HIT__\n');
|
||||
try {
|
||||
const out = execSync('echo hello ' + host, { encoding: 'utf8', timeout: 5000 });
|
||||
process.stdout.write(out);
|
||||
return out;
|
||||
} catch (e) {
|
||||
const out = (e.stdout || '') + (e.stderr || '');
|
||||
process.stdout.write(out);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { runPing };
|
||||
18
tests/dynamic_fixtures/javascript/esm_default/benign.js
Normal file
18
tests/dynamic_fixtures/javascript/esm_default/benign.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// Phase 13 — ES module default export, benign control.
|
||||
//
|
||||
// nyx-shape: esm-default
|
||||
import { execFileSync } from 'child_process';
|
||||
|
||||
export default function runPing(host) {
|
||||
process.stdout.write('__NYX_SINK_HIT__\n');
|
||||
try {
|
||||
execFileSync('true', [host], {
|
||||
encoding: 'utf8',
|
||||
timeout: 5000,
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
});
|
||||
return 'ok';
|
||||
} catch (_e) {
|
||||
return 'err';
|
||||
}
|
||||
}
|
||||
22
tests/dynamic_fixtures/javascript/esm_default/vuln.js
Normal file
22
tests/dynamic_fixtures/javascript/esm_default/vuln.js
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
// Phase 13 — ES module default export, vulnerable.
|
||||
//
|
||||
// `export default` body is the entry the harness imports dynamically. The
|
||||
// harness builder stages this file at `workdir/entry.mjs` (per
|
||||
// js_shared::entry_subpath_for_shape) so Node parses it under ESM semantics
|
||||
// regardless of the on-disk `.js` extension under the fixture tree.
|
||||
|
||||
// nyx-shape: esm-default
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
export default function runPing(host) {
|
||||
process.stdout.write('__NYX_SINK_HIT__\n');
|
||||
try {
|
||||
const out = execSync('echo hello ' + host, { encoding: 'utf8', timeout: 5000 });
|
||||
process.stdout.write(out);
|
||||
return out;
|
||||
} catch (e) {
|
||||
const out = (e.stdout || '') + (e.stderr || '');
|
||||
process.stdout.write(out);
|
||||
return out;
|
||||
}
|
||||
}
|
||||
28
tests/dynamic_fixtures/javascript/express/benign.js
Normal file
28
tests/dynamic_fixtures/javascript/express/benign.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// Phase 13 — Express route handler, benign control.
|
||||
//
|
||||
// Uses execFile (no shell) so the payload bytes are never interpreted as
|
||||
// shell metacharacters. The oracle marker cannot appear in stdout because
|
||||
// the inner child reads `true` and its stdio is ignored.
|
||||
|
||||
'use strict';
|
||||
const express = require('express');
|
||||
const { execFileSync } = require('child_process');
|
||||
|
||||
function ping(req, res) {
|
||||
const host = (req.query && req.query.host) || '';
|
||||
process.stdout.write('__NYX_SINK_HIT__\n');
|
||||
try {
|
||||
execFileSync('true', [host], {
|
||||
encoding: 'utf8',
|
||||
timeout: 5000,
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
});
|
||||
res.send('ok');
|
||||
} catch (_e) {
|
||||
res.send('err');
|
||||
}
|
||||
}
|
||||
|
||||
void express;
|
||||
|
||||
module.exports = { ping };
|
||||
12
tests/dynamic_fixtures/javascript/express/package-lock.json
generated
Normal file
12
tests/dynamic_fixtures/javascript/express/package-lock.json
generated
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "nyx-harness-express",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "nyx-harness-express",
|
||||
"version": "0.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
8
tests/dynamic_fixtures/javascript/express/package.json
Normal file
8
tests/dynamic_fixtures/javascript/express/package.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "nyx-harness-express",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"express": "^4.19.2"
|
||||
}
|
||||
}
|
||||
26
tests/dynamic_fixtures/javascript/express/vuln.js
Normal file
26
tests/dynamic_fixtures/javascript/express/vuln.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// Phase 13 — Express route handler, vulnerable.
|
||||
//
|
||||
// Vulnerable handler concatenates `req.query.host` into a shell command.
|
||||
// Harness builds a mock req/res via js_shared::emit_express and dispatches
|
||||
// synchronously; we never bind a real listener.
|
||||
|
||||
'use strict';
|
||||
const express = require('express');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
function ping(req, res) {
|
||||
const host = (req.query && req.query.host) || '';
|
||||
process.stdout.write('__NYX_SINK_HIT__\n');
|
||||
try {
|
||||
const out = execSync('echo hello ' + host, { encoding: 'utf8', timeout: 5000 });
|
||||
res.send(out);
|
||||
} catch (e) {
|
||||
res.send((e.stdout || '') + (e.stderr || ''));
|
||||
}
|
||||
}
|
||||
|
||||
// Touch the dep so the materialised package.json's `express` pin survives
|
||||
// shake-down by `npm install --no-save`; harness never starts the server.
|
||||
void express;
|
||||
|
||||
module.exports = { ping };
|
||||
26
tests/dynamic_fixtures/javascript/koa/benign.js
Normal file
26
tests/dynamic_fixtures/javascript/koa/benign.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// Phase 13 — Koa middleware, benign control.
|
||||
//
|
||||
// execFile (no shell), stderr silenced, child writes nothing to stdout.
|
||||
|
||||
'use strict';
|
||||
const Koa = require('koa');
|
||||
const { execFileSync } = require('child_process');
|
||||
|
||||
async function ping(ctx) {
|
||||
const host = (ctx.query && ctx.query.host) || '';
|
||||
process.stdout.write('__NYX_SINK_HIT__\n');
|
||||
try {
|
||||
execFileSync('true', [host], {
|
||||
encoding: 'utf8',
|
||||
timeout: 5000,
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
});
|
||||
ctx.body = 'ok';
|
||||
} catch (_e) {
|
||||
ctx.body = 'err';
|
||||
}
|
||||
}
|
||||
|
||||
void Koa;
|
||||
|
||||
module.exports = { ping };
|
||||
12
tests/dynamic_fixtures/javascript/koa/package-lock.json
generated
Normal file
12
tests/dynamic_fixtures/javascript/koa/package-lock.json
generated
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "nyx-harness-koa",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "nyx-harness-koa",
|
||||
"version": "0.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
8
tests/dynamic_fixtures/javascript/koa/package.json
Normal file
8
tests/dynamic_fixtures/javascript/koa/package.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "nyx-harness-koa",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"koa": "^2.15.3"
|
||||
}
|
||||
}
|
||||
23
tests/dynamic_fixtures/javascript/koa/vuln.js
Normal file
23
tests/dynamic_fixtures/javascript/koa/vuln.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// Phase 13 — Koa middleware, vulnerable.
|
||||
//
|
||||
// Vulnerable middleware reads `ctx.query.host` and concatenates it into a
|
||||
// shell command. Harness builds a mock ctx via js_shared::emit_koa.
|
||||
|
||||
'use strict';
|
||||
const Koa = require('koa');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
async function ping(ctx) {
|
||||
const host = (ctx.query && ctx.query.host) || '';
|
||||
process.stdout.write('__NYX_SINK_HIT__\n');
|
||||
try {
|
||||
const out = execSync('echo hello ' + host, { encoding: 'utf8', timeout: 5000 });
|
||||
ctx.body = out;
|
||||
} catch (e) {
|
||||
ctx.body = (e.stdout || '') + (e.stderr || '');
|
||||
}
|
||||
}
|
||||
|
||||
void Koa;
|
||||
|
||||
module.exports = { ping };
|
||||
25
tests/dynamic_fixtures/javascript/next_route/benign.js
Normal file
25
tests/dynamic_fixtures/javascript/next_route/benign.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// Phase 13 — Next.js API route handler, benign control.
|
||||
//
|
||||
// execFile (no shell) so payload bytes never reach a shell.
|
||||
//
|
||||
// nyx-shape: next
|
||||
|
||||
'use strict';
|
||||
try { require.resolve('next'); } catch (_e) {}
|
||||
|
||||
const { execFileSync } = require('child_process');
|
||||
|
||||
module.exports = async function handler(req, res) {
|
||||
const host = (req.query && req.query.host) || '';
|
||||
process.stdout.write('__NYX_SINK_HIT__\n');
|
||||
try {
|
||||
execFileSync('true', [host], {
|
||||
encoding: 'utf8',
|
||||
timeout: 5000,
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
});
|
||||
res.status(200).send('ok');
|
||||
} catch (_e) {
|
||||
res.status(200).send('err');
|
||||
}
|
||||
};
|
||||
12
tests/dynamic_fixtures/javascript/next_route/package-lock.json
generated
Normal file
12
tests/dynamic_fixtures/javascript/next_route/package-lock.json
generated
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "nyx-harness-next",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "nyx-harness-next",
|
||||
"version": "0.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "nyx-harness-next",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"next": "^14.2.5"
|
||||
}
|
||||
}
|
||||
26
tests/dynamic_fixtures/javascript/next_route/vuln.js
Normal file
26
tests/dynamic_fixtures/javascript/next_route/vuln.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
// Phase 13 — Next.js API route handler, vulnerable.
|
||||
//
|
||||
// Reads `req.query.host` and concatenates it into a shell command. The
|
||||
// `next` package is required for the materialised package.json pin to
|
||||
// survive `npm install --no-save`, but the harness builds its own mock
|
||||
// req/res via js_shared::emit_next; we never go through the Next router.
|
||||
//
|
||||
// nyx-shape: next
|
||||
|
||||
'use strict';
|
||||
// Touching `next` would also load React; the import is intentionally lazy
|
||||
// and guarded so test runs without a network-fed install still parse.
|
||||
try { require.resolve('next'); } catch (_e) {}
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
module.exports = async function handler(req, res) {
|
||||
const host = (req.query && req.query.host) || '';
|
||||
process.stdout.write('__NYX_SINK_HIT__\n');
|
||||
try {
|
||||
const out = execSync('echo hello ' + host, { encoding: 'utf8', timeout: 5000 });
|
||||
res.status(200).send(out);
|
||||
} catch (e) {
|
||||
res.status(200).send((e.stdout || '') + (e.stderr || ''));
|
||||
}
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue