Spaces:
Runtime error
Runtime error
Tales-Cunha commited on
Commit ·
8bf5d01
1
Parent(s): a852b48
refactor: remove old files
Browse files- src/agents/tester/README.md +41 -90
- src/agents/tester/graph.ts +4 -7
- src/agents/tester/nodes/context.ts +32 -0
- src/agents/tester/nodes/oracle.ts +0 -72
- src/agents/tester/nodes/router.ts +0 -32
- src/agents/tester/state.ts +1 -11
- src/agents/tester/tools/scaffoldGenerator.ts +0 -50
- src/agents/tester/types.ts +1 -9
- src/agents/tester/utils/dependencyStubber.ts +0 -85
- src/agents/tester/utils/projectContextExtractor.ts +0 -156
- src/benchmark/runTesterBenchmark.ts +2 -2
src/agents/tester/README.md
CHANGED
|
@@ -1,109 +1,60 @@
|
|
| 1 |
-
#
|
| 2 |
|
| 3 |
-
Este
|
| 4 |
|
| 5 |
## 1. Visão Geral
|
| 6 |
|
| 7 |
-
O
|
| 8 |
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
```
|
| 16 |
-
|
| 17 |
-
### Principais Funcionalidades:
|
| 18 |
-
- **Sandbox Autônomo:** O agente detecta e inicializa o ambiente Foundry (`/tmp/poc-sandbox`) automaticamente no primeiro uso.
|
| 19 |
-
- **Scaffold Automático:** Gera o arquivo `Exploit.t.sol` com o contrato vítima já instanciado e financiado.
|
| 20 |
-
- **Loop de Auto-correção:** Se o exploit falhar, o agente analisa os logs e tenta corrigir o código por até 5 iterações.
|
| 21 |
-
- **Integração com DeepSeek:** Utiliza o modelo `deepseek-v4-pro` via OpenRouter.
|
| 22 |
|
| 23 |
-
## 2. Arquitetura
|
| 24 |
|
| 25 |
-
O
|
| 26 |
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
|
|
|
| 31 |
|
| 32 |
-
## 3. Estrutura de
|
| 33 |
|
| 34 |
-
```
|
| 35 |
src/agents/tester/
|
| 36 |
-
├──
|
| 37 |
-
├──
|
| 38 |
-
├──
|
| 39 |
-
├──
|
| 40 |
-
|
| 41 |
-
├── tools/
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
├──
|
| 46 |
-
|
| 47 |
-
│
|
| 48 |
-
└── utils/
|
| 49 |
-
├── extractSolidity.ts # Parser de blocos de código
|
| 50 |
-
└── logAnalyzer.ts # Classificador de erros de execução
|
| 51 |
```
|
| 52 |
|
| 53 |
-
## 4.
|
| 54 |
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
O agente recebe um objeto `VulnerabilityReport`. Como o **Agente Auditor** gera objetos do tipo `Finding`, é necessário realizar um mapeamento (veja `src/index.ts` para o adapter).
|
| 58 |
-
|
| 59 |
-
#### Estrutura de Entrada (`VulnerabilityReport`)
|
| 60 |
-
```typescript
|
| 61 |
-
interface VulnerabilityReport {
|
| 62 |
-
id: string; // Identificador único do report
|
| 63 |
-
severity: string; // "high", "medium", "low"
|
| 64 |
-
title: string; // Título curto da falha
|
| 65 |
-
description: string; // Descrição técnica detalhada
|
| 66 |
-
affectedContract: {
|
| 67 |
-
name: string; // Nome da classe do contrato
|
| 68 |
-
sourceCode: string; // Código-fonte completo (Solidity)
|
| 69 |
-
};
|
| 70 |
-
attackVector: string; // Descrição do caminho de ataque
|
| 71 |
-
exploitablePaths?: string[]; // (Opcional) Passos detalhados
|
| 72 |
-
}
|
| 73 |
-
```
|
| 74 |
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
reportId: string;
|
| 79 |
-
status: "success" | "failed" | "timeout";
|
| 80 |
-
solidityCode: string; // Conteúdo final do Exploit.t.sol
|
| 81 |
-
executionLogs: string[]; // Logs brutos de todas as iterações
|
| 82 |
-
iterations: number; // Total de tentativas realizadas
|
| 83 |
-
}
|
| 84 |
```
|
| 85 |
|
| 86 |
-
##
|
| 87 |
-
```typescript
|
| 88 |
-
import { runPoCGenerator } from "./src/agents/tester";
|
| 89 |
-
|
| 90 |
-
// O orquestrador mapeia o Finding + Código Fonte para o Report
|
| 91 |
-
const result = await runPoCGenerator(report);
|
| 92 |
-
```
|
| 93 |
-
|
| 94 |
-
### Pré-requisitos
|
| 95 |
-
- **Foundry:** `forge` deve estar instalado e acessível. O agente busca em `~/.foundry/bin` e no PATH padrão.
|
| 96 |
-
- **API Key:** `OPENROUTER_API_KEY` deve estar configurada no arquivo `.env`.
|
| 97 |
-
|
| 98 |
-
## 5. Avaliação de Resultados
|
| 99 |
-
|
| 100 |
-
O `PoCResult` retorna um status que indica a validade da vulnerabilidade ou a eficácia de um patch:
|
| 101 |
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
|
| 108 |
-
|
| 109 |
-
A implementação deste agente foi inspirada no framework **PoCo** (Bergman et al., KTH 2025), adaptada para execução local determinística e suporte multi-agente.
|
|
|
|
| 1 |
+
# PoCo Agent (Proof-of-Concept Agent)
|
| 2 |
|
| 3 |
+
Este diretório contém a implementação principal do **Agente PoCo**, uma arquitetura autônoma baseada no framework **LangGraph**, desenvolvida para atuar como um auditor de segurança e desenvolvedor de exploits (Proof of Concepts) em Smart Contracts.
|
| 4 |
|
| 5 |
## 1. Visão Geral
|
| 6 |
|
| 7 |
+
O Agente recebe como entrada um relatório de vulnerabilidade (escrito por um auditor humano) e o código-fonte do contrato afetado. O objetivo do agente é explorar iterativamente o ambiente local usando o framework **Foundry** até conseguir escrever um arquivo `Exploit.t.sol` que prove matematicamente que a vulnerabilidade descrita é explorável (roubando fundos, burlando acessos, etc).
|
| 8 |
|
| 9 |
+
Diferente de scripts sequenciais convencionais, este agente emprega um **Loop ReAct** (Raciocínio e Ação) iterativo:
|
| 10 |
+
1. **Lê e entende** o contexto.
|
| 11 |
+
2. **Planeja** uma estratégia de ataque em múltiplos passos.
|
| 12 |
+
3. **Escreve** o código no disco.
|
| 13 |
+
4. **Compila e testa** localmente via terminal.
|
| 14 |
+
5. **Analisa o erro** de compilação ou de lógica e auto-corrige o exploit na próxima iteração.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 15 |
|
| 16 |
+
## 2. Componentes da Arquitetura
|
| 17 |
|
| 18 |
+
O sistema é orquestrado através de uma Máquina de Estados Finita (Graph) no `graph.ts`, composta por 5 nós fundamentais:
|
| 19 |
|
| 20 |
+
* **`contextNode`**: Nó de entrada. Carrega o relatório original do auditor e injeta no estado global do agente.
|
| 21 |
+
* **`routerNode`**: Formata as restrições do ambiente e monta o `System Prompt` que define a persona do LLM.
|
| 22 |
+
* **`pocoAgentNode`**: O motor cognitivo. Utiliza o modelo de linguagem avançado (ex: Claude 3.5 Sonnet) para raciocinar sobre as falhas e escolher qual ferramenta invocar.
|
| 23 |
+
* **`pocoToolsNode`**: O executor mecânico das ferramentas. Acessa o FileSystem (`read_file`, `write_file`) e o terminal (`smart_contract_test`, `smart_contract_compile`).
|
| 24 |
+
* **`trackToolCallsNode`**: Intercepta a saída do teste. Se o teste passar (`Test Passed Successfully!`), ele interrompe o grafo prematuramente definindo o status de `success`. Se falhar, devolve o feedback de erro para o `pocoAgentNode` tentar novamente, até o limite de 30 iterações.
|
| 25 |
|
| 26 |
+
## 3. Estrutura de Diretórios
|
| 27 |
|
| 28 |
+
```text
|
| 29 |
src/agents/tester/
|
| 30 |
+
├── index.ts # Entrypoint da biblioteca, orquestra e dispara o grafo LangGraph.
|
| 31 |
+
├── agent.ts # Definição e wrapper do agente para integração externa.
|
| 32 |
+
├── graph.ts # A topologia da rede ReAct (nodes e edges).
|
| 33 |
+
├── state.ts # Interface de Estado global que trafega entre os nós do grafo.
|
| 34 |
+
├── types.ts # Tipagens TypeScript (Report, Vulnerability, etc).
|
| 35 |
+
├── tools/ # (Depreciado) Ferramentas antigas de suporte.
|
| 36 |
+
├── utils/ # Scripts utilitários e stubs de dependências.
|
| 37 |
+
└── nodes/
|
| 38 |
+
├── context.ts # Setup inicial e parser do contexto.
|
| 39 |
+
├── router.ts # Montagem do prompt base.
|
| 40 |
+
└── pocoAgent.ts # Chamada direta à API do LLM com as Tools associadas.
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
```
|
| 42 |
|
| 43 |
+
## 4. Como Executar
|
| 44 |
|
| 45 |
+
O agente não é chamado isoladamente pelo usuário, mas sim invocado pelo orquestrador principal de Benchmark ou pela CLI da ferramenta. Para avaliar a eficácia do agente, recomenda-se executar os scripts do Benchmark na raiz do projeto:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
|
| 47 |
+
```bash
|
| 48 |
+
# Executa a avaliação em cima do dataset Hard (Proof-of-Patch)
|
| 49 |
+
DEBUG_CONTEXT=true FORCE_RERUN=true npx tsx src/benchmark/runTesterBenchmark.ts 100
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
```
|
| 51 |
|
| 52 |
+
## 5. Ferramentas (Tools)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
|
| 54 |
+
A maestria do agente vem de seu arsenal de ferramentas (`tools.ts`), que operam com alta precisão cirúrgica para economizar tokens:
|
| 55 |
+
* `read_file`, `list_dir`: Explorar a árvore de contratos vulneráveis.
|
| 56 |
+
* `todo_planner`: Criar uma lista de tarefas persistente para orientar a memória de longo prazo durante as 30 iterações.
|
| 57 |
+
* `write_file`, `edit_file`: Gerar ou alterar partes específicas do exploit de forma isolada.
|
| 58 |
+
* `smart_contract_compile`, `smart_contract_test`: Interagir diretamente com a CLI do `forge` para compilar ou rodar os testes, com os logs canalizados de volta para o agente.
|
| 59 |
|
| 60 |
+
> **Nota Metodológica:** O agente assume que está operando em um repositório configurado e funcional. Se as dependências do repositório alvo (ex: submódulos do foundry) estiverem quebradas ou faltantes fisicamente no disco, o agente tentará alucinar "Mocks" arquiteturais para forçar o projeto a compilar, o que foge do escopo do teste da vulnerabilidade. Sempre garanta que o projeto alvo passa por um `forge build` limpo antes de submetê-lo à auditoria.
|
|
|
src/agents/tester/graph.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
| 1 |
import { StateGraph, END, START } from "@langchain/langgraph";
|
| 2 |
import { ToolNode } from "@langchain/langgraph/prebuilt";
|
| 3 |
import { PoCStateAnnotation, PoCState } from "./state.js";
|
| 4 |
-
import {
|
| 5 |
-
import { routerNode } from "./nodes/router.js";
|
| 6 |
import { pocoAgentNode } from "./nodes/pocoAgent.js";
|
| 7 |
import { pocoTools } from "./tools.js";
|
| 8 |
|
|
@@ -54,15 +53,13 @@ function routeAfterTools(state: PoCState): "pocoAgentNode" | typeof END {
|
|
| 54 |
}
|
| 55 |
|
| 56 |
const graphBuilder = new StateGraph(PoCStateAnnotation)
|
| 57 |
-
.addNode("
|
| 58 |
-
.addNode("routerNode", routerNode)
|
| 59 |
.addNode("pocoAgentNode", pocoAgentNode)
|
| 60 |
.addNode("pocoToolsNode", pocoToolsNode)
|
| 61 |
.addNode("trackToolCallsNode", trackToolCallsNode)
|
| 62 |
|
| 63 |
-
.addEdge(START, "
|
| 64 |
-
.addEdge("
|
| 65 |
-
.addEdge("routerNode", "pocoAgentNode")
|
| 66 |
|
| 67 |
// ReAct Loop Routing
|
| 68 |
.addConditionalEdges("pocoAgentNode", routeAfterAgent, {
|
|
|
|
| 1 |
import { StateGraph, END, START } from "@langchain/langgraph";
|
| 2 |
import { ToolNode } from "@langchain/langgraph/prebuilt";
|
| 3 |
import { PoCStateAnnotation, PoCState } from "./state.js";
|
| 4 |
+
import { contextNode } from "./nodes/context.js";
|
|
|
|
| 5 |
import { pocoAgentNode } from "./nodes/pocoAgent.js";
|
| 6 |
import { pocoTools } from "./tools.js";
|
| 7 |
|
|
|
|
| 53 |
}
|
| 54 |
|
| 55 |
const graphBuilder = new StateGraph(PoCStateAnnotation)
|
| 56 |
+
.addNode("contextNode", contextNode)
|
|
|
|
| 57 |
.addNode("pocoAgentNode", pocoAgentNode)
|
| 58 |
.addNode("pocoToolsNode", pocoToolsNode)
|
| 59 |
.addNode("trackToolCallsNode", trackToolCallsNode)
|
| 60 |
|
| 61 |
+
.addEdge(START, "contextNode")
|
| 62 |
+
.addEdge("contextNode", "pocoAgentNode")
|
|
|
|
| 63 |
|
| 64 |
// ReAct Loop Routing
|
| 65 |
.addConditionalEdges("pocoAgentNode", routeAfterAgent, {
|
src/agents/tester/nodes/context.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import fs from "fs/promises";
|
| 2 |
+
import path from "path";
|
| 3 |
+
import { PoCState } from "../state.js";
|
| 4 |
+
|
| 5 |
+
export async function contextNode(state: PoCState): Promise<Partial<PoCState>> {
|
| 6 |
+
console.log("[contextNode] Preparando ambiente de testes para:", state.report.title);
|
| 7 |
+
|
| 8 |
+
if (state.report.customSandboxDir) {
|
| 9 |
+
try {
|
| 10 |
+
const testDir = path.join(state.report.customSandboxDir, "test");
|
| 11 |
+
const testEntries = await fs.readdir(testDir, { withFileTypes: true }).catch(() => []);
|
| 12 |
+
let removed = 0;
|
| 13 |
+
for (const entry of testEntries) {
|
| 14 |
+
if (entry.isFile() && entry.name.endsWith(".t.sol") && entry.name !== "Exploit.t.sol") {
|
| 15 |
+
await fs.unlink(path.join(testDir, entry.name));
|
| 16 |
+
removed++;
|
| 17 |
+
}
|
| 18 |
+
}
|
| 19 |
+
if (removed > 0) {
|
| 20 |
+
console.log(`[contextNode] Limpos ${removed} arquivos de teste antigos.`);
|
| 21 |
+
}
|
| 22 |
+
} catch (e) {
|
| 23 |
+
console.warn("[contextNode] falha na limpeza do diretório de testes:", (e as Error).message);
|
| 24 |
+
}
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
return {
|
| 28 |
+
templateCode: "",
|
| 29 |
+
pocCode: "",
|
| 30 |
+
infrastructurePhase: false
|
| 31 |
+
};
|
| 32 |
+
}
|
src/agents/tester/nodes/oracle.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
| 1 |
-
import fs from "fs/promises";
|
| 2 |
-
import path from "path";
|
| 3 |
-
import { PoCState } from "../state.js";
|
| 4 |
-
import { generateLocalScaffold } from "../tools/scaffoldGenerator.js";
|
| 5 |
-
import { extractConstructor } from "../utils/parserUtils.js";
|
| 6 |
-
import { analyzeSolidityFile } from "../../auditor/tools/solidity-analyzer-tool.js";
|
| 7 |
-
import { extractProjectContext } from "../utils/projectContextExtractor.js";
|
| 8 |
-
import { OracleContext } from "../types.js";
|
| 9 |
-
|
| 10 |
-
export async function oracleNode(state: PoCState): Promise<Partial<PoCState>> {
|
| 11 |
-
console.log("[oracleNode] gerando scaffold para:", state.report.title);
|
| 12 |
-
|
| 13 |
-
const solidityScaffold = generateLocalScaffold(state.report);
|
| 14 |
-
|
| 15 |
-
const constructorInfo = extractConstructor(state.report.affectedContract.sourceCode, state.report.affectedContract.name);
|
| 16 |
-
|
| 17 |
-
console.log("[oracleNode] analisando API do contrato e helpers de teste...");
|
| 18 |
-
const targetContractAPI = await analyzeSolidityFile(state.report.affectedContract.sourceCode, "short");
|
| 19 |
-
|
| 20 |
-
let referenceTestHelpers = "";
|
| 21 |
-
if (state.report.referenceTestCode) {
|
| 22 |
-
referenceTestHelpers = await analyzeSolidityFile(state.report.referenceTestCode, "short");
|
| 23 |
-
}
|
| 24 |
-
|
| 25 |
-
let projectRemappings = "";
|
| 26 |
-
let projectTestImports = "";
|
| 27 |
-
let projectTestFilePath: string | null = null;
|
| 28 |
-
if (state.report.customSandboxDir) {
|
| 29 |
-
console.log("[oracleNode] extracting project context (remappings, test imports)...");
|
| 30 |
-
try {
|
| 31 |
-
const projectCtx = await extractProjectContext(state.report.customSandboxDir);
|
| 32 |
-
projectRemappings = projectCtx.remappings;
|
| 33 |
-
projectTestImports = projectCtx.existingTestImports;
|
| 34 |
-
projectTestFilePath = projectCtx.existingTestFilePath;
|
| 35 |
-
} catch (e) {
|
| 36 |
-
console.warn("[oracleNode] could not extract project context:", (e as Error).message);
|
| 37 |
-
}
|
| 38 |
-
|
| 39 |
-
try {
|
| 40 |
-
const testDir = path.join(state.report.customSandboxDir, "test");
|
| 41 |
-
const testEntries = await fs.readdir(testDir, { withFileTypes: true }).catch(() => []);
|
| 42 |
-
let removed = 0;
|
| 43 |
-
for (const entry of testEntries) {
|
| 44 |
-
if (entry.isFile() && entry.name.endsWith(".t.sol") && entry.name !== "Exploit.t.sol") {
|
| 45 |
-
await fs.unlink(path.join(testDir, entry.name));
|
| 46 |
-
removed++;
|
| 47 |
-
}
|
| 48 |
-
}
|
| 49 |
-
} catch (e) {
|
| 50 |
-
console.warn("[oracleNode] test cleanup failed:", (e as Error).message);
|
| 51 |
-
}
|
| 52 |
-
}
|
| 53 |
-
|
| 54 |
-
const oracleContext: OracleContext = {
|
| 55 |
-
solidityScaffold,
|
| 56 |
-
constructorInfo: constructorInfo?.parameters,
|
| 57 |
-
targetContractAPI,
|
| 58 |
-
referenceTestHelpers,
|
| 59 |
-
projectRemappings,
|
| 60 |
-
projectTestImports,
|
| 61 |
-
projectTestFilePath,
|
| 62 |
-
};
|
| 63 |
-
|
| 64 |
-
// No longer generating static template. We leave it to the agent to build the setup.
|
| 65 |
-
console.log("[oracleNode] scaffold generation skipped. Context built.");
|
| 66 |
-
return {
|
| 67 |
-
oracleContext,
|
| 68 |
-
templateCode: "",
|
| 69 |
-
pocCode: "",
|
| 70 |
-
infrastructurePhase: false
|
| 71 |
-
};
|
| 72 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/agents/tester/nodes/router.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
| 1 |
-
import { PoCState } from "../state.js";
|
| 2 |
-
|
| 3 |
-
export const CATEGORY_REENTRANCY = "REENTRANCY";
|
| 4 |
-
export const CATEGORY_ACCESS_CONTROL = "ACCESS_CONTROL";
|
| 5 |
-
export const CATEGORY_ARITHMETIC = "ARITHMETIC";
|
| 6 |
-
export const CATEGORY_LOGIC = "LOGIC";
|
| 7 |
-
export const CATEGORY_DEFAULT = "DEFAULT";
|
| 8 |
-
|
| 9 |
-
export async function routerNode(state: PoCState): Promise<Partial<PoCState>> {
|
| 10 |
-
console.log("[routerNode] Classifying vulnerability deterministically...");
|
| 11 |
-
|
| 12 |
-
const type = (state.report.type || "").toLowerCase();
|
| 13 |
-
const desc = (state.report.description || "").toLowerCase();
|
| 14 |
-
|
| 15 |
-
const combined = `${type} ${desc}`;
|
| 16 |
-
|
| 17 |
-
let category = CATEGORY_DEFAULT;
|
| 18 |
-
|
| 19 |
-
if (combined.includes("reentrancy") || combined.includes("re-entrancy") || combined.includes("fallback")) {
|
| 20 |
-
category = CATEGORY_REENTRANCY;
|
| 21 |
-
} else if (combined.includes("access control") || combined.includes("unauthorized") || combined.includes("onlyowner") || combined.includes("permission")) {
|
| 22 |
-
category = CATEGORY_ACCESS_CONTROL;
|
| 23 |
-
} else if (combined.includes("overflow") || combined.includes("underflow") || combined.includes("arithmetic") || combined.includes("math")) {
|
| 24 |
-
category = CATEGORY_ARITHMETIC;
|
| 25 |
-
} else if (combined.includes("logic") || combined.includes("validation") || combined.includes("bypass")) {
|
| 26 |
-
category = CATEGORY_LOGIC;
|
| 27 |
-
}
|
| 28 |
-
|
| 29 |
-
console.log(`[routerNode] Classified as: ${category}`);
|
| 30 |
-
|
| 31 |
-
return { vulnerabilityCategory: category };
|
| 32 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/agents/tester/state.ts
CHANGED
|
@@ -1,15 +1,10 @@
|
|
| 1 |
import { Annotation } from "@langchain/langgraph";
|
| 2 |
import { BaseMessage } from "@langchain/core/messages";
|
| 3 |
-
import { VulnerabilityReport
|
| 4 |
|
| 5 |
export const PoCStateAnnotation = Annotation.Root({
|
| 6 |
report: Annotation<VulnerabilityReport>(),
|
| 7 |
|
| 8 |
-
oracleContext: Annotation<OracleContext | null>({
|
| 9 |
-
default: () => null,
|
| 10 |
-
reducer: (_, y) => y, // overwrite — filled once by oracleNode
|
| 11 |
-
}),
|
| 12 |
-
|
| 13 |
pocCode: Annotation<string>({
|
| 14 |
default: () => "",
|
| 15 |
reducer: (_, y) => y, // overwrite — full combined file
|
|
@@ -25,11 +20,6 @@ export const PoCStateAnnotation = Annotation.Root({
|
|
| 25 |
reducer: (_, y) => y, // overwrite — only the hack logic
|
| 26 |
}),
|
| 27 |
|
| 28 |
-
vulnerabilityCategory: Annotation<string>({
|
| 29 |
-
default: () => "",
|
| 30 |
-
reducer: (_, y) => y, // overwrite
|
| 31 |
-
}),
|
| 32 |
-
|
| 33 |
infrastructurePhase: Annotation<boolean>({
|
| 34 |
default: () => true,
|
| 35 |
reducer: (_, y) => y, // overwrite — true while fixing imports
|
|
|
|
| 1 |
import { Annotation } from "@langchain/langgraph";
|
| 2 |
import { BaseMessage } from "@langchain/core/messages";
|
| 3 |
+
import { VulnerabilityReport } from "./types.js";
|
| 4 |
|
| 5 |
export const PoCStateAnnotation = Annotation.Root({
|
| 6 |
report: Annotation<VulnerabilityReport>(),
|
| 7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
pocCode: Annotation<string>({
|
| 9 |
default: () => "",
|
| 10 |
reducer: (_, y) => y, // overwrite — full combined file
|
|
|
|
| 20 |
reducer: (_, y) => y, // overwrite — only the hack logic
|
| 21 |
}),
|
| 22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 23 |
infrastructurePhase: Annotation<boolean>({
|
| 24 |
default: () => true,
|
| 25 |
reducer: (_, y) => y, // overwrite — true while fixing imports
|
src/agents/tester/tools/scaffoldGenerator.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
| 1 |
-
import { VulnerabilityReport } from "../types.js";
|
| 2 |
-
|
| 3 |
-
export function generateLocalScaffold(report: VulnerabilityReport): string {
|
| 4 |
-
const cheatcodes = report.suggestedCheatcodes?.join(", ") ?? "vm.deal, vm.prank, vm.warp";
|
| 5 |
-
|
| 6 |
-
// Extrair pragma do código original para evitar conflitos de versão
|
| 7 |
-
const pragmaMatch = report.affectedContract.sourceCode.match(/pragma solidity ([^;]+);/);
|
| 8 |
-
const pragma = pragmaMatch ? pragmaMatch[0] : "pragma solidity ^0.8.20;";
|
| 9 |
-
|
| 10 |
-
let contractSetup = "";
|
| 11 |
-
if (report.affectedContract.sourceFilePath) {
|
| 12 |
-
// Se temos o caminho do arquivo, importamos ao invés de colar
|
| 13 |
-
contractSetup = `import { ${report.affectedContract.name} } from "../${report.affectedContract.sourceFilePath}";`;
|
| 14 |
-
} else {
|
| 15 |
-
// Fallback: colar o código (pode falhar por causa de imports ausentes no sandbox)
|
| 16 |
-
contractSetup = `// ── Código-fonte do contrato vulnerável ──────────────────────────────────────\n${report.affectedContract.sourceCode}\n// ─────────────────────────────────────────────────────────────────────────────`;
|
| 17 |
-
}
|
| 18 |
-
|
| 19 |
-
return `// SPDX-License-Identifier: UNLICENSED
|
| 20 |
-
${pragma}
|
| 21 |
-
|
| 22 |
-
import "forge-std/Test.sol";
|
| 23 |
-
import "forge-std/console.sol";
|
| 24 |
-
|
| 25 |
-
${contractSetup}
|
| 26 |
-
|
| 27 |
-
contract ExploitTest is Test {
|
| 28 |
-
${report.affectedContract.name} target;
|
| 29 |
-
address constant ATTACKER = address(0xBEEF);
|
| 30 |
-
|
| 31 |
-
// setUp() - O Oracle tentou gerar um básico, mas sinta-se à vontade para ajustar se o contrato for complexo
|
| 32 |
-
function setUp() public virtual {
|
| 33 |
-
// target = new ${report.affectedContract.name}(...); // TODO: ajustar se necessário
|
| 34 |
-
vm.deal(ATTACKER, 100 ether);
|
| 35 |
-
}
|
| 36 |
-
|
| 37 |
-
// Vulnerabilidade: ${report.title}
|
| 38 |
-
// Tipo: ${report.type}
|
| 39 |
-
// Vetor: ${report.attackVector}
|
| 40 |
-
${report.exploitablePaths ? `// Caminhos de Exploração:\n // - ${report.exploitablePaths.join("\n // - ")}` : ""}
|
| 41 |
-
// Cheatcodes sugeridos: ${cheatcodes}
|
| 42 |
-
//
|
| 43 |
-
// Implemente test_Exploit() e ajuste o setUp() se o contrato exigir argumentos no constructor.
|
| 44 |
-
function test_Exploit() public {
|
| 45 |
-
vm.startPrank(ATTACKER);
|
| 46 |
-
// TODO: implementar exploit aqui
|
| 47 |
-
vm.stopPrank();
|
| 48 |
-
}
|
| 49 |
-
}`.trim();
|
| 50 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/agents/tester/types.ts
CHANGED
|
@@ -34,15 +34,7 @@ export interface VulnerabilityReport {
|
|
| 34 |
patchDiff?: string; // Unified diff of the patch (vulnerable vs patched) for specificity guidance
|
| 35 |
}
|
| 36 |
|
| 37 |
-
|
| 38 |
-
solidityScaffold: string; // Exploit.t.sol parcial com setUp() pronto
|
| 39 |
-
constructorInfo?: string; // Assinatura do constructor para ajudar no deploy
|
| 40 |
-
targetContractAPI?: string; // Resumo dos métodos e variáveis do contrato alvo
|
| 41 |
-
referenceTestHelpers?: string; // Resumo das funções auxiliares disponíveis no ambiente de teste
|
| 42 |
-
projectRemappings?: string; // Content of remappings.txt for correct import paths
|
| 43 |
-
projectTestImports?: string; // Import lines from an existing test file in the project
|
| 44 |
-
projectTestFilePath?: string | null; // Path to the reference test file used
|
| 45 |
-
}
|
| 46 |
|
| 47 |
export interface PoCResult {
|
| 48 |
reportId: string;
|
|
|
|
| 34 |
patchDiff?: string; // Unified diff of the patch (vulnerable vs patched) for specificity guidance
|
| 35 |
}
|
| 36 |
|
| 37 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
|
| 39 |
export interface PoCResult {
|
| 40 |
reportId: string;
|
src/agents/tester/utils/dependencyStubber.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
| 1 |
-
import fs from "fs/promises";
|
| 2 |
-
import path from "path";
|
| 3 |
-
import { exec } from "child_process";
|
| 4 |
-
import { promisify } from "util";
|
| 5 |
-
|
| 6 |
-
const execAsync = promisify(exec);
|
| 7 |
-
|
| 8 |
-
/**
|
| 9 |
-
* Runs a minimal forge build probe to detect missing source files,
|
| 10 |
-
* then creates minimal stub contracts at those exact paths.
|
| 11 |
-
* This unblocks projects that use deep submodule dependencies (e.g. lib/caviar/lib/oracle/...)
|
| 12 |
-
* or node_modules imports that are not present in the sandbox.
|
| 13 |
-
*/
|
| 14 |
-
export async function createMissingDependencyStubs(sandboxDir: string): Promise<void> {
|
| 15 |
-
// Run forge build on just the project (no test files), capture errors
|
| 16 |
-
let combined = "";
|
| 17 |
-
try {
|
| 18 |
-
const { stdout, stderr } = await execAsync(
|
| 19 |
-
`cd "${sandboxDir}" && forge build --no-cache 2>&1 || true`,
|
| 20 |
-
{
|
| 21 |
-
timeout: 60_000,
|
| 22 |
-
env: { ...process.env, PATH: `${process.env.HOME}/.foundry/bin:${process.env.PATH}` }
|
| 23 |
-
}
|
| 24 |
-
);
|
| 25 |
-
combined = stdout + stderr;
|
| 26 |
-
} catch (e: any) {
|
| 27 |
-
combined = e.stdout || e.stderr || e.message || "";
|
| 28 |
-
}
|
| 29 |
-
|
| 30 |
-
// Extract all "Source X not found" paths
|
| 31 |
-
const missingPaths: string[] = [];
|
| 32 |
-
const sourceNotFoundRegex = /Source "([^"]+)" not found/g;
|
| 33 |
-
let match: RegExpExecArray | null;
|
| 34 |
-
while ((match = sourceNotFoundRegex.exec(combined)) !== null) {
|
| 35 |
-
const missing = match[1];
|
| 36 |
-
if (!missingPaths.includes(missing)) {
|
| 37 |
-
missingPaths.push(missing);
|
| 38 |
-
}
|
| 39 |
-
}
|
| 40 |
-
|
| 41 |
-
if (missingPaths.length === 0) return;
|
| 42 |
-
|
| 43 |
-
console.log(`[oracleNode] Detected ${missingPaths.length} missing dependencies, creating stubs...`);
|
| 44 |
-
|
| 45 |
-
// Detect Solidity version used in the project (for the stub pragma)
|
| 46 |
-
let pragmaVersion = "^0.8.0";
|
| 47 |
-
try {
|
| 48 |
-
const toml = await fs.readFile(path.join(sandboxDir, "foundry.toml"), "utf-8");
|
| 49 |
-
const vMatch = toml.match(/solc[_-]?version\s*=\s*"([^"]+)"/);
|
| 50 |
-
if (vMatch) pragmaVersion = vMatch[1];
|
| 51 |
-
} catch { /* use default */ }
|
| 52 |
-
|
| 53 |
-
for (const missing of missingPaths) {
|
| 54 |
-
// Build the stub path inside the sandbox
|
| 55 |
-
const stubPath = path.join(sandboxDir, missing);
|
| 56 |
-
|
| 57 |
-
// Skip if file already exists
|
| 58 |
-
try {
|
| 59 |
-
await fs.access(stubPath);
|
| 60 |
-
continue; // already exists
|
| 61 |
-
} catch { /* doesn't exist, create it */ }
|
| 62 |
-
|
| 63 |
-
// Skip forge-std — it should always be available
|
| 64 |
-
if (missing.startsWith("forge-std/") || missing.startsWith("lib/forge-std/")) continue;
|
| 65 |
-
|
| 66 |
-
try {
|
| 67 |
-
await fs.mkdir(path.dirname(stubPath), { recursive: true });
|
| 68 |
-
|
| 69 |
-
// Generate a minimal stub that satisfies the import
|
| 70 |
-
const contractName = path.basename(missing, ".sol");
|
| 71 |
-
const stubContent = `// SPDX-License-Identifier: MIT
|
| 72 |
-
// AUTO-GENERATED STUB — replaces missing dependency: ${missing}
|
| 73 |
-
pragma solidity ${pragmaVersion};
|
| 74 |
-
|
| 75 |
-
// Minimal stub to satisfy missing import
|
| 76 |
-
contract ${contractName} {}
|
| 77 |
-
interface I${contractName} {}
|
| 78 |
-
`;
|
| 79 |
-
await fs.writeFile(stubPath, stubContent);
|
| 80 |
-
console.log(` [stub] Created: ${missing}`);
|
| 81 |
-
} catch (e) {
|
| 82 |
-
console.warn(` [stub] Failed to create ${missing}:`, (e as Error).message);
|
| 83 |
-
}
|
| 84 |
-
}
|
| 85 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/agents/tester/utils/projectContextExtractor.ts
DELETED
|
@@ -1,156 +0,0 @@
|
|
| 1 |
-
import fs from "fs/promises";
|
| 2 |
-
import path from "path";
|
| 3 |
-
|
| 4 |
-
export interface ProjectContext {
|
| 5 |
-
remappings: string; // Content of remappings.txt or foundry.toml [profile.default.remappings]
|
| 6 |
-
existingTestImports: string; // First few import lines from an existing test file
|
| 7 |
-
foundryTomlProfile: string; // Relevant foundry.toml settings (src, libs)
|
| 8 |
-
existingTestFilePath: string | null; // Relative path to an existing test file for reference
|
| 9 |
-
}
|
| 10 |
-
|
| 11 |
-
/**
|
| 12 |
-
* Extracts project-level context needed for correct import paths in PoC tests.
|
| 13 |
-
* Reads remappings.txt, foundry.toml, and the first existing test file in the project.
|
| 14 |
-
*/
|
| 15 |
-
export async function extractProjectContext(sandboxDir: string): Promise<ProjectContext> {
|
| 16 |
-
let remappings = "";
|
| 17 |
-
let foundryTomlProfile = "";
|
| 18 |
-
let existingTestImports = "";
|
| 19 |
-
let existingTestFilePath: string | null = null;
|
| 20 |
-
|
| 21 |
-
// 1. Read remappings.txt
|
| 22 |
-
try {
|
| 23 |
-
const remappingsPath = path.join(sandboxDir, "remappings.txt");
|
| 24 |
-
remappings = await fs.readFile(remappingsPath, "utf-8");
|
| 25 |
-
} catch {
|
| 26 |
-
// fallback: try to extract from foundry.toml
|
| 27 |
-
}
|
| 28 |
-
|
| 29 |
-
// 2. Read foundry.toml for additional context
|
| 30 |
-
try {
|
| 31 |
-
const foundryTomlPath = path.join(sandboxDir, "foundry.toml");
|
| 32 |
-
const tomlContent = await fs.readFile(foundryTomlPath, "utf-8");
|
| 33 |
-
// Extract relevant lines (src, libs, remappings)
|
| 34 |
-
const relevantLines = tomlContent
|
| 35 |
-
.split("\n")
|
| 36 |
-
.filter(l =>
|
| 37 |
-
l.includes("src") ||
|
| 38 |
-
l.includes("libs") ||
|
| 39 |
-
l.includes("remapping") ||
|
| 40 |
-
l.includes("[profile")
|
| 41 |
-
)
|
| 42 |
-
.slice(0, 20)
|
| 43 |
-
.join("\n");
|
| 44 |
-
foundryTomlProfile = relevantLines;
|
| 45 |
-
|
| 46 |
-
// If no remappings.txt, try to extract from foundry.toml remappings array
|
| 47 |
-
if (!remappings) {
|
| 48 |
-
const remappingMatch = tomlContent.match(/remappings\s*=\s*\[([\s\S]*?)\]/);
|
| 49 |
-
if (remappingMatch) {
|
| 50 |
-
remappings = remappingMatch[1]
|
| 51 |
-
.split(",")
|
| 52 |
-
.map(s => s.trim().replace(/^["']|["']$/g, ""))
|
| 53 |
-
.filter(Boolean)
|
| 54 |
-
.join("\n");
|
| 55 |
-
}
|
| 56 |
-
}
|
| 57 |
-
} catch {
|
| 58 |
-
// ignore
|
| 59 |
-
}
|
| 60 |
-
|
| 61 |
-
// 3. Find an existing test file to use as import reference
|
| 62 |
-
try {
|
| 63 |
-
const testDir = path.join(sandboxDir, "test");
|
| 64 |
-
const testFile = await findFirstTestFile(testDir);
|
| 65 |
-
if (testFile) {
|
| 66 |
-
existingTestFilePath = path.relative(sandboxDir, testFile);
|
| 67 |
-
const testContent = await fs.readFile(testFile, "utf-8");
|
| 68 |
-
// Extract the first 20 lines which typically contain imports
|
| 69 |
-
existingTestImports = testContent
|
| 70 |
-
.split("\n")
|
| 71 |
-
.slice(0, 25)
|
| 72 |
-
.filter(l => l.startsWith("import") || l.startsWith("pragma") || l.startsWith("//") || l.startsWith("contract") || l.startsWith("abstract"))
|
| 73 |
-
.join("\n");
|
| 74 |
-
}
|
| 75 |
-
} catch {
|
| 76 |
-
// ignore
|
| 77 |
-
}
|
| 78 |
-
|
| 79 |
-
return { remappings, existingTestImports, foundryTomlProfile, existingTestFilePath };
|
| 80 |
-
}
|
| 81 |
-
|
| 82 |
-
/**
|
| 83 |
-
* Recursively finds the best reference test file in the test directory.
|
| 84 |
-
* Strategy:
|
| 85 |
-
* 1. Collect ALL .t.sol files (excluding Exploit.t.sol) across all subdirs (BFS)
|
| 86 |
-
* 2. Pick the one with the most import lines (most context-rich)
|
| 87 |
-
* 3. Fallback to .sol files that have "import" statements (e.g. BaseTest.sol, Fixture.sol)
|
| 88 |
-
* 4. Skip pure mock contracts (files in "mock" directories or named *Mock.sol)
|
| 89 |
-
*/
|
| 90 |
-
async function findFirstTestFile(dir: string): Promise<string | null> {
|
| 91 |
-
const tSolFiles: string[] = [];
|
| 92 |
-
const solFiles: string[] = [];
|
| 93 |
-
|
| 94 |
-
// BFS collect all files
|
| 95 |
-
const queue = [dir];
|
| 96 |
-
let depth = 0;
|
| 97 |
-
while (queue.length > 0 && depth < 4) {
|
| 98 |
-
const currentDepth: string[] = [...queue];
|
| 99 |
-
queue.length = 0;
|
| 100 |
-
depth++;
|
| 101 |
-
for (const currentDir of currentDepth) {
|
| 102 |
-
let entries: import('fs').Dirent[];
|
| 103 |
-
try {
|
| 104 |
-
entries = await fs.readdir(currentDir, { withFileTypes: true }) as import('fs').Dirent[];
|
| 105 |
-
} catch {
|
| 106 |
-
continue;
|
| 107 |
-
}
|
| 108 |
-
for (const entry of entries) {
|
| 109 |
-
const fullPath = path.join(currentDir, entry.name);
|
| 110 |
-
const name = entry.name as string;
|
| 111 |
-
if (entry.isFile()) {
|
| 112 |
-
if (name === "Exploit.t.sol") continue; // skip our own file
|
| 113 |
-
if (name.endsWith(".t.sol")) {
|
| 114 |
-
tSolFiles.push(fullPath);
|
| 115 |
-
} else if (name.endsWith(".sol")) {
|
| 116 |
-
// Skip mock contracts
|
| 117 |
-
const isMock = name.toLowerCase().includes("mock") || currentDir.toLowerCase().includes("mock");
|
| 118 |
-
if (!isMock) {
|
| 119 |
-
solFiles.push(fullPath);
|
| 120 |
-
}
|
| 121 |
-
}
|
| 122 |
-
} else if (entry.isDirectory()) {
|
| 123 |
-
queue.push(fullPath);
|
| 124 |
-
}
|
| 125 |
-
}
|
| 126 |
-
}
|
| 127 |
-
}
|
| 128 |
-
|
| 129 |
-
// Pick the .t.sol file with the most import lines (richest context)
|
| 130 |
-
if (tSolFiles.length > 0) {
|
| 131 |
-
let bestFile = tSolFiles[0];
|
| 132 |
-
let bestImportCount = 0;
|
| 133 |
-
for (const f of tSolFiles.slice(0, 10)) { // check up to 10
|
| 134 |
-
try {
|
| 135 |
-
const content = await fs.readFile(f, "utf-8");
|
| 136 |
-
const importCount = (content.match(/^import/gm) ?? []).length;
|
| 137 |
-
if (importCount > bestImportCount) {
|
| 138 |
-
bestImportCount = importCount;
|
| 139 |
-
bestFile = f;
|
| 140 |
-
}
|
| 141 |
-
} catch { /* skip */ }
|
| 142 |
-
}
|
| 143 |
-
return bestFile;
|
| 144 |
-
}
|
| 145 |
-
|
| 146 |
-
// Fallback: .sol files that have import statements (like BaseTest.sol, Fixture.sol)
|
| 147 |
-
for (const f of solFiles.slice(0, 10)) {
|
| 148 |
-
try {
|
| 149 |
-
const content = await fs.readFile(f, "utf-8");
|
| 150 |
-
if (content.includes("import ")) return f;
|
| 151 |
-
} catch { /* skip */ }
|
| 152 |
-
}
|
| 153 |
-
|
| 154 |
-
return null;
|
| 155 |
-
}
|
| 156 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
src/benchmark/runTesterBenchmark.ts
CHANGED
|
@@ -206,8 +206,8 @@ async function main() {
|
|
| 206 |
continue;
|
| 207 |
}
|
| 208 |
|
| 209 |
-
const
|
| 210 |
-
if (!
|
| 211 |
continue;
|
| 212 |
}
|
| 213 |
|
|
|
|
| 206 |
continue;
|
| 207 |
}
|
| 208 |
|
| 209 |
+
const targetIds = ["020"];
|
| 210 |
+
if (!targetIds.includes(id)) {
|
| 211 |
continue;
|
| 212 |
}
|
| 213 |
|