File size: 3,478 Bytes
184b6e6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5b62309
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2c8beb8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import fs from "node:fs";
import path from "node:path";

import { DOC_BASENAMES, DOC_EXTS, MAX_DEPTH, SKIP_DIRS, SOL_EXT, SOL_TEST_SUFFIXES } from "./config.ts";

export const walkDirectory = (dir: string, depth: number, solFiles: string[], docFiles: string[]) => {
  if (depth > MAX_DEPTH) return;

  let entries: fs.Dirent[];
  try {
    entries = fs.readdirSync(dir, { withFileTypes: true });
  } catch {
    return;
  }

  for (const entry of entries) {
    if (entry.isDirectory()) {
      if (!SKIP_DIRS.has(entry.name)) {
        walkDirectory(path.join(dir, entry.name), depth + 1, solFiles, docFiles);
      }
    } else if (entry.isFile()) {
      const fullPath = path.join(dir, entry.name);
      const ext = path.extname(entry.name).toLowerCase();
      const base = path.basename(entry.name, ext).toLowerCase();

      if (ext === SOL_EXT) {
        const isTest = SOL_TEST_SUFFIXES.some((suffix) => entry.name.endsWith(suffix));
        if (!isTest) solFiles.push(fullPath);
      } else if (DOC_EXTS.has(ext) || DOC_BASENAMES.has(base)) {
        docFiles.push(fullPath);
      }
    }
  }
};

export const matchLines = (fileContent: string, codeSnippet: string): string | null => {
  const fileLines = fileContent.split("\n");
  const snippetLines = codeSnippet.split("\n").map((line) => line.trim());

  for (let i = 0; i < fileLines.length; i++) {
    const fileLine = fileLines[i].trim();

    if (fileLine === snippetLines[0]) {
      let snippetIndex = 1;
      const startLine = i + 1;
      let endLine = i + 1;
      let fileIndex = i + 1;

      while (snippetIndex < snippetLines.length && fileIndex < fileLines.length) {
        const currentFileLine = fileLines[fileIndex].trim();

        if (currentFileLine === snippetLines[snippetIndex]) {
          endLine = fileIndex + 1;
          snippetIndex++;
        }

        fileIndex++;
      }

      if (snippetIndex === snippetLines.length) {
        if (startLine === endLine) {
          return `L${startLine}`;
        }

        return `L${startLine}-${endLine}`;
      }
    }
  }

  return null;
};

type ReviewEntry = {
  finding: {
    title: string;
    severity: string;
    location: string;
    description: string;
    codeSnippet: string;
  };
  review: {
    isFalsePositive: boolean;
    confidence: number;
    review: string;
    exploitablePaths: string[];
  };
};

export const buildReviewBlocks = (fileEntries: ReviewEntry[], iterationCount: number): string => {
  const blocks = fileEntries.map(({ finding, review }, i) => {
    const verdict = review.isFalsePositive
      ? `FALSO POSITIVO (confiança: ${review.confidence}/100)`
      : `VERDADEIRO POSITIVO (confiança: ${review.confidence}/100)`;
    const pathsLabel = review.isFalsePositive ? "Razão de Bloqueio" : "Caminhos de Exploração";
    const pathsContent =
      review.exploitablePaths.length > 0
        ? review.exploitablePaths.map((p) => `  - ${p}`).join("\n")
        : "  (nenhum fornecido)";

    return `[Achado ${i + 1}/${fileEntries.length}] ${finding.title}
Severidade: ${finding.severity}
Localização: linhas ${finding.location}
Descrição: ${finding.description}

Código:
\`\`\`solidity
${finding.codeSnippet}
\`\`\`

Veredito do Revisor: ${verdict}
Análise do Revisor: ${review.review}
${pathsLabel}:
${pathsContent}`;
  });

  return `=== Iteração ${iterationCount} — Achados e Revisões do Especialista (${fileEntries.length} achado(s)) ===\n\n${blocks.join("\n\n---\n\n")}`;
};