548 lines
23 KiB
Python
548 lines
23 KiB
Python
"""Unit-Tests für das hooks-Modul.
|
|
|
|
Tests für:
|
|
- HookManager: Installation, Deinstallation, Validierung
|
|
- Read-Only-Schutz: Prüfung gestaged Dateien in geschützten Pfaden
|
|
- Secret-Verschlüsselungs-Prüfung: Unverschlüsselte Secrets blockieren
|
|
- Integration mit ContextGuard und SecretEncryptionManager
|
|
- Hook-Installation bei `init`
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import stat
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
import yaml
|
|
|
|
from monorepo.hooks import HookManager, install_hooks, uninstall_hooks
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Fixtures
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest.fixture
|
|
def monorepo_root(tmp_path: Path) -> Path:
|
|
"""Erstellt ein minimales Monorepo mit .git-Verzeichnis und Konfiguration."""
|
|
root = tmp_path / "monorepo"
|
|
root.mkdir()
|
|
|
|
# Git-Verzeichnis simulieren
|
|
git_dir = root / ".git"
|
|
git_dir.mkdir()
|
|
(git_dir / "hooks").mkdir()
|
|
|
|
# Kontextordner
|
|
for ctx in ["privat", "dhive", "bahn", "shared"]:
|
|
(root / ctx).mkdir()
|
|
|
|
# shared/config
|
|
config_dir = root / "shared" / "config"
|
|
config_dir.mkdir(parents=True)
|
|
|
|
# repos.yaml mit einem Read-Only-Repo
|
|
repos_config = {
|
|
"repos": [
|
|
{
|
|
"name": "symphony-spec",
|
|
"url": "https://github.com/openai/symphony",
|
|
"mode": "read-only",
|
|
"target": "shared/references/symphony",
|
|
"pinned": "v1.0.0",
|
|
"mechanism": "subtree",
|
|
},
|
|
{
|
|
"name": "db-wissensdatenbank",
|
|
"url": "https://gitlab.2700.2db.it/team/db-wissen.git",
|
|
"mode": "upstream",
|
|
"target": "bahn/db-wissensdatenbank",
|
|
"pinned": "main",
|
|
"mechanism": "subtree",
|
|
},
|
|
]
|
|
}
|
|
with open(config_dir / "repos.yaml", "w", encoding="utf-8") as f:
|
|
yaml.dump(repos_config, f)
|
|
|
|
# access-config.yaml (für ContextGuard)
|
|
access_config = {
|
|
"contexts": {
|
|
"privat": {
|
|
"env_file": "privat/.env",
|
|
"allowed_shared": ["shared/tools/", "shared/powers/", "shared/config/"],
|
|
},
|
|
"dhive": {
|
|
"env_file": "dhive/.env",
|
|
"allowed_shared": ["shared/tools/", "shared/powers/", "shared/config/"],
|
|
},
|
|
"bahn": {
|
|
"env_file": "bahn/.env",
|
|
"allowed_shared": ["shared/tools/", "shared/powers/", "shared/config/"],
|
|
},
|
|
"shared": {
|
|
"env_file": "shared/.env",
|
|
"allowed_shared": ["*"],
|
|
},
|
|
}
|
|
}
|
|
with open(config_dir / "access-config.yaml", "w", encoding="utf-8") as f:
|
|
yaml.dump(access_config, f)
|
|
|
|
# .gitattributes im privat-Kontext (mit git-crypt-Filter)
|
|
privat_ga = root / "privat" / ".gitattributes"
|
|
privat_ga.write_text(
|
|
"# git-crypt Filter für Kontext: privat\n"
|
|
".env filter=git-crypt-privat diff=git-crypt-privat\n"
|
|
"*.pem filter=git-crypt-privat diff=git-crypt-privat\n"
|
|
"*.key filter=git-crypt-privat diff=git-crypt-privat\n"
|
|
"*token* filter=git-crypt-privat diff=git-crypt-privat\n"
|
|
"*secret* filter=git-crypt-privat diff=git-crypt-privat\n",
|
|
encoding="utf-8",
|
|
)
|
|
|
|
return root
|
|
|
|
|
|
@pytest.fixture
|
|
def hook_manager(monorepo_root: Path) -> HookManager:
|
|
"""Erstellt einen HookManager für das Test-Monorepo."""
|
|
return HookManager(monorepo_root=monorepo_root)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tests: Installation und Deinstallation
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestHookInstallation:
|
|
"""Tests für die Hook-Installation und -Deinstallation."""
|
|
|
|
def test_install_creates_hook_file(self, hook_manager: HookManager, monorepo_root: Path):
|
|
"""Hook-Installation erstellt die pre-commit Datei."""
|
|
hook_manager.install_hooks()
|
|
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
|
assert hook_path.exists()
|
|
|
|
def test_hook_is_executable(self, hook_manager: HookManager, monorepo_root: Path):
|
|
"""Installierter Hook hat Ausführungsrechte (Unix) bzw. existiert (Windows)."""
|
|
hook_manager.install_hooks()
|
|
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
|
if os.name != "nt":
|
|
mode = hook_path.stat().st_mode
|
|
assert mode & stat.S_IXUSR # Owner execute
|
|
else:
|
|
# Windows unterstützt keine Unix-Permissions, nur Existenz prüfen
|
|
assert hook_path.exists()
|
|
|
|
def test_hook_contains_shebang(self, hook_manager: HookManager, monorepo_root: Path):
|
|
"""Installierter Hook beginnt mit Shebang."""
|
|
hook_manager.install_hooks()
|
|
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
|
content = hook_path.read_text(encoding="utf-8")
|
|
assert content.startswith("#!/bin/sh")
|
|
|
|
def test_hook_contains_markers(self, hook_manager: HookManager, monorepo_root: Path):
|
|
"""Installierter Hook enthält Start- und End-Marker."""
|
|
hook_manager.install_hooks()
|
|
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
|
content = hook_path.read_text(encoding="utf-8")
|
|
assert "# === Monorepo Integrated Pre-Commit Hook (auto-generiert) ===" in content
|
|
assert "# === Ende Monorepo Integrated Pre-Commit Hook ===" in content
|
|
|
|
def test_hook_contains_readonly_section(self, hook_manager: HookManager, monorepo_root: Path):
|
|
"""Installierter Hook enthält Read-Only-Schutz-Abschnitt."""
|
|
hook_manager.install_hooks()
|
|
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
|
content = hook_path.read_text(encoding="utf-8")
|
|
assert "Read-Only-Repos Schutz" in content
|
|
assert "shared/references/symphony" in content
|
|
|
|
def test_hook_contains_secrets_section(self, hook_manager: HookManager, monorepo_root: Path):
|
|
"""Installierter Hook enthält Secret-Prüfungs-Abschnitt."""
|
|
hook_manager.install_hooks()
|
|
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
|
content = hook_path.read_text(encoding="utf-8")
|
|
assert "Unverschlüsselte Secrets Prüfung" in content
|
|
assert "check_encrypted" in content
|
|
assert "is_secret_file" in content
|
|
|
|
def test_hook_preserves_existing_content(self, hook_manager: HookManager, monorepo_root: Path):
|
|
"""Installation bewahrt bestehende Hook-Inhalte."""
|
|
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
|
hook_path.write_text("#!/bin/sh\n# Custom Hook Logic\necho 'custom'\n", encoding="utf-8")
|
|
|
|
hook_manager.install_hooks()
|
|
content = hook_path.read_text(encoding="utf-8")
|
|
|
|
# Bestehender Inhalt muss erhalten bleiben
|
|
assert "# Custom Hook Logic" in content
|
|
assert "echo 'custom'" in content
|
|
# Neuer Monorepo-Abschnitt muss vorhanden sein
|
|
assert "Monorepo Integrated Pre-Commit Hook" in content
|
|
|
|
def test_hook_update_replaces_existing_section(self, hook_manager: HookManager, monorepo_root: Path):
|
|
"""Erneute Installation aktualisiert nur den Monorepo-Abschnitt."""
|
|
hook_manager.install_hooks()
|
|
|
|
# Zweite Installation (simuliert Update)
|
|
hook_manager.install_hooks()
|
|
|
|
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
|
content = hook_path.read_text(encoding="utf-8")
|
|
|
|
# Sollte genau einmal den Marker enthalten (kein doppelter Abschnitt)
|
|
assert content.count("# === Monorepo Integrated Pre-Commit Hook") == 1
|
|
|
|
def test_uninstall_removes_section(self, hook_manager: HookManager, monorepo_root: Path):
|
|
"""Deinstallation entfernt den Monorepo-Abschnitt."""
|
|
hook_manager.install_hooks()
|
|
hook_manager.uninstall_hooks()
|
|
|
|
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
|
# Hook-Datei sollte entfernt sein (war nur Monorepo-Inhalt)
|
|
assert not hook_path.exists()
|
|
|
|
def test_uninstall_preserves_custom_content(self, hook_manager: HookManager, monorepo_root: Path):
|
|
"""Deinstallation bewahrt benutzerdefinierte Hook-Inhalte."""
|
|
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
|
hook_path.write_text("#!/bin/sh\n# Custom Logic\necho 'keep'\n", encoding="utf-8")
|
|
|
|
hook_manager.install_hooks()
|
|
hook_manager.uninstall_hooks()
|
|
|
|
assert hook_path.exists()
|
|
content = hook_path.read_text(encoding="utf-8")
|
|
assert "# Custom Logic" in content
|
|
assert "Monorepo Integrated Pre-Commit Hook" not in content
|
|
|
|
def test_is_hook_installed(self, hook_manager: HookManager):
|
|
"""is_hook_installed gibt korrekt True/False zurück."""
|
|
assert not hook_manager.is_hook_installed()
|
|
hook_manager.install_hooks()
|
|
assert hook_manager.is_hook_installed()
|
|
|
|
def test_uninstall_when_no_hook(self, hook_manager: HookManager):
|
|
"""Deinstallation ohne vorhandenen Hook läuft ohne Fehler."""
|
|
# Sollte keine Exception werfen
|
|
hook_manager.uninstall_hooks()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tests: Read-Only-Schutz
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestReadOnlyProtection:
|
|
"""Tests für die Read-Only-Repo-Schutzprüfung."""
|
|
|
|
def test_readonly_path_is_detected(self, hook_manager: HookManager):
|
|
"""Gestagte Dateien in Read-Only-Repos werden blockiert."""
|
|
errors = hook_manager.validate_staged_files([
|
|
"shared/references/symphony/README.md",
|
|
])
|
|
assert len(errors) == 1
|
|
assert "read-only" in errors[0]
|
|
assert "symphony" in errors[0]
|
|
|
|
def test_readonly_subdirectory_is_protected(self, hook_manager: HookManager):
|
|
"""Dateien in Unterverzeichnissen von Read-Only-Repos werden blockiert."""
|
|
errors = hook_manager.validate_staged_files([
|
|
"shared/references/symphony/src/main.py",
|
|
])
|
|
assert len(errors) == 1
|
|
assert "read-only" in errors[0]
|
|
|
|
def test_non_readonly_repo_is_allowed(self, hook_manager: HookManager):
|
|
"""Dateien in Upstream-Repos werden nicht blockiert (nur Read-Only)."""
|
|
errors = hook_manager.validate_staged_files([
|
|
"bahn/db-wissensdatenbank/docs/README.md",
|
|
])
|
|
# Upstream-Repos sollten nicht blockiert werden
|
|
readonly_errors = [e for e in errors if "read-only" in e]
|
|
assert len(readonly_errors) == 0
|
|
|
|
def test_normal_files_are_allowed(self, hook_manager: HookManager):
|
|
"""Normale Dateien außerhalb von Read-Only-Repos werden durchgelassen."""
|
|
errors = hook_manager.validate_staged_files([
|
|
"privat/my-project/src/main.py",
|
|
"dhive/tooling/config.yaml",
|
|
])
|
|
assert len(errors) == 0
|
|
|
|
def test_multiple_violations_reported(self, hook_manager: HookManager):
|
|
"""Mehrere Verstöße werden alle gemeldet."""
|
|
errors = hook_manager.validate_staged_files([
|
|
"shared/references/symphony/file1.py",
|
|
"shared/references/symphony/file2.py",
|
|
])
|
|
assert len(errors) == 2
|
|
|
|
def test_protected_paths_returned(self, hook_manager: HookManager):
|
|
"""get_protected_paths gibt die konfigurierten Read-Only-Pfade zurück."""
|
|
paths = hook_manager.get_protected_paths()
|
|
assert "shared/references/symphony" in paths
|
|
# Upstream-Repos sollten NICHT in der geschützten Liste sein
|
|
assert "bahn/db-wissensdatenbank" not in paths
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tests: Secret-Verschlüsselungs-Prüfung
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestSecretEncryptionCheck:
|
|
"""Tests für die Prüfung unverschlüsselter Secrets."""
|
|
|
|
def test_env_file_without_encryption_blocked(self, hook_manager: HookManager, monorepo_root: Path):
|
|
"""Unverschlüsselte .env-Datei wird blockiert (kein Filter konfiguriert)."""
|
|
# Erstelle eine .env im dhive-Kontext OHNE git-crypt-Filter
|
|
env_path = monorepo_root / "dhive" / ".env"
|
|
env_path.write_text("SECRET_KEY=abc123\n", encoding="utf-8")
|
|
|
|
errors = hook_manager.validate_staged_files(["dhive/.env"])
|
|
assert len(errors) == 1
|
|
assert "nicht verschlüsselt" in errors[0]
|
|
|
|
def test_env_file_with_filter_allowed(self, hook_manager: HookManager, monorepo_root: Path):
|
|
"""Unverschlüsselte .env-Datei mit git-crypt-Filter wird durchgelassen."""
|
|
# privat/.env hat einen git-crypt-Filter in .gitattributes
|
|
env_path = monorepo_root / "privat" / ".env"
|
|
env_path.write_text("MY_SECRET=xyz\n", encoding="utf-8")
|
|
|
|
errors = hook_manager.validate_staged_files(["privat/.env"])
|
|
assert len(errors) == 0
|
|
|
|
def test_pem_file_without_filter_blocked(self, hook_manager: HookManager, monorepo_root: Path):
|
|
"""Unverschlüsselte .pem-Datei ohne Filter wird blockiert."""
|
|
pem_path = monorepo_root / "dhive" / "cert.pem"
|
|
pem_path.write_text("-----BEGIN CERTIFICATE-----\n...\n", encoding="utf-8")
|
|
|
|
errors = hook_manager.validate_staged_files(["dhive/cert.pem"])
|
|
assert len(errors) == 1
|
|
assert "nicht verschlüsselt" in errors[0]
|
|
|
|
def test_pem_file_with_filter_allowed(self, hook_manager: HookManager, monorepo_root: Path):
|
|
"""Unverschlüsselte .pem-Datei mit git-crypt-Filter wird durchgelassen."""
|
|
pem_path = monorepo_root / "privat" / "server.pem"
|
|
pem_path.write_text("-----BEGIN RSA PRIVATE KEY-----\n...\n", encoding="utf-8")
|
|
|
|
errors = hook_manager.validate_staged_files(["privat/server.pem"])
|
|
assert len(errors) == 0
|
|
|
|
def test_key_file_detected(self, hook_manager: HookManager, monorepo_root: Path):
|
|
"""*.key-Dateien werden als Secrets erkannt."""
|
|
key_path = monorepo_root / "dhive" / "private.key"
|
|
key_path.write_text("key-data\n", encoding="utf-8")
|
|
|
|
errors = hook_manager.validate_staged_files(["dhive/private.key"])
|
|
assert len(errors) == 1
|
|
|
|
def test_token_file_detected(self, hook_manager: HookManager, monorepo_root: Path):
|
|
"""Dateien mit 'token' im Namen werden als Secrets erkannt."""
|
|
token_path = monorepo_root / "dhive" / "api-token.txt"
|
|
token_path.write_text("token123\n", encoding="utf-8")
|
|
|
|
errors = hook_manager.validate_staged_files(["dhive/api-token.txt"])
|
|
assert len(errors) == 1
|
|
|
|
def test_secret_file_detected(self, hook_manager: HookManager, monorepo_root: Path):
|
|
"""Dateien mit 'secret' im Namen werden als Secrets erkannt."""
|
|
secret_path = monorepo_root / "dhive" / "my-secret-config.yaml"
|
|
secret_path.write_text("data: secret\n", encoding="utf-8")
|
|
|
|
errors = hook_manager.validate_staged_files(["dhive/my-secret-config.yaml"])
|
|
assert len(errors) == 1
|
|
|
|
def test_encrypted_file_is_allowed(self, hook_manager: HookManager, monorepo_root: Path):
|
|
"""Verschlüsselte Dateien (mit git-crypt-Header) werden durchgelassen."""
|
|
# Simuliere eine git-crypt-verschlüsselte Datei
|
|
enc_path = monorepo_root / "dhive" / ".env"
|
|
enc_path.write_bytes(b"\x00GITCRYPT\x00" + b"encrypted-data")
|
|
|
|
errors = hook_manager.validate_staged_files(["dhive/.env"])
|
|
assert len(errors) == 0
|
|
|
|
def test_normal_files_not_flagged(self, hook_manager: HookManager, monorepo_root: Path):
|
|
"""Normale Dateien (nicht Secret-Patterns) werden nicht geprüft."""
|
|
py_path = monorepo_root / "privat" / "main.py"
|
|
py_path.write_text("print('hello')\n", encoding="utf-8")
|
|
|
|
errors = hook_manager.validate_staged_files(["privat/main.py"])
|
|
assert len(errors) == 0
|
|
|
|
def test_gitattributes_pattern_matching(self, hook_manager: HookManager):
|
|
"""Pattern-Matching für .gitattributes funktioniert korrekt."""
|
|
# Interne Methode testen
|
|
assert hook_manager._pattern_matches_file(".env", ".env")
|
|
assert hook_manager._pattern_matches_file("*.pem", "server.pem")
|
|
assert hook_manager._pattern_matches_file("*.key", "private.key")
|
|
assert hook_manager._pattern_matches_file("*token*", "api-token.txt")
|
|
assert hook_manager._pattern_matches_file("*secret*", "my-secret-config.yaml")
|
|
|
|
# Negative Cases
|
|
assert not hook_manager._pattern_matches_file(".env", ".envrc")
|
|
assert not hook_manager._pattern_matches_file("*.pem", "file.txt")
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tests: Convenience-Funktionen und CLI-Integration
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestConvenienceFunctions:
|
|
"""Tests für install_hooks/uninstall_hooks Convenience-Funktionen."""
|
|
|
|
def test_install_hooks_function(self, monorepo_root: Path):
|
|
"""install_hooks()-Funktion erstellt den Hook erfolgreich."""
|
|
install_hooks(monorepo_root)
|
|
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
|
assert hook_path.exists()
|
|
content = hook_path.read_text(encoding="utf-8")
|
|
assert "Monorepo Integrated Pre-Commit Hook" in content
|
|
|
|
def test_uninstall_hooks_function(self, monorepo_root: Path):
|
|
"""uninstall_hooks()-Funktion entfernt den Hook."""
|
|
install_hooks(monorepo_root)
|
|
uninstall_hooks(monorepo_root)
|
|
hook_path = monorepo_root / ".git" / "hooks" / "pre-commit"
|
|
assert not hook_path.exists()
|
|
|
|
def test_install_without_access_config(self, tmp_path: Path):
|
|
"""Installation funktioniert auch ohne access-config.yaml."""
|
|
root = tmp_path / "bare-mono"
|
|
root.mkdir()
|
|
(root / ".git" / "hooks").mkdir(parents=True)
|
|
(root / "shared" / "config").mkdir(parents=True)
|
|
|
|
# Nur repos.yaml, keine access-config.yaml
|
|
repos = {"repos": []}
|
|
with open(root / "shared" / "config" / "repos.yaml", "w") as f:
|
|
yaml.dump(repos, f)
|
|
|
|
install_hooks(root)
|
|
hook_path = root / ".git" / "hooks" / "pre-commit"
|
|
assert hook_path.exists()
|
|
|
|
def test_install_without_repos_yaml(self, tmp_path: Path):
|
|
"""Installation funktioniert auch ohne repos.yaml (leere Schutzliste)."""
|
|
root = tmp_path / "bare-mono"
|
|
root.mkdir()
|
|
(root / ".git" / "hooks").mkdir(parents=True)
|
|
(root / "shared" / "config").mkdir(parents=True)
|
|
|
|
install_hooks(root)
|
|
hook_path = root / ".git" / "hooks" / "pre-commit"
|
|
assert hook_path.exists()
|
|
content = hook_path.read_text(encoding="utf-8")
|
|
assert "Keine Read-Only-Repos konfiguriert" in content
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tests: ContextGuard-Integration
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestContextGuardIntegration:
|
|
"""Tests für die Integration mit dem ContextGuard."""
|
|
|
|
def test_hook_manager_with_context_guard(self, monorepo_root: Path):
|
|
"""HookManager funktioniert mit eingebundenem ContextGuard."""
|
|
from monorepo.security import ContextGuard
|
|
|
|
guard = ContextGuard(monorepo_root)
|
|
hm = HookManager(
|
|
monorepo_root=monorepo_root,
|
|
context_guard=guard,
|
|
)
|
|
|
|
# Normale Datei sollte kein Problem sein
|
|
errors = hm.validate_staged_files(["privat/project/main.py"])
|
|
assert len(errors) == 0
|
|
|
|
def test_hook_manager_without_context_guard(self, monorepo_root: Path):
|
|
"""HookManager funktioniert auch ohne ContextGuard."""
|
|
hm = HookManager(monorepo_root=monorepo_root)
|
|
errors = hm.validate_staged_files(["privat/project/main.py"])
|
|
assert len(errors) == 0
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tests: SecretEncryptionManager-Integration
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestEncryptionManagerIntegration:
|
|
"""Tests für die Integration mit dem SecretEncryptionManager."""
|
|
|
|
def test_hook_manager_with_encryption_manager(self, monorepo_root: Path):
|
|
"""HookManager funktioniert mit eingebundenem SecretEncryptionManager."""
|
|
from monorepo.encryption import SecretEncryptionManager
|
|
from monorepo.models import MachineContext
|
|
|
|
mc = MachineContext(
|
|
name="test-machine",
|
|
description="Test",
|
|
authorized_contexts=["privat", "dhive", "bahn"],
|
|
key_source="keyring",
|
|
)
|
|
enc = SecretEncryptionManager(monorepo_root, mc)
|
|
|
|
hm = HookManager(
|
|
monorepo_root=monorepo_root,
|
|
encryption_manager=enc,
|
|
)
|
|
hm.install_hooks()
|
|
assert hm.is_hook_installed()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Tests: is_secret_file Erkennung
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
class TestSecretFileDetection:
|
|
"""Tests für die _is_secret_file Methode."""
|
|
|
|
def test_env_detected(self, hook_manager: HookManager):
|
|
"""Standard .env wird erkannt."""
|
|
assert hook_manager._is_secret_file("privat/.env")
|
|
assert hook_manager._is_secret_file("dhive/.env")
|
|
assert hook_manager._is_secret_file(".env")
|
|
|
|
def test_env_prefix_detected(self, hook_manager: HookManager):
|
|
""".env.local und ähnliche werden erkannt."""
|
|
assert hook_manager._is_secret_file("privat/.env.local")
|
|
assert hook_manager._is_secret_file(".env.production")
|
|
|
|
def test_pem_detected(self, hook_manager: HookManager):
|
|
""".pem-Dateien werden erkannt."""
|
|
assert hook_manager._is_secret_file("certs/server.pem")
|
|
assert hook_manager._is_secret_file("privat/my.pem")
|
|
|
|
def test_key_detected(self, hook_manager: HookManager):
|
|
""".key-Dateien werden erkannt."""
|
|
assert hook_manager._is_secret_file("privat/ssl.key")
|
|
assert hook_manager._is_secret_file("private.key")
|
|
|
|
def test_token_detected(self, hook_manager: HookManager):
|
|
"""Dateien mit 'token' werden erkannt."""
|
|
assert hook_manager._is_secret_file("api-token.txt")
|
|
assert hook_manager._is_secret_file("github_token")
|
|
assert hook_manager._is_secret_file("TOKEN_FILE")
|
|
|
|
def test_secret_detected(self, hook_manager: HookManager):
|
|
"""Dateien mit 'secret' werden erkannt."""
|
|
assert hook_manager._is_secret_file("my-secret.yaml")
|
|
assert hook_manager._is_secret_file("SECRET_CONFIG")
|
|
|
|
def test_normal_files_not_detected(self, hook_manager: HookManager):
|
|
"""Normale Dateien werden nicht als Secrets erkannt."""
|
|
assert not hook_manager._is_secret_file("main.py")
|
|
assert not hook_manager._is_secret_file("README.md")
|
|
assert not hook_manager._is_secret_file("package.json")
|
|
assert not hook_manager._is_secret_file("config.yaml")
|