From bd53ee640f46ea9cf339717f214642fbcae37848 Mon Sep 17 00:00:00 2001 From: Codex Date: Sat, 17 Oct 2026 16:38:21 +0200 Subject: [PATCH] security: support private identifier deny lists --- docs/data-handling.md | 3 +++ scripts/check_public_repo.py | 26 ++++++++++++++++++++++++-- tests/test_public_repo_guard.py | 17 ++++++++++++++++- 3 files changed, 43 insertions(+), 3 deletions(-) diff --git a/docs/data-handling.md b/docs/data-handling.md index 0abaee7..b515e23 100644 --- a/docs/data-handling.md +++ b/docs/data-handling.md @@ -22,6 +22,9 @@ project artifact is created, edited, renamed, or reorganized there. - Never commit LightBurn Support Data to the public repository. - Do not execute software acquired from the installer stick during analysis. - Run the public repository guard before each public push. +- When PrivateStash is available, pass its private deny-list to the public + guard so known serials, source IDs, and local identifiers are checked without + storing those values publicly. ## Redaction flow diff --git a/scripts/check_public_repo.py b/scripts/check_public_repo.py index 734e91d..d0dd741 100644 --- a/scripts/check_public_repo.py +++ b/scripts/check_public_repo.py @@ -30,7 +30,7 @@ def tracked_files(root: Path) -> list[Path]: return [root / item.decode("utf-8") for item in result.stdout.split(b"\0") if item] -def check(root: Path) -> list[str]: +def check(root: Path, deny_patterns: tuple[str, ...] = ()) -> list[str]: failures: list[str] = [] for path in tracked_files(root): relative = path.relative_to(root).as_posix() @@ -48,14 +48,36 @@ def check(root: Path) -> list[str]: for label, pattern in SENSITIVE_TEXT.items(): if pattern.search(content): failures.append(f"{label} found in {relative}") + folded = content.casefold() + for pattern in deny_patterns: + if pattern.casefold() in folded: + failures.append(f"private deny-list token found in {relative}") return failures +def load_deny_patterns(path: Path | None) -> tuple[str, ...]: + if path is None: + return () + patterns = [] + for line in path.read_text(encoding="utf-8").splitlines(): + candidate = line.strip() + if candidate and not candidate.startswith("#"): + patterns.append(candidate) + return tuple(patterns) + + def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("root", type=Path, nargs="?", default=Path.cwd()) + parser.add_argument( + "--deny-pattern-file", + type=Path, + help="private newline-delimited literal tokens that must not appear publicly", + ) args = parser.parse_args() - failures = check(args.root.resolve()) + failures = check( + args.root.resolve(), load_deny_patterns(args.deny_pattern_file) + ) if failures: for failure in failures: print(f"ERROR: {failure}") diff --git a/tests/test_public_repo_guard.py b/tests/test_public_repo_guard.py index 37dc913..4029c4d 100644 --- a/tests/test_public_repo_guard.py +++ b/tests/test_public_repo_guard.py @@ -4,7 +4,7 @@ import unittest from pathlib import Path from unittest.mock import patch -from scripts.check_public_repo import check +from scripts.check_public_repo import check, load_deny_patterns class PublicRepoGuardTests(unittest.TestCase): @@ -36,6 +36,21 @@ class PublicRepoGuardTests(unittest.TestCase): with patch("scripts.check_public_repo.tracked_files", return_value=[candidate]): self.assertEqual(check(root), []) + def test_private_deny_list_is_applied_without_echoing_token(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + root = Path(temporary) + candidate = root / "notes.md" + candidate.write_text("machine-sensitive-value", encoding="utf-8") + with patch("scripts.check_public_repo.tracked_files", return_value=[candidate]): + failures = check(root, ("sensitive-value",)) + self.assertEqual(failures, ["private deny-list token found in notes.md"]) + + def test_deny_list_ignores_comments_and_blank_lines(self) -> None: + with tempfile.TemporaryDirectory() as temporary: + path = Path(temporary) / "deny.txt" + path.write_text("# private values\n\n first-token \nsecond-token\n", encoding="utf-8") + self.assertEqual(load_deny_patterns(path), ("first-token", "second-token")) + if __name__ == "__main__": unittest.main()