OSS · CLI en Bash · Git Hooks

private-lint

El historial de git no olvida. Bloquea los nombres privados antes de que entren.

Un gate de git hooks que atrapa nombres personales y privados (apellidos, correos personales, dominios de clientes y empleadores) antes del commit y del push. Bash puro, sin dependencias. Los patrones de detección viven solo en tu máquina, nunca en ningún repositorio, y un subcomando de auditoría del historial completo cubre el momento antes de hacer público un repositorio.

Ver en GitHub Los siete caminos de fuga →

Demo en terminal: un commit que contiene un nombre personal y un correo privado es bloqueado por el hook de pre-commit, que lista tres patrones encontrados con archivo y línea; tras borrar la línea marcada, el mismo commit pasa. Todos los nombres de la demo son ficticios.
Un archivo en stage con un nombre personal (ficticio) y un correo privado: el commit queda bloqueado con tres coincidencias detalladas. Borra la línea y el mismo commit pasa.

Siete caminos por los que se fuga un nombre

"Escanear el diff" no alcanzó. Desplegar la herramienta en 25 repositorios públicos sacó a la luz coincidencias reales en caminos que un escaneo de diff nunca ve. Un gate por camino:

Camino Gate Qué mira
Diff en stage + nombres de archivo pre-commit Líneas agregadas y nombres de ruta en stage (una captura guardada con el nombre de alguien se fuga solo por el nombre del archivo)
Mensaje de commit commit-msg Cada línea del cuerpo del mensaje: "corregido según lo que sugirió Yamada" se detiene antes de volverse historial
Diff del rango del push pre-push Diffs y nombres de archivo de los commits que el remoto no ha visto; las ramas nuevas revisan todo lo que ningún remoto alcanzó
El destino del push pre-push Nombre y URL del remote: un nombre de cliente en el slug del repo se cuela por más escaneo de contenido que haya
Identidad del autor pre-commit El correo efectivo de author/committer que resuelve git, cotejado con una allowlist; sin configurar también cuenta como violación
Lo que ya está en el historial audit (manual) Diffs del historial completo, todos los nombres de archivo, nombre del repo, URLs de remote y el HEAD actual, barridos antes de abrir el repo
Las propias válvulas de escape diseño Las reglas de allow, los marcadores inline y las baselines de auditoría aplican, cada una, un nivel más estrecho que el campo de al lado

Instalación

Clona y apúntalo a un repo. El install es idempotente y se inserta antes de los hooks existentes, así que un exit temprano en un hook viejo no puede saltarse el gate. El install-template cubre cada clone futuro vía 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 también corre a mano, y el audit es el barrido de pre-publicación:

./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

Cuatro decisiones de diseño

Las decisiones interesantes son todas sobre hacia qué lado falla cada mecanismo.

  1. Los gates fallan abiertos, la auditoría falla cerrada

    En una máquina sin archivo de patrones, los hooks dejan pasar todo: una allowlist que bloquea por defecto congelaría cada máquina donde aterrice antes de que alguien le distribuya los patrones. El audit invierte esto y sale con error cuando faltan los patrones, porque "ya lo audité" con cero patrones cargados es la salida más peligrosa que la herramienta puede producir.

  2. identities es una allowlist, no una denylist

    Los patrones dicen qué no puede aparecer. identities dice qué correos de autor pueden commitear. La inversión existe porque un repo sin user.email configurado hereda en silencio una dirección global o autodetectada, y así es exactamente como una dirección personal termina en un historial público.

  3. Tres de los siete caminos atraparon fugas reales durante el despliegue

    La verificación del destino del push, la de identidad y la auditoría del historial completo marcaron, cada una, una coincidencia real mientras la herramienta se desplegaba en los repositorios existentes. No son defensas hipotéticas.

  4. Las válvulas de escape tienen un alcance un nivel más estrecho

    El marcador inline (private-lint:allow) se ignora en líneas de metadatos generados, como nombres de archivo y URLs de remote, así que no se puede contrabandear a campos que no debería cubrir. Las baselines de auditoría silencian solo coincidencias del historial: un nombre en el nombre actual del repo o en la URL del remote sigue sonando, porque eso es una exposición activa, no una antigua ya aceptada.

Herramientas relacionadas para devs

Todos los productos →

← Volver a productos