mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-08-06 10:50:19 +03:00
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
This commit is contained in:
@@ -1,28 +1,210 @@
|
||||
# API Reference: Ransomware backup strategy audit
|
||||
# API Reference: Ransomware Backup Strategy Audit
|
||||
|
||||
## API Endpoints
|
||||
S3: list_buckets, get_bucket_versioning; AWS Backup: list_backup_plans; 3-2-1 rule
|
||||
## Libraries Used
|
||||
|
||||
| Library | Purpose |
|
||||
|---------|---------|
|
||||
| `boto3` | AWS SDK for S3, AWS Backup, and IAM auditing |
|
||||
| `json` | Parse backup policies and compliance data |
|
||||
| `subprocess` | Run local backup verification commands |
|
||||
| `datetime` | Calculate backup age and RPO/RTO compliance |
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pip install boto3 subprocess
|
||||
pip install boto3
|
||||
```
|
||||
|
||||
## Libraries
|
||||
|
||||
| Library | Use |
|
||||
|---------|-----|
|
||||
| `boto3` | boto3 SDK/client |
|
||||
| `subprocess` | subprocess SDK/client |
|
||||
|
||||
## Authentication
|
||||
|
||||
| Method | Header |
|
||||
|--------|--------|
|
||||
| Bearer Token | `Authorization: Bearer <token>` |
|
||||
| API Key | `X-API-Key: <key>` |
|
||||
```python
|
||||
import boto3
|
||||
import os
|
||||
|
||||
session = boto3.Session(
|
||||
aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID"),
|
||||
aws_secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY"),
|
||||
region_name=os.environ.get("AWS_REGION", "us-east-1"),
|
||||
)
|
||||
|
||||
s3 = session.client("s3")
|
||||
backup = session.client("backup")
|
||||
iam = session.client("iam")
|
||||
```
|
||||
|
||||
## AWS S3 Backup Audit
|
||||
|
||||
### Check Bucket Versioning (Ransomware Recovery)
|
||||
```python
|
||||
def audit_s3_versioning():
|
||||
findings = []
|
||||
buckets = s3.list_buckets()["Buckets"]
|
||||
for bucket in buckets:
|
||||
name = bucket["Name"]
|
||||
versioning = s3.get_bucket_versioning(Bucket=name)
|
||||
status = versioning.get("Status", "Disabled")
|
||||
mfa_delete = versioning.get("MFADelete", "Disabled")
|
||||
|
||||
if status != "Enabled":
|
||||
findings.append({
|
||||
"bucket": name,
|
||||
"issue": "Versioning not enabled",
|
||||
"severity": "high",
|
||||
"remediation": "Enable versioning for ransomware recovery",
|
||||
})
|
||||
if mfa_delete != "Enabled":
|
||||
findings.append({
|
||||
"bucket": name,
|
||||
"issue": "MFA Delete not enabled",
|
||||
"severity": "medium",
|
||||
"remediation": "Enable MFA Delete to prevent bulk deletion",
|
||||
})
|
||||
return findings
|
||||
```
|
||||
|
||||
### Check Object Lock (Immutable Backups)
|
||||
```python
|
||||
def check_object_lock(bucket_name):
|
||||
try:
|
||||
config = s3.get_object_lock_configuration(Bucket=bucket_name)
|
||||
lock = config["ObjectLockConfiguration"]
|
||||
rule = lock.get("Rule", {}).get("DefaultRetention", {})
|
||||
return {
|
||||
"bucket": bucket_name,
|
||||
"object_lock_enabled": lock.get("ObjectLockEnabled") == "Enabled",
|
||||
"retention_mode": rule.get("Mode", "NONE"),
|
||||
"retention_days": rule.get("Days", 0),
|
||||
}
|
||||
except s3.exceptions.ClientError:
|
||||
return {"bucket": bucket_name, "object_lock_enabled": False}
|
||||
```
|
||||
|
||||
### Check Cross-Region Replication
|
||||
```python
|
||||
def check_cross_region_replication(bucket_name):
|
||||
try:
|
||||
repl = s3.get_bucket_replication(Bucket=bucket_name)
|
||||
rules = repl["ReplicationConfiguration"]["Rules"]
|
||||
return {
|
||||
"bucket": bucket_name,
|
||||
"replication_enabled": True,
|
||||
"destinations": [
|
||||
r["Destination"]["Bucket"] for r in rules if r["Status"] == "Enabled"
|
||||
],
|
||||
}
|
||||
except s3.exceptions.ClientError:
|
||||
return {"bucket": bucket_name, "replication_enabled": False}
|
||||
```
|
||||
|
||||
## AWS Backup Service
|
||||
|
||||
### List Backup Plans
|
||||
```python
|
||||
def list_backup_plans():
|
||||
plans = backup.list_backup_plans()["BackupPlansList"]
|
||||
result = []
|
||||
for plan in plans:
|
||||
detail = backup.get_backup_plan(BackupPlanId=plan["BackupPlanId"])
|
||||
rules = detail["BackupPlan"]["Rules"]
|
||||
result.append({
|
||||
"name": plan["BackupPlanName"],
|
||||
"id": plan["BackupPlanId"],
|
||||
"rules": [
|
||||
{
|
||||
"name": r["RuleName"],
|
||||
"schedule": r.get("ScheduleExpression"),
|
||||
"lifecycle_delete_days": r.get("Lifecycle", {}).get("DeleteAfterDays"),
|
||||
"lifecycle_cold_days": r.get("Lifecycle", {}).get("MoveToColdStorageAfterDays"),
|
||||
"target_vault": r["TargetBackupVaultName"],
|
||||
}
|
||||
for r in rules
|
||||
],
|
||||
})
|
||||
return result
|
||||
```
|
||||
|
||||
### Audit Backup Vault Access Policy
|
||||
```python
|
||||
def audit_vault_access(vault_name):
|
||||
try:
|
||||
policy = backup.get_backup_vault_access_policy(BackupVaultName=vault_name)
|
||||
policy_doc = json.loads(policy["Policy"])
|
||||
# Check for overly permissive policies
|
||||
findings = []
|
||||
for stmt in policy_doc.get("Statement", []):
|
||||
if stmt.get("Effect") == "Allow" and stmt.get("Principal") == "*":
|
||||
findings.append({
|
||||
"vault": vault_name,
|
||||
"issue": "Vault policy allows public access",
|
||||
"severity": "critical",
|
||||
})
|
||||
return findings
|
||||
except backup.exceptions.ClientError:
|
||||
return [{"vault": vault_name, "issue": "No access policy set", "severity": "medium"}]
|
||||
```
|
||||
|
||||
### List Recovery Points (Check Backup Freshness)
|
||||
```python
|
||||
from datetime import datetime, timezone
|
||||
|
||||
def check_backup_freshness(vault_name, max_age_hours=24):
|
||||
recovery_points = backup.list_recovery_points_by_backup_vault(
|
||||
BackupVaultName=vault_name, MaxResults=100
|
||||
)["RecoveryPoints"]
|
||||
|
||||
stale = []
|
||||
for rp in recovery_points:
|
||||
age = datetime.now(timezone.utc) - rp["CreationDate"]
|
||||
if age.total_seconds() > max_age_hours * 3600:
|
||||
stale.append({
|
||||
"resource": rp["ResourceArn"],
|
||||
"last_backup": rp["CreationDate"].isoformat(),
|
||||
"age_hours": round(age.total_seconds() / 3600),
|
||||
"status": rp["Status"],
|
||||
})
|
||||
return stale
|
||||
```
|
||||
|
||||
## 3-2-1 Backup Rule Audit
|
||||
|
||||
```python
|
||||
def audit_321_rule(bucket_name):
|
||||
"""Verify the 3-2-1 backup rule: 3 copies, 2 media types, 1 offsite."""
|
||||
versioning = s3.get_bucket_versioning(Bucket=bucket_name)
|
||||
replication = check_cross_region_replication(bucket_name)
|
||||
object_lock = check_object_lock(bucket_name)
|
||||
|
||||
score = {
|
||||
"three_copies": versioning.get("Status") == "Enabled",
|
||||
"two_media": replication["replication_enabled"],
|
||||
"one_offsite": replication["replication_enabled"],
|
||||
"immutable": object_lock["object_lock_enabled"],
|
||||
}
|
||||
score["compliant"] = all([score["three_copies"], score["two_media"], score["one_offsite"]])
|
||||
return score
|
||||
```
|
||||
|
||||
## Output Format
|
||||
|
||||
```json
|
||||
{"timestamp": "ISO-8601", "findings": [], "risk_level": "HIGH"}
|
||||
{
|
||||
"audit_date": "2025-01-15",
|
||||
"backup_strategy": {
|
||||
"total_buckets": 15,
|
||||
"versioning_enabled": 12,
|
||||
"object_lock_enabled": 5,
|
||||
"cross_region_replication": 8,
|
||||
"three_two_one_compliant": 4
|
||||
},
|
||||
"backup_plans": 3,
|
||||
"recovery_points_stale": 2,
|
||||
"findings": [
|
||||
{
|
||||
"resource": "critical-data-bucket",
|
||||
"issue": "No Object Lock — vulnerable to ransomware deletion",
|
||||
"severity": "high",
|
||||
"remediation": "Enable S3 Object Lock in COMPLIANCE mode"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -1,61 +1,295 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Ransomware backup strategy audit."""
|
||||
import argparse, json, sys
|
||||
from datetime import datetime, timezone
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
requests = None
|
||||
"""Ransomware backup strategy audit agent.
|
||||
|
||||
def audit_config(target, token):
|
||||
findings = []
|
||||
if not requests: return [{"error": "requests required"}]
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
Audits backup infrastructure for ransomware resilience by checking
|
||||
3-2-1 backup rule compliance, air-gapped/immutable backup presence,
|
||||
backup encryption status, recovery point objectives (RPO), and
|
||||
backup integrity verification schedules.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
from datetime import datetime, timezone, timedelta
|
||||
|
||||
|
||||
def check_veeam_backups(server_url, token):
|
||||
"""Check Veeam backup status via REST API."""
|
||||
try:
|
||||
resp = requests.get(f"{target}/api/v1/status", headers=headers, timeout=10)
|
||||
if resp.status_code == 200:
|
||||
data = resp.json()
|
||||
if not data.get("enabled", True):
|
||||
findings.append({"check": "Service Status", "status": "DISABLED", "severity": "CRITICAL"})
|
||||
elif resp.status_code == 401:
|
||||
findings.append({"check": "Authentication", "status": "UNAUTHORIZED", "severity": "HIGH"})
|
||||
except requests.RequestException as e:
|
||||
findings.append({"error": str(e)})
|
||||
import requests
|
||||
except ImportError:
|
||||
return [{"check": "Veeam API", "status": "SKIP", "severity": "INFO",
|
||||
"detail": "requests library not installed"}]
|
||||
|
||||
findings = []
|
||||
headers = {"Authorization": f"Bearer {token}", "Accept": "application/json"}
|
||||
|
||||
# Check backup jobs
|
||||
try:
|
||||
resp = requests.get(f"{server_url}/api/v1/jobs", headers=headers, timeout=30)
|
||||
resp.raise_for_status()
|
||||
jobs = resp.json().get("data", [])
|
||||
for job in jobs:
|
||||
job_name = job.get("name", "Unknown")
|
||||
last_result = job.get("lastResult", "None")
|
||||
schedule_enabled = job.get("scheduleEnabled", False)
|
||||
if last_result == "Failed":
|
||||
findings.append({
|
||||
"check": f"Backup job: {job_name}",
|
||||
"status": "FAIL",
|
||||
"severity": "CRITICAL",
|
||||
"detail": "Last backup failed",
|
||||
})
|
||||
elif not schedule_enabled:
|
||||
findings.append({
|
||||
"check": f"Backup job: {job_name}",
|
||||
"status": "WARN",
|
||||
"severity": "HIGH",
|
||||
"detail": "Schedule disabled",
|
||||
})
|
||||
else:
|
||||
findings.append({
|
||||
"check": f"Backup job: {job_name}",
|
||||
"status": "PASS",
|
||||
"severity": "INFO",
|
||||
"detail": f"Last result: {last_result}",
|
||||
})
|
||||
except Exception as e:
|
||||
findings.append({"check": "Veeam job check", "status": "ERROR",
|
||||
"severity": "HIGH", "detail": str(e)})
|
||||
return findings
|
||||
|
||||
def check_compliance(target, token):
|
||||
|
||||
def check_restic_repository(repo_path, password_file=None):
|
||||
"""Audit a Restic backup repository for integrity and freshness."""
|
||||
findings = []
|
||||
if not requests: return []
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
restic_bin = None
|
||||
for name in ["restic", "restic.exe"]:
|
||||
for d in os.environ.get("PATH", "").split(os.pathsep):
|
||||
if os.path.isfile(os.path.join(d, name)):
|
||||
restic_bin = os.path.join(d, name)
|
||||
break
|
||||
if not restic_bin:
|
||||
findings.append({"check": "Restic binary", "status": "SKIP",
|
||||
"severity": "INFO", "detail": "restic not found"})
|
||||
return findings
|
||||
|
||||
env = dict(os.environ)
|
||||
env["RESTIC_REPOSITORY"] = repo_path
|
||||
if password_file:
|
||||
env["RESTIC_PASSWORD_FILE"] = password_file
|
||||
|
||||
# Check snapshots
|
||||
try:
|
||||
resp = requests.get(f"{target}/api/v1/compliance", headers=headers, timeout=10)
|
||||
if resp.status_code == 200:
|
||||
for item in resp.json().get("checks", []):
|
||||
if item.get("status") != "PASS":
|
||||
findings.append({"check": item.get("name"), "status": item.get("status"),
|
||||
"severity": item.get("severity", "MEDIUM")})
|
||||
except requests.RequestException:
|
||||
pass
|
||||
result = subprocess.run(
|
||||
[restic_bin, "snapshots", "--json"],
|
||||
capture_output=True, text=True, timeout=120, env=env,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
snapshots = json.loads(result.stdout)
|
||||
if not snapshots:
|
||||
findings.append({"check": "Snapshots exist", "status": "FAIL",
|
||||
"severity": "CRITICAL", "detail": "No snapshots found"})
|
||||
else:
|
||||
latest = max(snapshots, key=lambda s: s.get("time", ""))
|
||||
latest_time = latest.get("time", "")[:19]
|
||||
findings.append({"check": "Latest snapshot", "status": "PASS",
|
||||
"severity": "INFO",
|
||||
"detail": f"{latest_time} ({len(snapshots)} total)"})
|
||||
try:
|
||||
latest_dt = datetime.fromisoformat(latest_time.replace("Z", "+00:00"))
|
||||
age_hours = (datetime.now(timezone.utc) - latest_dt).total_seconds() / 3600
|
||||
if age_hours > 48:
|
||||
findings.append({"check": "Backup freshness", "status": "FAIL",
|
||||
"severity": "HIGH",
|
||||
"detail": f"Latest backup is {age_hours:.0f}h old (>48h)"})
|
||||
elif age_hours > 24:
|
||||
findings.append({"check": "Backup freshness", "status": "WARN",
|
||||
"severity": "MEDIUM",
|
||||
"detail": f"Latest backup is {age_hours:.0f}h old (>24h)"})
|
||||
except (ValueError, TypeError):
|
||||
pass
|
||||
else:
|
||||
findings.append({"check": "Repository access", "status": "FAIL",
|
||||
"severity": "CRITICAL", "detail": result.stderr[:200]})
|
||||
except subprocess.TimeoutExpired:
|
||||
findings.append({"check": "Repository access", "status": "FAIL",
|
||||
"severity": "HIGH", "detail": "Command timed out"})
|
||||
|
||||
# Check repository integrity
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[restic_bin, "check", "--read-data-subset=1%"],
|
||||
capture_output=True, text=True, timeout=300, env=env,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
findings.append({"check": "Repository integrity", "status": "PASS",
|
||||
"severity": "INFO", "detail": "Integrity check passed"})
|
||||
else:
|
||||
findings.append({"check": "Repository integrity", "status": "FAIL",
|
||||
"severity": "CRITICAL", "detail": "Integrity check failed"})
|
||||
except subprocess.TimeoutExpired:
|
||||
findings.append({"check": "Repository integrity", "status": "WARN",
|
||||
"severity": "MEDIUM", "detail": "Integrity check timed out"})
|
||||
|
||||
return findings
|
||||
|
||||
|
||||
def audit_321_rule(backup_config):
|
||||
"""Audit compliance with the 3-2-1 backup rule."""
|
||||
findings = []
|
||||
copies = backup_config.get("copies", 0)
|
||||
media_types = backup_config.get("media_types", [])
|
||||
offsite_locations = backup_config.get("offsite_locations", [])
|
||||
immutable = backup_config.get("immutable_backup", False)
|
||||
air_gapped = backup_config.get("air_gapped", False)
|
||||
|
||||
# 3 copies
|
||||
if copies >= 3:
|
||||
findings.append({"check": "3-2-1: At least 3 copies", "status": "PASS",
|
||||
"severity": "INFO", "detail": f"{copies} copies"})
|
||||
else:
|
||||
findings.append({"check": "3-2-1: At least 3 copies", "status": "FAIL",
|
||||
"severity": "CRITICAL",
|
||||
"detail": f"Only {copies} copies (need 3)"})
|
||||
|
||||
# 2 different media types
|
||||
if len(media_types) >= 2:
|
||||
findings.append({"check": "3-2-1: 2 different media types", "status": "PASS",
|
||||
"severity": "INFO", "detail": ", ".join(media_types)})
|
||||
else:
|
||||
findings.append({"check": "3-2-1: 2 different media types", "status": "FAIL",
|
||||
"severity": "HIGH",
|
||||
"detail": f"Only {len(media_types)} type(s): {', '.join(media_types)}"})
|
||||
|
||||
# 1 offsite copy
|
||||
if offsite_locations:
|
||||
findings.append({"check": "3-2-1: 1 offsite copy", "status": "PASS",
|
||||
"severity": "INFO", "detail": ", ".join(offsite_locations)})
|
||||
else:
|
||||
findings.append({"check": "3-2-1: 1 offsite copy", "status": "FAIL",
|
||||
"severity": "CRITICAL", "detail": "No offsite backup"})
|
||||
|
||||
# Immutable backup (ransomware protection)
|
||||
if immutable:
|
||||
findings.append({"check": "Immutable backup", "status": "PASS",
|
||||
"severity": "INFO", "detail": "WORM/immutable storage enabled"})
|
||||
else:
|
||||
findings.append({"check": "Immutable backup", "status": "FAIL",
|
||||
"severity": "CRITICAL",
|
||||
"detail": "No immutable backup - vulnerable to ransomware encryption"})
|
||||
|
||||
# Air-gapped backup
|
||||
if air_gapped:
|
||||
findings.append({"check": "Air-gapped backup", "status": "PASS",
|
||||
"severity": "INFO", "detail": "Offline/air-gapped copy exists"})
|
||||
else:
|
||||
findings.append({"check": "Air-gapped backup", "status": "WARN",
|
||||
"severity": "HIGH",
|
||||
"detail": "No air-gapped backup - consider tape or offline storage"})
|
||||
|
||||
# Encryption
|
||||
if backup_config.get("encrypted", False):
|
||||
findings.append({"check": "Backup encryption", "status": "PASS",
|
||||
"severity": "INFO"})
|
||||
else:
|
||||
findings.append({"check": "Backup encryption", "status": "FAIL",
|
||||
"severity": "HIGH", "detail": "Backups are not encrypted"})
|
||||
|
||||
return findings
|
||||
|
||||
|
||||
def format_summary(all_findings):
|
||||
"""Print audit summary."""
|
||||
print(f"\n{'='*60}")
|
||||
print(f" Ransomware Backup Strategy Audit")
|
||||
print(f"{'='*60}")
|
||||
|
||||
severity_counts = {}
|
||||
for f in all_findings:
|
||||
sev = f.get("severity", "INFO")
|
||||
severity_counts[sev] = severity_counts.get(sev, 0) + 1
|
||||
|
||||
pass_count = sum(1 for f in all_findings if f.get("status") == "PASS")
|
||||
fail_count = sum(1 for f in all_findings if f.get("status") == "FAIL")
|
||||
warn_count = sum(1 for f in all_findings if f.get("status") == "WARN")
|
||||
|
||||
print(f" Checks : {len(all_findings)}")
|
||||
print(f" Passed : {pass_count}")
|
||||
print(f" Failed : {fail_count}")
|
||||
print(f" Warnings : {warn_count}")
|
||||
|
||||
print(f"\n By Severity:")
|
||||
for sev in ["CRITICAL", "HIGH", "MEDIUM", "LOW", "INFO"]:
|
||||
count = severity_counts.get(sev, 0)
|
||||
if count > 0:
|
||||
print(f" {sev:10s}: {count}")
|
||||
|
||||
print(f"\n Detailed Results:")
|
||||
for f in all_findings:
|
||||
status_icon = "OK" if f["status"] == "PASS" else "!!" if f["status"] == "FAIL" else "~~"
|
||||
detail = f.get("detail", "")
|
||||
print(f" [{status_icon}] [{f['severity']:8s}] {f['check']}"
|
||||
+ (f": {detail}" if detail else ""))
|
||||
|
||||
return severity_counts
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description="Ransomware backup strategy audit")
|
||||
p.add_argument("--target", required=True, help="Target URL")
|
||||
p.add_argument("--token", required=True, help="API token")
|
||||
p.add_argument("--output", "-o", help="Output JSON report")
|
||||
p.add_argument("--verbose", "-v", action="store_true")
|
||||
a = p.parse_args()
|
||||
print("[*] Ransomware backup strategy audit")
|
||||
report = {"timestamp": datetime.now(timezone.utc).isoformat(), "findings": []}
|
||||
report["findings"].extend(audit_config(a.target, a.token))
|
||||
report["findings"].extend(check_compliance(a.target, a.token))
|
||||
high = sum(1 for f in report["findings"] if f.get("severity") in ("HIGH", "CRITICAL"))
|
||||
report["risk_level"] = "HIGH" if high else "MEDIUM" if report["findings"] else "LOW"
|
||||
print(f"[*] {len(report['findings'])} findings, risk: {report['risk_level']}")
|
||||
if a.output:
|
||||
with open(a.output, "w") as f: json.dump(report, f, indent=2)
|
||||
else:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Ransomware backup strategy audit agent"
|
||||
)
|
||||
parser.add_argument("--config", help="Backup configuration JSON file")
|
||||
parser.add_argument("--restic-repo", help="Restic repository path to audit")
|
||||
parser.add_argument("--restic-password-file", help="Restic password file")
|
||||
parser.add_argument("--veeam-url", help="Veeam server URL")
|
||||
parser.add_argument("--veeam-token", help="Veeam API token")
|
||||
parser.add_argument("--output", "-o", help="Output JSON report path")
|
||||
parser.add_argument("--verbose", "-v", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
all_findings = []
|
||||
|
||||
if args.config:
|
||||
with open(args.config, "r") as f:
|
||||
backup_config = json.load(f)
|
||||
all_findings.extend(audit_321_rule(backup_config))
|
||||
|
||||
if args.restic_repo:
|
||||
all_findings.extend(check_restic_repository(args.restic_repo, args.restic_password_file))
|
||||
|
||||
if args.veeam_url and args.veeam_token:
|
||||
all_findings.extend(check_veeam_backups(args.veeam_url, args.veeam_token))
|
||||
|
||||
if not all_findings:
|
||||
print("[!] No audit sources specified. Use --config, --restic-repo, or --veeam-url.",
|
||||
file=sys.stderr)
|
||||
parser.print_help()
|
||||
sys.exit(1)
|
||||
|
||||
severity_counts = format_summary(all_findings)
|
||||
|
||||
report = {
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
"tool": "Ransomware Backup Audit",
|
||||
"findings": all_findings,
|
||||
"severity_counts": severity_counts,
|
||||
"risk_level": (
|
||||
"CRITICAL" if severity_counts.get("CRITICAL", 0) > 0
|
||||
else "HIGH" if severity_counts.get("HIGH", 0) > 0
|
||||
else "MEDIUM" if severity_counts.get("MEDIUM", 0) > 0
|
||||
else "LOW"
|
||||
),
|
||||
}
|
||||
|
||||
if args.output:
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
print(f"\n[+] Report saved to {args.output}")
|
||||
elif args.verbose:
|
||||
print(json.dumps(report, indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
Reference in New Issue
Block a user