As ferramentas
Detecção de segredos, linter de segurança, análise estática, verificações em execução e trilha de auditoria — o que cada uma pega, e o que ela não pega.
Uma ferramenta de segurança vale pelo que ela pega e pelo que ela não acusa por engano. A segunda metade decide se ela continua ligada: um alarme falso no caminho comum ensina a ignorar a saída, e uma ferramenta ignorada não protege nada.
`dataforge seguranca`#
Duas varreduras sobre cada arquivo: segredos por formato e dez regras sintáticas. Ela lê .df, .env, .json, .toml, .yml, .sh, .ts e .md — um segredo vaza do arquivo de configuração muito mais do que do código.
// $ dataforge seguranca .
//
// ALTO src/config.df:12:14 [segredo-no-codigo]
// Stripe escrito no arquivo (sk_l************).
// dica: Leia de 'OS.env' e ROTACIONE a chave que ja esteve aqui.
//
// MEDIO src/relatorio.df:88:5 [md5-ou-sha1]
// MD5 e SHA-1 estao quebrados para assinatura.
// dica: Use 'sha256'. Para senha, 'hash_password'.
//
// 2 achado(s) em 143 arquivo(s) — 1 de gravidade alta
// --strict reprova a esteira de CI
// --json vira entrada de outra ferramenta
// --so=alto esconde os medios| Regra | Gravidade | O que ela acusa |
|---|---|---|
segredo-no-codigo | alto | chave de API, token ou bloco de chave privada no arquivo |
sql-concatenado | alto | SQL montado com interpolação ou concatenação |
shell-com-texto | alto | linha de shell montada com interpolação |
senha-sem-derivacao | alto | senha guardada com resumo simples |
verificacao-desligada | alto | verificar := no no caminho de produção |
md5-ou-sha1 | médio | MD5 e SHA-1 para assinatura |
html-sem-escape | médio | HTML montado com interpolação |
aleatorio-fraco | médio | randint para token, senha ou chave |
comparacao-de-segredo | médio | comparar segredo com is vaza tempo |
caminho-de-fora | médio | caminho montado com valor que veio de fora |
adopt Arcane.Seguranca as Seg
// A mesma varredura, chamada de dentro de um programa — e util num
// gancho de pre-commit ou antes de gravar um log.
fonte := "chave := \"ghp_abcdefghijklmnopqrstuvwxyz0123456789\""
achados := Seg.procurar_segredos(fonte)
assert len(achados) is 1
out $"{achados[0]['tipo']} na linha {achados[0]['linha']}: {achados[0]['trecho']}"
// O trecho ja vem MASCARADO: um relatorio de vazamento que imprime
// o segredo inteiro e mais um lugar onde ele esta.
assert "abcdefghijkl" not in achados[0]["trecho"]
// E 'redigir' devolve o texto com os segredos trocados no lugar,
// para gravar um corpo de pedido no log sem que o log vire o
// proximo vazamento.
assert "abcdefghijkl" not in Seg.redigir(fonte)Análise estática#
| Comando | O que ele pega que importa para segurança |
|---|---|
dataforge check | nome, aridade e tipo — inclusive entre arquivos; campo inexistente com sugestão |
dataforge check --strict | avisos viram erros; é o que vai na esteira |
dataforge lint | variável escrita e nunca lida, ramo redundante |
dataforge seguranca | as duas varreduras acima |
dataforge oop | acoplamento e coesão — complexidade é onde a falha se esconde |
dataforge deps | o grafo de imports, e o ciclo |
Verificações em execução#
O que a análise estática não consegue provar continua sendo conferido quando o programa roda — e o erro tem nome, linha e dica.
| Confere | O erro |
|---|---|
| tipo declarado, em toda fronteira | TypeError |
| índice fora do alcance | IndexError |
| chave que não existe | KeyError |
membro de void | NullReferenceError |
refinamento de um type … where | o erro nomeia o tipo, não a base |
| invariante de um agregado | InvariantError, e o comando é desfeito |
| limite de bloco e ponteiro nulo | BufferOverflowError, NullPointerError |
| entrada hostil recusada | UnsafeInputError |
Trilha de auditoria#
Responsabilização exige um registro que não possa ser alterado sem deixar marca. A trilha encadeia o resumo do registro anterior em cada registro.
adopt Arcane.Seguranca as Seg
adopt Arcane.OS as OS
adopt Arcane.IO as IO
caminho := $"{OS.temp_dir()}/df-auditoria-{randint(100000, 999999)}.log"
livro := Seg.auditoria(caminho)
livro.registrar("login", {"ip": "203.0.113.7"}, quem := "ana")
livro.registrar("apagou_pedido", {"pedido": 42}, quem := "ana")
livro.registrar("exportou_relatorio", {"linhas": 1200}, quem := "bruno")
r := livro.conferir()
assert r["ok"]
out $"{r['registros']} registros, cadeia fecha"
// Os dados passam por 'redigir' ANTES de serem gravados: uma trilha
// de auditoria e exatamente o tipo de arquivo que acaba anexado a
// um chamado.
livro.registrar("deploy", {"token": "ghp_abcdefghijklmnopqrstuvwxyz01"})
cycle linha in livro.ler():
assert "abcdefghijkl" not in str(linha)
out "o segredo nao entra nem na propria auditoria"
IO.delete(caminho)A esteira completa#
// .github/workflows/ci.yml — as cinco que reprovam de verdade
//
// dataforge check . --strict
// dataforge lint .
// dataforge seguranca . --strict
// dataforge test tests/ --cobertura --minimo=80
// dataforge devops doctor
//
// E o gancho local, que pega antes de o segredo sair da maquina:
//
// # .git/hooks/pre-commit
// #!/bin/sh
// dataforge seguranca . --strict || exit 1