#!/usr/bin/env python3 """Check API key age and alert if rotation needed.""" import re import sys from datetime import datetime, timezone from pathlib import Path def main() -> None: """Check API key age from keys/ metadata directory.""" print("=" * 70) print("AegisGitea MCP - API Key Age Check") print("=" * 70) print() # Check keys directory keys_dir = Path(__file__).parent.parent / "keys" if not keys_dir.exists(): print("⚠️ No keys/ directory found") print() print(" Key metadata is not being tracked.") print(" Run scripts/generate_api_key.py and save metadata to track key age.") print() sys.exit(0) # Find all key metadata files metadata_files = list(keys_dir.glob("key-*.txt")) if not metadata_files: print("⚠️ No key metadata files found in keys/") print() print(" Run scripts/generate_api_key.py and save metadata to track key age.") print() sys.exit(0) print(f"Found {len(metadata_files)} key metadata file(s)") print() print("-" * 70) print() now = datetime.now(timezone.utc) warnings = [] critical = [] for metadata_file in sorted(metadata_files): with open(metadata_file, "r", encoding="utf-8") as f: content = f.read() # Extract metadata key_id_match = re.search(r"Key ID:\s+([^\n]+)", content) created_match = re.search(r"Created:\s+([^\n]+)", content) expires_match = re.search(r"Expires:\s+([^\n]+)", content) if not all([key_id_match, created_match, expires_match]): print(f"⚠️ Could not parse: {metadata_file.name}") continue key_id = key_id_match.group(1).strip() created_str = created_match.group(1).strip() expires_str = expires_match.group(1).strip() # Parse dates try: created_at = datetime.fromisoformat(created_str.replace("Z", "+00:00")) expires_at = datetime.fromisoformat(expires_str.replace("Z", "+00:00")) except ValueError as e: print(f"⚠️ Could not parse dates in: {metadata_file.name}") print(f" Error: {e}") continue # Calculate age and time to expiration age_days = (now - created_at).days days_to_expiration = (expires_at - now).days # Status if days_to_expiration < 0: status = "❌ EXPIRED" critical.append(key_id) elif days_to_expiration <= 7: status = "⚠️ CRITICAL" critical.append(key_id) elif days_to_expiration <= 14: status = "⚠️ WARNING" warnings.append(key_id) else: status = "✓ OK" print(f"Key: {key_id}") print(f" Created: {created_at.strftime('%Y-%m-%d')} ({age_days} days ago)") print(f" Expires: {expires_at.strftime('%Y-%m-%d')} (in {days_to_expiration} days)") print(f" Status: {status}") print() print("-" * 70) print() # Summary if critical: print("❌ CRITICAL: {} key(s) require immediate rotation!".format(len(critical))) print() print(" Keys:") for key_id in critical: print(f" • {key_id}") print() print(" Action: Run scripts/rotate_api_key.py NOW") print() sys.exit(2) elif warnings: print("⚠️ WARNING: {} key(s) will expire soon".format(len(warnings))) print() print(" Keys:") for key_id in warnings: print(f" • {key_id}") print() print(" Action: Schedule key rotation in the next few days") print(" Run: scripts/rotate_api_key.py") print() sys.exit(1) else: print("✓ All keys are valid and within rotation schedule") print() print(" Next check: Run this script again in 1 week") print() sys.exit(0) if __name__ == "__main__": try: main() except KeyboardInterrupt: print("\n\nOperation cancelled.") sys.exit(1) except Exception as e: print(f"\n❌ Error: {e}", file=sys.stderr) import traceback traceback.print_exc() sys.exit(1)