mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-22 04:30:59 +03:00
- Add scripts/agent.py and references/api-reference.md to all remaining skills - Update all 648 LICENSE files: copyright now reads 'Mahipal' - Add implementing-security-monitoring-with-datadog (new skill with full anatomy) - All 649 skills now have: SKILL.md, LICENSE, scripts/agent.py, references/api-reference.md
177 lines
7.0 KiB
Python
177 lines
7.0 KiB
Python
#!/usr/bin/env python3
|
|
"""Agent for performing paste site monitoring for leaked credentials."""
|
|
|
|
import json
|
|
import argparse
|
|
import re
|
|
import time
|
|
from datetime import datetime
|
|
|
|
try:
|
|
import requests
|
|
except ImportError:
|
|
requests = None
|
|
|
|
|
|
def check_haveibeenpwned(email, api_key=None):
|
|
"""Check if an email appears in Have I Been Pwned breaches."""
|
|
headers = {"User-Agent": "PasteMonitor-Agent"}
|
|
if api_key:
|
|
headers["hibp-api-key"] = api_key
|
|
try:
|
|
resp = requests.get(f"https://haveibeenpwned.com/api/v3/breachedaccount/{email}",
|
|
headers=headers, timeout=15)
|
|
if resp.status_code == 200:
|
|
breaches = resp.json()
|
|
return {
|
|
"email": email, "breached": True,
|
|
"breach_count": len(breaches),
|
|
"breaches": [{"name": b["Name"], "date": b["BreachDate"],
|
|
"data_classes": b["DataClasses"]} for b in breaches[:10]],
|
|
}
|
|
elif resp.status_code == 404:
|
|
return {"email": email, "breached": False}
|
|
else:
|
|
return {"email": email, "error": f"HTTP {resp.status_code}"}
|
|
except Exception as e:
|
|
return {"email": email, "error": str(e)}
|
|
|
|
|
|
def check_paste_dumps(email, api_key=None):
|
|
"""Check for email in paste site dumps via HIBP paste API."""
|
|
headers = {"User-Agent": "PasteMonitor-Agent"}
|
|
if api_key:
|
|
headers["hibp-api-key"] = api_key
|
|
try:
|
|
resp = requests.get(f"https://haveibeenpwned.com/api/v3/pasteaccount/{email}",
|
|
headers=headers, timeout=15)
|
|
if resp.status_code == 200:
|
|
pastes = resp.json()
|
|
return {
|
|
"email": email, "found_in_pastes": True,
|
|
"paste_count": len(pastes),
|
|
"pastes": [{"source": p.get("Source"), "title": p.get("Title", "")[:100],
|
|
"date": p.get("Date"), "email_count": p.get("EmailCount")}
|
|
for p in pastes[:15]],
|
|
}
|
|
elif resp.status_code == 404:
|
|
return {"email": email, "found_in_pastes": False}
|
|
else:
|
|
return {"email": email, "error": f"HTTP {resp.status_code}"}
|
|
except Exception as e:
|
|
return {"email": email, "error": str(e)}
|
|
|
|
|
|
def bulk_check_domain(domain, email_list_file, api_key=None):
|
|
"""Check all emails from a domain for breach exposure."""
|
|
from pathlib import Path
|
|
emails = [e.strip() for e in Path(email_list_file).read_text().splitlines() if e.strip() and domain in e]
|
|
results = []
|
|
for email in emails[:50]:
|
|
result = check_haveibeenpwned(email, api_key)
|
|
results.append(result)
|
|
time.sleep(1.6)
|
|
breached = [r for r in results if r.get("breached")]
|
|
return {
|
|
"domain": domain,
|
|
"emails_checked": len(results),
|
|
"breached_accounts": len(breached),
|
|
"exposure_rate": round(len(breached) / max(len(results), 1) * 100, 1),
|
|
"results": results,
|
|
}
|
|
|
|
|
|
def scan_text_for_credentials(text_file):
|
|
"""Scan a text file (paste dump) for credential patterns."""
|
|
from pathlib import Path
|
|
content = Path(text_file).read_text(encoding="utf-8", errors="replace")
|
|
patterns = {
|
|
"email_password": re.compile(r"([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})\s*[:|;]\s*(\S+)"),
|
|
"username_password": re.compile(r"(?:user(?:name)?|login)\s*[:|=]\s*(\S+)\s+(?:pass(?:word)?)\s*[:|=]\s*(\S+)", re.I),
|
|
"api_key": re.compile(r"(?:api[_-]?key|apikey|access[_-]?token)\s*[:|=]\s*['\"]?([a-zA-Z0-9_\-]{20,})['\"]?", re.I),
|
|
"aws_key": re.compile(r"AKIA[0-9A-Z]{16}"),
|
|
"private_key": re.compile(r"-----BEGIN (?:RSA |EC )?PRIVATE KEY-----"),
|
|
"jwt_token": re.compile(r"eyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+"),
|
|
}
|
|
findings = {}
|
|
for name, pattern in patterns.items():
|
|
matches = pattern.findall(content)
|
|
if matches:
|
|
if isinstance(matches[0], tuple):
|
|
findings[name] = [{"match": m[0][:50]} for m in matches[:20]]
|
|
else:
|
|
findings[name] = [{"match": m[:50]} for m in matches[:20]]
|
|
domains = re.findall(r"@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})", content)
|
|
domain_counts = {}
|
|
for d in domains:
|
|
domain_counts[d] = domain_counts.get(d, 0) + 1
|
|
return {
|
|
"file": text_file,
|
|
"credential_types_found": list(findings.keys()),
|
|
"total_matches": sum(len(v) for v in findings.values()),
|
|
"findings": findings,
|
|
"affected_domains": dict(sorted(domain_counts.items(), key=lambda x: -x[1])[:15]),
|
|
}
|
|
|
|
|
|
def generate_exposure_report(domain, email_list_file, api_key=None):
|
|
"""Generate credential exposure report for an organization."""
|
|
bulk = bulk_check_domain(domain, email_list_file, api_key)
|
|
return {
|
|
"generated": datetime.utcnow().isoformat(),
|
|
"domain": domain,
|
|
"summary": {
|
|
"emails_checked": bulk["emails_checked"],
|
|
"breached": bulk["breached_accounts"],
|
|
"exposure_rate": bulk["exposure_rate"],
|
|
},
|
|
"recommendations": [
|
|
"Force password reset for all breached accounts",
|
|
"Enable MFA for all accounts",
|
|
"Monitor for credential stuffing attacks",
|
|
"Implement password breach detection (e.g., Azure AD Password Protection)",
|
|
],
|
|
"details": bulk["results"][:20],
|
|
}
|
|
|
|
|
|
def main():
|
|
if not requests:
|
|
print(json.dumps({"error": "requests not installed"}))
|
|
return
|
|
parser = argparse.ArgumentParser(description="Paste Site Credential Monitoring Agent")
|
|
parser.add_argument("--api-key", help="HIBP API key")
|
|
sub = parser.add_subparsers(dest="command")
|
|
c = sub.add_parser("check", help="Check single email")
|
|
c.add_argument("--email", required=True)
|
|
p = sub.add_parser("pastes", help="Check paste dumps")
|
|
p.add_argument("--email", required=True)
|
|
b = sub.add_parser("bulk", help="Bulk domain check")
|
|
b.add_argument("--domain", required=True)
|
|
b.add_argument("--emails", required=True, help="Email list file")
|
|
s = sub.add_parser("scan", help="Scan text for credentials")
|
|
s.add_argument("--file", required=True)
|
|
r = sub.add_parser("report", help="Exposure report")
|
|
r.add_argument("--domain", required=True)
|
|
r.add_argument("--emails", required=True)
|
|
args = parser.parse_args()
|
|
api_key = args.api_key if hasattr(args, "api_key") else None
|
|
if args.command == "check":
|
|
result = check_haveibeenpwned(args.email, api_key)
|
|
elif args.command == "pastes":
|
|
result = check_paste_dumps(args.email, api_key)
|
|
elif args.command == "bulk":
|
|
result = bulk_check_domain(args.domain, args.emails, api_key)
|
|
elif args.command == "scan":
|
|
result = scan_text_for_credentials(args.file)
|
|
elif args.command == "report":
|
|
result = generate_exposure_report(args.domain, args.emails, api_key)
|
|
else:
|
|
parser.print_help()
|
|
return
|
|
print(json.dumps(result, indent=2, default=str))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|