security: support private identifier deny lists

This commit is contained in:
Codex 2026-10-17 16:38:21 +02:00
parent 16c1b5f192
commit bd53ee640f
3 changed files with 43 additions and 3 deletions

View file

@ -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

View file

@ -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}")

View file

@ -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()