UNPKG

skills-ws

Version:

85 agent skills for AI coding assistants — marketing, growth, web3, dev, design & operations. Built for OpenClaw, Claude Code, Cursor, and Codex.

583 lines (463 loc) • 27.5 kB
--- name: security-sentinel description: "Runtime threat intel — URL/phishing scans, wallet scam detection, domain reputation, email-header validation, smart-contract risk, calibrated multi-source scoring. Use when an agent must vet an unknown link, address, contract, or sender before trusting it. Siblings: security-hardening, security-pentester, virustotal." --- # Security Sentinel > Disambiguation: this skill = runtime threat intel. For defensive code patterns see `security-hardening`. For active offensive testing see `security-pentester`. For deep VirusTotal API workflows see the optional `virustotal` skill (this skill works standalone without it). Autonomous threat detection and response. Scan URLs, wallets, domains, emails, and contracts before trusting them. ## Decision Framework When an agent encounters untrusted input, classify it and run ALL matching checks in parallel: ``` Classification (applied independently — input may match multiple): ───────────────────────────────────────────────────────────────── Contains URL pattern → URL scan + domain threat check Contains wallet address → Wallet reputation + contract scan (if contract) Contains email headers → Header analysis + sender domain check Contains domain name → WHOIS age + DNS + SSL + typosquatting check Contains contract address → Bytecode analysis + honeypot detection Contains IP/hash/IOC → Threat intelligence lookup Example: A URL with a wallet address as a query parameter triggers BOTH a URL scan AND a wallet reputation check. ``` Final severity = highest severity across all matched checks. **Severity responses:** - **Clean** → proceed normally - **Suspicious** → warn the user, explain why, let them decide - **Malicious** → block the action, explain the threat, suggest alternatives --- ## 1. URL & Phishing Detection ### Scan Before Clicking ```bash # VirusTotal URL scan vt url "https://example.com" --include=last_analysis_stats,reputation # Google Safe Browsing — v4 threatMatches:find still works until 2027-03-31. # Migrate to v5 (real-time SearchHashes / hash-prefix lookups) for new builds; see §9. curl -s "https://safebrowsing.googleapis.com/v4/threatMatches:find?key=$GSB_API_KEY" \ -d '{ "threatInfo": { "threatTypes": ["MALWARE", "SOCIAL_ENGINEERING", "UNWANTED_SOFTWARE"], "platformTypes": ["ANY_PLATFORM"], "threatEntryTypes": ["URL"], "threatEntries": [{"url": "https://example.com"}] } }' ``` ### Phishing Indicators (Heuristic) Check URLs against these red flags: | Indicator | Risk | Example | |-----------|------|---------| | Homoglyph characters | High | `goog1e.com` (1 instead of l) | | Excessive subdomains | Medium | `login.secure.account.example.xyz` | | Recently registered (<30 days) | High | WHOIS creation_date check | | Free hosting/URL shortener | Medium | `bit.ly`, `000webhostapp.com` | | IP address as URL | High | `http://192.168.1.1/login` | | Misspelled brand names | High | `paypa1.com`, `arnazon.com` | | HTTP (no TLS) for login page | Critical | `http://bank.example.com/login` | | Suspicious TLD | Medium | `.xyz`, `.top`, `.buzz`, `.tk` | ### Typosquatting Detection A correct check must (1) decode punycode (`xn--`) to catch IDN homograph attacks, (2) fold Unicode confusables to their ASCII skeleton (so `раура1.com` with Cyrillic letters collapses onto `paypal`), and (3) compare the **eTLD+1** (registrable domain), not `split('.')[0]` — otherwise `paypal.com.evil.ru` and `paypal.attacker.io` slip through, and multi-label brands like `co.uk` confuse the base extraction. Compare both the edit-distance ratio AND an exact skeleton match (skeleton match = almost certainly malicious). ```python # pip install tldextract idna confusable_homoglyphs from difflib import SequenceMatcher import idna, tldextract from confusable_homoglyphs import confusables KNOWN_BRANDS = [ # store as registrable domains (eTLD+1) "google.com", "facebook.com", "paypal.com", "amazon.com", "microsoft.com", "apple.com", "netflix.com", "coinbase.com", "binance.com", "metamask.io", "uniswap.org", "opensea.io", ] # Fold a label to its ASCII/Latin "skeleton" so cross-script lookalikes collapse onto # their Latin form. We pass preferred_aliases=['latin'] so is_confusable() returns the # LATIN homoglyph of a non-Latin char (e.g. Cyrillic 'а' U+0430 -> 'a'); pure-ASCII chars # return False and pass through unchanged. def skeleton(s: str) -> str: out = [] for ch in s: try: m = confusables.is_confusable(ch, greedy=True, preferred_aliases=["latin"]) except Exception: m = False if m and m[0].get("homoglyphs"): out.append(m[0]["homoglyphs"][0]["c"]) # Latin canonical form else: out.append(ch) return "".join(out).lower() BRAND_SKELETONS = {b: skeleton(tldextract.extract(b).domain) for b in KNOWN_BRANDS} def registrable(domain: str) -> str: """Decode punycode, return eTLD+1 (handles co.uk, .com.br, etc.).""" try: domain = idna.decode(domain.encode("ascii")) if "xn--" in domain else domain except idna.IDNAError: pass ext = tldextract.extract(domain.lower()) return f"{ext.domain}.{ext.suffix}" if ext.suffix else ext.domain def check_typosquat(domain: str, threshold: float = 0.85) -> list: alerts = [] reg = registrable(domain) # e.g. paypal.com.evil.ru -> evil.ru label = tldextract.extract(reg).domain label_skel = skeleton(label) for brand, brand_skel in BRAND_SKELETONS.items(): if reg == brand: continue if label_skel == brand_skel: # confusable/homoglyph hit alerts.append(f"CRITICAL: '{domain}' confusable-matches '{brand}' (homoglyph skeleton)") continue ratio = SequenceMatcher(None, label, tldextract.extract(brand).domain).ratio() if ratio >= threshold: alerts.append(f"'{domain}' (reg: {reg}) resembles '{brand}' (similarity: {ratio:.0%})") # brand name present but NOT the registrable domain → impersonation in a subdomain/path host for brand in KNOWN_BRANDS: bl = tldextract.extract(brand).domain if bl in domain.lower() and registrable(domain) != brand: alerts.append(f"HIGH: '{brand}' label appears in '{domain}' but it is not {brand}") return alerts ``` --- ## 2. Wallet & Address Reputation ### Before Transacting ```bash # 1) Chainabuse — community scam reports (Public API v1.2; verify at docs.chainabuse.com) # Endpoint: GET /v0/reports (the "/v0/addresses/{addr}" path is gone). Screen by ?address=&chain=. # Auth: HTTP Basic — put the SAME API key in BOTH the username and password fields. # "Authorization: Basic base64(API_KEY:API_KEY)" — curl -u does this for you. # chain ∈ {ETH, BTC, TRON, SOL, POLYGON, BSC, ARBITRUM, BASE, ...} curl -s -u "$CHAINABUSE_API_KEY:$CHAINABUSE_API_KEY" \ "https://api.chainabuse.com/v0/reports?address=$ADDRESS&chain=ETH&perPage=50" # Interpret the JSON: each item has `category` (PHISHING, RUG_PULL, SCAM, RANSOMWARE, # SEXTORTION, ...), `checked` (moderator-verified), `trustedReporter` (vetted source), # `createdAt`, and `addresses[]`. Treat checked OR trustedReporter reports as high-signal; # unverified single reports as Suspicious, not Malicious. NOTE: standard free keys are # capped at ~10 calls/month (1 call = up to 50 reports) — cache aggressively (see §8). # 2) OFAC / sanctions screening. Chainabuse is DEPRECATING its sanctions endpoint; use a # sanctions oracle instead. On-chain: Chainalysis free Sanctions Oracle (read isSanctioned). # Off-chain: TRM / Chainalysis sanctions API, or match against the OFAC SDN crypto list. # Mainnet oracle 0x40C57923924B5c5c5455c48D93317139ADDaC8fb — call isSanctioned(address). # (Same address on Polygon/BSC; verify at go.chainalysis.com/chainalysis-oracle-docs.html) cast call 0x40C57923924B5c5c5455c48D93317139ADDaC8fb \ "isSanctioned(address)(bool)" "$ADDRESS" --rpc-url "$ETH_RPC_URL" # 3) Etherscan V2 (multichain, single key). V1 was deprecated in 2025 — V2 REQUIRES chainid. # Base URL: https://api.etherscan.io/v2/api (chainid 1=ETH, 8453=Base, 42161=Arbitrum, # 137=Polygon, 56=BSC, 10=Optimism). Same key works on all 50+ supported chains. # Activity / age signal — does the address have history, or is it freshly funded? curl -s "https://api.etherscan.io/v2/api?chainid=1&module=account&action=txlist&address=$ADDRESS&startblock=0&endblock=99999999&page=1&offset=10&sort=asc&apikey=$ETHERSCAN_API_KEY" # Public name tag / label (e.g. "Phish/Hack", "Fake_Phishing", exchange labels) curl -s "https://api.etherscan.io/v2/api?chainid=1&module=nametag&action=getaddresstag&address=$ADDRESS&apikey=$ETHERSCAN_API_KEY" # Is the address a verified contract? (unverified source on a "token" = elevated risk) curl -s "https://api.etherscan.io/v2/api?chainid=1&module=contract&action=getsourcecode&address=$ADDRESS&apikey=$ETHERSCAN_API_KEY" ``` ### Scam Wallet Red Flags | Signal | Risk Level | What to Check | |--------|-----------|---------------| | Chainabuse report, `checked` or `trustedReporter` | Critical | Moderator-verified / vetted-source scam report | | Chainabuse report, single unverified | Suspicious | One unverified victim report — corroborate, don't auto-block | | OFAC/SDN sanctioned address | Critical | Sanctions oracle `isSanctioned` / SDN crypto list | | Etherscan name tag = `Phish/Hack` or `Fake_Phishing` | Critical | Explorer-applied malicious label | | Tornado Cash interaction | Context-dependent | See mixer assessment below | | High-frequency small txs | Medium | Dust attack / address poisoning pattern | | Contract with no verified source | Medium | Etherscan `getsourcecode` returns empty `SourceCode` | | Recently created + high value received | High | Potential rug pull collection wallet | ### Address Poisoning Detection ``` Attacker creates addresses that look like your recent contacts: Real: 0xAbC1234567890DEF1234567890abcdef12345678 Fake: 0xAbC12...............different............45678 ^^^^^ same prefix/suffix Defense: Always verify the FULL address, not just first/last characters. ``` ### Mixer / Privacy Protocol Assessment Do NOT automatically flag all mixer interactions as suspicious. Apply contextual analysis: ``` HIGH RISK (flag as Suspicious): - Direct deposits/withdrawals > $10,000 equivalent - Multiple mixer interactions within 24 hours - Mixer usage immediately followed by transfers to exchanges - Address appears on OFAC SDN list regardless of mixer use LOWER RISK (note but do not flag): - Single small-value mixer interaction - Interaction via intermediary contract (indirect) - Known privacy-preserving DeFi protocols (not mixers) ``` When mixer interaction is detected, include this context: "This address has interacted with [protocol]. Privacy tool usage alone is not inherently malicious. Risk assessment considers transaction patterns, volume, and regulatory context." --- ## 3. Smart Contract Risk Assessment ### Honeypot Detection ```bash # Quick honeypot check (token contracts) # A honeypot lets you buy but blocks selling # Check with honeypot.is API curl -s "https://api.honeypot.is/v2/IsHoneypot?address=$TOKEN_ADDRESS&chainID=1" ``` ### Rug Pull Indicators | Check | How | Red Flag | |-------|-----|----------| | Ownership | Read `owner()` or `Ownable` | Owner can mint unlimited tokens | | Renounced | Check if owner is `0x0` | Not renounced = owner can rug | | Liquidity lock | Check LP token holder | LP tokens not locked or short lock | | Proxy contract | Check for `delegatecall` patterns | Owner can change logic at will | | Hidden mint | Search for `_mint` outside constructor | Can inflate supply post-launch | | Transfer restrictions | Check `_transfer` overrides | May block selling | | Fee manipulation | Check `setFee`/`setTax` functions | Owner can set 100% sell tax | | Blacklist function | Search for `blacklist`/`isBlacklisted` | Owner can freeze your tokens | ### Automated Contract Scan Checklist ``` 1. Is source code verified on block explorer? → No = HIGH RISK 2. Is ownership renounced (owner == 0x0)? → No = CHECK FURTHER 3. Are there mint functions callable by owner? → Yes = HIGH RISK 4. Are there blacklist/whitelist functions? → Yes = MEDIUM RISK 5. Is there a max transaction/wallet limit? → Check if owner-adjustable 6. Are LP tokens locked? For how long? → <30 days = HIGH RISK 7. Are there pausable functions? → Yes = MEDIUM RISK (could be legitimate) 8. Does the contract use upgradeable proxy? → Yes = CHECK proxy admin ``` --- ## 4. Email Header Analysis ### Validate Sender Authenticity ```bash # Check SPF record dig TXT example.com | grep "v=spf1" # Check DKIM selector (replace "selector" with the real one from the email's # DKIM-Signature header s= tag — common defaults: google, default, k1, s1) dig TXT selector._domainkey.example.com # Check DMARC policy dig TXT _dmarc.example.com ``` ### Header Red Flags | Header Field | Check | Red Flag | |-------------|-------|----------| | `Return-Path` | Match with `From` | Different domain = spoofing attempt | | `Received` chain | Trace hops | Unexpected mail servers | | `Authentication-Results` | SPF/DKIM/DMARC | `fail` or `none` on any | | `X-Mailer` | Software used | Bulk mailer or suspicious client | | `Reply-To` | Match with `From` | Different address = phishing likely | | `Message-ID` domain | Match with sender | Mismatch = forged email | ### Interpreting Authentication Results ``` Authentication-Results: mx.google.com; dkim=pass header.d=example.com; ← GOOD: signed by claimed domain spf=pass (google.com: domain of noreply@example.com designates 1.2.3.4 as permitted sender); dmarc=pass (p=REJECT) ← GOOD: strict DMARC policy If ANY of dkim/spf/dmarc = fail → SUSPICIOUS If sender domain has no DMARC record → MEDIUM RISK (no spoofing protection) If DMARC policy = none → LOW protection (monitoring only, not enforcing) ``` --- ## 5. Domain Intelligence ### WHOIS Age Check ```bash # Check domain registration age whois example.com | grep -i "creation date" # Age is a RISK MULTIPLIER, never a verdict on its own. Legitimate new domains exist: # product launches, marketing campaigns, startups, and incident-response/takedown domains # are routinely days old. Weight age UP only when combined with another signal (typosquat, # a credential/login or "connect wallet" page, or a threat-intel hit — see §6 STEP 2/3). # < 7 days → strong risk signal; CRITICAL only if it ALSO impersonates a brand or # collects credentials/funds. Bare new domain = elevated, not confirmed-bad. # < 30 days → HIGH contribution to score # < 90 days → MEDIUM (commonly a legitimate startup or campaign) # > 1 year → LOW (age is reassuring but not proof — aged domains get hijacked too) ``` ### SSL/TLS Assessment ```bash # Check certificate details echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \ | openssl x509 -text -noout ``` ``` Key checks (TLS is weak signal for malice — calibrate to context): - Issuer: free CAs (Let's Encrypt, ZeroSSL, Google Trust) are the norm now, NOT a red flag. - Subject / SAN: does CN/SAN actually cover the host? A mismatch or a wildcard that does not include the brand it claims to be = real signal. - Validity window: SHORT-LIVED CERTS ARE NORMAL in 2026 (ACME automation; the CA/Browser Forum is driving max lifetimes toward ~47 days by 2029). Do NOT flag short rotation as abuse. A LONG-lived cert with a brand mismatch is more suspicious than a fresh ACME cert. - Self-signed / private CA: expected for internal, *.internal, RFC-1918, and corp-PKI hosts — DOWN-weight there (see §6 STEP 3). Treat as a real problem ONLY on a PUBLIC site that presents itself as a bank/exchange/brand or collects credentials, where a browser would show a trust error. Combine with the host's public reputation before concluding. ``` ### DNS Anomalies ```bash # Check for suspicious DNS patterns dig A example.com +short # IP resolution dig MX example.com +short # Mail servers dig NS example.com +short # Name servers dig TXT example.com +short # SPF, verification records # Red flags: # - Cloudflare/hosting IP resolving to a brand-impersonating domain # - No MX records for a domain claiming to send email # - Recently changed NS records (domain hijack indicator) ``` --- ## 6. Threat Intelligence Lookups ### IOC Enrichment ```bash # AbuseIPDB — check IP reputation (use a placeholder IP; never hardcode a real target) curl -s "https://api.abuseipdb.com/api/v2/check?ipAddress=203.0.113.10&maxAgeInDays=90" \ -H "Key: $ABUSEIPDB_API_KEY" \ -H "Accept: application/json" # OpenPhish — current phishing-URL feed (PhishTank's public API is deprecated; see §9). # Free community feed, no key; refreshed frequently. Match the target against the feed. curl -s "https://openphish.com/feed.txt" | grep -Fxq "https://suspicious.example.com" \ && echo "OPENPHISH: listed (high-confidence phishing)" || echo "OPENPHISH: not listed" # OTX AlienVault — threat indicators (free, key required) curl -s "https://otx.alienvault.com/api/v1/indicators/domain/example.com/general" \ -H "X-OTX-API-KEY: $OTX_API_KEY" ``` ### Threat Intelligence Decision (calibrated, not a naive sum) Summing heterogeneous vendor weights and blocking at a fixed threshold produces false blocks (e.g. a year-old AbuseIPDB note + a 0.5 OTX prior would "block" a clean target). Use this ordered decision procedure instead. Run it AFTER the allowlist gate. ``` STEP 0 — Known-good gate (prevents the worst false positives): - Target is an org-maintained allowlist entry (your own domains/contracts)? → CLEAN, stop. - Domain on a major reputable list (e.g. Tranco/Cisco-Umbrella top ~10k) AND not flagged by any AUTHORITATIVE source below? → CLEAN, lower the weight of weak signals. STEP 1 — Authoritative override (any ONE ⇒ verdict immediately): - Google Safe Browsing v5 match (MALWARE / SOCIAL_ENGINEERING / UNWANTED) → MALICIOUS. - OpenPhish / verified Chainabuse (checked|trustedReporter) listing → MALICIOUS. - OFAC/SDN sanctioned address → MALICIOUS (legal, not heuristic). - VirusTotal ≥ 5 engines flagging, OR ≥ 3 reputable engines agreeing → MALICIOUS. These are high-precision; a single hit is sufficient. Do NOT average them away. STEP 2 — Corroboration tier (needs ≥ 2 independent signals OR 1 strong + recency): Score each source, then require AGREEMENT rather than a raw sum: VirusTotal = engines_flagging / total_engines (1–4 engines = weak) AbuseIPDB = abuseConfidenceScore/100, ×0.5 if newest report > 90d old OTX pulses = 0.4 (prior/context only — never decisive alone) Chainabuse = 0.8 if checked|trusted else 0.3 (unverified) Domain age = +0.3 if eTLD+1 < 30d (see §5 — age alone is NOT proof) Typosquat hit = skeleton match 0.9 / edit-distance 0.5 (see §1) - ≥ 2 independent sources each ≥ 0.4 → SUSPICIOUS (warn, show every source). - 1 source ≥ 0.4 AND report age < 7d → SUSPICIOUS (fresh single-vendor signal). - exactly 1 weak source (< 0.4) → LOW-CONFIDENCE note, proceed with caution. - 0 signals → CLEAN. STEP 3 — Target-category weighting: - Money-moving target (wallet, contract, "connect wallet"/login page) → escalate one band on uncertainty (SUSPICIOUS→treat as block-worthy until confirmed). - Internal/private host (RFC-1918, .internal, corp CA) → DOWN-weight TLS/age/self-signed heuristics; private PKI and fresh certs are normal there. STEP 4 — False-positive escalation (before blocking anything high-impact): - Conflict (authoritative CLEAN vs heuristic MALICIOUS)? Surface BOTH, do not auto-block; ask the user or require a second authoritative source. - Always record: source name, exact verdict field, report timestamp, and your confidence. New threats often start with one vendor — log uncertainty, never fabricate corroboration. ``` ### Output Templates (evidence, source, timestamp, uncertainty — always) ``` CLEAN ✅ <target> — no threats found. Checked: VirusTotal (0/72), Google Safe Browsing v5 (no match), Chainabuse (0 reports). Sources current as of <ISO-8601 ts>. Absence of reports ≠ proof of safety. SUSPICIOUS ⚠️ <target> — proceed with caution. Confidence: MEDIUM. Evidence: • VirusTotal: 3/72 engines (Fortinet, Sophos, Kaspersky) flag "phishing" [scanned <ts>] • Domain age: registered 5 days ago (eTLD+1 <reg-domain>) No authoritative source confirms. Recommend not entering credentials/funds until verified. MALICIOUS 🛑 <target> — BLOCKED. Confidence: HIGH. Authoritative match: • Google Safe Browsing v5: SOCIAL_ENGINEERING [<ts>] • Chainabuse: 4 reports, category RUG_PULL, moderator-checked [oldest <ts>] Action taken: <blocked tx / blocked navigation>. Alternative: <safe path>. ``` --- ## 7. Continuous Monitoring Playbook ### Agent-Initiated Security Checks An autonomous security agent should proactively scan at these trigger points: ``` TRIGGER ACTION FREQUENCY ──────────────────────────────── ────────────────────────────── ────────── User shares a URL → url_scan + domain_threat Every time User provides wallet address → wallet_check Every time New dependency added → npm audit + snyk check On change Pre-deployment → header_scan + ssl_audit Per deploy Weekly maintenance → full domain posture check Weekly Email campaign setup → SPF/DKIM/DMARC validation On setup Smart contract interaction → contract_scan + honeypot Every time File download from external → VirusTotal file hash check Every time ``` ### Incident Response Quick Actions ``` 1. PHISHING DETECTED → Block URL in security headers (CSP) → Notify affected users → Report it: Google Safe Browsing (safebrowsing.google.com/safebrowsing/report_phish), APWG (reportphishing@apwg.org), and the impersonated brand's abuse contact → Check if credentials were entered → force password reset 2. SCAM WALLET DETECTED → Block transaction → Warn user with specific evidence → Report to Chainabuse (chainabuse.com/report) → Check transaction history for prior interactions 3. COMPROMISED DOMAIN DETECTED → Revoke any API keys associated with domain → Update DNS if you control it → Notify users who may have visited → Check for data exfiltration in logs 4. MALICIOUS CONTRACT DETECTED → Revoke token approvals (approve(0)) → Warn user with contract analysis → Check for pending transactions to cancel → Report to block explorer ``` --- ## 8. Result Caching Cache scan results to preserve API quota and avoid redundant checks: | Check Type | Cache TTL | Cache Key | |-----------|----------|-----------| | URL scan | 1 hour | Normalized URL (strip tracking params) | | Domain WHOIS | 24 hours | Domain name | | Wallet reputation | 15 minutes | Address (lowercased) | | Contract scan | 1 hour | Contract address + chain ID | | Threat intel IOC | 30 minutes | IOC value | - Cache is in-memory only (no persistence across sessions) - Force refresh available via user request: "rescan [target]" - Cache hit returns cached result with age note (e.g., "cached 12 min ago") --- ## 9. API Quick Reference ### Free Tier APIs Free-tier terms change — figures below are "as of Jun 2026, verify at the linked page." | Service | Free Limit (verify) | Best For | Notes | |---------|-----------|----------|-------| | VirusTotal (v3) | 4/min, 500/day, 15.5k/month | URL, file, domain, IP scans | Public API; verify docs.virustotal.com/reference/public-vs-premium-api | | AbuseIPDB | ~1,000 checks/day | IP reputation | verify abuseipdb.com/pricing | | PhishTank | Deprecated | — | Public API restricted; do not rely on it. Use OpenPhish instead | | OpenPhish | Community feed | Phishing URL feed | Free, no key; `openphish.com/feed.txt`. PhishTank replacement | | OTX AlienVault | Free, key required | Threat indicators, IOCs | "Unlimited" no longer guaranteed — verify otx.alienvault.com | | Google Safe Browsing v5 | Free, default quota (raise via Cloud Console) | URL safety check | v4 ends 2027-03-31; migrate to v5. No published hard 10k/day cap | | Etherscan API V2 | ~5 req/sec, ~100k/day | Multichain contract/tx/label lookups | One key, 50+ chains via `chainid`; verify etherscan.io/apis | | Chainabuse (Public API v1.2) | ~10 calls/month (≤50 reports each) | Crypto scam reports | Basic auth; very low free quota — cache hard. docs.chainabuse.com | | Honeypot.is | Generous free tier | Token honeypot detection | verify honeypot.is | | WHOIS / RDAP (CLI) | ~30-50/min per registrar | Domain age and registrar | RDAP is the modern replacement for port-43 WHOIS; backoff on failures | ### Environment Variables ```bash VT_API_KEY= # VirusTotal (v3 public API) GSB_API_KEY= # Google Safe Browsing (v5; v4 sunsets 2027-03-31) ABUSEIPDB_API_KEY= # AbuseIPDB OTX_API_KEY= # AlienVault OTX ETHERSCAN_API_KEY= # Etherscan API V2 — single key covers all chains via chainid ETH_RPC_URL= # JSON-RPC endpoint (for cast calls, e.g. the sanctions oracle) CHAINABUSE_API_KEY= # Chainabuse v1.2 — same key used as BOTH Basic-auth user AND password # PhishTank removed: public API deprecated; OpenPhish needs no key (see §6). # Never commit real keys — use a .env file or secrets manager; rotate if exposed. ``` ### Graceful Degradation Not all API keys are required. The agent should adapt based on what's available: ``` Keys configured Capability level Behavior ─────────────── ─────────────────── ──────────────────────────────────────────── All keys Full All checks enabled 4-6 keys Partial Run available checks, warn about gaps 1-3 keys Degraded Heuristic-heavy mode, warn prominently 0 keys Heuristic-only Pattern matching only, no external lookups ``` On startup, log which checks are unavailable: - Example: "VT_API_KEY not set — URL reputation checks will use heuristics only" On API errors during operation: - Timeout (>5s): skip source, note in output, continue with other sources - Rate limited (429): queue and retry with exponential backoff, warn user of delay - Server error (5xx): skip source, note in output, continue - All external sources fail: switch to heuristic mode and warn explicitly