Tales-Cunha
update theagent to work with docker
2a70ee5
Raw
History Blame Contribute Delete
5.63 kB
import "dotenv/config";
import { mkdirSync, writeFileSync, readFileSync } from "node:fs";
import { resolve } from "node:path";
import { tmpdir } from "node:os";
import { serve } from "@hono/node-server";
import { Hono } from "hono";
import { cors } from "hono/cors";
import { streamSSE } from "hono/streaming";
import { serveStatic } from "@hono/node-server/serve-static";
import { coderAgent } from "./agents/coder/agent.ts";
import { auditorAgent } from "./agents/auditor/agent.ts";
import { testerAgent } from "./agents/tester/agent.ts";
import { mapFindingToReport } from "./utils/mapFinding.js";
import { createEmptyFoundryProject } from "./utils/forgeSandbox.js";
import { logger, setLogSink, clearLogSink, setStepSink, clearStepSink } from "./logger.ts";
const app = new Hono();
app.use("/api/*", cors());
app.post("/api/run", (c) => {
return streamSSE(c, async (stream) => {
const body = await c.req.json<{ requirements: string }>();
const requirements = body.requirements?.trim();
if (!requirements) {
await stream.writeSSE({ event: "error", data: "Requisitos não fornecidos." });
return;
}
let eventId = 0;
const send = async (event: string, data: string) => {
await stream.writeSSE({ id: String(eventId++), event, data });
};
try {
setLogSink((msg) => send("log", msg));
setStepSink((event) => send("step", JSON.stringify(event)));
// === CODER ===
logger.info("[Coder] Gerando smart contract a partir dos requisitos...");
const coderResult = await coderAgent.invoke({ requirements: [requirements] });
if (coderResult.compilationErrors.length > 0) {
logger.info(`[Coder] Erros de compilação restantes: ${coderResult.compilationErrors.length}`);
} else {
logger.info("[Coder] Contrato compilado sem erros.");
}
await send(
"coder",
JSON.stringify({
contract: coderResult.contract,
compilationErrors: coderResult.compilationErrors,
reviewSummary: coderResult.reviewSummary,
}),
);
// === AUDITOR ===
const outputDir = resolve(tmpdir(), `talp1-${Date.now()}`);
mkdirSync(outputDir, { recursive: true });
writeFileSync(resolve(outputDir, "Contract.sol"), coderResult.contract, "utf-8");
writeFileSync(resolve(outputDir, "README.md"), requirements, "utf-8");
logger.info("[Auditor] Iniciando auditoria de segurança...");
const auditorResult = await auditorAgent.invoke({ repoPath: outputDir });
logger.info(`[Auditor] ${auditorResult.findings.length} vulnerabilidade(s) encontrada(s).`);
for (const f of auditorResult.findings) {
logger.info(`[Auditor] [${f.severity.toUpperCase()}] ${f.title}${f.location}`);
}
await send(
"auditor",
JSON.stringify({
findings: auditorResult.findings,
}),
);
// === TESTER ===
logger.info("[Tester] Gerando testes de prova de conceito...");
if (auditorResult.findings.length > 0) {
const report = mapFindingToReport(
auditorResult.findings[0],
coderResult.contract,
auditorResult.repoContext // ← now forwarded to tester
);
const sandboxDir = resolve(tmpdir(), `talp1-tester-${Date.now()}`);
await createEmptyFoundryProject(sandboxDir, coderResult.contract, "Contract");
report.customSandboxDir = sandboxDir; // ← tester runs in real project sandbox
report.affectedContract.sourceFilePath = "src/Contract.sol"; // Fix bug with relative path
const testerResult = await testerAgent.invoke(
{ report },
{ recursionLimit: 100, configurable: { sandboxDir } }
);
logger.info(`[Tester] Execução concluída com status: ${testerResult.status}`);
let finalPocCode = testerResult.pocCode || "";
try {
finalPocCode = readFileSync(resolve(sandboxDir, "test/Exploit.t.sol"), "utf-8");
} catch (e) {
// Ignorar se não criou
}
let finalExecutionLogs: string[] = testerResult.executionLogs || [];
if (finalExecutionLogs.length === 0 && testerResult.messages) {
const testMsgs = testerResult.messages.filter((m: any) => m._getType() === "tool" && m.name === "smart_contract_test");
if (testMsgs.length > 0) {
const content = testMsgs[testMsgs.length - 1].content;
finalExecutionLogs = [typeof content === "string" ? content : JSON.stringify(content)];
}
}
// Garante que o objeto enviado tem exatamente o que o front espera
await send(
"tester",
JSON.stringify({
status: testerResult.status,
pocCode: finalPocCode,
executionLogs: finalExecutionLogs,
iterations: testerResult.iterations,
}),
);
} else {
logger.info("[Tester] Nenhuma vulnerabilidade para testar.");
await send("tester", JSON.stringify({ status: "skipped", iterations: 0 }));
}
logger.info("Pipeline concluído.");
await send("done", "ok");
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
await send("error", message);
} finally {
clearLogSink();
clearStepSink();
}
});
});
// Serve static frontend files (built React app)
app.use("/*", serveStatic({ root: "./frontend/dist" }));
const port = Number(process.env.PORT) || 7860;
console.log(`Servidor rodando em http://localhost:${port}`);
serve({ fetch: app.fetch, port });