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.
📋 Pré-requisitos: Completou 13-effect.md e 29-sandbox.md
1. O Que Você Vai Aprender
- Melhores práticas de gerenciamento de credenciais
- Relatórios e validação de resultados
- Garantia de limpeza
- Limites de efeitos colaterais
- Cultura de revisão de incidentes
- Checklist de auditoria de segurança
2. Melhores Práticas de Gerenciamento de Credenciais
▶ Exemplo 1:
// ❌ 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:
// ✅ 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
// ✅ 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
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
// ❌ 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
// ❌ 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
// ❌ 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
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
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
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
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
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
# 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:
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
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
# 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
always. Operações com efeitos colaterais (escrever arquivos, executar comandos, enviar requisições) precisam de aprovação.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
- Gerenciamento de credenciais: sem hardcoding, sem logs, usar config/variáveis de ambiente, rotacionar regularmente
- Validação de resultados: validar valores retornados por tools, respostas de API, entrada do usuário — não confie em dados externos
- Garantia de limpeza: todos os recursos registram funções de limpeza, gerenciamento unificado, exceções não bloqueiam outras limpezas
- Limites de efeitos colaterais: minimizar efeitos colaterais, tornar reversíveis, tornar auditáveis, operações destrutivas precisam de aprovação
- Revisão de incidentes: análise de causa raiz com 5 Porquês, estabelecer correções e medidas preventivas
- Checklist de auditoria de segurança: 14 itens de verificação, autoauditoria do desenvolvedor + revisão da equipe + varredura automatizada
📝 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.