File size: 4,353 Bytes
5710d63
 
 
 
 
 
 
 
 
 
 
 
 
 
3f143e9
5710d63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3f143e9
4e2e30e
 
 
 
 
 
3f143e9
 
4e2e30e
3f143e9
d26fc11
 
3f143e9
 
 
 
 
 
 
 
 
 
 
d26fc11
3f143e9
5710d63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import "dotenv/config";

import { mkdirSync, writeFileSync } 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";

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 {
      // === CODER ===
      await send("log", "[Coder] Gerando smart contract a partir dos requisitos...");
      const coderResult = await coderAgent.invoke({ requirements: [requirements] });

      await send("log", "[Coder] Contrato gerado com sucesso.");

      if (coderResult.compilationErrors.length > 0) {
        await send("log", `[Coder] Erros de compilação restantes: ${coderResult.compilationErrors.length}`);
      } else {
        await send("log", "[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");

      await send("log", "[Auditor] Iniciando auditoria de segurança...");
      const auditorResult = await auditorAgent.invoke({ repoPath: outputDir });
      await send("log", `[Auditor] ${auditorResult.findings.length} vulnerabilidade(s) encontrada(s).`);
      for (const f of auditorResult.findings) {
        await send("log", `[Auditor] [${f.severity.toUpperCase()}] ${f.title}${f.location}`);
      }

      await send(
        "auditor",
        JSON.stringify({
          findings: auditorResult.findings,
        }),
      );

      // === TESTER ===
      await send("log", "[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
        );
        report.customSandboxDir = outputDir;  // ← tester runs in real project sandbox
        const testerResult = await testerAgent.invoke({ report });


        await send("log", `[Tester] Execução concluída com status: ${testerResult.status}`);
        
        // Garante que o objeto enviado tem exatamente o que o front espera
        await send(
          "tester",
          JSON.stringify({
            status: testerResult.status,
            pocCode: testerResult.pocCode,
            executionLogs: testerResult.executionLogs,
            iterations: testerResult.iterations,
          }),
        );
      } else {
        await send("log", "[Tester] Nenhuma vulnerabilidade para testar.");
        await send("tester", JSON.stringify({ status: "skipped", iterations: 0 }));
      }

      await send("log", "Pipeline concluído.");
      await send("done", "ok");
    } catch (err) {
      const message = err instanceof Error ? err.message : String(err);
      await send("error", message);
    }
  });
});

// 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 });