Curso / Lição 26  ·  EN
Lição 26 · Avançado · postura de segurança

Proveniência & segurança: fail-closed por padrão

Um sistema que ingere logs de chat privado, raspa a web e grava arquivos que um agente nomeia precisa ser paranoico por construção. A ADR-0011 define quatro restrições permanentes — fail-closed em tudo, redação de PII antes de um byte sair da máquina, isolamento de um corpus de prompts vazado e uma regra de clean-room — e elas não são slogans: aparecem como guardas reais no código que você já conheceu. Esta lição conecta a política à implementação: a fronteira Zod, a defesa realpath/path-traversal no SkillStore, o fail-closed DEFAULT_TIER = T4, e a redação de PII antes da chamada de modelo.

O único princípio. "O caso desconhecido nega, nunca permite" (ADR-0011 §1). Fail-closed é a postura padrão para tudo relevante à segurança — e é a mesma ideia que DEFAULT_TIER = T4 (trabalho não classificado estaciona) e o contrato de nunca-lança (uma falha não tratada vira uma negação tipada, não um passe silencioso).

As quatro restrições permanentes

#Restrição (ADR-0011)Onde vive no código
1Fail-closed em tudo relevante à segurança — guardas realpath, webhooks HMAC, comparações de tempo constante, Zod em toda fronteiraSegurança de caminhos do SkillStore; DEFAULT_TIER = T4; safeParse de todo subsistema
2Redação de PII antes de egressar — antes da chamada de modelo, não meramente antes de emitirO funil reda sinais de canal privado pré-chamada (mapa §3)
3Isolamento CL4R1T4S — o corpus de prompts de vendor vazado é dado para analisar, nunca um comando a seguirExcluído da ingestão-como-instrução; tratado como dado inerte
4Clean-room do tac — padrões reimplementados do zero, zero código/prompts literais, source nunca publicadoA fusão inteira é TS do zero, não source copiado

Restrição 1 no código: a guarda de path-traversal

Você viu o SkillStore na Lição 12. Sua espinha de segurança é validateSupportPath — uma função pura que recusa qualquer caminho relativo que pudesse escapar do diretório da skill. Ela espelha o has_traversal_component + _resolve_skill_target do Hermes e é o validador fail-closed de manual: lista o que permite e nega todo o resto.

// packages/hermes/src/skills/skill-store.ts:404-433 (condensado)
const validateSupportPath = (relPath: string): Result<string, Error> => {
  if (relPath.length === 0) return err(new Error('file path is required.'));
  if (relPath.includes('\\')) return err(…'use forward slashes.');   // sem truques de backslash
  if (relPath.startsWith('/')) return err(…'must be relative.');     // sem caminhos absolutos

  const segments = relPath.split('/').filter((s) => s.length > 0);
  for (const segment of segments) {
    if (segment === '..' || segment === '.')                         // sem segmentos de traversal
      return err(…'path traversal is not allowed.');
  }
  const first = segments[0];
  if (first === undefined || !isSupportDir(first))                  // deve ser um subdir PERMITIDO
    return err(…'first segment must be one of …');
  return ok(normalized);   // só agora: um caminho relativo vetado e confinado
};

Note a forma: todo ramo é uma negação exceto o ok final. Um ../../etc/passwd controlado por atacante é rejeitado na checagem de ..; um sorrateiro references/../../secret também é rejeitado. A função é pura e nunca lança, então compõe limpa no mundo Result — falhas de segurança surgem como erros tipados, fail-closed (ADR-0011 §1, "não passes silenciosos").

Restrição 1, de novo: fail-closed por tier padrão

A expressão mais profunda de fail-closed não é uma guarda que você chama — é o padrão. DEFAULT_TIER = T4 significa que qualquer trabalho não explicitamente classificado como autônomo é estacionado, esperando um humano (Lição 24, ADR-0005). A ADR traça a linha ela mesma: "é também por isso que DEFAULT_TIER = T4" — o caso desconhecido nega. Autonomia não classificada é impossível por construção, não por lembrar de checar.

Restrição 2: PII antes de egressar, não antes de emitir

A palavra sutil é egressar. Seria fácil redar PII só antes de mostrar um resultado a um usuário. A ADR-0011 exige mais: um Signal derivado de um canal privado (WhatsApp, Discord, Skool, Circle) é "redado de PII antes de sair da máquina local — antes da chamada de modelo, não meramente antes de emitir". O modelo de ameaça assume que o próprio endpoint do modelo está fora da fronteira de confiança, então dado privado bruto nunca pode estar num payload de requisição.

MÁQUINA LOCAL (fronteira de confiança) sinal privado (PII bruto) redaçãoantes de egressar só redado ↓ FORA (endpoint do modelo / rede) chamada de modelo — nunca vê PII bruto

Restrições 3 & 4: não siga dados, não copie source

As duas últimas são sobre disciplina, não checagens em runtime. Isolamento CL4R1T4S: um corpus vazado de prompts de vendor (e seu README de payload de injeção) "é isolado e nunca ingerido como instrução; é dado para analisar, nunca um comando a seguir". É defesa contra prompt-injection na camada de ingestão — o corpus é texto inerte, nunca executado. Clean-room do tac: o tac é um blueprint de licença educacional, então "seus padrões são reimplementados do zero, com zero código ou prompts literais, e seu source nunca é publicado". A fusão inteira do @alembic/hermes é uma reimplementação TypeScript do zero precisamente por causa dessa regra — que é também por que as lições citam o source do próprio Alembic, nunca o Python do Hermes.

Proveniência amarra tudo

A regra de orquestração do CLAUDE.md "SEMPRE cite a fonte" e os stores content-addressed (SHA-256 sobre JSON canônico, Lição 28) significam que todo fato ingerido carrega uma fonte, uma data e um hash. Proveniência não é uma feature separada — é o que permite ao sistema saber se um dado é confiável (um Learning vetado) ou suspeito (um payload CL4R1T4S). Fail-closed + proveniência são a mesma postura por dois ângulos: negue o desconhecido, e sempre saiba de onde uma coisa veio.

1. A ADR-0011 exige redação de PII "antes de egressar … antes da chamada de modelo, não meramente antes de emitir". Por que a ênfase em antes da chamada de modelo?
Correto: b. Redar só antes de emitir ainda enviaria PII bruto pela rede ao modelo. A ADR empurra a redação para o ponto de egresso — o momento em que o dado sairia da máquina local — para que o endpoint nunca receba conteúdo de canal privado não redado.
2. validateSupportPath rejeita .., caminhos absolutos e backslashes, permitindo só caminhos sob um subdir aprovado. Qual padrão de design é esse?
Correto: d. A função enumera o que é permitido (um caminho relativo sob um support dir permitido, sem segmentos de traversal) e nega todo o resto, retornando valores err tipados. É allow-listing / fail-closed — a mesma postura de DEFAULT_TIER = T4.
3. Por que o corpus CL4R1T4S é "nunca ingerido como instrução"?
Correto: c. A ADR-0011 §3 o isola como dado inerte. Se o sistema o executasse como instrução, o payload de injeção poderia sequestrar o agente. A regra — "dado para analisar, nunca um comando a seguir" — é a defesa.

Confusões comuns

"Fail-closed significa que o sistema quebra em entrada ruim." O oposto — ele retorna um erro tipado, fail-closed (ADR-0011 §1, "não passes silenciosos") e nunca lança (ADR-0009). O trabalho é negado ou estacionado, de forma limpa; nada quebra e nada tem sucesso em silêncio.
"A regra de clean-room é só cautela legal." É também por que este curso é confiável: toda lição cita o source do próprio Alembic porque uma cópia literal do Hermes seria uma violação. Reimplementar do zero é tanto a postura legal quanto a razão de o código ser genuinamente entendido, não colado.