89 lines
2.6 KiB
Python
Executable File
89 lines
2.6 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Pre-commit hook to prevent committing secret files that are not encrypted with Ansible Vault.
|
|
|
|
This hook checks if any staged files contain 'secret' or 'secrets' in their filename.
|
|
If such files are found and they don't start with '$ANSIBLE_VAULT', the commit is blocked.
|
|
"""
|
|
|
|
import sys
|
|
import subprocess
|
|
import os
|
|
|
|
|
|
ANSIBLE_VAULT_MARKER = '$ANSIBLE_VAULT'
|
|
|
|
|
|
def get_staged_files():
|
|
"""Get list of staged files for commit."""
|
|
try:
|
|
result = subprocess.run(
|
|
['git', 'diff', '--cached', '--name-only'],
|
|
capture_output=True,
|
|
text=True,
|
|
check=True
|
|
)
|
|
return result.stdout.strip().split('\n') if result.stdout.strip() else []
|
|
except subprocess.CalledProcessError as e:
|
|
print(f"Error getting staged files: {e}")
|
|
return []
|
|
|
|
|
|
def has_secret_in_name(filename):
|
|
"""Check if filename contains 'secret' or 'secrets'."""
|
|
basename = os.path.basename(filename).lower()
|
|
return 'secret' in basename or 'secrets' in basename
|
|
|
|
|
|
def is_ansible_vault_file(filepath):
|
|
"""Check if file starts with special vault marker."""
|
|
try:
|
|
if not os.path.exists(filepath):
|
|
return False
|
|
|
|
with open(filepath, 'r', encoding='utf-8') as f:
|
|
first_line = f.readline().strip()
|
|
return first_line.startswith(ANSIBLE_VAULT_MARKER)
|
|
except (IOError, UnicodeDecodeError):
|
|
# If we can't read the file or it's binary, assume it's not a vault file
|
|
return False
|
|
|
|
|
|
def main():
|
|
"""Main function to check staged files."""
|
|
staged_files = get_staged_files()
|
|
|
|
if not staged_files:
|
|
return 0
|
|
|
|
violations = []
|
|
|
|
for filepath in staged_files:
|
|
if has_secret_in_name(filepath):
|
|
if not is_ansible_vault_file(filepath):
|
|
violations.append(filepath)
|
|
|
|
if violations:
|
|
print("❌ COMMIT BLOCKED: Secret files must be encrypted with Ansible Vault!")
|
|
print("\nThe following files contain 'secret' or 'secrets' in their name")
|
|
print("but are not encrypted with Ansible Vault:")
|
|
print()
|
|
|
|
for violation in violations:
|
|
print(f" • {violation}")
|
|
|
|
print()
|
|
print("To fix this issue:")
|
|
print("1. Encrypt the file(s) with: ansible-vault encrypt <filename>")
|
|
print("2. Or rename the file(s) to not contain 'secret' or 'secrets'")
|
|
print("3. Or add the file(s) to .gitignore if they shouldn't be committed")
|
|
print()
|
|
|
|
return 1
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|