OSS · CLI em Bash · Git Hooks

private-lint

O histórico do git não esquece. Bloqueie nomes privados antes que entrem nele.

Um gate de git hooks que pega nomes pessoais e privados (nomes de família, e-mails pessoais, domínios de clientes e empregadores) antes do commit e do push. Bash puro, sem dependências. Os padrões de detecção vivem só na sua máquina, nunca em repositório algum, e um subcomando de auditoria do histórico completo cobre o momento antes de tornar um repositório público.

Ver no GitHub Os sete caminhos de vazamento →

Demo no terminal: um commit contendo um nome pessoal e um e-mail privado é bloqueado pelo hook de pre-commit, que lista três padrões encontrados com arquivo e linha; depois de apagar a linha marcada, o mesmo commit passa. Todos os nomes da demo são fictícios.
Um arquivo em stage com um nome pessoal (fictício) e um e-mail privado: o commit é bloqueado com três ocorrências detalhadas. Apague a linha e o mesmo commit passa.

Sete caminhos por onde um nome vaza

"Escanear o diff" não bastou. Implantar a ferramenta em 25 repositórios públicos revelou ocorrências reais em caminhos que uma varredura de diff nunca enxerga. Um gate por caminho:

Caminho Gate O que ele olha
Diff em stage + nomes de arquivo pre-commit Linhas adicionadas e nomes de caminho em stage (uma captura de tela salva com o nome de alguém vaza só pelo nome do arquivo)
Mensagem de commit commit-msg Cada linha do corpo da mensagem: "corrigido conforme sugestão do Yamada" é parado antes de virar histórico
Diff do intervalo do push pre-push Diffs e nomes de arquivo dos commits que o remoto ainda não viu; branches novos checam tudo que nenhum remoto alcançou
O destino do push pre-push Nome e URL do remote: um nome de cliente no slug do repo passa por qualquer quantidade de varredura de conteúdo
Identidade do autor pre-commit O e-mail efetivo de author/committer resolvido pelo git, comparado com uma allowlist; não configurado também conta como violação
O que já está no histórico audit (manual) Diffs do histórico completo, todos os nomes de arquivo, nome do repo, URLs de remote e o HEAD atual, varridos antes de abrir o repo
As próprias válvulas de escape design Regras de allow, marcadores inline e baselines de auditoria valem, cada um, um nível mais estreito que o campo ao lado

Instalação

Clone e aponte para um repo. O install é idempotente e se insere antes dos hooks existentes, então um exit antecipado em um hook antigo não consegue contornar o gate. O install-template cobre cada clone futuro via init.templateDir.

git clone https://github.com/kenimo49/private-lint.git
cd private-lint

mkdir -p ~/.config/private-lint
$EDITOR ~/.config/private-lint/patterns    # 1 ERE per line; never committed anywhere

./private-lint install ~/repos/<repo>      # idempotent; inserts itself before existing hooks
echo "mode=block" > ~/repos/<repo>/.privatelintrc   # public repos: block

./private-lint install-template            # auto-install into every future clone / init

Uso

Cada hook também roda na mão, e o audit é a varredura de pré-publicação:

./private-lint check --staged                  # pre-commit, by hand
./private-lint check --message <file>          # commit-msg, by hand
./private-lint check --push-dest <name> <url>  # remote name / URL
./private-lint audit ~/repos/<repo>            # full history + repo name + remote + HEAD
./private-lint status                          # effective mode, hooks, pattern count

PRIVATE_LINT=off git commit ...                # intentional bypass

Quatro decisões de design

As decisões interessantes são todas sobre para que lado cada mecanismo falha.

  1. Os gates falham abertos, a auditoria falha fechada

    Numa máquina sem arquivo de padrões, os hooks deixam tudo passar: uma allowlist que bloqueia por padrão congelaria cada máquina onde caísse antes de alguém distribuir os padrões. O audit inverte isso e sai com erro quando os padrões faltam, porque "eu auditei" com zero padrões carregados é a saída mais perigosa que a ferramenta pode produzir.

  2. identities é uma allowlist, não uma denylist

    Os padrões dizem o que não pode aparecer. O identities diz quais e-mails de autor podem commitar. A inversão existe porque um repo sem user.email configurado herda em silêncio um endereço global ou autodetectado, e é exatamente assim que um endereço pessoal acaba num histórico público.

  3. Três dos sete caminhos pegaram vazamentos reais durante a implantação

    A checagem de destino do push, a de identidade e a auditoria do histórico completo marcaram, cada uma, uma ocorrência real enquanto a ferramenta era implantada nos repositórios existentes. Não são defesas hipotéticas.

  4. As válvulas de escape têm escopo um nível mais estreito

    O marcador inline (private-lint:allow) é ignorado em linhas de metadados gerados, como nomes de arquivo e URLs de remote, então não dá para contrabandeá-lo para campos que ele não deveria cobrir. As baselines de auditoria silenciam apenas ocorrências do histórico: um nome no nome atual do repo ou na URL do remote continua soando, porque isso é uma exposição ativa, não uma antiga já aceita.

Ferramentas relacionadas para devs

Todos os produtos →

← Voltar para produtos