Ctrl K
ComunidadGuías…

tests/test_no_private_paths.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""The tracked tree carries no private paths, hosts, addresses or session trailers.

Written after the 2026-08-19 scrub was undone twelve days later by ordinary
feature work: a scrub is a state, not a commit, and nothing was checking the
state.

The identifiers are not in the repository -- spelling them out, even in
fragments, would publish them. They are read, one per line, from the
environment variable JOBD_PRIVACY_FORBIDDEN (CI sets it from the
PRIVACY_FORBIDDEN repository secret) or else from
~/.config/jobd/privacy-forbidden.txt. With neither, both scan tests FAIL rather
than pass on an empty list. Hits are reported by entry number (#k), never by
the matched text, so a CI log cannot print what it found.
"""

from __future__ import annotations

import os
import subprocess
from pathlib import Path

import pytest

ROOT = Path(__file__).resolve().parents[1]

_ENV = "JOBD_PRIVACY_FORBIDDEN"
_FILE = Path.home() / ".config/jobd/privacy-forbidden.txt"
_raw = os.environ.get(_ENV) or (_FILE.read_text(encoding="utf-8") if _FILE.is_file() else "")
FORBIDDEN = [line.strip() for line in _raw.splitlines() if line.strip()]


def _require_list() -> None:
    if not FORBIDDEN:
        pytest.fail(f"no forbidden list: set {_ENV} or write {_FILE} (one identifier per line)")


def scan(paths: list[Path]) -> list[str]:
    hits: list[str] = []
    needles = [n.encode() for n in FORBIDDEN]
    for p in paths:
        try:
            data = p.read_bytes()
        except (IsADirectoryError, FileNotFoundError):
            continue
        for i, line in enumerate(data.split(b"\n"), 1):
            for k, n in enumerate(needles, 1):
                if n in line:
                    hits.append(f"{p.relative_to(ROOT) if p.is_relative_to(ROOT) else p}:{i}: #{k}")
    return hits


def _tracked() -> list[Path]:
    out = subprocess.run(
        ["git", "ls-files", "-z"], cwd=ROOT, capture_output=True, check=True
    ).stdout
    return [ROOT / f.decode() for f in out.split(b"\0") if f]


def test_tracked_tree_has_no_private_paths():
    _require_list()
    files = _tracked()
    assert Path(__file__) in files, "this guard must itself be tracked"
    hits = scan(files)
    assert hits == [], "private content is tracked:\n" + "\n".join(hits)


def test_scanner_reports_every_planted_hit(tmp_path):
    """Positive control: the scanner is worth exactly what it can catch."""
    _require_list()
    planted = tmp_path / "planted.bin"
    planted.write_bytes(b"\x89PNG\n" + b"\n".join(n.encode() for n in FORBIDDEN) + b"\n")
    hits = scan([planted])
    found = {h.split(": ", 1)[1] for h in hits}
    want = {f"#{k}" for k in range(1, len(FORBIDDEN) + 1)}
    assert found == want, sorted(want - found)
    assert all(h.split(":")[1].isdigit() for h in hits)


def test_the_private_overlay_is_gitignored():
    """The real roots and names live in config/projects.local.yaml. If git does
    not ignore that path, the deploy step that copies it beside projects.yaml
    turns the next `git add -A` into a publication of everything this file
    exists to keep private. (#129 appended the rule as one line containing
    literal backslash-n sequences, so the pattern never matched.)"""
    r = subprocess.run(
        ["git", "check-ignore", "-q", "config/projects.local.yaml"], cwd=ROOT, check=False
    )
    assert r.returncode == 0, "config/projects.local.yaml is not gitignored"