mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-06-13 06:34:57 +03:00
c21af3347e
- 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
202 lines
8.2 KiB
Python
202 lines
8.2 KiB
Python
#!/usr/bin/env python3
|
|
"""Agent for assessing ISO 27001:2022 ISMS compliance."""
|
|
|
|
import json
|
|
import argparse
|
|
from datetime import datetime
|
|
from collections import Counter
|
|
|
|
ANNEX_A_CATEGORIES = {
|
|
"A.5": {"name": "Organizational controls", "count": 37},
|
|
"A.6": {"name": "People controls", "count": 8},
|
|
"A.7": {"name": "Physical controls", "count": 14},
|
|
"A.8": {"name": "Technological controls", "count": 34},
|
|
}
|
|
|
|
REQUIRED_DOCUMENTS = [
|
|
"ISMS Scope (4.3)", "Information Security Policy (5.2)",
|
|
"Risk Assessment Methodology (6.1.2)", "Risk Treatment Plan (6.1.3)",
|
|
"Statement of Applicability (6.1.3d)", "Information Security Objectives (6.2)",
|
|
"Evidence of Competence (7.2)", "Documented Operating Procedures (8.1)",
|
|
"Risk Assessment Results (8.2)", "Risk Treatment Results (8.3)",
|
|
"Monitoring and Measurement Results (9.1)", "Internal Audit Program (9.2)",
|
|
"Management Review Results (9.3)", "Nonconformities and Corrective Actions (10.1)",
|
|
]
|
|
|
|
|
|
def assess_soa_completeness(soa_path):
|
|
"""Assess Statement of Applicability completeness."""
|
|
with open(soa_path) as f:
|
|
soa = json.load(f)
|
|
controls = soa if isinstance(soa, list) else soa.get("controls", [])
|
|
total = len(controls)
|
|
implemented = sum(1 for c in controls if c.get("status", "").lower() == "implemented")
|
|
partially = sum(1 for c in controls if c.get("status", "").lower() == "partial")
|
|
not_implemented = sum(1 for c in controls if c.get("status", "").lower() == "not_implemented")
|
|
excluded = sum(1 for c in controls if c.get("status", "").lower() == "excluded")
|
|
|
|
missing_justification = [c for c in controls
|
|
if c.get("status", "").lower() == "excluded"
|
|
and not c.get("justification")]
|
|
findings = []
|
|
if missing_justification:
|
|
findings.append({
|
|
"issue": f"{len(missing_justification)} excluded controls without justification",
|
|
"severity": "HIGH",
|
|
"controls": [c.get("id", "") for c in missing_justification],
|
|
})
|
|
|
|
no_evidence = [c for c in controls
|
|
if c.get("status", "").lower() == "implemented"
|
|
and not c.get("evidence")]
|
|
if no_evidence:
|
|
findings.append({
|
|
"issue": f"{len(no_evidence)} implemented controls without evidence",
|
|
"severity": "MEDIUM",
|
|
})
|
|
|
|
return {
|
|
"total_controls": total,
|
|
"implemented": implemented,
|
|
"partial": partially,
|
|
"not_implemented": not_implemented,
|
|
"excluded": excluded,
|
|
"implementation_rate": round(implemented / total * 100, 1) if total else 0,
|
|
"findings": findings,
|
|
}
|
|
|
|
|
|
def assess_documentation(docs_inventory_path):
|
|
"""Check required ISMS documentation against inventory."""
|
|
with open(docs_inventory_path) as f:
|
|
inventory = json.load(f)
|
|
docs = inventory if isinstance(inventory, list) else inventory.get("documents", [])
|
|
doc_names = [d.get("name", d.get("title", "")).lower() for d in docs]
|
|
|
|
missing = []
|
|
for req in REQUIRED_DOCUMENTS:
|
|
found = any(req.lower().split("(")[0].strip() in name for name in doc_names)
|
|
if not found:
|
|
missing.append(req)
|
|
|
|
outdated = [d for d in docs
|
|
if d.get("last_review") and
|
|
(datetime.utcnow() - datetime.fromisoformat(
|
|
d["last_review"].replace("Z", ""))).days > 365]
|
|
|
|
return {
|
|
"required": len(REQUIRED_DOCUMENTS),
|
|
"present": len(REQUIRED_DOCUMENTS) - len(missing),
|
|
"missing": missing,
|
|
"outdated_documents": len(outdated),
|
|
"compliance_rate": round(
|
|
(len(REQUIRED_DOCUMENTS) - len(missing)) / len(REQUIRED_DOCUMENTS) * 100, 1),
|
|
}
|
|
|
|
|
|
def assess_risk_register(risk_register_path):
|
|
"""Assess risk register for completeness and currency."""
|
|
with open(risk_register_path) as f:
|
|
register = json.load(f)
|
|
risks = register if isinstance(register, list) else register.get("risks", [])
|
|
findings = []
|
|
by_level = Counter()
|
|
|
|
for risk in risks:
|
|
level = risk.get("risk_level", risk.get("rating", "unknown")).lower()
|
|
by_level[level] += 1
|
|
if not risk.get("treatment", risk.get("mitigation")):
|
|
findings.append({
|
|
"risk": risk.get("id", risk.get("name", "")),
|
|
"issue": "No treatment plan defined",
|
|
"severity": "HIGH",
|
|
})
|
|
if not risk.get("owner"):
|
|
findings.append({
|
|
"risk": risk.get("id", ""),
|
|
"issue": "No risk owner assigned",
|
|
"severity": "MEDIUM",
|
|
})
|
|
if risk.get("treatment", "").lower() == "accept" and not risk.get("acceptance_authority"):
|
|
findings.append({
|
|
"risk": risk.get("id", ""),
|
|
"issue": "Risk accepted without management approval",
|
|
"severity": "HIGH",
|
|
})
|
|
|
|
return {
|
|
"total_risks": len(risks),
|
|
"by_level": dict(by_level),
|
|
"findings": findings,
|
|
"untreated": sum(1 for r in risks if not r.get("treatment")),
|
|
}
|
|
|
|
|
|
def generate_gap_analysis(current_state):
|
|
"""Generate ISO 27001 gap analysis from current state assessment."""
|
|
clauses = {
|
|
"4_context": ["scope_defined", "interested_parties", "isms_scope_document"],
|
|
"5_leadership": ["security_policy", "roles_responsibilities", "management_commitment"],
|
|
"6_planning": ["risk_methodology", "risk_assessment", "soa", "security_objectives"],
|
|
"7_support": ["resources", "competence", "awareness", "communication", "documented_info"],
|
|
"8_operation": ["operational_planning", "risk_assessment_results", "risk_treatment"],
|
|
"9_evaluation": ["monitoring", "internal_audit", "management_review"],
|
|
"10_improvement": ["nonconformity_handling", "corrective_actions", "continual_improvement"],
|
|
}
|
|
gaps = {}
|
|
for clause, requirements in clauses.items():
|
|
clause_state = current_state.get(clause, {})
|
|
met = sum(1 for r in requirements if clause_state.get(r, False))
|
|
gaps[clause] = {
|
|
"total": len(requirements),
|
|
"met": met,
|
|
"gaps": [r for r in requirements if not clause_state.get(r, False)],
|
|
"compliance": round(met / len(requirements) * 100, 1),
|
|
}
|
|
return gaps
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description="ISO 27001 ISMS Compliance Agent")
|
|
parser.add_argument("--soa", help="Statement of Applicability JSON")
|
|
parser.add_argument("--docs", help="Documentation inventory JSON")
|
|
parser.add_argument("--risks", help="Risk register JSON")
|
|
parser.add_argument("--state", help="Current state assessment JSON for gap analysis")
|
|
parser.add_argument("--action", choices=["soa", "docs", "risks", "gaps", "full"],
|
|
default="full")
|
|
parser.add_argument("--output", default="iso27001_report.json")
|
|
args = parser.parse_args()
|
|
|
|
report = {"generated_at": datetime.utcnow().isoformat(), "results": {}}
|
|
|
|
if args.action in ("soa", "full") and args.soa:
|
|
result = assess_soa_completeness(args.soa)
|
|
report["results"]["soa"] = result
|
|
print(f"[+] SoA: {result['implementation_rate']}% implemented")
|
|
|
|
if args.action in ("docs", "full") and args.docs:
|
|
result = assess_documentation(args.docs)
|
|
report["results"]["documentation"] = result
|
|
print(f"[+] Documentation: {result['compliance_rate']}%, {len(result['missing'])} missing")
|
|
|
|
if args.action in ("risks", "full") and args.risks:
|
|
result = assess_risk_register(args.risks)
|
|
report["results"]["risks"] = result
|
|
print(f"[+] Risk register: {result['total_risks']} risks, {result['untreated']} untreated")
|
|
|
|
if args.action in ("gaps", "full") and args.state:
|
|
with open(args.state) as f:
|
|
state = json.load(f)
|
|
gaps = generate_gap_analysis(state)
|
|
report["results"]["gap_analysis"] = gaps
|
|
avg = sum(g["compliance"] for g in gaps.values()) / len(gaps)
|
|
print(f"[+] Gap analysis: {avg:.1f}% average compliance")
|
|
|
|
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()
|