This commit is contained in:
Eli Peter 2026-06-05 10:16:30 -05:00 committed by GitHub
parent 55247b7fcd
commit 991c84a1eb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
1464 changed files with 225448 additions and 1985 deletions

View 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 };

View 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 };

View 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 `&lt;`.
'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 };

View 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"
}
}
}

View file

@ -0,0 +1,8 @@
{
"name": "nyx-harness-jsdom",
"version": "0.0.0",
"private": true,
"dependencies": {
"jsdom": "^24.1.1"
}
}

View 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 };

View 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 };

View 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 };

View 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';
}
}

View 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;
}
}

View 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 };

View 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"
}
}
}

View file

@ -0,0 +1,8 @@
{
"name": "nyx-harness-express",
"version": "0.0.0",
"private": true,
"dependencies": {
"express": "^4.19.2"
}
}

View 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 };

View 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 };

View 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"
}
}
}

View file

@ -0,0 +1,8 @@
{
"name": "nyx-harness-koa",
"version": "0.0.0",
"private": true,
"dependencies": {
"koa": "^2.15.3"
}
}

View 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 };

View 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');
}
};

View 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"
}
}
}

View file

@ -0,0 +1,8 @@
{
"name": "nyx-harness-next",
"version": "0.0.0",
"private": true,
"dependencies": {
"next": "^14.2.5"
}
}

View 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 || ''));
}
};