mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-07-16 20:55:17 +03:00
Complete folder anatomy for all 649 cybersecurity skills + update LICENSE to Mahipal
- 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
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Agent for assessing and managing GDPR data protection compliance."""
|
||||
|
||||
import json
|
||||
import csv
|
||||
import argparse
|
||||
import re
|
||||
from datetime import datetime, timedelta
|
||||
from collections import Counter, defaultdict
|
||||
|
||||
|
||||
GDPR_ARTICLES = {
|
||||
"Art5": "Principles of processing",
|
||||
"Art6": "Lawful basis",
|
||||
"Art13": "Information to data subject (direct collection)",
|
||||
"Art14": "Information to data subject (indirect collection)",
|
||||
"Art15": "Right of access",
|
||||
"Art17": "Right to erasure",
|
||||
"Art20": "Right to data portability",
|
||||
"Art25": "Data protection by design and by default",
|
||||
"Art30": "Records of processing activities",
|
||||
"Art32": "Security of processing",
|
||||
"Art33": "Breach notification (72h to DPA)",
|
||||
"Art34": "Breach communication to data subjects",
|
||||
"Art35": "Data Protection Impact Assessment",
|
||||
"Art44": "Cross-border transfer principles",
|
||||
}
|
||||
|
||||
PII_PATTERNS = {
|
||||
"email": r"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b",
|
||||
"phone_eu": r"\b\+?[0-9]{1,3}[\s.-]?[0-9]{6,14}\b",
|
||||
"iban": r"\b[A-Z]{2}\d{2}[A-Z0-9]{11,30}\b",
|
||||
"ip_address": r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b",
|
||||
"date_of_birth": r"\b\d{2}[/.-]\d{2}[/.-]\d{4}\b",
|
||||
"national_id": r"\b\d{3}-\d{2}-\d{4}\b",
|
||||
"passport": r"\b[A-Z]{1,2}\d{6,9}\b",
|
||||
}
|
||||
|
||||
|
||||
def scan_for_pii(file_path, max_bytes=1024 * 1024):
|
||||
"""Scan a file for GDPR-relevant personal data patterns."""
|
||||
try:
|
||||
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
|
||||
content = f.read(max_bytes)
|
||||
except (OSError, PermissionError):
|
||||
return None
|
||||
matches = {}
|
||||
for pii_type, pattern in PII_PATTERNS.items():
|
||||
found = re.findall(pattern, content)
|
||||
if found:
|
||||
matches[pii_type] = len(found)
|
||||
if not matches:
|
||||
return None
|
||||
return {"file": str(file_path), "pii_types": matches,
|
||||
"total_matches": sum(matches.values()),
|
||||
"category": classify_data_category(matches)}
|
||||
|
||||
|
||||
def classify_data_category(matches):
|
||||
"""Classify data into GDPR special categories."""
|
||||
if any(k in matches for k in ["national_id", "passport"]):
|
||||
return "special_category"
|
||||
if "iban" in matches:
|
||||
return "financial"
|
||||
return "standard_personal_data"
|
||||
|
||||
|
||||
def assess_ropa_completeness(ropa_json):
|
||||
"""Assess Records of Processing Activities (ROPA) completeness per Art 30."""
|
||||
with open(ropa_json) as f:
|
||||
ropa = json.load(f)
|
||||
required_fields = [
|
||||
"controller_name", "purposes", "data_categories", "data_subjects",
|
||||
"recipients", "transfers", "retention_periods", "security_measures",
|
||||
]
|
||||
findings = []
|
||||
activities = ropa if isinstance(ropa, list) else ropa.get("activities", [])
|
||||
for activity in activities:
|
||||
missing = [f for f in required_fields if not activity.get(f)]
|
||||
findings.append({
|
||||
"activity": activity.get("name", activity.get("purpose", "unknown")),
|
||||
"complete": len(missing) == 0,
|
||||
"missing_fields": missing,
|
||||
"compliance": "COMPLIANT" if not missing else "NON_COMPLIANT",
|
||||
})
|
||||
total = len(findings)
|
||||
compliant = sum(1 for f in findings if f["compliance"] == "COMPLIANT")
|
||||
return {
|
||||
"activities_assessed": total,
|
||||
"compliant": compliant,
|
||||
"compliance_rate": round(compliant / total * 100, 1) if total else 0,
|
||||
"details": findings,
|
||||
}
|
||||
|
||||
|
||||
def assess_dsr_handling(dsr_log_path):
|
||||
"""Assess Data Subject Request handling compliance."""
|
||||
with open(dsr_log_path) as f:
|
||||
requests_log = json.load(f)
|
||||
dsrs = requests_log if isinstance(requests_log, list) else requests_log.get("requests", [])
|
||||
findings = []
|
||||
for dsr in dsrs:
|
||||
received = datetime.fromisoformat(dsr.get("received_date", "2024-01-01"))
|
||||
completed = dsr.get("completed_date")
|
||||
if completed:
|
||||
completed_dt = datetime.fromisoformat(completed)
|
||||
days = (completed_dt - received).days
|
||||
else:
|
||||
days = (datetime.utcnow() - received).days
|
||||
overdue = days > 30 # GDPR requires response within one month
|
||||
findings.append({
|
||||
"request_id": dsr.get("id", ""),
|
||||
"type": dsr.get("type", dsr.get("right", "")),
|
||||
"status": dsr.get("status", "pending"),
|
||||
"days_elapsed": days,
|
||||
"overdue": overdue,
|
||||
"severity": "HIGH" if overdue else "INFO",
|
||||
})
|
||||
overdue_count = sum(1 for f in findings if f["overdue"])
|
||||
return {
|
||||
"total_requests": len(findings),
|
||||
"overdue": overdue_count,
|
||||
"by_type": dict(Counter(f["type"] for f in findings)),
|
||||
"details": findings,
|
||||
}
|
||||
|
||||
|
||||
def assess_breach_notification_readiness(breach_log_path):
|
||||
"""Assess breach notification compliance (Art 33/34)."""
|
||||
with open(breach_log_path) as f:
|
||||
breaches = json.load(f)
|
||||
breach_list = breaches if isinstance(breaches, list) else breaches.get("breaches", [])
|
||||
findings = []
|
||||
for breach in breach_list:
|
||||
detected = datetime.fromisoformat(breach.get("detected", "2024-01-01"))
|
||||
notified = breach.get("dpa_notified")
|
||||
if notified:
|
||||
notified_dt = datetime.fromisoformat(notified)
|
||||
hours = (notified_dt - detected).total_seconds() / 3600
|
||||
else:
|
||||
hours = None
|
||||
compliant = hours is not None and hours <= 72
|
||||
findings.append({
|
||||
"breach_id": breach.get("id", ""),
|
||||
"detected": str(detected),
|
||||
"notification_hours": round(hours, 1) if hours else None,
|
||||
"art33_compliant": compliant,
|
||||
"data_subjects_affected": breach.get("subjects_affected", 0),
|
||||
"severity": breach.get("severity", "HIGH"),
|
||||
})
|
||||
return {"total_breaches": len(findings), "details": findings,
|
||||
"art33_compliance_rate": round(
|
||||
sum(1 for f in findings if f["art33_compliant"]) / len(findings) * 100, 1
|
||||
) if findings else 0}
|
||||
|
||||
|
||||
def generate_art32_checklist():
|
||||
"""Generate Article 32 security measures compliance checklist."""
|
||||
return {
|
||||
"encryption": {
|
||||
"data_at_rest": {"required": True, "standard": "AES-256"},
|
||||
"data_in_transit": {"required": True, "standard": "TLS 1.2+"},
|
||||
"key_management": {"required": True, "standard": "HSM or KMS"},
|
||||
},
|
||||
"pseudonymization": {
|
||||
"tokenization": {"required": True},
|
||||
"key_separation": {"required": True},
|
||||
},
|
||||
"access_controls": {
|
||||
"rbac": {"required": True},
|
||||
"mfa": {"required": True},
|
||||
"least_privilege": {"required": True},
|
||||
"access_reviews": {"required": True, "frequency": "quarterly"},
|
||||
},
|
||||
"resilience": {
|
||||
"backup_strategy": {"required": True},
|
||||
"disaster_recovery": {"required": True, "rto": "4h", "rpo": "1h"},
|
||||
"business_continuity": {"required": True},
|
||||
},
|
||||
"testing": {
|
||||
"penetration_testing": {"required": True, "frequency": "annual"},
|
||||
"vulnerability_scanning": {"required": True, "frequency": "monthly"},
|
||||
"security_awareness": {"required": True, "frequency": "annual"},
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="GDPR Data Protection Agent")
|
||||
parser.add_argument("--scan-pii", help="File or directory to scan for PII")
|
||||
parser.add_argument("--ropa", help="ROPA JSON file for completeness check")
|
||||
parser.add_argument("--dsr-log", help="Data Subject Request log JSON")
|
||||
parser.add_argument("--breach-log", help="Breach notification log JSON")
|
||||
parser.add_argument("--action", choices=["scan", "ropa", "dsr", "breach",
|
||||
"art32", "full"], default="full")
|
||||
parser.add_argument("--output", default="gdpr_compliance_report.json")
|
||||
args = parser.parse_args()
|
||||
|
||||
report = {"generated_at": datetime.utcnow().isoformat(), "results": {}}
|
||||
|
||||
if args.action in ("scan", "full") and args.scan_pii:
|
||||
result = scan_for_pii(args.scan_pii)
|
||||
report["results"]["pii_scan"] = result
|
||||
if result:
|
||||
print(f"[+] PII found: {result['total_matches']} matches ({result['category']})")
|
||||
else:
|
||||
print("[+] No PII detected")
|
||||
|
||||
if args.action in ("ropa", "full") and args.ropa:
|
||||
result = assess_ropa_completeness(args.ropa)
|
||||
report["results"]["ropa"] = result
|
||||
print(f"[+] ROPA compliance: {result['compliance_rate']}%")
|
||||
|
||||
if args.action in ("dsr", "full") and args.dsr_log:
|
||||
result = assess_dsr_handling(args.dsr_log)
|
||||
report["results"]["dsr"] = result
|
||||
print(f"[+] DSRs: {result['total_requests']} total, {result['overdue']} overdue")
|
||||
|
||||
if args.action in ("breach", "full") and args.breach_log:
|
||||
result = assess_breach_notification_readiness(args.breach_log)
|
||||
report["results"]["breach"] = result
|
||||
print(f"[+] Art 33 compliance rate: {result['art33_compliance_rate']}%")
|
||||
|
||||
if args.action in ("art32", "full"):
|
||||
checklist = generate_art32_checklist()
|
||||
report["results"]["art32_checklist"] = checklist
|
||||
print("[+] Article 32 security checklist generated")
|
||||
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2, default=str)
|
||||
print(f"[+] Report saved to {args.output}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user