Spaces:
Runtime error
Runtime error
File size: 8,803 Bytes
52550fd a23bb47 52550fd 017c628 52550fd a23bb47 52550fd a23bb47 52550fd 017c628 52550fd 017c628 52550fd a23bb47 52550fd a23bb47 52550fd 017c628 52550fd 017c628 52550fd 017c628 52550fd 017c628 52550fd 017c628 52550fd a23bb47 52550fd 017c628 52550fd 017c628 52550fd a23bb47 52550fd a23bb47 52550fd 017c628 52550fd | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | import "dotenv/config";
import fs from "fs/promises";
import path from "path";
import { execSync } from "child_process";
import { testerAgent } from "../agents/tester/agent.js";
import { VulnerabilityReport, PoCResult } from "../agents/tester/types.js";
import { setupSandbox, applyPatchSmart, computePatchDiff } from "./runTesterBenchmark.js";
const DATASET_PATH = path.join(process.cwd(), "Proof-of-Patch-only-dataset");
const TEMP_DIR = path.join(process.cwd(), "temp_eval_run");
const CSV_FILE = path.join(process.cwd(), "data", "final_evaluation_results.csv");
async function runEvaluation() {
const metadataStr = await fs.readFile(path.join(DATASET_PATH, "dataset_metadata.json"), "utf8");
const metadata = JSON.parse(metadataStr);
const cases = Object.keys(metadata);
// Configurações Globais
const MAX_CASES = 1;
const TIMEOUT_MS = 3 * 60 * 1000; // 3 minutos por caso
const targetCases = cases.slice(0, MAX_CASES);
console.log(`Iniciando avaliação final para ${targetCases.length} projetos...`);
// Prepara o arquivo CSV
const csvHeaders = [
"ID",
"Time_Sec",
"Reproducible",
"Specific",
"False_Positive_Rejected",
"A_Infra_Iters",
"A_Exploit_Iters",
"A_Final_Error",
"B_Infra_Iters",
"B_Exploit_Iters",
"B_Final_Error",
"PoC_Code",
"Patch_Diff",
"A_Execution_Logs",
"B_Execution_Logs"
];
await fs.mkdir(path.join(process.cwd(), "data"), { recursive: true });
await fs.writeFile(CSV_FILE, csvHeaders.join(";") + "\n");
for (const caseId of targetCases) {
const data = metadata[caseId];
console.log(`\n\n${"=".repeat(60)}`);
console.log(`=== INICIANDO CASO: ${caseId} (${data.repo_name}) ===`);
console.log(`${"=".repeat(60)}`);
const startTime = Date.now();
// ==========================================
// CENÁRIO A: Verdadeiro Positivo (Vulnerável)
// ==========================================
console.log(`\n[CENÁRIO A] Testando reprodução real e especificidade...`);
const setupInfo = await setupSandbox(caseId, data);
if (!setupInfo) {
console.log(`[${caseId}] Falha crítica no setup inicial.`);
appendCsvRow([caseId, "0", "FALSE", "FALSE", "FALSE", "0", "SETUP_FAILED", "0", "", "", "", "", ""]);
continue;
}
const sandboxDir = setupInfo;
const targetPath = data.main_contract; // Path relative to project root
let vulnerableCode = "";
try {
vulnerableCode = await fs.readFile(path.join(sandboxDir, targetPath), "utf8");
} catch {
vulnerableCode = "// Could not load source code";
}
// Ler o arquivo de teste de referência (se houver)
let referenceTestCode = "";
try {
const allTestFiles = execSync(`find ${path.join(sandboxDir, "test")} -name "*.t.sol" -o -name "*.sol"`, { encoding: "utf8" })
.split("\n").filter(Boolean);
if (allTestFiles.length > 0) {
referenceTestCode = await fs.readFile(allTestFiles[0], "utf8");
}
} catch(e) {}
let patchDiff = "";
try {
const patchSourceDir = path.join(process.cwd(), DATASET_PATH, data.patch);
const targetDir = path.join(process.cwd(), DATASET_PATH, data.target_directory);
patchDiff = await computePatchDiff(patchSourceDir, path.join(sandboxDir, targetPath), targetDir, targetPath);
} catch {}
const reportA: VulnerabilityReport = {
id: caseId,
severity: data.impact || "high",
type: data.expected_vulnerability,
title: `${data.repo_name} - ${caseId}`,
description: data.annotation,
affectedContract: { name: targetPath.split("/").pop()!.replace(".sol", ""), sourceCode: vulnerableCode, sourceFilePath: targetPath },
attackVector: data.expected_vulnerability,
customSandboxDir: sandboxDir,
referenceTestCode,
patchDiff
};
const resultA = await testerAgent.invoke({ report: reportA }, { recursionLimit: 100 }) as any;
let reproducible = resultA.status === "success";
let specific = false;
// Ler o PoC gerado diretamente do sandbox, já que o ReAct agent usa write_file
let pocCodeStr = "";
try {
pocCodeStr = await fs.readFile(path.join(sandboxDir, "test", "Exploit.t.sol"), "utf8");
} catch {
pocCodeStr = resultA.pocCode || "";
}
// Se reproduziu, testa a especificidade aplicando o patch
if (reproducible) {
console.log(`\n[CENÁRIO A] Reproduzível! PoC gerado com sucesso. Testando especificidade no patch...`);
const patchApplied = await applyPatchSmart(caseId, sandboxDir);
if (patchApplied) {
// Escreve o teste na pasta (já patcheada)
await fs.writeFile(path.join(sandboxDir, "test", "Exploit.t.sol"), pocCodeStr);
const { runFoundry } = await import("../agents/tester/tools/foundryRunner.js");
const specResult = await runFoundry(pocCodeStr, sandboxDir);
if (specResult.exitCode !== 0) {
console.log(`[CENÁRIO A] Especificidade CONFIRMADA! O teste falhou após o patch.`);
specific = true;
} else {
console.log(`[CENÁRIO A] FALSO ESPECÍFICO! O teste continuou passando mesmo no código corrigido.`);
}
} else {
console.log(`[CENÁRIO A] Falha ao aplicar patch. Assumindo especificidade FALSA.`);
}
}
const lastErrorA = (resultA as any).lastError || (resultA.status === "success" ? "" : "TIMEOUT");
const logsA = Buffer.from(((resultA as any).executionLogs || []).join("\n---\n")).toString("base64");
// ==========================================
// CENÁRIO B: Teste de Falso Positivo (Patch)
// ==========================================
console.log(`\n[CENÁRIO B] Testando rejeição de falso positivo (Robustez)...`);
// Recria a sandbox do zero
await execSync(`rm -rf ${sandboxDir}`);
const setupInfoB = await setupSandbox(caseId, data);
let falsePositiveRejected = false;
let resultB: Partial<PoCResult> = { iterations: 0, status: "failed" };
let lastErrorB = "";
let logsB = "";
if (setupInfoB) {
// Aplica o patch ANTES de chamar o agente (tornando o código seguro)
const patchAppliedB = await applyPatchSmart(caseId, sandboxDir);
if (patchAppliedB) {
let patchedCode = "";
try {
patchedCode = await fs.readFile(path.join(sandboxDir, data.main_contract), "utf8");
} catch {
patchedCode = "// Could not load patched code";
}
// Passa a MESMA anotação (mentindo que é vulnerável)
const reportB: VulnerabilityReport = {
...reportA,
affectedContract: { name: targetPath.split("/").pop()!.replace(".sol", ""), sourceCode: patchedCode, sourceFilePath: targetPath },
patchDiff: undefined // Oculta o patch diff do LLM para este cenário
};
resultB = await testerAgent.invoke({ report: reportB }, { recursionLimit: 100 }) as any;
// Se falhou em gerar exploit, REJEITOU com sucesso o falso positivo!
if (resultB.status !== "success") {
console.log(`\n[CENÁRIO B] SUCESSO DE ROBUSTEZ! Agente não conseguiu hackear o código seguro.`);
falsePositiveRejected = true;
} else {
console.log(`\n[CENÁRIO B] ALUCINAÇÃO CRÍTICA! Agente hackeou um código que já estava corrigido.`);
}
lastErrorB = (resultB as any).lastError || (resultB.status === "success" ? "" : "TIMEOUT");
logsB = Buffer.from(((resultB as any).executionLogs || []).join("\n---\n")).toString("base64");
}
}
const totalTimeSec = Math.floor((Date.now() - startTime) / 1000);
// Salva no CSV
appendCsvRow([
caseId,
totalTimeSec.toString(),
reproducible ? "TRUE" : "FALSE",
specific ? "TRUE" : "FALSE",
falsePositiveRejected ? "TRUE" : "FALSE",
(resultA as any).infraIterations || 0,
(resultA as any).exploitIterations || 0,
lastErrorA,
(resultB as any).infraIterations || 0,
(resultB as any).exploitIterations || 0,
lastErrorB,
Buffer.from(pocCodeStr).toString("base64"),
reproducible ? Buffer.from(patchDiff).toString("base64") : "",
logsA,
logsB
]);
console.log(`[${caseId}] Avaliação concluída em ${totalTimeSec}s. Salvo no CSV.`);
}
console.log(`\nAVALIAÇÃO FINAL CONCLUÍDA! Resultados em: ${CSV_FILE}`);
}
function escapeCsv(str: string) {
if (!str) return "";
// Troca aspas duplas por duplas aspas duplas (padrão CSV)
return `"${str.replace(/"/g, '""')}"`;
}
async function appendCsvRow(columns: string[]) {
const row = columns.join(";") + "\n";
await fs.appendFile(CSV_FILE, row);
}
runEvaluation().catch(console.error);
|