Skip to content

fix: auditoria problem-finder — 22 correções de segurança e bugs - #485

Merged
patrickbrandao merged 20 commits into
mainfrom
ship/v0.31.1-audit
Nov 7, 2026
Merged

patrickbrandao merged 20 commits into
mainfrom
ship/v0.31.1-audit

Conversation

@patrickbrandao

Copy link
Copy Markdown
Owner

Shipped by scripts/ship.sh as v0.31.1.

patrickbrandao and others added 20 commits November 7, 2026 14:16
…an organization, keys honour must_change_password (001-004)

001 — Problema: GET /api/v1/agents/:name/key devolvia a chave thk_ completa a viewers.
Solução: @MinRole('member', { includeReads: true }) na rota.
002 — Problema: o criador removido voltava como owner via ensureForUser.
Solução: removeMember zera owner_user_id; ensureForUser só junta uma organização sem outros membros.
003 — Decisão: chave de gestão representa a conta do criador (documentado na ADR 0006 e no guia).
004 — Problema: reset de senha não alcançava chaves de gestão.
Solução: nenhuma chave autentica com must_change_password; o reset apaga os tokens thk_pat_.
Especificações atualizadas: docs/adr/0006-organizacoes-e-papeis.md, docs/dev/guia.md
Resolve ./tasks/001-VIEWER-LE-CHAVE-COMPLETA-DO-HARNESS.md, 002-EX-OWNER-REMOVIDO-VOLTA-COMO-OWNER.md, 003-CHAVE-ADMIN-SEM-ESCOPO-DE-ORGANIZACAO.md, 004-RESET-DE-SENHA-NAO-REVOGA-CHAVES.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problema: desativar a organização não cortava o proxy das chaves thk_ dos seus harnesses.
Solução: AgentKeyAuthGuard exige tenant ativo na consulta e na rechecagem do cache.
Especificações atualizadas: docs/dev/guia.md
Resolve ./tasks/005-ORGANIZACAO-DESATIVADA-SEGUE-NO-PROXY.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ores

Problema: e-mail com maiúsculas no /setup deixava a conta sem verificação e sem admin da instância.
Solução: o e-mail é normalizado (trim + minúsculas) e as consultas usam lower(email).
Especificações atualizadas: nenhuma
Resolve ./tasks/006-SETUP-EMAIL-MAIUSCULO-SEM-ADMIN.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ild; Codex and DeepSeek Harness read THINKER_AGENT_KEY

007 — Problema: thinker run repassava THINKER_ENCRYPTION_KEY (root) ao processo filho.
Solução: THINKER_ENCRYPTION_KEY, _PREVIOUS e THINKER_ROOT_USER entram nas variáveis reservadas.
008 — Problema: os snippets usavam THINKER_API_KEY, o nome da credencial de gestão da CLI.
Solução: env_key/apiKeyEnv e o export passam a THINKER_AGENT_KEY (catálogo da CLI regenerado).
Especificações atualizadas: packages/cli/README.md, docs/usuario/conectar-harnesses.md
Resolve ./tasks/007-THINKER-RUN-VAZA-CHAVE-MESTRA.md, 008-SNIPPET-CODEX-USA-THINKER-API-KEY.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ile, honour the rate limits and abort with the client

Problema: embeddings, transcrições e count_tokens ignoravam o perfil de saída, os limites e o abort.
Solução: egressScope vem com a credencial; runWithOutboundScope, AbortSignal (close/300 s) e M201-M203 nas rotas pagas.
Especificações atualizadas: docs/dev/guia.md
Parcial: o registro de custo/tokens dessas rotas continua em ./tasks/009-PASSTHROUGH-FORA-DO-PIPELINE-DO-PROXY.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… refuses redirects

Problema: a descoberta de modelos com base URL própria seguia redirects e resolvia o DNS de novo (SSRF até metadata).
Solução: com accountBaseUrl, requestModelsJson usa ssrfSafeFetch com redirect: 'error', como o proxy.
Especificações atualizadas: nenhuma (o invariante já está na ADR 0006 item 7)
Resolve ./tasks/010-DESCOBERTA-DE-MODELOS-SEGUE-REDIRECT-SSRF.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ady reported, and its cost

Problema: stream cancelado era gravado com 0 tokens e custo zero.
Solução: pipeStream anexa a usage parcial ao erro (partialUsageOf); recordCancelledRequest grava tokens e custo.
Especificações atualizadas: nenhuma (comentários no código)
Resolve ./tasks/011-STREAM-CANCELADO-GRAVADO-SEM-CUSTO.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Problema: numa regra 'both', o alerta suave do cron suprimia o aviso de bloqueio (mesma chave rule_id+period_start).
Solução: notification_logs.alert_type (soft|hard) entra na deduplicação e no índice único (migração 1805800000000).
Especificações atualizadas: nenhuma (migração e entidade documentadas)
Resolve ./tasks/012-REGRA-BOTH-ALERTA-SUPRIME-AVISO-DE-BLOQUEIO.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nnounced reset releases the block

Problema: o período 'hour' começava na hora anterior (60-120 min) e o reset anunciado não liberava o bloqueio.
Solução: computePeriodBoundaries('hour') começa na hora cheia corrente, como day/week/month.
Especificações atualizadas: nenhuma (comentário no código; specs ajustados)
Resolve ./tasks/013-PERIODO-HORA-COBRE-DUAS-HORAS.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… UTC

Problema: os gráficos tratavam os buckets UTC como hora local; fora de UTC as últimas horas sumiam do gráfico de 24h.
Solução: fillDailyGaps chaveia por UTC e parseTimestamps lê o bucket sem offset como UTC.
Especificações atualizadas: comentário de sqlHourBucket (postgres-sql.ts)
Resolve ./tasks/014-GRAFICOS-TRATAM-UTC-COMO-HORA-LOCAL.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…; ship.sh checks Docker before pushing anything

015 — Problema: os templates de deploy ficaram presos em tmsoftbrasil/thinker:0.1.
Solução: scripts/pin-deploy-image.sh reescreve a tag X.Y (também a forma URL-encoded do Koyeb); ship.sh o chama a cada release; templates em 0.31.
019 — Problema: ship.sh publicava main e a tag antes de validar o build da imagem.
Solução: pré-checagem de docker, jq, gh e do builder buildx antes de qualquer push; recuperação documentada.
Especificações atualizadas: docs/dev/guia.md
Resolve ./tasks/015-TEMPLATES-DE-DEPLOY-PRESOS-NA-0-1.md, 019-SHIP-SH-PUBLICA-TAG-ANTES-DA-IMAGEM.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…and targets PORT inside the server environment

016 — Problema: o token root (sem audiência, reutilizável por 5 min) era enviado a qualquer origem.
Solução: só para origem de loopback; THINKER_ROOT_REMOTE=1 libera outro host.
017 — Problema: no contêiner, a CLI ignorava PORT e mirava localhost:2099.
Solução: com THINKER_ENCRYPTION_KEY e PORT no ambiente, o alvo padrão é http://localhost:$PORT.
Especificações atualizadas: docs/dev/guia.md, packages/cli/README.md
Resolve ./tasks/016-TOKEN-ROOT-SEM-ORIGEM-E-SEM-ANTI-REPLAY.md, 017-CLI-NO-CONTAINER-IGNORA-PORT.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…namesake starts clean

Problema: regras de limite do harness apagado continuavam ativas e eram herdadas por outro com o mesmo nome.
Solução: deleteAgent apaga as notification_rules do agente na mesma transação; a migração 1805800000001 remove as órfãs sem homônimo vivo.
Especificações atualizadas: nenhuma (comentários e migração)
Resolve ./tasks/020-HARNESS-RECRIADO-HERDA-REGRAS-DE-LIMITE.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d-only for the app user

Problema: node_modules, dist e o launcher pertenciam ao usuário nonroot que executa o app.
Solução: COPY sem --chown para o código; só /data/request-recordings pertence a nonroot. Verificado numa imagem local: escrita em /app negada (EACCES), gravação em /data ok.
Especificações atualizadas: comentário no docker/Dockerfile
Resolve ./tasks/018-CODIGO-DA-IMAGEM-GRAVAVEL-PELO-PROCESSO.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…row's millisecond

Problema: o cursor 'before' (ms) contra created_at (µs) e sem desempate perdia entregas na fronteira da página.
Solução: comparação em ms e inclusiva (date_trunc('milliseconds') <= before), ORDER BY created_at, id; o cliente já deduplica por id.
Especificações atualizadas: nenhuma (comentário no código)
Resolve ./tasks/021-PAGINACAO-DE-ENTREGAS-DE-TRIGGER-PERDE-LINHAS.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… showing the raw 401

Problema: o 401 real ('X-API-Key header required') não casava com o detector de sessão expirada do dashboard.
Solução: ApiKeyGuard devolve code: 'unauthenticated' (mensagem mantida); fetchJson e fetchMutate reconhecem o código e redirecionam.
Especificações atualizadas: nenhuma (comentários no código)
Resolve ./tasks/022-DETECTOR-DE-SESSAO-EXPIRADA-NAO-CASA-COM-401.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…before the account exists

Problema: e-mail inválido virava 500, e papel/organização inválidos deixavam uma conta órfã com a senha temporária perdida.
Solução: createAccount recusa e-mail inválido com 400; o controller roda assertCanAddMember antes de criar a conta.
Especificações atualizadas: nenhuma (teste antes marcado como BUG reativado)
Resolve ./tasks/023-ADMIN-USERS-500-E-CONTA-ORFA.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… ADR 0003 as amended by ADR 0009

Problema: o comentário ainda citava raw.githubusercontent.com e omitia models.opencode.ai.
Solução: comentário alinhado às ADRs 0003 e 0009.
Especificações atualizadas: comentário em common/utils/catalog-sync.ts
Resolve ./tasks/024-COMENTARIO-CATALOG-SYNC-DESATUALIZADO.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e name the Codex snippet now uses

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@patrickbrandao
patrickbrandao merged commit d7606e6 into main Nov 7, 2026
@patrickbrandao
patrickbrandao deleted the ship/v0.31.1-audit branch November 7, 2026 17:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant