Precinto · sala de evaluación
Todo lo que se afirma en el sitio se mide acá, con el comando que lo reproduce en tu máquina en menos de un minuto. Incluida la parte que no favorece al producto.
pip install precinto
precinto demo --out banco
precinto bench banco/support-bundle-demo.tar.gz \
--canaries banco/canaries.json --profile demo
Un paquete de diagnóstico sintético con 17 canarios en 14 clases: credenciales, cadenas de conexión, identificadores personales y nombres internos, plantados en registros, configuración, variables de entorno y volcados binarios. Ningún valor es real; se generan con semilla fija, así que cualquiera obtiene exactamente los mismos.
| Desenlace | Qué significa | Cantidad |
|---|---|---|
| Neutralizado | reemplazado por un pseudónimo estable | 15 |
| Retenido | su archivo se bloqueó entero por no ser inspeccionable | 1 |
| Marcado | también enmascarado, pero con una decisión humana pendiente | 1 |
| Escapado | salió en claro sin que nadie lo señalara | 0 |
El desglose tiene cuatro estados y no dos a propósito. Un banco binario se felicita solo: la primera versión de éste informaba 100% sin haber medido nada, porque comparaba cada canario contra cada archivo y lo daba por contenido en cuanto faltaba en uno cualquiera. Y hubo una segunda vez: cuando pasamos a enmascarar también lo marcado, la condición «el valor ya no está» ganaba siempre y el estado «marcado» se volvió inalcanzable — este sitio llegó a publicar 16 neutralizados cuando eran 15 más 1 marcado.
La pregunta que corresponde no es «¿encuentra cosas?» sino «¿encuentra más que un guion de veinte líneas con expresiones regulares?». Medido sobre el mismo banco:
| Método | Contenidos | Tiempo |
|---|---|---|
| Expresiones regulares obvias (contraseña, clave de AWS, token de GitHub, PEM, correo) | 6 de 17 · 35% | 1 ms |
| Precinto — reglas, política, bloqueo de formatos | 17 de 17 · 100% | 5 ms |
Es la lista que importa, porque describe qué estás comprando:
| Clase | Dónde estaba |
|---|---|
| token JWT | logs/app.log |
| cadena de conexión con contraseña | logs/app.log |
cabecera Authorization: Bearer | logs/app.log |
| dirección de red interna | logs/app.log |
| asignación de contraseña en YAML | config/application.yaml |
| clave de OpenAI y de Stripe | config/.env |
| token de Slack | support/diagnostics.json |
| nombres de cliente y de proyecto | support/diagnostics.json |
| cadena opaca de alta entropía | logs/worker.log |
Es un banco sintético. Mide que los detectores encuentran lo que se les plantó — no qué encontrarían en tus paquetes, que es la única pregunta que importa. Un banco propio siempre favorece a quien lo escribió: los canarios los elegí yo, sabiendo qué detecta la herramienta.
Por eso el paso siguiente con un fabricante es correrlo sobre su corpus, en su máquina, con la herramienta sin ver nada. Si en tus datos el número es malo, es un resultado útil y lo diré.
| Afirmación | Cómo se comprueba |
|---|---|
| No abre conexiones de red | un test corre un escaneo completo con socket.socket, create_connection y getaddrinfo anulados, y falla si algo intenta usarlos |
| Nunca modifica el original | tests con --output-name apuntando al propio original, con .., con el nombre reservado del manifiesto, y con el manifiesto siendo un enlace simbólico |
| El manifiesto no lleva valores de secreto | se comprueba que ningún canario aparezca en el JSON, incluidos los que estaban en nombres de archivo |
| Una firma falsificada no pasa | clave identidad con S=0 —la firma universal— rechazada; codificación prohibida rechazada; 200 firmas legítimas de OpenSSL aceptadas |
| La copia no importa archivos de fuera | enlaces simbólicos a archivos y a directorios externos: no se copian ni se siguen |
git clone https://github.com/hernaninverso/precinto.git && cd precinto python3 test_precinto.py # 104 comprobaciones node test_verificador_web.js # 26 sobre el verificador del navegador
El código pasó por tres pases de auditoría con un modelo de frontera, con el encargo explícito de demolerlo. Dos encontraron fallos que destruían datos del usuario, y los tres informes están resumidos acá porque ocultarlos sería exactamente lo que este proyecto critica.
| Pase | Lo más grave que encontró |
|---|---|
| Primero | Un perfil con una acción inventada dejaba el token intacto y el resultado seguía siendo «pasa». Y el verificador del navegador aceptaba una firma universalmente falsificable (clave identidad, R identidad, S=0): daba «Verificada» a cualquier mensaje. |
| Segundo | --output-name con una ruta truncaba el paquete original: reproducido, de 2755 a 1774 bytes. Y shutil.copytree(symlinks=False) sigue los enlaces en vez de preservarlos, así que importaba archivos de fuera del paquete. |
| Tercero | El manifiesto se escribía sin ninguna de las comprobaciones puestas para el tar: una entrada llamada manifest.json se procesaba bien y después se destruía. |
La primera auditoría corrió después de publicar. Fue el error, y por eso las otras dos corrieron antes. Un producto que sostiene que la evidencia debe preceder a la afirmación no puede auditarse a posteriori.