70 lines
3.2 KiB
Python
70 lines
3.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Check publishable files; print locations, never matching secret contents."""
|
|
import hashlib
|
|
import json
|
|
import re
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
REPO = Path(__file__).resolve().parents[1]
|
|
PATTERNS = {
|
|
'personal home path': re.compile(r'/home/(?!<|\{|\$)[A-Za-z0-9_.-]+'),
|
|
'machine UUID': re.compile(r'\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b', re.I),
|
|
'LAN address': re.compile(r'\b(?:192\.168\.|10\.\d+\.|172\.(?:1[6-9]|2\d|3[01])\.)\d+\.\d+\b'),
|
|
'MAC address': re.compile(r'\b(?:[0-9a-f]{2}:){5}[0-9a-f]{2}\b', re.I),
|
|
'access token': re.compile(r'\b(?:gh[pousr]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{30,}|sk-[A-Za-z0-9_-]{30,})\b'),
|
|
'email address': re.compile(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b'),
|
|
'literal secret': re.compile(r'(?im)^\s*[\w.-]*(?:password|api[_-]?key|auth[_-]?token)[\w.-]*\s*[=:]\s*["\'][^"\'\n$]{12,}["\']'),
|
|
}
|
|
|
|
|
|
def check():
|
|
result = subprocess.run(['git', 'ls-files', '--cached', '--others', '--exclude-standard'], cwd=REPO, text=True, capture_output=True)
|
|
if result.returncode:
|
|
files = [p.relative_to(REPO).as_posix() for p in REPO.rglob('*') if p.is_file()
|
|
and not any(part in ('local', 'build', '.git', '__pycache__') for part in p.relative_to(REPO).parts)]
|
|
else:
|
|
files = result.stdout.splitlines()
|
|
snapshot = json.loads((REPO / 'optional/kernel/snapshot.json').read_text())
|
|
attribution = json.loads((REPO / 'tools/public-attribution.json').read_text())
|
|
errors = []
|
|
for relative in sorted(set(files)):
|
|
path = REPO / relative
|
|
if path.is_symlink():
|
|
if path.readlink().is_absolute():
|
|
errors.append(relative + ': absolute symlink')
|
|
continue
|
|
if not path.is_file():
|
|
continue
|
|
raw = path.read_bytes()
|
|
if relative.startswith('optional/kernel/patches/'):
|
|
if hashlib.sha256(raw).hexdigest() != snapshot['patch_sha256']:
|
|
errors.append(relative + ': upstream patch checksum changed')
|
|
# Reviewed upstream source patch contains public test UUIDs and
|
|
# author addresses; only this fingerprinted artifact is exempt.
|
|
continue
|
|
if b'\0' in raw:
|
|
continue
|
|
try:
|
|
text = raw.decode('utf-8')
|
|
except UnicodeDecodeError:
|
|
continue
|
|
# Third-party licence notices must remain intact.
|
|
if path.name.lower().startswith(('license', 'copying')):
|
|
continue
|
|
for reason, pattern in PATTERNS.items():
|
|
if reason == 'email address' and attribution.get(relative) == hashlib.sha256(raw).hexdigest():
|
|
# Original upstream author/contact credits, not user account data.
|
|
# Only the reviewed exact files receive this email-only exception.
|
|
continue
|
|
for match in pattern.finditer(text):
|
|
errors.append(relative + ':' + str(text.count('\n', 0, match.start()) + 1) + ': ' + reason)
|
|
if errors:
|
|
print('\n'.join(errors))
|
|
return 1
|
|
print('Private-data checks passed (' + str(len(set(files))) + ' files).')
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
raise SystemExit(check())
|