- Purges any existing Node artifacts (package.json, node_modules) ensuring a pristine Deno + Rust environment. - Refactors Gen 2 Proof of Concepts (cfg, code_intelligence, protobuf, tool_sandbox) to execute external tools (tree-sitter, semgrep, protoc) as native system commands using Deno.Command. - Introduces `sys_exec.ts` to handle pre-flight dependency checks, ensuring scripts fail gracefully rather than breaking when a required host tool is missing. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
149 lines
4.1 KiB
TypeScript
149 lines
4.1 KiB
TypeScript
import {
|
|
assert,
|
|
assertEquals,
|
|
} from "https://deno.land/std@0.224.0/testing/asserts.ts";
|
|
import {
|
|
dirname,
|
|
fromFileUrl,
|
|
join,
|
|
} from "https://deno.land/std@0.224.0/path/mod.ts";
|
|
import { execTool, requireTool } from "./sys_exec.ts";
|
|
|
|
/**
|
|
* Proof of Concept: Abstract Syntax Trees & Control Flow Graphs (Gen 2)
|
|
*
|
|
* Demonstrates the Adversary agent consuming a CFG. Instead of a hardcoded JSON,
|
|
* we dynamically generate a basic flow graph by traversing an actual AST of some
|
|
* target code, and then trace if unsanitized user input reaches a sensitive sink.
|
|
*
|
|
* This version uses the native `tree-sitter` CLI to produce an AST representation.
|
|
*/
|
|
|
|
// Simulated malicious or vulnerable code segment
|
|
const targetSource = `
|
|
function handleRequest(req) {
|
|
let userInput = req.query.id; // entry
|
|
|
|
// safe path
|
|
let safeInput = sanitize(userInput);
|
|
db_query(safeInput); // sink
|
|
|
|
// vulnerable path
|
|
let rawHeader = req.headers['user-agent']; // entry
|
|
db_query(rawHeader); // sink
|
|
}
|
|
`;
|
|
|
|
async function generateAndAnalyzeCFG(code: string): Promise<string[]> {
|
|
const currentDir = dirname(fromFileUrl(import.meta.url));
|
|
const TEMP_FILE = join(currentDir, "dummy_cfg_target.js");
|
|
const vulnerabilities: string[] = [];
|
|
|
|
try {
|
|
await Deno.writeTextFile(TEMP_FILE, code);
|
|
|
|
// Call native tree-sitter parser to get XML AST
|
|
const { code: exitCode, stdout, stderr } = await execTool("tree-sitter", [
|
|
"parse",
|
|
TEMP_FILE,
|
|
"-x",
|
|
]);
|
|
|
|
if (exitCode !== 0) {
|
|
throw new Error(`Tree-sitter CLI execution failed: ${stderr || stdout}`);
|
|
}
|
|
|
|
// A very rudimentary data-flow tracker for local variables based on the tree-sitter XML output
|
|
const variableTaints: Record<string, boolean> = {};
|
|
|
|
// 1. Find variable assignments (variable_declarator)
|
|
const varMatches = stdout.matchAll(
|
|
/<variable_declarator.*?<identifier field="name".*?>(.*?)<\/identifier>.*?field="value".*?>(.*?)<\/variable_declarator>/gs,
|
|
);
|
|
for (const match of varMatches) {
|
|
const varName = match[1];
|
|
const valueBlock = match[2];
|
|
|
|
let isTainted = false;
|
|
|
|
// Simplistic check: is 'req' anywhere inside the value block?
|
|
if (valueBlock.includes(">req<")) {
|
|
isTainted = true;
|
|
}
|
|
|
|
// Check if it's assigned from a sanitize call
|
|
if (
|
|
valueBlock.includes("call_expression") &&
|
|
valueBlock.includes(">sanitize<")
|
|
) {
|
|
isTainted = false; // It's clean
|
|
}
|
|
|
|
variableTaints[varName] = isTainted;
|
|
}
|
|
|
|
// 2. Find function calls (call_expression)
|
|
const callMatches = stdout.matchAll(
|
|
/<call_expression.*?<identifier field="function".*?>(.*?)<\/identifier>.*?<arguments.*?<identifier.*?>(.*?)<\/identifier>.*?<\/arguments>.*?<\/call_expression>/gs,
|
|
);
|
|
for (const match of callMatches) {
|
|
const funcName = match[1];
|
|
const argName = match[2];
|
|
|
|
if (funcName === "db_query") {
|
|
if (variableTaints[argName]) {
|
|
vulnerabilities.push(
|
|
`Vulnerability: Unsanitized input '${argName}' reached sink 'db_query'`,
|
|
);
|
|
}
|
|
}
|
|
}
|
|
} finally {
|
|
try {
|
|
await Deno.remove(TEMP_FILE);
|
|
} catch {
|
|
// ignore
|
|
}
|
|
}
|
|
|
|
return vulnerabilities;
|
|
}
|
|
|
|
async function run() {
|
|
const hasTreeSitter = await requireTool(
|
|
"tree-sitter",
|
|
"npm install -g tree-sitter-cli",
|
|
);
|
|
if (!hasTreeSitter) {
|
|
console.warn(
|
|
"⚠️ CFG Security Proving PoC skipped due to missing host dependency.",
|
|
);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const vulns = await generateAndAnalyzeCFG(targetSource);
|
|
|
|
console.log("Adversary Agent Dynamic CFG Analysis Results:");
|
|
vulns.forEach((v) => console.log(` - ${v}`));
|
|
|
|
assertEquals(vulns.length, 1);
|
|
assert(
|
|
vulns[0].includes("rawHeader"),
|
|
"Expected rawHeader to flag a vulnerability",
|
|
);
|
|
|
|
console.log(
|
|
"✅ CFG Security Proving PoC (Gen 2) successful: Real AST traversal traced taint to a sink.",
|
|
);
|
|
} catch (err) {
|
|
console.error("❌ CFG Security Proving PoC (Gen 2) failed:", err);
|
|
Deno.exit(1);
|
|
}
|
|
}
|
|
|
|
if (import.meta.main) {
|
|
console.log("Running CFG Security Proving PoC (Gen 2) tests...");
|
|
run();
|
|
}
|