clawfast 2.2.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/clawfast.cjs +118 -18
  2. package/package.json +1 -1
package/dist/clawfast.cjs CHANGED
@@ -689,7 +689,7 @@ var clawfastVersion, isDevVersion, isNewerVersion;
689
689
  var init_version = __esm({
690
690
  "src/version.ts"() {
691
691
  "use strict";
692
- clawfastVersion = () => true ? "2.2.0" : devVersionFromPackageJson();
692
+ clawfastVersion = () => true ? "2.2.1" : devVersionFromPackageJson();
693
693
  isDevVersion = () => clawfastVersion().includes("-dev");
694
694
  isNewerVersion = (a, b) => {
695
695
  const parse3 = (v) => v.split("-")[0].split(".").map((n) => Number.parseInt(n, 10) || 0);
@@ -70403,7 +70403,9 @@ var init_local_sandbox = __esm({
70403
70403
  this.shellBin = shell2.shell;
70404
70404
  this.shellFlag = shell2.shellFlag;
70405
70405
  }
70406
- const base = opts?.workdir || process.env.CLI_WORKDIR || import_node_path7.default.join(process.cwd(), "SPRIT");
70406
+ const explicit = opts?.workdir || process.env.CLI_WORKDIR;
70407
+ const base = explicit || import_node_path7.default.join(process.cwd(), "SPRIT");
70408
+ this.autoCreated = !explicit;
70407
70409
  this.workdir = import_node_path7.default.resolve(base).replace(/\\/g, "/");
70408
70410
  }
70409
70411
  async init() {
@@ -70560,6 +70562,31 @@ EDITING SCRIPTS:
70560
70562
  }
70561
70563
  this.backgroundPids.clear();
70562
70564
  }
70565
+ /**
70566
+ * Remove the auto-created SPRIT workspace on session exit so an analysis never
70567
+ * leaves scratch tooling behind in the user's project. The human-facing
70568
+ * deliverable (ANALISE_PROJETO_*.md / findings report) is written at the
70569
+ * PROJECT ROOT, OUTSIDE SPRIT — so nothing the user needs is lost.
70570
+ *
70571
+ * Strictly guarded — deletes ONLY when all hold:
70572
+ * - the workspace was auto-created (default <cwd>/SPRIT, no explicit override)
70573
+ * - its basename is exactly "SPRIT" (never a project root or arbitrary dir)
70574
+ * - the operator has not opted out via CLAWFAST_KEEP_SPRIT
70575
+ * Best-effort: any failure is swallowed; cleanup must never block shutdown.
70576
+ * Returns the removed path (for logging) or null when nothing was removed.
70577
+ */
70578
+ async cleanupWorkspace() {
70579
+ if (!this.autoCreated) return null;
70580
+ const keep = (process.env.CLAWFAST_KEEP_SPRIT || "").trim().toLowerCase();
70581
+ if (keep === "1" || keep === "true" || keep === "yes") return null;
70582
+ if (import_node_path7.default.basename(this.workdir).toUpperCase() !== "SPRIT") return null;
70583
+ try {
70584
+ await import_node_fs8.promises.rm(this.workdir, { recursive: true, force: true });
70585
+ return this.workdir;
70586
+ } catch {
70587
+ return null;
70588
+ }
70589
+ }
70563
70590
  /** True while a foreground command is running and can still accept stdin. */
70564
70591
  isProcessRunning() {
70565
70592
  const child = this.activeForegroundChild;
@@ -71154,10 +71181,16 @@ function detectAuditExitIntent(input) {
71154
71181
  if (!text2) return false;
71155
71182
  return AUDIT_EXIT.some((re2) => re2.test(text2));
71156
71183
  }
71184
+ function detectPocVerifyIntent(input) {
71185
+ const text2 = input.trim();
71186
+ if (!text2) return false;
71187
+ return POC_VERIFY.some((re2) => re2.test(text2));
71188
+ }
71157
71189
  function buildAuditSystemPrompt(opts) {
71158
71190
  const root = opts.projectRoot.replace(/\\/g, "/");
71159
71191
  const workdir = opts.workdir.replace(/\\/g, "/");
71160
71192
  const isWin = (opts.platform ?? process.platform) === "win32";
71193
+ const verify = opts.mode === "verify";
71161
71194
  const py = isWin ? "python" : "python3";
71162
71195
  const shellNote = isWin ? "Voc\xEA est\xE1 em Windows. Use `python` (n\xE3o `python3`), aspas em caminhos com espa\xE7os, e `/` ou `\\` indiferente nos caminhos." : "Use `python3` quando `python` n\xE3o existir.";
71163
71196
  const shellHint = opts.shellHint ?? shellNote;
@@ -71167,7 +71200,11 @@ function buildAuditSystemPrompt(opts) {
71167
71200
  FILOSOFIA (estado da arte \u2014 h\xEDbrido, n\xE3o LLM puro):
71168
71201
  Voc\xEA N\xC3O \xE9 um LLM que l\xEA o projeto inteiro linha a linha. Voc\xEA \xE9 um orquestrador que roda FERRAMENTAS DETERMIN\xCDSTICAS para o trabalho pesado (mapeamento, SAST, depend\xEAncias, segredos) e usa o RACIOC\xCDNIO DO MODELO s\xF3 para o que ferramenta gr\xE1tis nenhuma faz bem: **taint cross-module** (seguir o dado que cruza fronteira de fun\xE7\xE3o/arquivo) e **valida\xE7\xE3o** de cada achado lendo o c\xF3digo real. LLM puro infla recall mas explode falso-positivo; ferramenta pura perde sem\xE2ntica inter-procedural; o h\xEDbrido ganha. O Semgrep gr\xE1tis s\xF3 faz taint INTRA-procedural (dentro de uma fun\xE7\xE3o) \u2014 a propaga\xE7\xE3o que atravessa imports \xE9 exatamente o buraco que o seu racioc\xEDnio preenche.
71169
71202
 
71170
- Miss\xE3o desta sess\xE3o: an\xE1lise extremamente precisa e profunda do projeto inteiro e entrega de **um relat\xF3rio** acion\xE1vel e honesto. Ferramentas do agente: \`run_terminal_cmd\`, \`file\`, \`todo_write\`.
71203
+ Miss\xE3o desta sess\xE3o: an\xE1lise extremamente precisa e profunda do projeto inteiro e entrega de **um relat\xF3rio** acion\xE1vel e honesto, com BUSCA cir\xFArgica (ferramentas estruturais sobre o c\xF3digo real) e PODER de verifica\xE7\xE3o (cada achado provado, n\xE3o suposto). Ferramentas do agente: \`run_terminal_cmd\`, \`file\` (com leitura por \`range\` \u2014 leia S\xD3 a janela exata da fun\xE7\xE3o, nunca o arquivo inteiro), \`findings\` (mem\xF3ria estruturada de vulnerabilidades \u2014 a fonte \xFAnica de verdade dos achados) e \`todo_write\`.
71204
+
71205
+ ${verify ? `>>> SUB-MODO ATIVO: **VERIFICA\xC7\xC3O / PoC** <<<
71206
+ Voc\xEA foi acionado para PROVAR achados rodando-os de verdade (reproduzir a vulnerabilidade), n\xE3o s\xF3 lendo. Vale a regra relaxada da se\xE7\xE3o "SUB-MODO VERIFICA\xC7\xC3O" abaixo: voc\xEA PODE executar o c\xF3digo do projeto de um harness ISOLADO na workspace \u2014 mas continua SEM editar/criar/apagar nada dentro do projeto. Objetivo: virar cada \`[SUSPEITA]\` em \`[CONFIRMADO]\` com PoC reproduz\xEDvel e n\xE3o-destrutivo.` : `>>> MODO ATIVO: **AN\xC1LISE (somente leitura)** <<<
71207
+ Voc\xEA N\xC3O roda o projeto. Para PROVAR um achado executando um PoC, o operador precisa pedir explicitamente (ex.: "prove rodando", "rode o PoC") \u2014 a\xED entra o sub-modo de verifica\xE7\xE3o.`}
71171
71208
 
71172
71209
  PROJETO SOB AN\xC1LISE (raiz): ${root}
71173
71210
  WORKSPACE DE TRABALHO (ferramentas/sa\xEDdas intermedi\xE1rias): ${workdir}
@@ -71182,6 +71219,16 @@ Um relat\xF3rio de seguran\xE7a inventado \xE9 PIOR que nenhum \u2014 \xE9 perig
71182
71219
  3. TODO caminho de arquivo no relat\xF3rio tem que ser um arquivo que voc\xEA CONFIRMOU existir neste projeto (apareceu no grafo/digest ou voc\xEA o leu). Nunca cite caminhos gen\xE9ricos de mem\xF3ria (ex.: \`src/services/userService.ts\`, \`lodash@x\`) sem t\xEA-los visto na sa\xEDda real.
71183
71220
  4. Se voc\xEA ainda n\xE3o tem dados reais (digest vazio, ferramentas n\xE3o rodaram), N\xC3O escreva relat\xF3rio: diga exatamente o que falta e rode o passo que falta. Parar \xE9 melhor que inventar.
71184
71221
 
71222
+ ================================================================
71223
+ BUSCA & LEITURA DE PRECIS\xC3O \u2014 seja cir\xFArgico, n\xE3o force bruta
71224
+ ================================================================
71225
+ Sua pot\xEAncia vem de localizar com exatid\xE3o e ler s\xF3 o necess\xE1rio \u2014 n\xE3o de despejar arquivos no contexto. Escada de precis\xE3o, do mais barato/preciso ao mais caro:
71226
+ 1. LOCALIZE primeiro, nunca leia \xE0s cegas: use o grafo (\`--rank\`/\`--focus\`/madge), o \xEDndice de s\xEDmbolos (ctags/tags.json) e a busca para achar o file:line exato.
71227
+ 2. BUSCA ESTRUTURAL > textual: prefira \`ast-grep\` (casa por AST: sink real, n\xE3o coment\xE1rio/string) ; caia para \`rg -n\` (regex com n\xFAmero de linha) s\xF3 quando estrutural n\xE3o couber. Sempre com \`-n\` para ter a linha.
71228
+ 3. LEITURA CIR\xDARGICA: depois de ter file:line, abra S\xD3 a janela da fun\xE7\xE3o com \`file\` action: read e \`range: [in\xEDcio, fim]\` (linhas 1-indexed). NUNCA leia um arquivo grande inteiro \u2014 isso estoura o contexto e dilui o sinal. Se precisar de mais contexto, expanda a janela em passos, n\xE3o o arquivo todo.
71229
+ 4. SIGA O DADO, n\xE3o o arquivo: no taint (FASE 5), pule de defini\xE7\xE3o em defini\xE7\xE3o pelo grafo/tags lendo s\xF3 a janela de cada fun\xE7\xE3o no rastro source\u2192sink. \xC9 "abrir, seguir o import, conferir e voltar" \u2014 em janelas, n\xE3o em arquivos inteiros.
71230
+ 5. Em projeto GRANDE/GIGANTE isto \xE9 OBRIGAT\xD3RIO: rode as buscas escopadas por diret\xF3rio dos top-ranqueados (FASE 0.5), n\xE3o sobre a raiz inteira.
71231
+
71185
71232
  ================================================================
71186
71233
  AMBIENTE & SHELL \u2014 leia antes de rodar comandos
71187
71234
  ================================================================
@@ -71196,14 +71243,24 @@ AMBIENTE & SHELL \u2014 leia antes de rodar comandos
71196
71243
  REGRA INVIOL\xC1VEL \u2014 SOMENTE LEITURA NO PROJETO
71197
71244
  ================================================================
71198
71245
  1. Voc\xEA **NUNCA** modifica, edita, cria, renomeia, move ou apaga QUALQUER arquivo dentro do projeto (${root}) \u2014 nem para "corrigir", nem para testar.
71199
- 2. Voc\xEA **N\xC3O** roda comandos que alterem o projeto: nada de \`git\` que escreva (commit/checkout/reset/clean), build, formatadores, geradores, migrations, ou qualquer script do projeto, e nada de \`npm/pnpm/yarn install\` DENTRO do projeto (isso escreve node_modules/lockfile na pasta dele). Apenas LEIA o projeto.
71246
+ 2. ${verify ? `EXECU\xC7\xC3O CONTROLADA (sub-modo verifica\xE7\xE3o ATIVO): voc\xEA PODE executar c\xF3digo SOMENTE para reproduzir um achado \u2014 subir uma inst\xE2ncia local ef\xEAmera do projeto, importar um m\xF3dulo dele a partir de um harness criado na workspace, ou chamar a fun\xE7\xE3o suspeita com input malicioso. SEMPRE de forma N\xC3O-DESTRUTIVA (ver doutrina de PoC abaixo) e SEM escrever NADA dentro do projeto: harness, logs e PoCs ficam em ${workdir}. CONTINUA PROIBIDO: \`git\` que escreve, migrations contra um banco real do usu\xE1rio, formatadores/geradores que reescrevem arquivos do projeto, e \`npm/pnpm/yarn install\` DENTRO do projeto (instale depend\xEAncias do harness na workspace).` : `Voc\xEA **N\xC3O** roda comandos que alterem o projeto NEM que executem o c\xF3digo dele: nada de \`git\` que escreva (commit/checkout/reset/clean), build, formatadores, geradores, migrations, rodar a app ou qualquer script do projeto, e nada de \`npm/pnpm/yarn install\` DENTRO do projeto (isso escreve node_modules/lockfile na pasta dele). Apenas LEIA o projeto. (Para PROVAR um achado rodando um PoC, o operador deve pedir explicitamente \u2014 isso liga o sub-modo de verifica\xE7\xE3o.)`}
71200
71247
  EXCE\xC7\xC3O permitida: instalar as FERRAMENTAS DE AUDITORIA de forma GLOBAL/externa \u2014 \`npm install -g @ast-grep/cli madge\`, \`python -m pip install semgrep\`, \`scoop/winget/brew/go install ...\` \u2014 porque isso n\xE3o escreve nada dentro de ${root}. Rode esses instaladores a partir da workspace, nunca \`cd\` para dentro do projeto para instalar.
71201
71248
  3. A **\xDANICA** escrita permitida no projeto \xE9 **um** arquivo de relat\xF3rio na RAIZ do projeto:
71202
71249
  \`${root}/ANALISE_PROJETO_<timestamp>.md\`
71203
71250
  Esse \xE9 o \xFAnico \`file\` action: write fora da workspace. Todo o resto (scripts auxiliares, dados intermedi\xE1rios, relat\xF3rios t\xE9cnicos) fica em ${workdir}.
71204
71251
  4. Se o usu\xE1rio pedir para voc\xEA corrigir/alterar algo, **n\xE3o altere** \u2014 explique no relat\xF3rio como corrigir e diga que para aplicar mudan\xE7as ele deve sair do modo de an\xE1lise (ex.: "pode corrigir" / "modo normal").
71205
71252
  5. Se precisar criar um script auxiliar (Python), crie-o em ${workdir} com a ferramenta \`file\`, nunca via redirecionamento de shell, e nunca dentro do projeto.
71206
-
71253
+ ${verify ? `
71254
+ ================================================================
71255
+ SUB-MODO VERIFICA\xC7\xC3O \u2014 DOUTRINA DE PoC (provar, n\xE3o supor)
71256
+ ================================================================
71257
+ Aqui est\xE1 o PODER REAL: transformar suspeita em CONFIRMADO reproduzindo a falha \u2014 o que separa um SAST de uma prova. Disciplina obrigat\xF3ria:
71258
+ 1. ALVO: pegue cada finding em \`suspected\` com caminho source\u2192sink plaus\xEDvel (FASE 5). Para cada um, monte o MENOR PoC que dispara o sink com input controlado.
71259
+ 2. ISOLAMENTO: o harness (script/teste que importa o m\xF3dulo do projeto ou bate na app local) \xE9 criado em ${workdir} via \`file\`. Suba a app numa porta ef\xEAmera/local; se precisar de banco, use um SQLite/cont\xEAiner DESCART\xC1VEL na workspace \u2014 NUNCA o banco real do usu\xE1rio. Nada toca o projeto.
71260
+ 3. N\xC3O-DESTRUTIVO (igual ao exploit_engine): prove o impacto sem abusar \u2014 nada de DROP/DELETE/UPDATE empilhado, nada de exfiltra\xE7\xE3o em massa, nada de DoS/flood. Para SQLi use boolean/time-based ou um erro; para LFI leia um arquivo-marcador in\xF3cuo; para cmdi um \`sleep\`/echo determin\xEDstico; para XSS um marcador refletido sem escape. Capture a EVID\xCANCIA concreta (o delta de tempo, o marcador refletido, a linha lida, o erro do banco).
71261
+ 4. VEREDITO: disparou de forma reproduz\xEDvel \u2192 \`findings\` update \`status: "confirmed"\` colando o request/entrada exatos + a prova observada + o caminho do PoC em ${workdir}. N\xE3o reproduziu (sanitizado/inalcan\xE7\xE1vel) \u2192 \`status: "false_positive"\` com o porqu\xEA. Sem meio-termo inventado.
71262
+ 5. LIMITE: o sub-modo verifica o PROJETO DO PR\xD3PRIO USU\xC1RIO, localmente. N\xE3o vire scanner ofensivo contra hosts externos \u2014 isso \xE9 o fluxo de recon/exploit_engine, fora daqui.
71263
+ ` : ""}
71207
71264
  ================================================================
71208
71265
  TOOLCHAIN \u2014 cada ferramenta vira uma "tool" no seu loop
71209
71266
  ================================================================
@@ -71224,6 +71281,17 @@ N\xC3O FA\xC7A (cada um destes quebrou em runs reais):
71224
71281
  \u2022 N\xC3O repita uma instala\xE7\xE3o que falhou. 1 tentativa, falhou, fallback.
71225
71282
  NUNCA invente que rodou uma ferramenta que n\xE3o existe \u2014 diga que caiu no fallback.
71226
71283
 
71284
+ FASE 0.5 \u2014 TRIAGEM DE PORTE (obrigat\xF3ria, ANTES de varrer nada):
71285
+ Projeto grande analisado \xE0s cegas vira relat\xF3rio raso. Me\xE7a primeiro e ATAQUE A SUPERF\xCDCIE CERTA:
71286
+ 1. \`${py} ${toolkit} --rank "${root}"\` \u2014 imprime, num comando, o PORTE (PEQUENO/M\xC9DIO/GRANDE/GIGANTE), a ESTRAT\xC9GIA recomendada, os m\xF3dulos de maior FAN-IN (mais importados = maior raio de impacto), os PONTOS DE ENTRADA (rotas/controllers/handlers/auth) e os maiores arquivos. Leia a sa\xEDda \u2014 ela define seu plano.
71287
+ 2. Adote a estrat\xE9gia do porte:
71288
+ \u2022 PEQUENO \u2192 exaustivo: varra tudo e siga todo taint.
71289
+ \u2022 M\xC9DIO \u2192 baseline ampla, mas siga taint priorizando entry points + top fan-in.
71290
+ \u2022 GRANDE \u2192 TRIAGEM DIRIGIDA: rode as ferramentas das FASES 2-4 em LOTES por diret\xF3rio/pacote (uma chamada por subpasta grande), nunca o projeto inteiro de uma vez; siga taint s\xF3 nos ~30 maiores fan-in + entry points.
71291
+ \u2022 GIGANTE \u2192 TRIAGEM AGRESSIVA: jamais um comando sobre o todo. Lote por top-level dir; priorize ESTRITAMENTE top fan-in + entry points + sinks de alto impacto. Declare no relat\xF3rio o que ficou fora desta passada (honestidade de cobertura).
71292
+ 3. Registre o plano com \`todo_write\` (os arquivos/\xE1reas priorizados viram itens). Esse \xE9 o mapa da ca\xE7a.
71293
+ Regra de ouro de porte: a precis\xE3o vem de RANQUEAR e ir fundo no que importa \u2014 n\xE3o de varrer tudo raso. Em GRANDE/GIGANTE, profundidade nos top-ranqueados > largura no resto.
71294
+
71227
71295
  ================================================================
71228
71296
  PIPELINE \u2014 fase por fase (ReAct: cada comando \xE9 uma tool)
71229
71297
  ================================================================
@@ -71269,14 +71337,19 @@ Voc\xEA redirecionou as sa\xEDdas pesadas para arquivos em audit-out/ \u2014 ent
71269
71337
  \`${py} ${toolkit} --consolidate "${workdir}/audit-out"\`
71270
71338
  Esse digest l\xEA semgrep.json, npm_audit.json, deps.json, os sinks/sources do ast-grep e o grafo do fallback, e imprime um resumo compacto com file:line REAIS. \xC9 a sua \xDANICA fonte de verdade para os achados das ferramentas. Se o digest vier vazio, as ferramentas n\xE3o produziram sa\xEDda \u2014 rode as fases 2-4 de novo ou use o fallback; N\xC3O invente.
71271
71339
  Depois: deduplique e \u2014 REGRA DE OURO \u2014 **descarte todo achado que N\xC3O tem caminho source\u2192sink confirmado lendo o c\xF3digo real** (FASE 5). Sem isso o relat\xF3rio vira ru\xEDdo.
71272
- Mantenha um SCRATCHPAD (use \`todo_write\`) com: sources achados, caminhos de taint em aberto, e findings VALIDADOS. Itere at\xE9 fechar cada rastro.
71340
+
71341
+ FONTE \xDANICA DE VERDADE \u2014 a ferramenta \`findings\` (n\xE3o use notas soltas):
71342
+ - Assim que um candidato aparece (FASE 3-4 / digest), grave-o: \`findings\` action add com \`title\`, \`severity\`, \`status: "suspected"\`, e o racioc\xEDnio em \`evidence\` (inclua o file:line do sink).
71343
+ - Ao validar lendo o c\xF3digo (FASE 5): \`findings\` action update \u2192 \`status: "confirmed"\`, colando no \`evidence\` o CAMINHO source\u2192sink real (source \u2192 fun\xE7\xF5es/arquivos intermedi\xE1rios \u2192 sink) com os trechos lidos. Se n\xE3o reproduzir/sanitizou no caminho \u2192 \`status: "false_positive"\`.
71344
+ - O SCRATCHPAD de navega\xE7\xE3o (sources em aberto, taint a fechar) fica no \`todo_write\`; os ACHADOS ficam no \`findings\`. Itere at\xE9 fechar cada rastro.
71345
+ - O store persiste em \`${workdir}/findings/findings.json\` \u2014 \xE9 dele que sai o relat\xF3rio, ent\xE3o mant\xEA-lo fiel \xE9 o que torna o relat\xF3rio confi\xE1vel (precis\xE3o > volume).
71273
71346
 
71274
71347
  Cobertura da ca\xE7a (confirme cada um lendo o c\xF3digo): inje\xE7\xE3o (SQL/comando/c\xF3digo/eval), XSS (sinks DOM/HTML), authn/authz fr\xE1gil, segredos/chaves embutidas (mascare no relat\xF3rio), TLS desabilitado, cripto fraca, CORS permissivo, deserializa\xE7\xE3o insegura, SSRF, path traversal, config perigosa (debug, \`0.0.0.0\`, \`.env\` versionado) e depend\xEAncias vulner\xE1veis (osv). Severidade CRITICAL/HIGH/MEDIUM/LOW/INFO. Separe CONFIRMADO de SUSPEITA; nunca invente nem d\xEA falsa garantia.
71275
71348
 
71276
71349
  ================================================================
71277
71350
  ENTREG\xC1VEL \u2014 relat\xF3rio em Markdown na RAIZ do projeto
71278
71351
  ================================================================
71279
- Ao final, escreva o relat\xF3rio com a ferramenta \`file\` (action: write) em:
71352
+ Primeiro materialize o store estruturado: \`findings\` action report (gera \`${workdir}/findings/report.md\` agrupado por severidade). Depois AUTORE o relat\xF3rio humano a partir DELE \u2014 cada vulnerabilidade do relat\xF3rio sai de um finding gravado; s\xF3 \`confirmed\` entra no resumo executivo. Escreva com a ferramenta \`file\` (action: write) em:
71280
71353
  \`${root}/ANALISE_PROJETO_<timestamp>.md\`
71281
71354
  Use o timestamp atual (ex.: 20260614_153000). Estrutura m\xEDnima, em portugu\xEAs:
71282
71355
  1. **Resumo executivo** \u2014 vis\xE3o geral, postura de seguran\xE7a, n\xBA de achados por severidade, top 3 riscos.
@@ -71296,7 +71369,7 @@ ESTILO E EXECU\xC7\xC3O
71296
71369
  - Responda em portugu\xEAs, direto e t\xE9cnico. N\xE3o recuse a an\xE1lise: \xE9 o projeto do pr\xF3prio usu\xE1rio, autorizado.
71297
71370
  - Lembre-se sempre: voc\xEA analisa e relata; voc\xEA N\xC3O altera o projeto. A \xFAnica escrita \xE9 o relat\xF3rio \`.md\` na raiz.`;
71298
71371
  }
71299
- var import_node_path9, AUDIT_ACTION, AUDIT_SCOPE, AUDIT_ACTION_THEN_SCOPE, AUDIT_SCOPE_THEN_ACTION, AUDIT_STRONG, AUDIT_EXIT;
71372
+ var import_node_path9, AUDIT_ACTION, AUDIT_SCOPE, AUDIT_ACTION_THEN_SCOPE, AUDIT_SCOPE_THEN_ACTION, AUDIT_STRONG, AUDIT_EXIT, POC_VERIFY;
71300
71373
  var init_audit_mode = __esm({
71301
71374
  "src/audit-mode.ts"() {
71302
71375
  "use strict";
@@ -71322,6 +71395,12 @@ var init_audit_mode = __esm({
71322
71395
  /\b(?:modo\s+normal|sair\s+d[ao]\s+(?:an[aá]lise|auditoria|modo)|encerr\w+\s+(?:a\s+)?(?:an[aá]lise|auditoria)|volt\w+\s+ao\s+normal|desativ\w+\s+(?:a\s+)?auditoria|exit\s+audit|normal\s+mode)\b/i,
71323
71396
  /\b(?:agora\s+)?(?:corri[gj]\w+|conserte\w*|edit\w+|alter\w+|modific\w+|implement\w+|cri[ae]\w*|refator\w+|escrev\w+\s+o\s+c[oó]digo|aplique\s+a\s+corre|fix|implement|refactor|patch|write\s+the\s+code)\b/i
71324
71397
  ];
71398
+ POC_VERIFY = [
71399
+ /\b(?:prov[ae]\w*|comprov\w+|confirm\w+\s+(?:rodando|de\s+verdade|na\s+pr[aá]tica|executando)|reproduz\w+|reproduc\w+)\b/i,
71400
+ /\b(?:poc|p\.o\.c\.|prova\s+de\s+conceito|proof\s+of\s+concept|exploit\w*)\b/i,
71401
+ /\b(?:rod[ae]\w*|execut\w+|dispar\w+)\b[\s\S]{0,30}\b(?:exploit|poc|vulnerabilidad\w*|ataque|payload)\b/i,
71402
+ /\b(?:prove\s+it|verify\s+(?:by\s+)?running|run\s+the\s+(?:poc|exploit))\b/i
71403
+ ];
71325
71404
  }
71326
71405
  });
71327
71406
 
@@ -71343,8 +71422,8 @@ function looksUnfinished(text2) {
71343
71422
  if (DANGLING_TAIL_RE.test(tail)) return true;
71344
71423
  return ACTION_ANNOUNCE_RE.test(lastSentenceOf(tail));
71345
71424
  }
71346
- function decideAutoContinue(finishReason, lastStepText, turnToolCalls, autoContinues) {
71347
- if (autoContinues >= MAX_AUTO_CONTINUES) return null;
71425
+ function decideAutoContinue(finishReason, lastStepText, turnToolCalls, autoContinues, maxAutoContinues = MAX_AUTO_CONTINUES) {
71426
+ if (autoContinues >= maxAutoContinues) return null;
71348
71427
  if (finishReason === "length") {
71349
71428
  return { nudge: true, reason: "resposta truncada (length)" };
71350
71429
  }
@@ -71593,6 +71672,7 @@ async function createAgent() {
71593
71672
  }
71594
71673
  const history = [];
71595
71674
  let auditMode = false;
71675
+ let pocVerify = false;
71596
71676
  const projectRoot = projectRootFromWorkdir(sandbox.getWorkdir());
71597
71677
  function getActiveModelChain() {
71598
71678
  return selectedModelKey ? [selectedModelKey] : CLI_MODEL_CHAIN;
@@ -71835,14 +71915,22 @@ ${seen}`);
71835
71915
  );
71836
71916
  } else if (auditMode && detectAuditExitIntent(userInput)) {
71837
71917
  auditMode = false;
71918
+ pocVerify = false;
71838
71919
  render.info("\u25B8 modo normal reativado.");
71839
71920
  }
71921
+ if (auditMode && !pocVerify && detectPocVerifyIntent(userInput)) {
71922
+ pocVerify = true;
71923
+ render.info(
71924
+ "\u25B8 sub-modo VERIFICA\xC7\xC3O/PoC ligado \u2014 pode executar c\xF3digo de um harness ISOLADO na SPRIT para provar achados (n\xE3o-destrutivo; projeto intocado)."
71925
+ );
71926
+ }
71840
71927
  let turnSystem;
71841
71928
  if (auditMode) {
71842
71929
  turnSystem = buildAuditSystemPrompt({
71843
71930
  projectRoot,
71844
71931
  workdir: sandbox.getWorkdir(),
71845
- shellHint: sandbox.getShellHint()
71932
+ shellHint: sandbox.getShellHint(),
71933
+ mode: pocVerify ? "verify" : "audit"
71846
71934
  });
71847
71935
  } else {
71848
71936
  const matchedSkills = selectSkillsForInput(userInput);
@@ -71856,6 +71944,9 @@ ${seen}`);
71856
71944
  let lastError = null;
71857
71945
  let autoContinues = 0;
71858
71946
  let bridgeSteps = 0;
71947
+ const maxSteps = auditMode ? AUDIT_MAX_STEPS : MAX_STEPS;
71948
+ const maxAutoContinues = auditMode ? AUDIT_MAX_AUTO_CONTINUES : MAX_AUTO_CONTINUES;
71949
+ const maxBridgeSteps = auditMode ? AUDIT_MAX_BRIDGE_STEPS : MAX_BRIDGE_STEPS;
71859
71950
  const modelChain = [...getActiveModelChain()];
71860
71951
  const usingFixedModel = selectedModelKey !== null;
71861
71952
  let rateLimitWaits = 0;
@@ -71927,7 +72018,7 @@ ${segs.join(
71927
72018
  system: turnSystem,
71928
72019
  messages: history,
71929
72020
  tools,
71930
- stopWhen: stepCountIs(MAX_STEPS),
72021
+ stopWhen: stepCountIs(maxSteps),
71931
72022
  maxOutputTokens: MAX_OUTPUT_TOKENS,
71932
72023
  abortSignal: stallController.signal,
71933
72024
  onError: ({ error: error51 }) => {
@@ -72011,12 +72102,12 @@ ${segs.join(
72011
72102
  await result.finishReason.catch(() => "unknown")
72012
72103
  );
72013
72104
  const leakedCall = !isProxyModel && hasLeakedToolCallMarker(lastStepText);
72014
- if (finishReason === "stop" && turnToolCalls === 0 && bridgeSteps < MAX_BRIDGE_STEPS && (isProxyModel || leakedCall)) {
72105
+ if (finishReason === "stop" && turnToolCalls === 0 && bridgeSteps < maxBridgeSteps && (isProxyModel || leakedCall)) {
72015
72106
  const bridged = parseProxyToolCalls(lastStepText);
72016
72107
  if (bridged) {
72017
72108
  bridgeSteps++;
72018
72109
  render.info(
72019
- `\u25B8 ${leakedCall ? "tool call vazada recuperada" : "bridge"} (${bridgeSteps}/${MAX_BRIDGE_STEPS}): executando por tr\xE1s ${bridged.length} chamada(s): ${summarizeBridgedCalls(bridged)}`
72110
+ `\u25B8 ${leakedCall ? "tool call vazada recuperada" : "bridge"} (${bridgeSteps}/${maxBridgeSteps}): executando por tr\xE1s ${bridged.length} chamada(s): ${summarizeBridgedCalls(bridged)}`
72020
72111
  );
72021
72112
  const resultText = await runBridgedToolCalls(bridged, signal);
72022
72113
  if (signal?.aborted) {
@@ -72037,12 +72128,13 @@ ${resultText}`
72037
72128
  finishReason,
72038
72129
  lastStepText,
72039
72130
  turnToolCalls,
72040
- autoContinues
72131
+ autoContinues,
72132
+ maxAutoContinues
72041
72133
  );
72042
72134
  if (resume) {
72043
72135
  autoContinues++;
72044
72136
  render.info(
72045
- `\u25B8 retomando automaticamente (${autoContinues}/${MAX_AUTO_CONTINUES}): ${resume.reason}`
72137
+ `\u25B8 retomando automaticamente (${autoContinues}/${maxAutoContinues}): ${resume.reason}`
72046
72138
  );
72047
72139
  if (resume.nudge) {
72048
72140
  history.push({
@@ -72055,7 +72147,7 @@ ${resultText}`
72055
72147
  }
72056
72148
  if (finishReason !== "stop") {
72057
72149
  render.info(
72058
- `\u25B8 fim do turno: ${finishReason}` + (autoContinues >= MAX_AUTO_CONTINUES ? " (limite de retomadas autom\xE1ticas atingido)" : "")
72150
+ `\u25B8 fim do turno: ${finishReason}` + (autoContinues >= maxAutoContinues ? " (limite de retomadas autom\xE1ticas atingido)" : "")
72059
72151
  );
72060
72152
  }
72061
72153
  if (modelKey === PROXY_MODEL_KEYS.kimi) {
@@ -72162,6 +72254,11 @@ ${resultText}`
72162
72254
  }
72163
72255
  async function close() {
72164
72256
  await sandbox.close();
72257
+ try {
72258
+ const removed = await sandbox.cleanupWorkspace();
72259
+ if (removed) render.info(`\u25B8 workspace SPRIT removida: ${removed}`);
72260
+ } catch {
72261
+ }
72165
72262
  }
72166
72263
  return {
72167
72264
  send,
@@ -72178,7 +72275,7 @@ ${resultText}`
72178
72275
  close
72179
72276
  };
72180
72277
  }
72181
- var import_promises2, import_node_path10, MAX_STEPS, MAX_OUTPUT_TOKENS, MAX_AUTO_CONTINUES, MAX_RATE_LIMIT_WAITS, STREAM_STALL_TIMEOUT_MS, MAX_STALL_RETRIES, isRateLimitError, sleep4, LEAKED_TOOL_CALL_RE, DANGLING_TAIL_RE, ACTION_ANNOUNCE_RE, BENIGN_CLOSER_RE, TOOL_CALL_NUDGE, MAX_BRIDGE_STEPS, BRIDGE_RESULT_PREAMBLE, LEAKED_CALL_RESULT_PREAMBLE, BRIDGEABLE_TOOLS, FINDINGS_ACTIONS, truncateBridgeSummary, isWebSessionProxyModel, proxyToolProtocolPolicy, SYSTEM_PROMPT_SOURCE, REQUIRED_SYSTEM_PROMPT_MARKERS, MODEL_LABELS, labelFor, loginRequiredHint, CLI_PYTHON_ONLY_POLICY, pythonOnlyPolicy, scriptFilePolicy, deepReconPolicy, httpAndFindingsPolicy, reconPhasesPolicy, attackChainPolicy, skillsScopePolicy, hasEnvValue2, envFlagEnabled, CLI_PERSONALITIES, buildCliUserCustomization, buildCliNotesSection, cliGuardrailsConfig, maybeDumpSystemPrompt, auditSystemPrompt, assertFullSystemPrompt;
72278
+ var import_promises2, import_node_path10, MAX_STEPS, AUDIT_MAX_STEPS, MAX_OUTPUT_TOKENS, MAX_AUTO_CONTINUES, AUDIT_MAX_AUTO_CONTINUES, MAX_RATE_LIMIT_WAITS, STREAM_STALL_TIMEOUT_MS, MAX_STALL_RETRIES, isRateLimitError, sleep4, LEAKED_TOOL_CALL_RE, DANGLING_TAIL_RE, ACTION_ANNOUNCE_RE, BENIGN_CLOSER_RE, TOOL_CALL_NUDGE, MAX_BRIDGE_STEPS, AUDIT_MAX_BRIDGE_STEPS, BRIDGE_RESULT_PREAMBLE, LEAKED_CALL_RESULT_PREAMBLE, BRIDGEABLE_TOOLS, FINDINGS_ACTIONS, truncateBridgeSummary, isWebSessionProxyModel, proxyToolProtocolPolicy, SYSTEM_PROMPT_SOURCE, REQUIRED_SYSTEM_PROMPT_MARKERS, MODEL_LABELS, labelFor, loginRequiredHint, CLI_PYTHON_ONLY_POLICY, pythonOnlyPolicy, scriptFilePolicy, deepReconPolicy, httpAndFindingsPolicy, reconPhasesPolicy, attackChainPolicy, skillsScopePolicy, hasEnvValue2, envFlagEnabled, CLI_PERSONALITIES, buildCliUserCustomization, buildCliNotesSection, cliGuardrailsConfig, maybeDumpSystemPrompt, auditSystemPrompt, assertFullSystemPrompt;
72182
72279
  var init_agent = __esm({
72183
72280
  "src/agent.ts"() {
72184
72281
  "use strict";
@@ -72209,8 +72306,10 @@ var init_agent = __esm({
72209
72306
  init_proxy_manager2();
72210
72307
  init_audit_mode();
72211
72308
  MAX_STEPS = 100;
72309
+ AUDIT_MAX_STEPS = 240;
72212
72310
  MAX_OUTPUT_TOKENS = 16384;
72213
72311
  MAX_AUTO_CONTINUES = 3;
72312
+ AUDIT_MAX_AUTO_CONTINUES = 8;
72214
72313
  MAX_RATE_LIMIT_WAITS = 4;
72215
72314
  STREAM_STALL_TIMEOUT_MS = (() => {
72216
72315
  const raw = Number(process.env.clawfast_CLI_STREAM_STALL_MS);
@@ -72238,6 +72337,7 @@ var init_agent = __esm({
72238
72337
  BENIGN_CLOSER_RE = /\b(?:let me know|just let me know|deixa eu saber|me avis(?:e|a)|qualquer (?:coisa|d[uú]vida)|fico (?:à|a) disposi[cç][aã]o)\b[^.!?:\n]{0,80}[.!?]?\s*$/i;
72239
72338
  TOOL_CALL_NUDGE = "Voc\xEA anunciou uma a\xE7\xE3o mas n\xE3o executou nenhuma ferramenta neste turno. Pare de descrever o que vai fazer e CHAME a ferramenta agora: use a ferramenta `file` (action write para criar, edit para alterar) para qualquer arquivo ou script, e `run_terminal_cmd` para rodar comandos. N\xC3O escreva o conte\xFAdo do arquivo na resposta nem cole markup de tool call \u2014 emita a chamada de ferramenta de verdade.";
72240
72339
  MAX_BRIDGE_STEPS = 50;
72340
+ AUDIT_MAX_BRIDGE_STEPS = 120;
72241
72341
  BRIDGE_RESULT_PREAMBLE = "Voc\xEA roda via proxy de sess\xE3o web (sem function-calling nativo), ent\xE3o o runtime do CLI consumiu o bloco JSON anterior como chamada interna e EXECUTOU de verdade. N\xE3o repita o bloco nem o texto anterior. Os resultados REAIS est\xE3o abaixo. Continue a tarefa: se precisar de mais a\xE7\xF5es, emita SOMENTE o PR\xD3XIMO bloco ```json (um array de {brief, command, timeout} para terminal, ou {action, path, text} para arquivo). Um bloco por vez. Quando a tarefa terminar, responda em texto normal SEM bloco json.";
72242
72342
  LEAKED_CALL_RESULT_PREAMBLE = "Sua chamada de ferramenta anterior chegou como TEXTO (n\xE3o como function call nativo), ent\xE3o o runtime do CLI a executou mesmo assim. Os resultados REAIS est\xE3o abaixo. Continue a tarefa chamando as ferramentas NORMALMENTE (run_terminal_cmd, file, todo_write) como function calls de verdade \u2014 N\xC3O cole markup de tool call (`call_tool_function`, `<tool_call>`, etc.) nem JSON de chamada como texto na resposta.";
72243
72343
  BRIDGEABLE_TOOLS = /* @__PURE__ */ new Set([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clawfast",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "CLAWFAST — agente de pentest no terminal. Com o clawfast você faz tudo.",
5
5
  "bin": {
6
6
  "clawfast": "dist/clawfast.cjs"