DeepSeek Harness: Programação Defensiva e Revisão de…

Última atualização: 2026-08-31

O poder de um framework de Agent significa que erros também têm maior impacto — um timer não limpo pode vazar memória, uma API key hardcoded pode vazar para logs, um resultado de tool não validado pode causar um Agent a tomar decisões erradas. Programação defensiva não é opcional — é obrigatória.

💡 Dica: O princípio central da programação defensiva é "não confie em nenhuma entrada externa" — entrada do usuário, valores retornados por tools, respostas de API todos precisam de validação. Seu plugin travar é aceitável; seu plugin causar perda de dados ou vazamento de credenciais não é.

📋 Pré-requisitos: Completou 13-effect.md e 29-sandbox.md

1. O Que Você Vai Aprender

Padrões de Programação Defensiva


2. Melhores Práticas de Gerenciamento de Credenciais

▶ Exemplo 1:

TYPESCRIPT
// ❌ API Key hardcoded
const apiKey = 'sk-abc123def456'

// ❌ API Key escrita em logs
ctx.logger.info(`connecting with key: ${apiKey}`)

// ❌ API Key na URL
const url = `https://api.example.com?key=${apiKey}`

// ❌ API Key na mensagem de erro
throw new Error(`Authentication failed for key: ${apiKey}`)

▶ Exemplo 2:

TYPESCRIPT
// ✅ Ler da configuração
export const Config = Schema.object({
  apiKey: Schema.string().required().hidden()
})

export function apply(ctx: Context) {
  const apiKey = ctx.config.apiKey
  // apiKey usada apenas dentro de apply, não vazada externamente
}

// ✅ Usar variáveis de ambiente
const apiKey = process.env.MY_PLUGIN_API_KEY

// ✅ Passar via header da requisição (não na URL)
const response = await fetch(url, {
  headers: { 'Authorization': `Bearer ${apiKey}` }
})

(3) Proteção de Credenciais em Logs

TYPESCRIPT
// ✅ Ocultar informações sensíveis em logs
ctx.logger.info(`connecting to ${endpoint}`)  // Não logar a key

// ✅ Filtro de log personalizado
function sanitize(obj: any): any {
  const sanitized = { ...obj }
  if (sanitized.apiKey) sanitized.apiKey = '***'
  if (sanitized.authorization) sanitized.authorization = '***'
  return sanitized
}

ctx.logger.info('request:', sanitize(request))

(4) Rotação de Credenciais

TYPESCRIPT
export const Config = Schema.object({
  apiKey: Schema.string().required().hidden(),
  keyRotationDays: Schema.number().default(90)
})

export function apply(ctx: Context) {
  ctx.setInterval(() => {
    const age = getKeyAge()
    if (age > ctx.config.keyRotationDays * 86400000) {
      ctx.logger.warn('API key is overdue for rotation')
    }
  }, 86400000)
}

3. Relatórios e Validação de Resultados

(1) Validando Valores Retornados por Tools

TYPESCRIPT
// ❌ Não validar resultados de tools
async execute({ path }, ctx) {
  const result = await ctx.shell.execute(`ls ${path}`)
  return result.stdout  // Pode estar vazio ou malformado
}

// ✅ Validar resultados de tools
async execute({ path }, ctx) {
  const result = await ctx.shell.execute(`ls ${path}`)
  
  if (result.exitCode !== 0) {
    return {
      error: true,
      message: `ls failed: ${result.stderr}`,
      exitCode: result.exitCode
    }
  }
  
  if (!result.stdout || result.stdout.trim().length === 0) {
    return {
      error: true,
      message: 'Directory is empty or does not exist'
    }
  }
  
  const files = result.stdout.trim().split('\n')
  return { count: files.length, files }
}

(2) Validando Respostas de API

TYPESCRIPT
// ❌ Não validar respostas de API
const data = await response.json()
return data.result

// ✅ Validar respostas de API
async function callAPI(ctx: Context, endpoint: string): Promise<any> {
  const response = await fetch(endpoint)
  
  if (!response.ok) {
    throw new Error(`API error: ${response.status} ${response.statusText}`)
  }
  
  let data: any
  try {
    data = await response.json()
  } catch {
    throw new Error('API returned invalid JSON')
  }
  
  if (!data || typeof data !== 'object') {
    throw new Error('API returned unexpected format')
  }
  
  return data
}

(3) Validação de Entrada

TYPESCRIPT
// ❌ Não validar entrada do usuário
async execute({ command }, ctx) {
  return await ctx.shell.execute(command)  // Risco de injeção de comando
}

// ✅ Validar e sanitizar entrada
async execute({ command }, ctx) {
  if (!command || typeof command !== 'string') {
    return { error: true, message: 'Invalid command' }
  }
  
  if (command.length > 1000) {
    return { error: true, message: 'Command too long' }
  }
  
  // Verificação de whitelist
  const allowed = ['ls', 'cat', 'grep', 'wc', 'head', 'tail']
  const baseCommand = command.split(' ')[0]
  if (!allowed.includes(baseCommand)) {
    return { error: true, message: `Command not allowed: ${baseCommand}` }
  }
  
  return await ctx.shell.execute(command)
}

4. Garantia de Limpeza

(1) Princípio da Garantia de Limpeza

Independentemente de como um plugin sai (descarregamento normal, crash, parada manual), todos os recursos devem ser limpos.

(2) Checklist de Limpeza

Tipo de Recurso Método de Limpeza Mecanismo de Garantia
Timers ctx.setInterval/setTimeout Auto-limpeza
Event listeners ctx.on() Auto-limpeza
Conexões de rede ctx.effect() Registro manual
Arquivos temporários ctx.effect() Registro manual
Processos filhos ctx.effect() Registro manual
Estado global ctx.effect() Registro manual

(3) Padrão de Garantia de Limpeza

TYPESCRIPT
export function apply(ctx: Context) {
  // Todos os recursos que precisam de limpeza
  const resources: { close: () => void | Promise<void> }[] = []

  // Registrar limpeza imediatamente ao adquirir recurso
  function acquireResource<T extends { close: () => void | Promise<void> }>(
    resource: T
  ): T {
    resources.push(resource)
    return resource
  }

  // Limpeza unificada
  ctx.effect(() => {
    return async () => {
      for (const r of resources.reverse()) {
        try {
          await r.close()
        } catch (e) {
          ctx.logger.warn('cleanup error:', e)
        }
      }
    }
  })

  // Uso
  const db = acquireResource(openDatabase())
  const ws = acquireResource(new WebSocket('ws://localhost:8080'))
}

(4) Limpeza de Arquivos Temporários

TYPESCRIPT
export function apply(ctx: Context) {
  const tempFiles: string[] = []

  ctx.effect(() => {
    return async () => {
      for (const file of tempFiles.reverse()) {
        try {
          await ctx.fs.unlink(file)
        } catch {}
      }
    }
  })

  async function createTempFile(content: string): Promise<string> {
    const path = `/tmp/dsh-${Date.now()}-${Math.random().toString(36).slice(2)}`
    await ctx.fs.writeFile(path, content)
    tempFiles.push(path)
    return path
  }
}

5. Limites de Efeitos Colaterais

(1) Classificação de Efeitos Colaterais

Tipo Descrição Exemplo Risco
Somente leitura Não modifica estado externo Ler arquivo, consultar banco de dados Baixo
Escrita idempotente Execução repetida tem mesmo efeito Criar arquivo (sobrescrever se existir) Médio
Escrita não idempotente Execução repetida tem efeitos diferentes Enviar email, anexar log Alto
Destrutiva Operação irreversível Excluir arquivo, DROP TABLE Muito alto

(2) Princípios de Limites de Efeitos Colaterais

TEXT 📖 Somente leitura
Princípio 1: Minimizar efeitos colaterais
  → Executar apenas operações necessárias
  → Preferir somente leitura, depois escritas idempotentes

Princípio 2: Efeitos colaterais devem ser reversíveis
  → Salvar estado original antes de escrever
  → Fornecer operações de desfazer

Princípio 3: Efeitos colaterais devem ser auditáveis
  → Registrar detalhes de cada efeito colateral
  → Usuários podem visualizar histórico de operações

Princípio 4: Efeitos colaterais requerem aprovação
  → Operações destrutivas devem ser aprovadas
  → Operações não idempotentes devem ser aprovadas

(3) Padrão de Rollback

TYPESCRIPT
interface ReversibleAction {
  execute(): Promise<void>
  rollback(): Promise<void>
}

class FileEditAction implements ReversibleAction {
  private originalContent: string | null = null

  constructor(
    private path: string,
    private newContent: string,
    private ctx: Context
  ) {}

  async execute() {
    try {
      this.originalContent = await this.ctx.fs.readFile(this.path)
    } catch {
      this.originalContent = null
    }
    await this.ctx.fs.writeFile(this.path, this.newContent)
  }

  async rollback() {
    if (this.originalContent !== null) {
      await this.ctx.fs.writeFile(this.path, this.originalContent)
    } else {
      await this.ctx.fs.unlink(this.path)
    }
  }
}

(4) Auditoria de Efeitos Colaterais

TYPESCRIPT
const auditLog: AuditEntry[] = []

function audit(action: string, details: any, reversible: boolean) {
  auditLog.push({
    timestamp: Date.now(),
    action,
    details,
    reversible,
    user: 'agent'
  })
  ctx.emit('audit/action', { action, details, reversible })
}

// Uso
audit('file_edit', { path: 'src/app.ts', operation: 'edit' }, true)
audit('email_send', { to: 'alice@example.com' }, false)

6. Cultura de Revisão de Incidentes

(1) Template de Revisão de Incidente

MARKDOWN
# Revisão de Incidente: [Título]

## Informações Básicas
- Data: YYYY-MM-DD
- Impacto: [Funcionalidades/usuários afetados]
- Severidade: P0/P1/P2
- Responsável: [Nome]

## Linha do Tempo
- HH:MM — [Evento 1]
- HH:MM — [Evento 2]
- HH:MM — [Correção]

## Análise de Causa Raiz
[Análise dos 5 Porquês]

## Correção
- Curto prazo: [Correção imediata]
- Longo prazo: [Medida preventiva]

## Lições Aprendidas
- [Lição 1]
- [Lição 2]

(2) Padrões Comuns de Incidentes

Padrão de Incidente Causa Raiz Prevenção
Vazamento de API Key Credenciais impressas em logs Sanitização de logs
Vazamento de memória Timers não limpos Usar ctx.setInterval
Perda de dados Operações de exclusão sem confirmação Política de aprovação
Loop infinito Tools chamando umas às outras Limites de profundidade de chamada
Falha em cascata Exceções não isoladas try-catch + Fiber independente

▶ Exemplo 3:

TEXT 📖 Somente leitura
Incidente: Agent excluiu arquivos de projeto do usuário

Porquê 1: Agent executou rm -rf /project
  → Porque a tool não tinha validação de caminho

Porquê 2: A tool não tinha validação de caminho
  → Porque o desenvolvedor não implementou verificação de whitelist

Porquê 3: O desenvolvedor não implementou verificação de whitelist
  → Porque não havia um processo de revisão de segurança

Porquê 4: Não havia um processo de revisão de segurança
  → Porque a equipe não havia estabelecido um checklist de segurança

Porquê 5: A equipe não havia estabelecido um checklist de segurança
  → Por causa de treinamento insuficiente em conscientização de segurança

Correção: Estabelecer um checklist de auditoria de segurança; todos os plugins devem ser verificados antes da publicação

7. Checklist de Auditoria de Segurança

(1) Checklist de Auditoria de Segurança do Plugin

# Item de Verificação Categoria Prioridade
1 API Key não está hardcoded Credenciais P0
2 Informações sensíveis não estão em logs Credenciais P0
3 Todos os ctx.effect têm funções de limpeza Limpeza P0
4 Timers usam ctx.setInterval/setTimeout Limpeza P0
5 Valores retornados por tools têm tratamento de erros Validação P1
6 Entrada do usuário é validada e sanitizada Validação P1
7 Formato de resposta de API é validado Validação P1
8 Operações destrutivas têm política de aprovação Efeitos colaterais P1
9 Operações não idempotentes são reversíveis Efeitos colaterais P2
10 Efeitos colaterais têm logs de auditoria Efeitos colaterais P2
11 Declarações de permissão estão completas Permissões P1
12 Não há solicitações de permissão desnecessárias Permissões P2
13 Versões de dependências têm declarações de compatibilidade Compatibilidade P2
14 Sem vulnerabilidades de segurança conhecidas nas dependências Dependências P1

(2) Processo de Auditoria

100%
graph TD
    CODE[Desenvolvimento do plugin concluído] --> SELF[Autoauditoria do desenvolvedor]
    SELF --> CHECK{Todos os itens do checklist passaram?}
    CHECK -->|Não| FIX[Corrigir problemas]
    FIX --> SELF
    CHECK -->|Sim| REVIEW[Revisão da equipe]
    REVIEW --> APPROVE{Revisão aprovada?}
    APPROVE -->|Não| FIX2[Modificar código]
    FIX2 --> REVIEW
    APPROVE -->|Sim| PUBLISH[Publicar]

(3) Auditoria Automatizada

BASH
# Executar auditoria de segurança
dsh audit my-plugin

# Saída
🔒 Security Audit: my-plugin

✅ No hardcoded credentials
✅ All ctx.effect() have cleanup functions
⚠️ Tool 'db_query' has no input validation
❌ API response not validated in 'fetch_data'
✅ Approval policy configured for destructive operations
⚠️ Permission 'shell.execute' may not be necessary

2 errors, 2 warnings found. Fix before publishing.

❓ Perguntas Frequentes

P Programação defensiva deixa o código mais lento?
R O overhead de tempo de execução da lógica de validação é geralmente insignificante. Custos de remediação de problemas de segurança superam em muito os custos de prevenção.
P Toda tool precisa de aprovação?
R Não. Operações somente leitura podem usar aprovação always. Operações com efeitos colaterais (escrever arquivos, executar comandos, enviar requisições) precisam de aprovação.
P Como lidar com falhas de limpeza não recuperáveis?
R Registre o erro e continue limpando outros recursos. O Cordis internamente envolve cada função de limpeza em try-catch; uma falha não impede as outras.
P Com que frequência devem ocorrer revisões de incidentes?
R Imediatamente após cada incidente P0/P1. Incidentes P2 podem ser agrupados para revisão periódica. Revise padrões de incidentes regularmente (ex.: mensalmente).
P O checklist de auditoria de segurança se aplica a todos os plugins?
R Sim. Os itens do checklist são requisitos de segurança universais. Plugins diferentes podem precisar de verificações adicionais (ex.: proteção contra injeção SQL para plugins de banco de dados).
P Como testar garantias de limpeza?
R Carregue e descarregue o plugin repetidamente monitorando o uso de recursos: bash # Teste em loop for i in {1..100}; do dsh plugin enable my-plugin dsh plugin disable my-plugin done # Verificar se memória e contagem de conexões permanecem estáveis

📖 Resumo


📝 Exercícios

1. ⭐ Básico: Revise o código do plugin que você escreveu anteriormente contra o checklist de auditoria de segurança. Liste os problemas encontrados e planos de correção.

2. ⭐⭐ Intermediário: Adicione validação de entrada completa a um plugin de tool — verifique tipos de parâmetros, comprimento, formato, filtre parâmetros de Shell com whitelist. Teste: insira vários parâmetros ilegais e confirme que todos retornam mensagens de erro significativas.

3. ⭐⭐⭐ Desafio: Implemente um sistema ReversibleAction — cada operação com efeitos colaterais cria um objeto ReversibleAction que salva o estado original na execução e restaura no rollback. Escreva uma tool de edição de arquivo com suporte a rollback: após editar um arquivo, chame undo_last para reverter a última edição. Teste: edite três vezes consecutivamente, reverta sequencialmente, confirme que o arquivo retorna ao estado original.

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%