npx skills add mukul975/Anthropic-Cybersecurity-SkillsMITRE ATT&CK
Overview
Proofpoint Email Protection is a cloud-native secure email gateway (SEG) that acts as a security checkpoint where all inbound and outbound mail traffic routes through the gateway before reaching user inboxes. It combines signature-based detection for known malware, machine learning algorithms for emerging threats, real-time threat intelligence feeds, URL rewriting with time-of-click sandboxing, and behavioral analysis for BEC detection. Proofpoint processes over 2.8 billion emails daily and blocks over 1 million extortion attempts per day.
When to Use
- When deploying or configuring implementing proofpoint email security gateway capabilities in your environment
- When establishing security controls aligned to compliance requirements
- When building or improving security architecture for this domain
- When conducting security assessments that require this implementation
Prerequisites
- Proofpoint Email Protection license (PPS on-premises or Proofpoint on Demand cloud)
- Administrative access to DNS management for MX record changes
- Microsoft 365 or Google Workspace email environment
- Understanding of mail flow architecture and SPF/DKIM/DMARC
- Network firewall rules permitting Proofpoint IP ranges
Key Concepts
Deployment Models
- MX-Based Gateway (Traditional SEG): All mail routes through Proofpoint via MX record changes; intercepts threats before delivery
- API-Based Integration: Connects directly to Microsoft 365 or Google Workspace via API; no MX changes required; can be operational within 48 hours
- Hybrid Deployment: Combines gateway and API for layered protection
Core Detection Technologies
- Impostor Classifier: ML model detecting BEC/impersonation with no malicious URLs or attachments
- URL Defense: Rewrites URLs and performs real-time sandboxing at time of click
- Attachment Defense: Sandboxes suspicious attachments in virtual environments
- Nexus Threat Graph: Cross-customer threat intelligence correlation engine
- Supplier Threat Detection: Identifies compromised vendor email accounts
Protection Layers
| Layer | Technology | Threat Type |
|---|---|---|
| Connection | IP reputation, rate limiting | Spam botnets |
| Authentication | SPF, DKIM, DMARC enforcement | Spoofing |
| Content | ML classifiers, NLP analysis | BEC, phishing |
| URL | Rewriting + time-of-click sandbox | Credential theft |
| Attachment | Static + dynamic sandboxing | Malware, ransomware |
| Post-delivery | TRAP (auto-retraction) | Weaponized after delivery |
Workflow
Step 1: Plan Mail Flow Architecture
- Document current MX records and mail flow path
- Identify all legitimate sending sources (marketing platforms, CRM, ticketing systems)
- Map inbound connectors and transport rules in Microsoft 365 or Google Workspace
- Plan IP allowlisting for Proofpoint egress IPs on receiving infrastructure
- Configure SPF record to include Proofpoint:
v=spf1 include:spf.protection.outlook.com include:spf-a.proofpoint.com -all
Step 2: Configure Proofpoint Policies
- Create organizational units matching business structure
- Define inbound mail policies: anti-spam, anti-virus, impostor detection
- Configure Smart Search quarantine with end-user digest notifications
- Set up Proofpoint Encryption for sensitive outbound messages
- Enable Targeted Attack Protection (TAP) for URL and attachment sandboxing
Step 3: Deploy Email Authentication
- Configure DKIM signing through Proofpoint for outbound messages
- Set DMARC policy to monitor mode initially:
v=DMARC1; p=none; rua=mailto:dmarc@company.com - Enable inbound DMARC enforcement to reject spoofed messages
- Configure anti-spoofing rules for executive impersonation protection
Step 4: Enable Advanced Threat Protection
- Activate URL Defense with rewriting enabled for all inbound messages
- Configure Attachment Defense sandbox policies (safe attachment mode)
- Enable Threat Response Auto-Pull (TRAP) for post-delivery remediation
- Set up TAP Dashboard alerts for targeted attack campaigns
- Configure Supplier Risk monitoring for vendor email compromise
Step 5: Migrate MX Records
- Lower MX record TTL to 300 seconds 48 hours before cutover
- Update MX records to point to Proofpoint:
company-com.mail.protection.proofpoint.com - Configure connector restrictions in Microsoft 365 to accept mail only from Proofpoint IPs
- Monitor mail flow through Proofpoint Message Trace for 48-72 hours
- Verify no legitimate mail is being blocked or delayed
Step 6: Tune and Optimize
- Review quarantine and false positive/negative rates weekly for first month
- Adjust spam thresholds based on organizational tolerance
- Add approved senders and safe lists for legitimate bulk mail
- Configure data loss prevention (DLP) rules for outbound sensitive content
- Enable email warning banners for external sender identification
Tools & Resources
- Proofpoint TAP Dashboard: Real-time threat visibility and campaign tracking
- Proofpoint TRAP: Automated post-delivery email retraction
- Proofpoint SER (Spam/End-user Release): Self-service quarantine management
- Proofpoint Closed-Loop Email Analysis (CLEAR): Phishing report button integration
- MX Toolbox: DNS record verification and mail flow testing
Validation
- All inbound email routes through Proofpoint (verify MX records and message headers)
- TAP Dashboard shows threat detections and blocked campaigns
- URL Defense rewrites links in test messages and sandboxes at click time
- Attachment Defense detonates test malware samples in sandbox
- TRAP successfully retracts test phishing message from inboxes post-delivery
- False positive rate below 0.1% after initial tuning period
- DMARC/SPF/DKIM authentication passes for all legitimate outbound mail
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md5.1 KB
API Reference: Proofpoint Email Security Gateway
Libraries Used
| Library | Purpose |
|---|---|
requests |
HTTP client for Proofpoint TAP API v2 |
json |
Parse threat and message event data |
os |
Read PROOFPOINT_SP and PROOFPOINT_SECRET credentials |
datetime |
Build ISO-8601 time range queries |
Installation
pip install requestsAuthentication
Proofpoint TAP API uses HTTP Basic Auth with service principal and secret:
import requests
import os
from requests.auth import HTTPBasicAuth
PROOFPOINT_URL = "https://tap-api-v2.proofpoint.com"
auth = HTTPBasicAuth(
os.environ["PROOFPOINT_SP"], # Service Principal
os.environ["PROOFPOINT_SECRET"], # Secret
)TAP API v2 Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /v2/siem/messages/blocked |
Messages blocked by Proofpoint |
| GET | /v2/siem/messages/delivered |
Messages delivered (with threats) |
| GET | /v2/siem/clicks/blocked |
Blocked URL clicks |
| GET | /v2/siem/clicks/permitted |
Permitted URL clicks (with threats) |
| GET | /v2/siem/all |
All events (messages + clicks) |
| GET | /v2/siem/issues |
Campaign and threat issues |
| GET | /v2/people/vap |
Very Attacked People report |
| GET | /v2/forensics |
Threat forensics detail |
| POST | /v2/quarantine/release |
Release message from quarantine |
| POST | /v2/quarantine/delete |
Delete message from quarantine |
Core Operations
Fetch Blocked Messages
from datetime import datetime, timedelta
def get_blocked_messages(hours_back=1):
since = (datetime.utcnow() - timedelta(hours=hours_back)).strftime(
"%Y-%m-%dT%H:%M:%SZ"
)
resp = requests.get(
f"{PROOFPOINT_URL}/v2/siem/messages/blocked",
auth=auth,
params={
"sinceTime": since,
"format": "json",
},
timeout=60,
)
resp.raise_for_status()
return resp.json().get("messagesBlocked", [])Fetch Permitted Clicks with Threats
def get_permitted_clicks(hours_back=24):
since = (datetime.utcnow() - timedelta(hours=hours_back)).strftime(
"%Y-%m-%dT%H:%M:%SZ"
)
resp = requests.get(
f"{PROOFPOINT_URL}/v2/siem/clicks/permitted",
auth=auth,
params={"sinceTime": since, "format": "json"},
timeout=60,
)
resp.raise_for_status()
return resp.json().get("clicksPermitted", [])Get All SIEM Events
def get_all_events(hours_back=1):
since = (datetime.utcnow() - timedelta(hours=hours_back)).strftime(
"%Y-%m-%dT%H:%M:%SZ"
)
resp = requests.get(
f"{PROOFPOINT_URL}/v2/siem/all",
auth=auth,
params={"sinceTime": since, "format": "json"},
timeout=120,
)
resp.raise_for_status()
data = resp.json()
return {
"messages_blocked": data.get("messagesBlocked", []),
"messages_delivered": data.get("messagesDelivered", []),
"clicks_blocked": data.get("clicksBlocked", []),
"clicks_permitted": data.get("clicksPermitted", []),
}Get Very Attacked People (VAP)
def get_vap_report(days=30):
resp = requests.get(
f"{PROOFPOINT_URL}/v2/people/vap",
auth=auth,
params={"window": days, "size": 100},
timeout=60,
)
resp.raise_for_status()
return resp.json().get("users", [])Extract Threat IOCs
def extract_iocs(events):
iocs = {"urls": set(), "senders": set(), "subjects": set(), "sha256": set()}
for msg in events.get("messages_blocked", []) + events.get("messages_delivered", []):
iocs["senders"].add(msg.get("sender", ""))
iocs["subjects"].add(msg.get("subject", ""))
for threat in msg.get("threatsInfoMap", []):
if threat.get("threatUrl"):
iocs["urls"].add(threat["threatUrl"])
if threat.get("sha256"):
iocs["sha256"].add(threat["sha256"])
return {k: list(v) for k, v in iocs.items()}Query Parameters
| Parameter | Type | Description |
|---|---|---|
sinceTime |
ISO-8601 | Start time (required, max 1 hour back for /all) |
sinceSeconds |
int | Seconds before now (alternative to sinceTime) |
format |
string | Response format: json (default) or syslog |
threatType |
string | Filter: url, attachment, messageText |
threatStatus |
string | Filter: active, cleared, falsePositive |
Output Format
{
"messagesBlocked": [
{
"GUID": "abc123-def456",
"QID": "r1234567",
"sender": "attacker@malicious.example.com",
"recipient": ["user@company.com"],
"subject": "Invoice #12345 Attached",
"messageTime": "2025-01-15T10:30:00Z",
"threatsInfoMap": [
{
"threat": "https://evil.example.com/payload",
"threatType": "url",
"threatStatus": "active",
"classification": "phish",
"sha256": "a1b2c3d4e5f6..."
}
],
"malwareScore": 100,
"phishScore": 95,
"spamScore": 0
}
]
}standards.md1.7 KB
Standards & References: Implementing Proofpoint Email Security Gateway
Industry Standards
- NIST SP 800-177 Rev.1: Trustworthy Email - guidelines for email security deployment
- RFC 7208: Sender Policy Framework (SPF) for authorizing use of domains in email
- RFC 6376: DomainKeys Identified Mail (DKIM) Signatures
- RFC 7489: Domain-based Message Authentication, Reporting & Conformance (DMARC)
- CIS Controls v8 Control 9: Email and Web Browser Protections
MITRE ATT&CK References
- T1566.001: Phishing: Spearphishing Attachment
- T1566.002: Phishing: Spearphishing Link
- T1566.003: Phishing: Spearphishing via Service
- T1534: Internal Spearphishing
- T1598: Phishing for Information
- T1114.003: Email Collection: Email Forwarding Rule
Proofpoint-Specific References
- Proofpoint Email Protection (PPS): On-premises protection platform
- Proofpoint on Demand (PoD): Cloud-hosted email security service
- Proofpoint TAP (Targeted Attack Protection): Advanced threat detection
- Proofpoint TRAP (Threat Response Auto-Pull): Post-delivery remediation
- Proofpoint Nexus Threat Graph: Cross-customer threat intelligence
Compliance Alignment
| Framework | Control | Description |
|---|---|---|
| SOC 2 | CC6.1 | Logical and physical access controls |
| HIPAA | 164.312(a)(1) | Access control for ePHI |
| PCI DSS 4.0 | 5.2 | Anti-malware solutions |
| NIST CSF 2.0 | PR.DS-1 | Data-at-rest and data-in-transit protection |
| ISO 27001 | A.8.23 | Web filtering |
Email Security Gateway Market
- Proofpoint processes 2.8+ billion messages daily (2024)
- Gartner Magic Quadrant Leader for Email Security (2019-2024)
- Over 50% of Fortune 100 companies use Proofpoint
workflows.md2.7 KB
Workflows: Implementing Proofpoint Email Security Gateway
Workflow 1: Inbound Mail Processing Pipeline
External sender sends email
|
v
[DNS MX lookup resolves to Proofpoint]
|
v
[Connection-level filtering]
+-- IP reputation check (Proofpoint Nexus)
+-- Rate limiting and connection throttling
+-- REJECT if known-bad IP
|
v
[Authentication checks]
+-- SPF validation
+-- DKIM signature verification
+-- DMARC policy evaluation
+-- FAIL actions: quarantine or reject per policy
|
v
[Content analysis]
+-- Anti-spam scoring (ML classifier)
+-- Anti-virus scanning (multi-engine)
+-- Impostor classifier (BEC detection)
+-- NLP analysis for social engineering language
|
v
[URL Defense]
+-- Extract all URLs from body and attachments
+-- Rewrite URLs through Proofpoint proxy
+-- Pre-delivery URL reputation check
+-- BLOCK if known malicious
|
v
[Attachment Defense]
+-- Static analysis (signatures, heuristics)
+-- Dynamic sandbox detonation (if suspicious)
+-- Wait for sandbox verdict (up to 7 minutes)
+-- QUARANTINE if malicious
|
v
[Policy action]
+-- DELIVER: Clean email to mailbox
+-- TAG: Add warning banner for external/suspicious
+-- QUARANTINE: Hold for admin/user review
+-- REJECT: Block with NDR to senderWorkflow 2: Post-Delivery Threat Response (TRAP)
Threat intelligence update received
|
v
[TRAP scans delivered messages retroactively]
+-- URL becomes malicious after delivery
+-- New malware signature matches delivered attachment
|
v
[Auto-Pull action triggered]
+-- Move message from user inbox to quarantine
+-- Log retraction in TRAP dashboard
+-- Notify SOC team of post-delivery threat
|
v
[SOC investigation]
+-- Review TRAP alert and threat details
+-- Check if user clicked URL before retraction
+-- If clicked: initiate incident response
+-- If not clicked: close as contained
|
v
[Update policies]
+-- Add sender/domain to block list if needed
+-- Create detection rule for similar campaigns
+-- Update TAP Dashboard threat trackingWorkflow 3: Phishing Report and CLEAR Integration
User receives suspicious email
|
v
[User clicks "Report Phishing" button (Proofpoint CLEAR)]
|
v
[Email forwarded to Proofpoint analysis pipeline]
+-- Automated classification (phishing/spam/clean)
+-- URL and attachment analysis
|
v
[CLEAR verdict]
+-- MALICIOUS: Auto-retract from all inboxes that received it
+-- SPAM: Move to junk for all recipients
+-- CLEAN: Return to inbox, thank reporter
|
v
[Metrics and feedback]
+-- Track reporter accuracy rate
+-- Update user risk score
+-- Feed into security awareness metricsScripts 2
agent.py8.2 KB
#!/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()
process.py11.1 KB
#!/usr/bin/env python3
"""
Proofpoint Email Security Gateway Configuration Validator
Validates MX records, SPF/DKIM/DMARC alignment, and Proofpoint
mail flow configuration for a given domain. Uses DNS lookups to
verify that email is routing through Proofpoint correctly.
Usage:
python process.py check-mx --domain example.com
python process.py check-auth --domain example.com
python process.py validate-headers --eml-file message.eml
"""
import argparse
import json
import re
import sys
from dataclasses import dataclass, field, asdict
from typing import Optional
try:
import dns.resolver
HAS_DNS = True
except ImportError:
HAS_DNS = False
@dataclass
class MXCheckResult:
"""MX record validation result."""
domain: str = ""
mx_records: list = field(default_factory=list)
routes_through_proofpoint: bool = False
proofpoint_mx: str = ""
issues: list = field(default_factory=list)
@dataclass
class AuthCheckResult:
"""Email authentication check result."""
domain: str = ""
spf_record: str = ""
spf_includes_proofpoint: bool = False
dmarc_record: str = ""
dmarc_policy: str = ""
dkim_selector: str = ""
issues: list = field(default_factory=list)
@dataclass
class HeaderAnalysis:
"""Email header forensic analysis."""
from_address: str = ""
return_path: str = ""
received_chain: list = field(default_factory=list)
spf_result: str = ""
dkim_result: str = ""
dmarc_result: str = ""
proofpoint_headers: list = field(default_factory=list)
x_proofpoint_spam_details: str = ""
x_proofpoint_virus_version: str = ""
passed_through_proofpoint: bool = False
issues: list = field(default_factory=list)
PROOFPOINT_MX_PATTERNS = [
r'\.pphosted\.com$',
r'\.proofpoint\.com$',
r'mail\.protection\.proofpoint\.com$',
]
PROOFPOINT_SPF_INCLUDES = [
'spf-a.proofpoint.com',
'spf-b.proofpoint.com',
'spf.proofpoint.com',
'pphosted.com',
]
PROOFPOINT_HEADER_MARKERS = [
'X-Proofpoint-Spam-Details',
'X-Proofpoint-Virus-Version',
'X-Proofpoint-GUID',
'X-Proofpoint-ORIG-GUID',
]
def check_mx_records(domain: str) -> MXCheckResult:
"""Check if domain MX records route through Proofpoint."""
result = MXCheckResult(domain=domain)
if not HAS_DNS:
result.issues.append("dnspython not installed. Install with: pip install dnspython")
return result
try:
answers = dns.resolver.resolve(domain, 'MX')
for rdata in sorted(answers, key=lambda r: r.preference):
mx_host = str(rdata.exchange).rstrip('.')
result.mx_records.append({
"priority": rdata.preference,
"host": mx_host
})
for pattern in PROOFPOINT_MX_PATTERNS:
if re.search(pattern, mx_host, re.IGNORECASE):
result.routes_through_proofpoint = True
result.proofpoint_mx = mx_host
break
except dns.resolver.NXDOMAIN:
result.issues.append(f"Domain {domain} does not exist")
except dns.resolver.NoAnswer:
result.issues.append(f"No MX records found for {domain}")
except Exception as e:
result.issues.append(f"DNS query failed: {str(e)}")
if not result.routes_through_proofpoint and result.mx_records:
result.issues.append(
"MX records do not point to Proofpoint. "
"Expected pattern: *.pphosted.com or *.proofpoint.com"
)
return result
def check_authentication(domain: str) -> AuthCheckResult:
"""Check SPF, DKIM, and DMARC records for Proofpoint alignment."""
result = AuthCheckResult(domain=domain)
if not HAS_DNS:
result.issues.append("dnspython not installed. Install with: pip install dnspython")
return result
# Check SPF
try:
answers = dns.resolver.resolve(domain, 'TXT')
for rdata in answers:
txt = str(rdata).strip('"')
if txt.startswith('v=spf1'):
result.spf_record = txt
for include in PROOFPOINT_SPF_INCLUDES:
if include in txt:
result.spf_includes_proofpoint = True
break
break
except Exception:
result.issues.append("Could not retrieve SPF record")
if result.spf_record and not result.spf_includes_proofpoint:
result.issues.append(
"SPF record does not include Proofpoint. "
"Add: include:spf-a.proofpoint.com"
)
# Check DMARC
try:
dmarc_domain = f"_dmarc.{domain}"
answers = dns.resolver.resolve(dmarc_domain, 'TXT')
for rdata in answers:
txt = str(rdata).strip('"')
if txt.startswith('v=DMARC1'):
result.dmarc_record = txt
policy_match = re.search(r'p=(\w+)', txt)
if policy_match:
result.dmarc_policy = policy_match.group(1)
break
except Exception:
result.issues.append("No DMARC record found")
if result.dmarc_policy == "none":
result.issues.append(
"DMARC policy is set to 'none' (monitoring only). "
"Plan rollout to 'quarantine' then 'reject'"
)
# Check Proofpoint DKIM selector
try:
dkim_domain = f"proofpoint._domainkey.{domain}"
answers = dns.resolver.resolve(dkim_domain, 'TXT')
for rdata in answers:
result.dkim_selector = "proofpoint"
break
except Exception:
pass
return result
def analyze_headers(eml_content: str) -> HeaderAnalysis:
"""Analyze email headers for Proofpoint routing and authentication."""
analysis = HeaderAnalysis()
# Extract From header
from_match = re.search(r'^From:\s*(.+)$', eml_content, re.MULTILINE | re.IGNORECASE)
if from_match:
analysis.from_address = from_match.group(1).strip()
# Extract Return-Path
rp_match = re.search(r'^Return-Path:\s*(.+)$', eml_content, re.MULTILINE | re.IGNORECASE)
if rp_match:
analysis.return_path = rp_match.group(1).strip()
# Extract Received chain
received_headers = re.findall(
r'^Received:\s*(.*?)(?=\n\S|\nReceived:|\n\n)',
eml_content, re.MULTILINE | re.DOTALL | re.IGNORECASE
)
for hdr in received_headers:
clean = ' '.join(hdr.split())
analysis.received_chain.append(clean)
if any(p.replace(r'$', '').replace(r'\.', '.') in clean.lower()
for p in ['pphosted.com', 'proofpoint.com']):
analysis.passed_through_proofpoint = True
# Extract Authentication-Results
auth_match = re.search(
r'^Authentication-Results:\s*(.*?)(?=\n\S)',
eml_content, re.MULTILINE | re.DOTALL | re.IGNORECASE
)
if auth_match:
auth_text = auth_match.group(1)
spf_match = re.search(r'spf=(\w+)', auth_text)
if spf_match:
analysis.spf_result = spf_match.group(1)
dkim_match = re.search(r'dkim=(\w+)', auth_text)
if dkim_match:
analysis.dkim_result = dkim_match.group(1)
dmarc_match = re.search(r'dmarc=(\w+)', auth_text)
if dmarc_match:
analysis.dmarc_result = dmarc_match.group(1)
# Check for Proofpoint-specific headers
for marker in PROOFPOINT_HEADER_MARKERS:
marker_match = re.search(
rf'^{re.escape(marker)}:\s*(.+)$',
eml_content, re.MULTILINE | re.IGNORECASE
)
if marker_match:
analysis.proofpoint_headers.append({
"header": marker,
"value": marker_match.group(1).strip()
})
if marker == 'X-Proofpoint-Spam-Details':
analysis.x_proofpoint_spam_details = marker_match.group(1).strip()
elif marker == 'X-Proofpoint-Virus-Version':
analysis.x_proofpoint_virus_version = marker_match.group(1).strip()
if not analysis.passed_through_proofpoint and not analysis.proofpoint_headers:
analysis.issues.append("Email does not appear to have routed through Proofpoint")
if analysis.spf_result and analysis.spf_result != 'pass':
analysis.issues.append(f"SPF check failed: {analysis.spf_result}")
if analysis.dkim_result and analysis.dkim_result != 'pass':
analysis.issues.append(f"DKIM check failed: {analysis.dkim_result}")
if analysis.dmarc_result and analysis.dmarc_result != 'pass':
analysis.issues.append(f"DMARC check failed: {analysis.dmarc_result}")
return analysis
def format_report(title: str, data: dict) -> str:
"""Format check results as a readable report."""
lines = []
lines.append("=" * 60)
lines.append(f" {title}")
lines.append("=" * 60)
for key, value in data.items():
if key == 'issues':
if value:
lines.append(f"\n [ISSUES]")
for i, issue in enumerate(value, 1):
lines.append(f" {i}. {issue}")
elif isinstance(value, list):
lines.append(f"\n {key}:")
for item in value:
if isinstance(item, dict):
lines.append(f" - {json.dumps(item)}")
else:
lines.append(f" - {item}")
elif isinstance(value, bool):
status = "YES" if value else "NO"
lines.append(f" {key}: {status}")
else:
lines.append(f" {key}: {value}")
lines.append("=" * 60)
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(description="Proofpoint Email Gateway Validator")
subparsers = parser.add_subparsers(dest="command")
mx_parser = subparsers.add_parser("check-mx", help="Check MX records for Proofpoint routing")
mx_parser.add_argument("--domain", required=True, help="Domain to check")
auth_parser = subparsers.add_parser("check-auth", help="Check email authentication records")
auth_parser.add_argument("--domain", required=True, help="Domain to check")
hdr_parser = subparsers.add_parser("validate-headers", help="Analyze email headers")
hdr_parser.add_argument("--eml-file", required=True, help="Path to .eml file")
parser.add_argument("--json", action="store_true", help="Output as JSON")
args = parser.parse_args()
if args.command == "check-mx":
result = check_mx_records(args.domain)
if args.json:
print(json.dumps(asdict(result), indent=2))
else:
print(format_report("PROOFPOINT MX RECORD CHECK", asdict(result)))
elif args.command == "check-auth":
result = check_authentication(args.domain)
if args.json:
print(json.dumps(asdict(result), indent=2))
else:
print(format_report("EMAIL AUTHENTICATION CHECK", asdict(result)))
elif args.command == "validate-headers":
with open(args.eml_file, 'r', errors='replace') as f:
content = f.read()
result = analyze_headers(content)
if args.json:
print(json.dumps(asdict(result), indent=2))
else:
print(format_report("EMAIL HEADER ANALYSIS", asdict(result)))
else:
parser.print_help()
if __name__ == "__main__":
main()