mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-08-31 21:50:50 +03:00
Add GDPR compliance assessment skill
Adds comprehensive GDPR compliance assessment skill covering Article 30 records, lawful basis validation, data subject rights, DPIAs, breach notification, international transfers, and technical/organizational measures. Features: - 295-line skill body (under 500-line cap) - 1008-char description with negative triggers (under 1024 limit) - 9 files total, all within skill directory - 3 production scripts: article30_parser.py, article30_validator.py, generate_ropa_report.py - Detailed workflow, templates, and references Negative triggers direct users to: - implementing-gdpr-data-protection-controls for Article 32 technical controls - implementing-gdpr-data-subject-access-request for DSAR automation Legal basis: EU Regulation 2016/679, UK GDPR as amended by Data Protection Act 2018 and Data (Use and Access) Act 2025. Effective date: August 2026. Validation: validate-skill.py PASS, lint-descriptions.py PASS
This commit is contained in:
@@ -0,0 +1,186 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
GDPR Article 30 RoPA Validator.
|
||||
|
||||
Validates a Records of Processing Activities (RoPA) JSON file against Article 30
|
||||
mandatory fields and flags incomplete or missing entries.
|
||||
|
||||
Checks:
|
||||
- All mandatory Article 30 fields present
|
||||
- Retention periods specified (or criteria documented)
|
||||
- International transfers have documented safeguards
|
||||
- Special category data properly flagged
|
||||
- Security measures documented
|
||||
|
||||
Usage:
|
||||
python article30_validator.py --ropa ropa.json
|
||||
python article30_validator.py --ropa ropa.json --check-retention --check-transfers --strict
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
|
||||
MANDATORY_FIELDS = [
|
||||
"purposes",
|
||||
"data_subjects",
|
||||
"personal_data_categories",
|
||||
"recipients"
|
||||
]
|
||||
|
||||
SPECIAL_CATEGORY_KEYWORDS = [
|
||||
"health", "medical", "biometric", "genetic", "racial", "ethnic",
|
||||
"political", "religious", "trade union", "sex life", "sexual orientation"
|
||||
]
|
||||
|
||||
def validate_entry(entry, index, args):
|
||||
"""Validate a single processing activity entry."""
|
||||
issues = []
|
||||
entry_id = entry.get("source_file", f"Entry {index}")
|
||||
|
||||
# Check mandatory fields
|
||||
for field in MANDATORY_FIELDS:
|
||||
if field not in entry or not entry[field]:
|
||||
issues.append({
|
||||
"severity": "ERROR",
|
||||
"field": field,
|
||||
"message": f"Missing mandatory field: {field}"
|
||||
})
|
||||
elif isinstance(entry[field], list) and len(entry[field]) == 0:
|
||||
issues.append({
|
||||
"severity": "ERROR",
|
||||
"field": field,
|
||||
"message": f"Empty list for mandatory field: {field}"
|
||||
})
|
||||
|
||||
# Check retention period
|
||||
if args.check_retention:
|
||||
if "retention_period" not in entry or not entry["retention_period"]:
|
||||
issues.append({
|
||||
"severity": "WARNING",
|
||||
"field": "retention_period",
|
||||
"message": "Retention period not specified (Article 30(1)(f))"
|
||||
})
|
||||
elif "[TO BE COMPLETED]" in str(entry.get("retention_period", "")):
|
||||
issues.append({
|
||||
"severity": "WARNING",
|
||||
"field": "retention_period",
|
||||
"message": "Retention period placeholder not completed"
|
||||
})
|
||||
|
||||
# Check international transfers
|
||||
if args.check_transfers:
|
||||
transfers = entry.get("international_transfers", [])
|
||||
if len(transfers) > 0:
|
||||
# Check if safeguards are documented
|
||||
safeguards_mentioned = False
|
||||
for transfer in transfers:
|
||||
transfer_lower = str(transfer).lower()
|
||||
if any(word in transfer_lower for word in ["scc", "standard contractual clause", "adequacy", "bcr", "binding corporate rule"]):
|
||||
safeguards_mentioned = True
|
||||
break
|
||||
|
||||
if not safeguards_mentioned:
|
||||
issues.append({
|
||||
"severity": "ERROR",
|
||||
"field": "international_transfers",
|
||||
"message": "International transfers identified but no safeguards documented (Chapter V)"
|
||||
})
|
||||
|
||||
# Check for special category data without additional legal basis
|
||||
personal_data_str = " ".join(entry.get("personal_data_categories", [])).lower()
|
||||
if any(keyword in personal_data_str for keyword in SPECIAL_CATEGORY_KEYWORDS):
|
||||
if "special_category_legal_basis" not in entry:
|
||||
issues.append({
|
||||
"severity": "WARNING",
|
||||
"field": "special_category_legal_basis",
|
||||
"message": "Possible special category data (Article 9) but no additional legal basis documented"
|
||||
})
|
||||
|
||||
# Check security measures
|
||||
if not entry.get("security_measures"):
|
||||
issues.append({
|
||||
"severity": "WARNING",
|
||||
"field": "security_measures",
|
||||
"message": "No security measures documented (Article 32)"
|
||||
})
|
||||
|
||||
return entry_id, issues
|
||||
|
||||
def generate_report(results, args):
|
||||
"""Generate validation report."""
|
||||
total_errors = sum(1 for _, issues in results for issue in issues if issue["severity"] == "ERROR")
|
||||
total_warnings = sum(1 for _, issues in results for issue in issues if issue["severity"] == "WARNING")
|
||||
|
||||
print(f"\n{'='*70}")
|
||||
print(f"GDPR Article 30 RoPA Validation Report")
|
||||
print(f"Generated: {datetime.now(timezone.utc).isoformat()}")
|
||||
print(f"{'='*70}\n")
|
||||
|
||||
print(f"Total Entries: {len(results)}")
|
||||
print(f"Total Errors: {total_errors}")
|
||||
print(f"Total Warnings: {total_warnings}")
|
||||
print()
|
||||
|
||||
if total_errors == 0 and total_warnings == 0:
|
||||
print("✓ All entries passed validation")
|
||||
return 0
|
||||
|
||||
for entry_id, issues in results:
|
||||
if not issues:
|
||||
continue
|
||||
|
||||
print(f"\n{entry_id}")
|
||||
print(f"{'-'*70}")
|
||||
for issue in issues:
|
||||
icon = "✗" if issue["severity"] == "ERROR" else "⚠"
|
||||
print(f" {icon} [{issue['severity']}] {issue['field']}: {issue['message']}")
|
||||
|
||||
print(f"\n{'='*70}")
|
||||
if total_errors > 0:
|
||||
print(f"VALIDATION FAILED: {total_errors} error(s) found")
|
||||
print("Fix errors before using this RoPA for compliance purposes.")
|
||||
return 1
|
||||
else:
|
||||
print(f"VALIDATION PASSED with {total_warnings} warning(s)")
|
||||
print("Review warnings and complete missing optional fields.")
|
||||
return 0
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="Validate GDPR Article 30 RoPA")
|
||||
parser.add_argument("--ropa", required=True, help="Path to RoPA JSON file")
|
||||
parser.add_argument("--check-retention", action="store_true",
|
||||
help="Validate retention periods are specified")
|
||||
parser.add_argument("--check-transfers", action="store_true",
|
||||
help="Validate international transfer safeguards")
|
||||
parser.add_argument("--strict", action="store_true",
|
||||
help="Treat warnings as errors")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
with open(args.ropa, 'r') as f:
|
||||
ropa = json.load(f)
|
||||
except (OSError, json.JSONDecodeError) as e:
|
||||
print(f"ERROR: Could not read RoPA file: {e}", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
processing_activities = ropa.get("processing_activities", [])
|
||||
if not processing_activities:
|
||||
print("ERROR: No processing activities found in RoPA", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
results = []
|
||||
for i, entry in enumerate(processing_activities):
|
||||
entry_id, issues = validate_entry(entry, i, args)
|
||||
if args.strict:
|
||||
# Promote warnings to errors in strict mode
|
||||
for issue in issues:
|
||||
if issue["severity"] == "WARNING":
|
||||
issue["severity"] = "ERROR"
|
||||
results.append((entry_id, issues))
|
||||
|
||||
return generate_report(results, args)
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user