File size: 3,197 Bytes
81f30c1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
"""Fase 2 del plan de corrección — comprobación por contenido (SEC-2): ningún
fragmento del corpus informal (texto de un documento con licencia desconocida,
`Document.redistributable=false`) debe aparecer en lo que se publica, ni en
git (docs/PLAN_CORRECCIONES.md Fase 2, tarea 2) ni en la copia que sube
`scripts/publish_hf.py` (tarea 3) — misma función, dos llamadores.

Uso: python scripts/check_no_leaked_content.py [--root .] [--informal data/corpus/informal.jsonl]
"""

from __future__ import annotations

import json
import subprocess
from pathlib import Path


def pdf_text_fingerprints(informal_jsonl: str | Path, n_chars: int = 60) -> list[str]:
    """Primeros `n_chars` del `content_md` de cada card/figura del corpus
    informal: huellas cortas y específicas — alcanzan para detectar una fuga
    sin que el propio script de comprobación termine guardando el contenido
    protegido."""
    path = Path(informal_jsonl)
    if not path.exists():
        return []
    prints = []
    for line in path.read_text(encoding="utf-8").splitlines():
        if not line.strip():
            continue
        text = (json.loads(line).get("content_md") or "").strip()
        if len(text) >= n_chars:
            prints.append(text[:n_chars])
    return prints


def find_leaks(fingerprints: list[str], files: list[Path]) -> dict[str, list[str]]:
    """{archivo: [huellas encontradas]} sólo para los archivos con alguna coincidencia."""
    leaks: dict[str, list[str]] = {}
    for f in files:
        try:
            text = f.read_text(encoding="utf-8", errors="ignore")
        except OSError:
            continue
        hits = [fp for fp in fingerprints if fp in text]
        if hits:
            leaks[str(f)] = hits
    return leaks


def git_committable_files(root: str | Path = ".") -> list[Path]:
    """Lo mismo que incluiría `git add .`: trackeados + no ignorados (respeta
    `.gitignore`); usado por la tarea 2 (comprobación antes de un commit)."""
    root = Path(root)
    out = subprocess.run(["git", "-C", str(root), "ls-files", "-co", "--exclude-standard"], capture_output=True, text=True, check=True)
    return [root / p for p in out.stdout.splitlines() if p.strip() and (root / p).is_file()]


def check(files: list[Path], informal_jsonl: str | Path = "data/corpus/informal.jsonl") -> dict[str, list[str]]:
    """Punto de entrada compartido: huellas del corpus informal contra `files`."""
    return find_leaks(pdf_text_fingerprints(informal_jsonl), files)


if __name__ == "__main__":
    import argparse

    ap = argparse.ArgumentParser(description=__doc__)
    ap.add_argument("--root", default=".")
    ap.add_argument("--informal", default="data/corpus/informal.jsonl")
    args = ap.parse_args()

    files = git_committable_files(args.root)
    leaks = check(files, args.informal)
    if leaks:
        print(f"FUGA: {len(leaks)} archivo(s) contienen texto del corpus informal:")
        for f, hits in leaks.items():
            print(f"  {f}: {len(hits)} coincidencia(s)")
        raise SystemExit(1)
    n_fp = len(pdf_text_fingerprints(args.informal))
    print(f"OK: 0 coincidencias en {len(files)} archivos ({n_fp} huellas comprobadas)")