Files

211 lines
8.2 KiB
Python

#!/usr/bin/env python3
"""Proofpoint email security gateway audit agent.
Audits Proofpoint TAP (Targeted Attack Protection) via the SIEM API
to retrieve blocked threats, clicked URLs, delivered messages, and
campaign attribution data for email security monitoring.
"""
import argparse
import json
import os
import sys
from datetime import datetime, timezone, timedelta
try:
import requests
from requests.auth import HTTPBasicAuth
except ImportError:
print("[!] 'requests' required: pip install requests", file=sys.stderr)
sys.exit(1)
PROOFPOINT_BASE = "https://tap-api-v2.proofpoint.com"
def get_pp_config():
"""Return Proofpoint TAP API credentials."""
principal = os.environ.get("PROOFPOINT_PRINCIPAL", "")
secret = os.environ.get("PROOFPOINT_SECRET", "")
if not principal or not secret:
print("[!] Set PROOFPOINT_PRINCIPAL and PROOFPOINT_SECRET env vars", file=sys.stderr)
sys.exit(1)
return principal, secret
def pp_api(endpoint, principal, secret, params=None):
"""Make authenticated Proofpoint TAP API call."""
url = f"{PROOFPOINT_BASE}{endpoint}"
resp = requests.get(url, auth=HTTPBasicAuth(principal, secret),
params=params, timeout=30)
resp.raise_for_status()
return resp.json()
def get_blocked_clicks(principal, secret, hours=24):
"""Get URLs that were blocked when clicked."""
print(f"[*] Fetching blocked clicks (last {hours}h)...")
since = (datetime.now(timezone.utc) - timedelta(hours=hours)).strftime("%Y-%m-%dT%H:%M:%SZ")
data = pp_api("/v2/siem/clicks/blocked", principal, secret,
params={"sinceTime": since, "format": "json"})
clicks = data.get("clicksBlocked", [])
print(f"[+] {len(clicks)} blocked clicks")
return clicks
def get_blocked_messages(principal, secret, hours=24):
"""Get messages that were blocked."""
print(f"[*] Fetching blocked messages (last {hours}h)...")
since = (datetime.now(timezone.utc) - timedelta(hours=hours)).strftime("%Y-%m-%dT%H:%M:%SZ")
data = pp_api("/v2/siem/messages/blocked", principal, secret,
params={"sinceTime": since, "format": "json"})
messages = data.get("messagesBlocked", [])
print(f"[+] {len(messages)} blocked messages")
return messages
def get_delivered_threats(principal, secret, hours=24):
"""Get threats that were delivered (missed by filters)."""
print(f"[*] Fetching delivered threats (last {hours}h)...")
since = (datetime.now(timezone.utc) - timedelta(hours=hours)).strftime("%Y-%m-%dT%H:%M:%SZ")
data = pp_api("/v2/siem/messages/delivered", principal, secret,
params={"sinceTime": since, "format": "json"})
messages = data.get("messagesDelivered", [])
threats = [m for m in messages if m.get("threatsInfoMap")]
print(f"[+] {len(messages)} delivered, {len(threats)} with threats")
return threats
def get_permitted_clicks(principal, secret, hours=24):
"""Get URLs that were permitted when clicked (potential misses)."""
print(f"[*] Fetching permitted clicks (last {hours}h)...")
since = (datetime.now(timezone.utc) - timedelta(hours=hours)).strftime("%Y-%m-%dT%H:%M:%SZ")
data = pp_api("/v2/siem/clicks/permitted", principal, secret,
params={"sinceTime": since, "format": "json"})
clicks = data.get("clicksPermitted", [])
print(f"[+] {len(clicks)} permitted clicks")
return clicks
def analyze_threats(blocked_msgs, delivered_threats, blocked_clicks, permitted_clicks):
"""Analyze threat data for security insights."""
findings = []
# Delivered threats are highest priority
for msg in delivered_threats:
for threat_info in msg.get("threatsInfoMap", []):
findings.append({
"type": "delivered_threat",
"severity": "CRITICAL",
"threat_type": threat_info.get("threatType", ""),
"classification": threat_info.get("classification", ""),
"threat_url": threat_info.get("threat", "")[:100],
"recipient": msg.get("recipient", [""])[0] if msg.get("recipient") else "",
"sender": msg.get("sender", ""),
"subject": msg.get("subject", "")[:80],
"timestamp": msg.get("messageTime", ""),
})
# Summarize blocked activity
threat_types = {}
for msg in blocked_msgs:
for t in msg.get("threatsInfoMap", []):
tt = t.get("threatType", "unknown")
threat_types[tt] = threat_types.get(tt, 0) + 1
if threat_types:
findings.append({
"type": "blocked_summary",
"severity": "INFO",
"detail": f"Blocked threats by type: {json.dumps(threat_types)}",
"total_blocked": len(blocked_msgs),
})
# Permitted clicks on potentially malicious URLs
for click in permitted_clicks:
if click.get("threatStatus") == "active":
findings.append({
"type": "permitted_malicious_click",
"severity": "HIGH",
"url": click.get("url", "")[:100],
"user": click.get("recipient", [""])[0] if click.get("recipient") else "",
"click_time": click.get("clickTime", ""),
})
return findings
def format_summary(findings, blocked_msgs, delivered, blocked_clicks, permitted_clicks):
"""Print email security summary."""
print(f"\n{'='*60}")
print(f" Proofpoint Email Security Report")
print(f"{'='*60}")
print(f" Blocked Messages : {len(blocked_msgs)}")
print(f" Delivered Threats : {len(delivered)}")
print(f" Blocked Clicks : {len(blocked_clicks)}")
print(f" Permitted Clicks : {len(permitted_clicks)}")
print(f" Security Findings : {len(findings)}")
critical = [f for f in findings if f["severity"] == "CRITICAL"]
if critical:
print(f"\n CRITICAL - Threats That Bypassed Filters ({len(critical)}):")
for f in critical[:10]:
print(f" {f.get('threat_type', 'N/A'):15s} | "
f"To: {f.get('recipient', 'N/A'):30s} | "
f"{f.get('subject', '')[:40]}")
severity_counts = {}
for f in findings:
sev = f.get("severity", "INFO")
severity_counts[sev] = severity_counts.get(sev, 0) + 1
return severity_counts
def main():
parser = argparse.ArgumentParser(description="Proofpoint email security audit agent")
parser.add_argument("--principal", help="TAP API principal (or PROOFPOINT_PRINCIPAL env)")
parser.add_argument("--secret", help="TAP API secret (or PROOFPOINT_SECRET env)")
parser.add_argument("--hours", type=int, default=24, help="Hours to look back (default: 24)")
parser.add_argument("--output", "-o", help="Output JSON report")
parser.add_argument("--verbose", "-v", action="store_true")
args = parser.parse_args()
if args.principal:
os.environ["PROOFPOINT_PRINCIPAL"] = args.principal
if args.secret:
os.environ["PROOFPOINT_SECRET"] = args.secret
principal, secret = get_pp_config()
blocked_msgs = get_blocked_messages(principal, secret, args.hours)
delivered = get_delivered_threats(principal, secret, args.hours)
blocked_clicks = get_blocked_clicks(principal, secret, args.hours)
permitted_clicks = get_permitted_clicks(principal, secret, args.hours)
findings = analyze_threats(blocked_msgs, delivered, blocked_clicks, permitted_clicks)
severity_counts = format_summary(findings, blocked_msgs, delivered, blocked_clicks, permitted_clicks)
report = {
"timestamp": datetime.now(timezone.utc).isoformat(),
"tool": "Proofpoint TAP",
"period_hours": args.hours,
"blocked_messages": len(blocked_msgs),
"delivered_threats": len(delivered),
"findings": 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 "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()