|
| 1 | +import re |
| 2 | +import sys |
| 3 | +import json |
| 4 | +import os |
| 5 | +from typing import Union |
| 6 | + |
| 7 | + |
| 8 | +SECRET_PATTERNS = [ |
| 9 | + re.compile(r'[\'"]?subscription_id[\'"]?\s*[:=]\s*[\'"][0-9a-f\-]{36}[\'"]', re.IGNORECASE), |
| 10 | + re.compile(r'[\'"]?resource_group_name[\'"]?\s*[:=]\s*[\'"][a-zA-Z0-9\-_]+[\'"]', re.IGNORECASE), |
| 11 | + re.compile(r'[\'"]?project_name[\'"]?\s*[:=]\s*[\'"][a-zA-Z0-9\-_]+[\'"]', re.IGNORECASE), |
| 12 | + re.compile(r'[\'"]?api_key[\'"]?\s*[:=]\s*[\'"][A-Za-z0-9\-_]{40,}[\'"]', re.IGNORECASE), |
| 13 | + re.compile( |
| 14 | + r'[\'"]?azure_endpoint[\'"]?\s*[:=]\s*[\'"]https:\/\/[a-zA-Z0-9\-\.]+\.azure\.com[\/a-zA-Z0-9\.\-]*[\'"]', |
| 15 | + re.IGNORECASE, |
| 16 | + ), |
| 17 | + re.compile(r'export\s+[A-Z_][A-Z0-9_]*\s*=\s*["\'][^"\']+["\']', re.IGNORECASE), |
| 18 | + re.compile(r'os\.environ\["\s*[A-Za-z0-9_]*(API_KEY|ENDPOINT)[A-Za-z0-9_]*\s*"\]', re.IGNORECASE), |
| 19 | +] |
| 20 | + |
| 21 | + |
| 22 | +def check_ipynb_for_secrets(filename: Union[str, os.PathLike]) -> bool: |
| 23 | + """Jupyter notebooks can't be parsed directly - need to convert to JSON first""" |
| 24 | + try: |
| 25 | + with open(filename, "r", encoding="utf-8") as file: |
| 26 | + notebook_data = json.load(file) |
| 27 | + failed = False |
| 28 | + for cell in notebook_data.get("cells", []): |
| 29 | + if cell["cell_type"] == "code": |
| 30 | + for line_number, line in enumerate(cell["source"], start=1): |
| 31 | + for pattern in SECRET_PATTERNS: |
| 32 | + if pattern.search(line): |
| 33 | + print(f"Secret detected in {filename} on line {line_number}: {line.strip()}") |
| 34 | + failed = True |
| 35 | + return failed |
| 36 | + except (UnicodeDecodeError, json.JSONDecodeError) as e: |
| 37 | + print(f"Failed to read {filename}. Skipping secrets check. Error: {e}") |
| 38 | + return True |
| 39 | + |
| 40 | + |
| 41 | +def main(): |
| 42 | + failed = False |
| 43 | + |
| 44 | + for filename in sys.argv[1:]: |
| 45 | + if filename.endswith((".py", ".yaml", ".yml", ".md")): |
| 46 | + try: |
| 47 | + with open(filename, "r", encoding="utf-8") as file: |
| 48 | + for line_number, line in enumerate(file, start=1): |
| 49 | + for pattern in SECRET_PATTERNS: |
| 50 | + if pattern.search(line): |
| 51 | + print(f"Secret detected in {filename} on line {line_number}: {line.strip()}") |
| 52 | + failed = True |
| 53 | + except UnicodeDecodeError: |
| 54 | + print(f"Failed to read {filename}. Skipping secrets check.") |
| 55 | + elif filename.endswith(".ipynb"): |
| 56 | + if check_ipynb_for_secrets(filename): |
| 57 | + failed = True |
| 58 | + |
| 59 | + if failed: |
| 60 | + sys.exit(1) |
| 61 | + |
| 62 | + |
| 63 | +if __name__ == "__main__": |
| 64 | + main() |
0 commit comments