Files
Anthropic-Cybersecurity-Skills/skills/securing-azure-with-microsoft-defender/scripts/agent.py
T
mukul975 c47eed6a64 Production hardening: security fixes, code quality, 724 skills complete
- Fix 25 shell=True subprocess calls with list-based commands
- Fix 49 verify=False in defensive skills (env-var override)
- Add timeout to 231 HTTP/subprocess/socket calls
- Fix 6 SQL injection patterns with whitelist validation
- Replace 8 __import__() with standard imports
- Remove 701 unused imports across 442 files
- Add authorized-testing disclaimers to all offensive skills
- Complete 11 incomplete skill directories
- Expand 10 stub SKILL.md files with full content
- Fix 2 YAML parse errors in frontmatter
- Fix 5 pre-existing syntax errors
- Convert 22 hardcoded paths/ports to environment variables
- Back up 21 redundant skill pairs to .bak
- Fix 2 global declaration errors
- 724/724 skills with full folder anatomy (SKILL.md + agent.py + api-reference.md + LICENSE)
- 0 compile errors across all 724 agent.py files
2026-03-19 13:26:49 +01:00

179 lines
6.8 KiB
Python

#!/usr/bin/env python3
"""Agent for monitoring and managing Microsoft Defender for Cloud security posture."""
import subprocess
import json
import argparse
from datetime import datetime
def run_az_command(args_list):
"""Execute an Azure CLI command and return parsed JSON output."""
cmd = ["az"] + args_list + ["--output", "json"]
try:
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
if result.returncode != 0:
print(f" [-] Error: {result.stderr.strip()}")
return None
return json.loads(result.stdout) if result.stdout.strip() else None
except (subprocess.TimeoutExpired, json.JSONDecodeError) as e:
print(f" [-] Command failed: {e}")
return None
def get_defender_plans():
"""List all Defender for Cloud pricing plans and their status."""
print("[*] Checking Defender for Cloud plan status...")
plans = run_az_command(["security", "pricing", "list"])
if not plans:
return []
enabled = []
for plan in plans:
tier = plan.get("pricingTier", "Free")
name = plan.get("name", "Unknown")
sub_plan = plan.get("subPlan", "")
status = "ENABLED" if tier == "Standard" else "FREE"
indicator = "[+]" if tier == "Standard" else "[-]"
sub_info = f" ({sub_plan})" if sub_plan else ""
print(f" {indicator} {name}: {status}{sub_info}")
if tier == "Standard":
enabled.append({"name": name, "tier": tier, "subPlan": sub_plan})
print(f"[*] {len(enabled)} plans enabled out of {len(plans)} total")
return enabled
def get_secure_score():
"""Retrieve the current Secure Score across subscriptions."""
print("\n[*] Fetching Secure Score...")
scores = run_az_command(["security", "secure-score", "list"])
if not scores:
return {}
for score in scores:
current = score.get("current", 0)
maximum = score.get("max", 0)
pct = round((current / maximum * 100), 1) if maximum > 0 else 0
print(f" Score: {current}/{maximum} ({pct}%)")
return scores
def get_security_assessments(severity_filter=None):
"""List security assessments and their health status."""
print("\n[*] Fetching security assessments...")
assessments = run_az_command(["security", "assessment", "list"])
if not assessments:
return []
unhealthy = []
for a in assessments:
props = a.get("properties", {})
status = props.get("status", {}).get("code", "")
if status == "Unhealthy":
sev = props.get("metadata", {}).get("severity", "Unknown")
display = props.get("displayName", "Unknown")
if severity_filter and sev.lower() != severity_filter.lower():
continue
unhealthy.append({"name": display, "severity": sev, "status": status})
print(f" [{sev.upper()}] {display}")
print(f"[*] {len(unhealthy)} unhealthy assessments found")
return unhealthy
def get_security_alerts(days=7):
"""Retrieve recent security alerts from Defender for Cloud."""
print(f"\n[*] Fetching security alerts (last {days} days)...")
alerts = run_az_command(["security", "alert", "list"])
if not alerts:
return []
severity_counts = {"High": 0, "Medium": 0, "Low": 0, "Informational": 0}
active_alerts = []
for alert in alerts:
props = alert.get("properties", {})
status = props.get("status", "")
if status in ("Active", "InProgress"):
sev = props.get("severity", "Unknown")
severity_counts[sev] = severity_counts.get(sev, 0) + 1
active_alerts.append({
"name": props.get("alertDisplayName", "Unknown"),
"severity": sev,
"status": status,
"timestamp": props.get("timeGeneratedUtc", ""),
})
for sev, count in severity_counts.items():
if count > 0:
print(f" [{sev}] {count} alerts")
return active_alerts
def check_security_contacts():
"""Verify security contact configuration for alert notifications."""
print("\n[*] Checking security contact configuration...")
contacts = run_az_command(["security", "contact", "list"])
if not contacts:
print(" [!] No security contacts configured")
return False
for contact in contacts:
email = contact.get("email", "Not set")
alerts = contact.get("alertNotifications", "off")
print(f" Email: {email} | Alert notifications: {alerts}")
return True
def check_auto_provisioning():
"""Check auto-provisioning settings for security agents."""
print("\n[*] Checking auto-provisioning settings...")
settings = run_az_command(["security", "auto-provisioning-setting", "list"])
if not settings:
return []
for s in settings:
name = s.get("name", "Unknown")
auto_prov = s.get("autoProvision", "Off")
indicator = "[+]" if auto_prov == "On" else "[-]"
print(f" {indicator} {name}: {auto_prov}")
return settings
def generate_posture_report(output_path):
"""Generate a comprehensive security posture report."""
print("[*] Generating security posture report...")
report = {
"report_date": datetime.now().isoformat(),
"defender_plans": get_defender_plans(),
"secure_score": get_secure_score(),
"unhealthy_assessments": get_security_assessments(),
"active_alerts": get_security_alerts(),
"contacts_configured": check_security_contacts(),
"auto_provisioning": check_auto_provisioning(),
}
with open(output_path, "w") as f:
json.dump(report, f, indent=2, default=str)
print(f"\n[*] Report saved to {output_path}")
return report
def main():
parser = argparse.ArgumentParser(description="Microsoft Defender for Cloud Security Agent")
parser.add_argument("action", choices=["plans", "score", "assessments", "alerts",
"contacts", "auto-provision", "full-report"])
parser.add_argument("--severity", choices=["high", "medium", "low"], help="Filter by severity")
parser.add_argument("--days", type=int, default=7, help="Alert lookback in days")
parser.add_argument("-o", "--output", default="defender_report.json")
args = parser.parse_args()
if args.action == "plans":
get_defender_plans()
elif args.action == "score":
get_secure_score()
elif args.action == "assessments":
get_security_assessments(args.severity)
elif args.action == "alerts":
get_security_alerts(args.days)
elif args.action == "contacts":
check_security_contacts()
elif args.action == "auto-provision":
check_auto_provisioning()
elif args.action == "full-report":
generate_posture_report(args.output)
if __name__ == "__main__":
main()