mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-08-12 21:33:20 +03:00
Initial commit - 611 cybersecurity skills across all subdomains
This commit is contained in:
@@ -0,0 +1,302 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Device Posture Assessment - Compliance Audit Tool
|
||||
|
||||
Queries CrowdStrike ZTA scores, Microsoft Intune compliance status,
|
||||
and generates a consolidated device posture compliance report.
|
||||
|
||||
Requirements:
|
||||
pip install requests msal pandas
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any
|
||||
|
||||
import requests
|
||||
|
||||
|
||||
class DevicePostureAuditor:
|
||||
"""Audit device posture compliance across EDR and MDM platforms."""
|
||||
|
||||
def __init__(self, cs_client_id: str, cs_client_secret: str,
|
||||
azure_tenant_id: str, azure_client_id: str, azure_client_secret: str):
|
||||
self.cs_client_id = cs_client_id
|
||||
self.cs_client_secret = cs_client_secret
|
||||
self.azure_tenant_id = azure_tenant_id
|
||||
self.azure_client_id = azure_client_id
|
||||
self.azure_client_secret = azure_client_secret
|
||||
self.cs_token = None
|
||||
self.azure_token = None
|
||||
|
||||
def authenticate_crowdstrike(self):
|
||||
"""Get CrowdStrike API bearer token."""
|
||||
resp = requests.post(
|
||||
"https://api.crowdstrike.com/oauth2/token",
|
||||
data={
|
||||
"client_id": self.cs_client_id,
|
||||
"client_secret": self.cs_client_secret
|
||||
},
|
||||
timeout=30
|
||||
)
|
||||
resp.raise_for_status()
|
||||
self.cs_token = resp.json()["access_token"]
|
||||
print("[AUTH] CrowdStrike authenticated")
|
||||
|
||||
def authenticate_azure(self):
|
||||
"""Get Microsoft Graph API token."""
|
||||
resp = requests.post(
|
||||
f"https://login.microsoftonline.com/{self.azure_tenant_id}/oauth2/v2.0/token",
|
||||
data={
|
||||
"client_id": self.azure_client_id,
|
||||
"client_secret": self.azure_client_secret,
|
||||
"scope": "https://graph.microsoft.com/.default",
|
||||
"grant_type": "client_credentials"
|
||||
},
|
||||
timeout=30
|
||||
)
|
||||
resp.raise_for_status()
|
||||
self.azure_token = resp.json()["access_token"]
|
||||
print("[AUTH] Microsoft Graph authenticated")
|
||||
|
||||
def get_zta_scores(self) -> dict[str, Any]:
|
||||
"""Get CrowdStrike ZTA score distribution."""
|
||||
print("\n[1/4] Querying CrowdStrike ZTA scores...")
|
||||
headers = {"Authorization": f"Bearer {self.cs_token}"}
|
||||
|
||||
# Get all device AIDs with ZTA assessments
|
||||
resp = requests.get(
|
||||
"https://api.crowdstrike.com/zero-trust-assessment/queries/assessments/v1",
|
||||
headers=headers,
|
||||
params={"limit": 5000},
|
||||
timeout=60
|
||||
)
|
||||
resp.raise_for_status()
|
||||
device_ids = resp.json().get("resources", [])
|
||||
|
||||
if not device_ids:
|
||||
print(" No ZTA assessments found")
|
||||
return {"total": 0, "distribution": {}}
|
||||
|
||||
# Get detailed assessments in batches of 100
|
||||
scores = []
|
||||
for i in range(0, len(device_ids), 100):
|
||||
batch = device_ids[i:i+100]
|
||||
resp = requests.get(
|
||||
"https://api.crowdstrike.com/zero-trust-assessment/entities/assessments/v1",
|
||||
headers=headers,
|
||||
params={"ids": batch},
|
||||
timeout=60
|
||||
)
|
||||
if resp.status_code == 200:
|
||||
for resource in resp.json().get("resources", []):
|
||||
assessment = resource.get("assessment", {})
|
||||
scores.append({
|
||||
"aid": resource.get("aid"),
|
||||
"overall": assessment.get("overall", 0),
|
||||
"os_score": assessment.get("os", 0),
|
||||
"sensor_score": assessment.get("sensor_config", 0)
|
||||
})
|
||||
|
||||
# Calculate distribution
|
||||
distribution = {
|
||||
"critical_90_100": sum(1 for s in scores if s["overall"] >= 90),
|
||||
"high_80_89": sum(1 for s in scores if 80 <= s["overall"] < 90),
|
||||
"medium_65_79": sum(1 for s in scores if 65 <= s["overall"] < 80),
|
||||
"low_50_64": sum(1 for s in scores if 50 <= s["overall"] < 65),
|
||||
"blocked_below_50": sum(1 for s in scores if s["overall"] < 50),
|
||||
}
|
||||
avg_score = sum(s["overall"] for s in scores) / len(scores) if scores else 0
|
||||
|
||||
print(f" Total devices: {len(scores)}")
|
||||
print(f" Average ZTA score: {avg_score:.1f}")
|
||||
print(f" Distribution: {distribution}")
|
||||
|
||||
return {
|
||||
"total": len(scores),
|
||||
"average_score": round(avg_score, 1),
|
||||
"distribution": distribution,
|
||||
"below_threshold_50": distribution["blocked_below_50"]
|
||||
}
|
||||
|
||||
def get_intune_compliance(self) -> dict[str, Any]:
|
||||
"""Get Microsoft Intune device compliance status."""
|
||||
print("\n[2/4] Querying Intune compliance status...")
|
||||
headers = {"Authorization": f"Bearer {self.azure_token}"}
|
||||
|
||||
resp = requests.get(
|
||||
"https://graph.microsoft.com/v1.0/deviceManagement/managedDevices",
|
||||
headers=headers,
|
||||
params={
|
||||
"$select": "id,deviceName,userPrincipalName,complianceState,"
|
||||
"operatingSystem,osVersion,isEncrypted,lastSyncDateTime,"
|
||||
"managementAgent",
|
||||
"$top": 999
|
||||
},
|
||||
timeout=60
|
||||
)
|
||||
resp.raise_for_status()
|
||||
devices = resp.json().get("value", [])
|
||||
|
||||
stats = {
|
||||
"total": len(devices),
|
||||
"compliant": 0,
|
||||
"noncompliant": 0,
|
||||
"in_grace_period": 0,
|
||||
"unknown": 0,
|
||||
"os_distribution": {},
|
||||
"encryption_status": {"encrypted": 0, "not_encrypted": 0},
|
||||
"stale_devices": 0,
|
||||
"noncompliant_details": []
|
||||
}
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
for device in devices:
|
||||
compliance = device.get("complianceState", "unknown")
|
||||
os_name = device.get("operatingSystem", "unknown")
|
||||
encrypted = device.get("isEncrypted", False)
|
||||
|
||||
stats["os_distribution"][os_name] = stats["os_distribution"].get(os_name, 0) + 1
|
||||
|
||||
if encrypted:
|
||||
stats["encryption_status"]["encrypted"] += 1
|
||||
else:
|
||||
stats["encryption_status"]["not_encrypted"] += 1
|
||||
|
||||
if compliance == "compliant":
|
||||
stats["compliant"] += 1
|
||||
elif compliance == "noncompliant":
|
||||
stats["noncompliant"] += 1
|
||||
stats["noncompliant_details"].append({
|
||||
"device": device.get("deviceName"),
|
||||
"user": device.get("userPrincipalName"),
|
||||
"os": f"{os_name} {device.get('osVersion', '')}",
|
||||
"encrypted": encrypted
|
||||
})
|
||||
elif compliance == "inGracePeriod":
|
||||
stats["in_grace_period"] += 1
|
||||
else:
|
||||
stats["unknown"] += 1
|
||||
|
||||
# Check for stale devices (no sync in 30 days)
|
||||
last_sync = device.get("lastSyncDateTime")
|
||||
if last_sync:
|
||||
try:
|
||||
sync_dt = datetime.fromisoformat(last_sync.replace("Z", "+00:00"))
|
||||
if (now - sync_dt).days > 30:
|
||||
stats["stale_devices"] += 1
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
|
||||
compliance_rate = (stats["compliant"] / stats["total"] * 100) if stats["total"] else 0
|
||||
print(f" Total: {stats['total']}, Compliant: {stats['compliant']} ({compliance_rate:.1f}%)")
|
||||
print(f" Non-compliant: {stats['noncompliant']}, Grace period: {stats['in_grace_period']}")
|
||||
print(f" Encryption: {stats['encryption_status']}")
|
||||
print(f" Stale devices (>30d no sync): {stats['stale_devices']}")
|
||||
|
||||
return stats
|
||||
|
||||
def correlate_posture(self, zta: dict, intune: dict) -> dict[str, Any]:
|
||||
"""Correlate ZTA and MDM compliance for overall posture score."""
|
||||
print("\n[3/4] Correlating posture signals...")
|
||||
|
||||
total_devices = max(zta["total"], intune["total"])
|
||||
zta_passing = zta["total"] - zta.get("below_threshold_50", 0)
|
||||
intune_passing = intune["compliant"] + intune["in_grace_period"]
|
||||
|
||||
overall_compliance = min(
|
||||
(zta_passing / zta["total"] * 100) if zta["total"] else 0,
|
||||
(intune_passing / intune["total"] * 100) if intune["total"] else 0
|
||||
)
|
||||
|
||||
summary = {
|
||||
"estimated_total_devices": total_devices,
|
||||
"zta_passing_rate": round((zta_passing / zta["total"] * 100) if zta["total"] else 0, 1),
|
||||
"intune_passing_rate": round((intune_passing / intune["total"] * 100) if intune["total"] else 0, 1),
|
||||
"overall_compliance_rate": round(overall_compliance, 1),
|
||||
"risk_level": "LOW" if overall_compliance >= 90 else "MEDIUM" if overall_compliance >= 75 else "HIGH"
|
||||
}
|
||||
|
||||
print(f" ZTA passing: {summary['zta_passing_rate']}%")
|
||||
print(f" Intune passing: {summary['intune_passing_rate']}%")
|
||||
print(f" Overall compliance: {summary['overall_compliance_rate']}%")
|
||||
print(f" Risk level: {summary['risk_level']}")
|
||||
|
||||
return summary
|
||||
|
||||
def generate_report(self, zta: dict, intune: dict, correlated: dict) -> str:
|
||||
"""Generate consolidated posture report."""
|
||||
print("\n[4/4] Generating report...")
|
||||
now = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
|
||||
|
||||
report = f"""
|
||||
Device Posture Compliance Report
|
||||
{'=' * 55}
|
||||
Generated: {now}
|
||||
|
||||
1. CROWDSTRIKE ZTA SCORES
|
||||
Total devices assessed: {zta['total']}
|
||||
Average ZTA score: {zta['average_score']}
|
||||
Score >= 90 (Critical OK): {zta['distribution'].get('critical_90_100', 0)}
|
||||
Score 80-89 (High OK): {zta['distribution'].get('high_80_89', 0)}
|
||||
Score 65-79 (Medium OK): {zta['distribution'].get('medium_65_79', 0)}
|
||||
Score 50-64 (Low): {zta['distribution'].get('low_50_64', 0)}
|
||||
Score < 50 (BLOCKED): {zta['distribution'].get('blocked_below_50', 0)}
|
||||
|
||||
2. INTUNE COMPLIANCE
|
||||
Total managed devices: {intune['total']}
|
||||
Compliant: {intune['compliant']}
|
||||
Non-compliant: {intune['noncompliant']}
|
||||
In grace period: {intune['in_grace_period']}
|
||||
Stale (>30d no sync): {intune['stale_devices']}
|
||||
Encrypted: {intune['encryption_status']['encrypted']}
|
||||
Not encrypted: {intune['encryption_status']['not_encrypted']}
|
||||
|
||||
3. OVERALL POSTURE
|
||||
ZTA passing rate: {correlated['zta_passing_rate']}%
|
||||
Intune passing rate: {correlated['intune_passing_rate']}%
|
||||
Combined compliance: {correlated['overall_compliance_rate']}%
|
||||
Risk level: {correlated['risk_level']}
|
||||
|
||||
4. RECOMMENDATIONS
|
||||
"""
|
||||
recs = []
|
||||
if zta["distribution"].get("blocked_below_50", 0) > 0:
|
||||
recs.append(f" - {zta['distribution']['blocked_below_50']} devices below ZTA 50 - investigate immediately")
|
||||
if intune["encryption_status"]["not_encrypted"] > 0:
|
||||
recs.append(f" - {intune['encryption_status']['not_encrypted']} devices lack encryption - enforce BitLocker/FileVault")
|
||||
if intune["stale_devices"] > 0:
|
||||
recs.append(f" - {intune['stale_devices']} stale devices - verify active use or remove")
|
||||
if correlated["overall_compliance_rate"] < 95:
|
||||
recs.append(f" - Overall compliance {correlated['overall_compliance_rate']}% below 95% target")
|
||||
if not recs:
|
||||
recs.append(" - All devices meet compliance requirements")
|
||||
report += "\n".join(recs)
|
||||
return report
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 6:
|
||||
print("Usage: python process.py <cs_client_id> <cs_client_secret> "
|
||||
"<azure_tenant_id> <azure_client_id> <azure_client_secret>")
|
||||
sys.exit(1)
|
||||
|
||||
auditor = DevicePostureAuditor(*sys.argv[1:6])
|
||||
auditor.authenticate_crowdstrike()
|
||||
auditor.authenticate_azure()
|
||||
|
||||
zta = auditor.get_zta_scores()
|
||||
intune = auditor.get_intune_compliance()
|
||||
correlated = auditor.correlate_posture(zta, intune)
|
||||
report = auditor.generate_report(zta, intune, correlated)
|
||||
print(report)
|
||||
|
||||
filename = f"device_posture_report_{datetime.now().strftime('%Y%m%d')}.txt"
|
||||
with open(filename, "w") as f:
|
||||
f.write(report)
|
||||
print(f"\nReport saved to: {filename}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user