mirror of
https://github.com/mukul975/Anthropic-Cybersecurity-Skills.git
synced 2026-08-03 09:20:18 +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,61 +1,322 @@
|
||||
#!/usr/bin/env python3
|
||||
"""STIX/TAXII feed integration audit."""
|
||||
import argparse, json, sys
|
||||
"""STIX/TAXII threat intelligence feed integration agent.
|
||||
|
||||
Connects to TAXII 2.0/2.1 servers to discover and consume threat intelligence
|
||||
feeds in STIX format. Extracts indicators of compromise (IOCs), threat actors,
|
||||
malware families, and attack patterns from STIX bundles.
|
||||
"""
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
|
||||
try:
|
||||
from taxii2client.v20 import Server as Server20, Collection as Collection20
|
||||
from taxii2client.v21 import Server as Server21, Collection as Collection21
|
||||
HAS_TAXII_CLIENT = True
|
||||
except ImportError:
|
||||
HAS_TAXII_CLIENT = False
|
||||
|
||||
try:
|
||||
from stix2 import parse as stix_parse
|
||||
HAS_STIX2 = True
|
||||
except ImportError:
|
||||
HAS_STIX2 = False
|
||||
|
||||
try:
|
||||
import requests
|
||||
except ImportError:
|
||||
requests = None
|
||||
|
||||
def audit_config(target, token):
|
||||
findings = []
|
||||
if not requests: return [{"error": "requests required"}]
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
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)})
|
||||
return findings
|
||||
|
||||
def check_compliance(target, token):
|
||||
findings = []
|
||||
if not requests: return []
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
def connect_taxii_server(url, username=None, password=None, version="2.1"):
|
||||
"""Connect to a TAXII server and return Server object."""
|
||||
if not HAS_TAXII_CLIENT:
|
||||
print("[!] taxii2-client required: pip install taxii2-client", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
kwargs = {}
|
||||
if username and password:
|
||||
kwargs["user"] = username
|
||||
kwargs["password"] = password
|
||||
|
||||
print(f"[*] Connecting to TAXII {version} server: {url}")
|
||||
if version == "2.0":
|
||||
server = Server20(url, **kwargs)
|
||||
else:
|
||||
server = Server21(url, **kwargs)
|
||||
|
||||
print(f"[+] Connected: {server.title or 'Untitled'}")
|
||||
return server
|
||||
|
||||
|
||||
def discover_collections(server, version="2.1"):
|
||||
"""Discover available collections on the TAXII server."""
|
||||
collections = []
|
||||
for api_root in server.api_roots:
|
||||
print(f"[*] API Root: {api_root.title or api_root.url}")
|
||||
for col in api_root.collections:
|
||||
col_info = {
|
||||
"id": col.id,
|
||||
"title": col.title or "Untitled",
|
||||
"description": getattr(col, "description", ""),
|
||||
"can_read": getattr(col, "can_read", True),
|
||||
"can_write": getattr(col, "can_write", False),
|
||||
"media_types": getattr(col, "media_types", []),
|
||||
"api_root": api_root.title or str(api_root.url),
|
||||
}
|
||||
collections.append(col_info)
|
||||
print(f" Collection: {col_info['title']} ({col.id})")
|
||||
return collections
|
||||
|
||||
|
||||
def fetch_collection_objects(collection, added_after=None, limit=100, obj_type=None):
|
||||
"""Fetch STIX objects from a TAXII collection."""
|
||||
kwargs = {}
|
||||
if added_after:
|
||||
kwargs["added_after"] = added_after
|
||||
|
||||
print(f"[*] Fetching objects from collection: {collection.title or collection.id}")
|
||||
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
|
||||
return findings
|
||||
envelope = collection.get_objects(**kwargs)
|
||||
except Exception as e:
|
||||
print(f"[!] Error fetching objects: {e}", file=sys.stderr)
|
||||
return []
|
||||
|
||||
if isinstance(envelope, dict):
|
||||
objects = envelope.get("objects", [])
|
||||
elif hasattr(envelope, "objects"):
|
||||
objects = envelope.objects or []
|
||||
else:
|
||||
objects = []
|
||||
|
||||
if obj_type:
|
||||
objects = [o for o in objects if o.get("type") == obj_type]
|
||||
|
||||
if limit and len(objects) > limit:
|
||||
objects = objects[:limit]
|
||||
|
||||
print(f"[+] Retrieved {len(objects)} object(s)")
|
||||
return objects
|
||||
|
||||
|
||||
def extract_indicators(stix_objects):
|
||||
"""Extract indicators of compromise from STIX objects."""
|
||||
indicators = []
|
||||
for obj in stix_objects:
|
||||
obj_type = obj.get("type", "")
|
||||
if obj_type == "indicator":
|
||||
indicators.append({
|
||||
"type": "indicator",
|
||||
"id": obj.get("id", ""),
|
||||
"name": obj.get("name", ""),
|
||||
"description": obj.get("description", "")[:200],
|
||||
"pattern": obj.get("pattern", ""),
|
||||
"pattern_type": obj.get("pattern_type", "stix"),
|
||||
"valid_from": obj.get("valid_from", ""),
|
||||
"valid_until": obj.get("valid_until", ""),
|
||||
"labels": obj.get("labels", []),
|
||||
"confidence": obj.get("confidence", 0),
|
||||
"created": obj.get("created", ""),
|
||||
})
|
||||
return indicators
|
||||
|
||||
|
||||
def extract_threat_actors(stix_objects):
|
||||
"""Extract threat actor information from STIX objects."""
|
||||
actors = []
|
||||
for obj in stix_objects:
|
||||
if obj.get("type") == "threat-actor":
|
||||
actors.append({
|
||||
"type": "threat-actor",
|
||||
"id": obj.get("id", ""),
|
||||
"name": obj.get("name", ""),
|
||||
"description": obj.get("description", "")[:200],
|
||||
"aliases": obj.get("aliases", []),
|
||||
"roles": obj.get("roles", []),
|
||||
"goals": obj.get("goals", []),
|
||||
"sophistication": obj.get("sophistication", ""),
|
||||
"resource_level": obj.get("resource_level", ""),
|
||||
"primary_motivation": obj.get("primary_motivation", ""),
|
||||
})
|
||||
return actors
|
||||
|
||||
|
||||
def extract_malware(stix_objects):
|
||||
"""Extract malware family info from STIX objects."""
|
||||
malware = []
|
||||
for obj in stix_objects:
|
||||
if obj.get("type") == "malware":
|
||||
malware.append({
|
||||
"type": "malware",
|
||||
"id": obj.get("id", ""),
|
||||
"name": obj.get("name", ""),
|
||||
"description": obj.get("description", "")[:200],
|
||||
"malware_types": obj.get("malware_types", []),
|
||||
"is_family": obj.get("is_family", False),
|
||||
"aliases": obj.get("aliases", []),
|
||||
"capabilities": obj.get("capabilities", []),
|
||||
})
|
||||
return malware
|
||||
|
||||
|
||||
def extract_attack_patterns(stix_objects):
|
||||
"""Extract MITRE ATT&CK patterns from STIX objects."""
|
||||
patterns = []
|
||||
for obj in stix_objects:
|
||||
if obj.get("type") == "attack-pattern":
|
||||
external_refs = obj.get("external_references", [])
|
||||
mitre_id = ""
|
||||
for ref in external_refs:
|
||||
if ref.get("source_name") in ("mitre-attack", "mitre-mobile-attack"):
|
||||
mitre_id = ref.get("external_id", "")
|
||||
break
|
||||
patterns.append({
|
||||
"type": "attack-pattern",
|
||||
"id": obj.get("id", ""),
|
||||
"name": obj.get("name", ""),
|
||||
"mitre_id": mitre_id,
|
||||
"description": obj.get("description", "")[:200],
|
||||
"kill_chain_phases": obj.get("kill_chain_phases", []),
|
||||
})
|
||||
return patterns
|
||||
|
||||
|
||||
def summarize_stix_objects(stix_objects):
|
||||
"""Group and count STIX objects by type."""
|
||||
type_counts = {}
|
||||
for obj in stix_objects:
|
||||
t = obj.get("type", "unknown")
|
||||
type_counts[t] = type_counts.get(t, 0) + 1
|
||||
return type_counts
|
||||
|
||||
|
||||
def format_summary(type_counts, indicators, actors, malware, attack_patterns):
|
||||
"""Print human-readable summary."""
|
||||
print(f"\n{'='*60}")
|
||||
print(f" STIX/TAXII Feed Intelligence Report")
|
||||
print(f"{'='*60}")
|
||||
|
||||
print(f"\n Object Type Distribution:")
|
||||
for obj_type, count in sorted(type_counts.items(), key=lambda x: -x[1]):
|
||||
print(f" {obj_type:30s}: {count}")
|
||||
|
||||
if indicators:
|
||||
print(f"\n Indicators ({len(indicators)}):")
|
||||
for ind in indicators[:10]:
|
||||
print(f" {ind['name'][:40]:40s} | {ind['pattern_type']:8s} | "
|
||||
f"{ind['pattern'][:50]}")
|
||||
|
||||
if actors:
|
||||
print(f"\n Threat Actors ({len(actors)}):")
|
||||
for actor in actors[:10]:
|
||||
aliases = ", ".join(actor["aliases"][:3]) if actor["aliases"] else ""
|
||||
print(f" {actor['name']:30s} | {actor['sophistication']:12s} | {aliases}")
|
||||
|
||||
if malware:
|
||||
print(f"\n Malware Families ({len(malware)}):")
|
||||
for m in malware[:10]:
|
||||
types = ", ".join(m["malware_types"][:3]) if m["malware_types"] else ""
|
||||
print(f" {m['name']:30s} | {types}")
|
||||
|
||||
if attack_patterns:
|
||||
print(f"\n ATT&CK Patterns ({len(attack_patterns)}):")
|
||||
for ap in attack_patterns[:10]:
|
||||
print(f" {ap.get('mitre_id', 'N/A'):12s} | {ap['name']}")
|
||||
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(description="STIX/TAXII feed integration 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("[*] STIX/TAXII feed integration 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="STIX/TAXII threat intelligence feed integration agent"
|
||||
)
|
||||
parser.add_argument("--server", required=True, help="TAXII server discovery URL")
|
||||
parser.add_argument("--username", help="TAXII authentication username")
|
||||
parser.add_argument("--password", help="TAXII authentication password")
|
||||
parser.add_argument("--version", choices=["2.0", "2.1"], default="2.1",
|
||||
help="TAXII version (default: 2.1)")
|
||||
parser.add_argument("--collection-id", help="Specific collection ID to fetch")
|
||||
parser.add_argument("--added-after", help="Only fetch objects added after date (ISO format)")
|
||||
parser.add_argument("--type", dest="obj_type",
|
||||
help="Filter by STIX object type (e.g., indicator, malware)")
|
||||
parser.add_argument("--limit", type=int, default=500,
|
||||
help="Max objects to retrieve (default: 500)")
|
||||
parser.add_argument("--discover-only", action="store_true",
|
||||
help="Only discover collections, don't fetch objects")
|
||||
parser.add_argument("--output", "-o", help="Output JSON report path")
|
||||
parser.add_argument("--verbose", "-v", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
server = connect_taxii_server(args.server, args.username, args.password, args.version)
|
||||
collections = discover_collections(server, args.version)
|
||||
|
||||
if args.discover_only:
|
||||
report = {
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
"tool": "STIX/TAXII Client",
|
||||
"server": args.server,
|
||||
"collections": collections,
|
||||
}
|
||||
if args.output:
|
||||
with open(args.output, "w") as f:
|
||||
json.dump(report, f, indent=2)
|
||||
print(f"\n[+] Report saved to {args.output}")
|
||||
else:
|
||||
print(json.dumps(report, indent=2))
|
||||
return
|
||||
|
||||
# Fetch objects from specified or all readable collections
|
||||
all_objects = []
|
||||
for col_info in collections:
|
||||
if args.collection_id and col_info["id"] != args.collection_id:
|
||||
continue
|
||||
if not col_info.get("can_read", True):
|
||||
continue
|
||||
try:
|
||||
if args.version == "2.0":
|
||||
col = Collection20(
|
||||
f"{args.server.rstrip('/')}/collections/{col_info['id']}/",
|
||||
user=args.username, password=args.password
|
||||
)
|
||||
else:
|
||||
col = Collection21(
|
||||
f"{args.server.rstrip('/')}/collections/{col_info['id']}/",
|
||||
user=args.username, password=args.password
|
||||
)
|
||||
objects = fetch_collection_objects(col, args.added_after, args.limit, args.obj_type)
|
||||
all_objects.extend(objects)
|
||||
except Exception as e:
|
||||
print(f"[!] Error fetching {col_info['title']}: {e}")
|
||||
|
||||
type_counts = summarize_stix_objects(all_objects)
|
||||
indicators = extract_indicators(all_objects)
|
||||
actors = extract_threat_actors(all_objects)
|
||||
malware_items = extract_malware(all_objects)
|
||||
attack_patterns = extract_attack_patterns(all_objects)
|
||||
|
||||
format_summary(type_counts, indicators, actors, malware_items, attack_patterns)
|
||||
|
||||
report = {
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
"tool": "STIX/TAXII Client",
|
||||
"server": args.server,
|
||||
"taxii_version": args.version,
|
||||
"collections_discovered": len(collections),
|
||||
"total_objects": len(all_objects),
|
||||
"type_distribution": type_counts,
|
||||
"indicators": indicators,
|
||||
"threat_actors": actors,
|
||||
"malware": malware_items,
|
||||
"attack_patterns": attack_patterns,
|
||||
}
|
||||
|
||||
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