1140 lines
62 KiB
Python
Executable File
1140 lines
62 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""KeyHunter v11 — Universal Secret Parser (consolidated from 9 catalogs).
|
|
|
|
Features:
|
|
- 180+ provider patterns (unified 160 + CN-ENV wave + SOTA)
|
|
- Bracket-key extraction: [], {}, (), <>
|
|
- Enrich output: provider, key, source, entropy, validation, masked, fingerprint, ts
|
|
- Modular: --extract-only | --validate
|
|
- 305+ ENV variable mappings
|
|
- VALIDATION_META for 34 providers
|
|
|
|
Canonical base: unified_key_patterns.py + src/key_patterns.py CN-wave + RESEARCH_upgrade.md + imba patterns_extra.py
|
|
"""
|
|
import argparse
|
|
import base64
|
|
import hashlib
|
|
import json
|
|
import math
|
|
import re
|
|
import sys
|
|
import time
|
|
from collections import Counter
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# PATTERNS — canonical catalog (sorted: specific prefixes first, loose forms last)
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
PATTERNS = [
|
|
# ── v6.5 gitleaks/trufflehog high-value rules ───────────────────────────
|
|
('OPENAI_ADMIN', re.compile(r'\bsk-admin-[A-Za-z0-9_-]{58}T3BlbkFJ[A-Za-z0-9_-]{58}\b')),
|
|
('LANGSMITH', re.compile(r'\blsv2_(?:pt|sk)_[a-f0-9]{32}_[a-f0-9]{10}\b')),
|
|
('DATABRICKS', re.compile(r'\bdapi[a-f0-9]{32}(?:-\d)?\b')),
|
|
('DOPPLER', re.compile(r'\bdp\.pt\.[a-z0-9]{43}\b')),
|
|
('ARTIFACTORY', re.compile(r'\bAKCp[A-Za-z0-9]{69}\b')),
|
|
('ALIBABA_LTAI', re.compile(r'\bLTAI[a-z0-9]{12,22}\b')),
|
|
('AGE_KEY', re.compile(r'\bAGE-SECRET-KEY-1[QPZRY9X8GF2TVDW0S3JN54KHCE6MUA7L]{58}\b')),
|
|
('ONEPASSWORD', re.compile(r'\bA3-[A-Z0-9]{6}-(?:[A-Z0-9]{11}|[A-Z0-9]{6}-[A-Z0-9]{5})-[A-Z0-9]{5}-[A-Z0-9]{5}-[A-Z0-9]{5}\b')),
|
|
('AZURE_AD', re.compile(r'\b[a-zA-Z0-9_~.]{3}\dQ~[a-zA-Z0-9_~.-]{31,34}\b')),
|
|
|
|
# ── batch-2 (2026-10-06): HEROKU + SUPABASE ─────────────────────────────
|
|
('HEROKU', re.compile(r'\bHRKU-AA[0-9a-zA-Z_-]{58}')),
|
|
('SUPABASE', re.compile(r'\bsbp-[a-f0-9]{40}')),
|
|
|
|
# ── AI inference providers (specific prefixes first) ────────────────────
|
|
('ZAI', re.compile(r'[a-f0-9]{32}\.[A-Za-z0-9]{10,30}')),
|
|
('SAKANA', re.compile(r'fish_[a-f0-9]{64}')),
|
|
('OPENROUTER', re.compile(r'sk-or-v1-[0-9a-f]{48,70}')),
|
|
('DASHSCOPE', re.compile(r'sk-sp-[A-Za-z0-9_\-]{20,64}|sk-ws-[A-Za-z0-9_\-\.]{16,200}|sk-[a-f0-9]{32}(?![0-9a-fA-F])')),
|
|
('MINIMAX', re.compile(r'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9\.[A-Za-z0-9_\-]{20,}\.[A-Za-z0-9_\-]{20,}')),
|
|
# NOTE: DEEPSEEK regex identical to DASHSCOPE alt — first-wins dedup
|
|
('DEEPSEEK', re.compile(r'sk-[a-f0-9]{32}')),
|
|
('SILICONFLOW', re.compile(r'sk-[a-zA-Z0-9]{48}')),
|
|
('KIMI', re.compile(r'sk-[A-Za-z0-9]{48}')),
|
|
('FAL', re.compile(r'\b[0-9a-f]{32}:[0-9a-f]{64}\b')),
|
|
('ARK', re.compile(r'(?:ARK|VOLCENGINE|DOUBAO|SEEDANCE|BYTEDANCE)_?API_KEY["\']?\s*[=:]\s*["\']?([A-Za-z0-9\-]{20,64})', re.IGNORECASE)),
|
|
('FIREWORKS', re.compile(r'(?:fireworks|FIREWORKS)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([A-Za-z0-9]{48,52})', re.IGNORECASE)),
|
|
('FIREWORKS', re.compile(r'fw_[A-Za-z0-9]{40,60}')),
|
|
('GROQ', re.compile(r'gsk_[A-Za-z0-9]{40,60}')),
|
|
('COHERE', re.compile(r'(?:cohere|COHERE)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([A-Za-z0-9]{40})', re.IGNORECASE)),
|
|
('NOVITA', re.compile(r'(?:novita|NOVITA)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([A-Za-z0-9_-]{32,40})', re.IGNORECASE)),
|
|
('PERPLEXITY', re.compile(r'pplx-[a-z0-9]{56}')),
|
|
('ELEVENLABS', re.compile(r'\b(?:elevenlabs|ELEVENLABS|xi)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([a-f0-9]{32})', re.IGNORECASE)),
|
|
('STABILITY', re.compile(r'sk-[A-Za-z0-9]{99}')),
|
|
('NVIDIA', re.compile(r'\bnvapi-[A-Za-z0-9_\-]{40,80}(?![A-Za-z0-9_\-])')),
|
|
('HUGGINGFACE', re.compile(r'hf_[A-Za-z0-9]{34,}')),
|
|
('GROK', re.compile(r'xai-[A-Za-z0-9_]{40,100}')),
|
|
('MESHY', re.compile(r'\bmsh_[A-Za-z0-9]{30,40}\b')),
|
|
('LUMA', re.compile(r'\bluma-[a-z0-9]{8}-[a-z0-9]{8}-[a-z0-9]{8}\b')),
|
|
('MISTRAL', re.compile(r'(?:mistral|MISTRAL)[_a-zA-Z]{0,15}["\']?\s*[=:]\s*["\']?([A-Za-z0-9]{32})(?![A-Za-z0-9])')),
|
|
('TOGETHER', re.compile(r'tgp_[A-Za-z0-9\-]{40,60}')),
|
|
('REPLICATE', re.compile(r'r8_[A-Za-z0-9]{37}')),
|
|
('CEREBRAS', re.compile(r'csk-[a-z0-9]{60}')),
|
|
|
|
# ── SOTA — 2026-10-15 from RESEARCH_upgrade.md ──────────────────────────
|
|
('FEATHERLESS', re.compile(r'\bfea_[A-Za-z0-9]{30,}\b')),
|
|
('BROWSERBASE', re.compile(r'\bbbs_[A-Za-z0-9]{30,}\b')),
|
|
('MODAL_TOKEN', re.compile(r'\bAk-[A-Za-z0-9]{40}\b')),
|
|
('INFRACOST', re.compile(r'\bico-[a-zA-Z0-9]{32}\b')),
|
|
('HF_ORG', re.compile(r'\bapi_org_[A-Za-z0-9]{34}\b')),
|
|
('GITLAB_ROUTABLE', re.compile(r'\bglrt-[A-Za-z0-9_\-]{20,}\b')),
|
|
|
|
# ── captcha solver providers ────────────────────────────────────────────
|
|
('TWOCAPTCHA', re.compile(r'(?:2captcha|rucaptcha|twocaptcha|two_captcha)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([a-zA-Z0-9]{32})', re.IGNORECASE)),
|
|
('CAPSOLVER', re.compile(r'(?:capsolver|cap_solver)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?(CAP-[A-Za-z0-9_-]{20,45})', re.IGNORECASE)),
|
|
('ANTICAPTCHA', re.compile(r'(?:anticaptcha|anti_captcha)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([a-f0-9]{32})', re.IGNORECASE)),
|
|
('CAPMONSTER', re.compile(r'(?:capmonster|cap_monster)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([a-f0-9]{32})', re.IGNORECASE)),
|
|
('EZSOLVER', re.compile(r'(?:ezsolver|ez_solver)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([a-zA-Z0-9]{32})', re.IGNORECASE)),
|
|
('NOPECHA', re.compile(r'(?:nopecha|nope_cha)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([a-zA-Z0-9_\-]{20,64})', re.IGNORECASE)),
|
|
('AZCAPTCHA', re.compile(r'(?:azcaptcha|az_captcha)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([a-zA-Z0-9]{32})', re.IGNORECASE)),
|
|
('YESSOLVER', re.compile(r'(?:yessolver|yes_solver)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([a-zA-Z0-9]{32,64})', re.IGNORECASE)),
|
|
('TWOCAPTCHA_URL', re.compile(r'2captcha\.com/in\.\w+\?key=([a-f0-9]{32})')),
|
|
|
|
# ── relay/gateway base URLs (ONE_API/NEW_API/DIFY/FASTGPT/LOBECHAT/
|
|
# OPENWEBUI/OLLAMA). MUST precede PROXY_HTTP/HTTPS_AUTH: _dedup keeps the
|
|
# first label per value and relay URLs almost always carry a port.
|
|
('ENDPOINT_CN', re.compile(
|
|
r'(?:ONE[_\s-]?API|NEW[_\s-]?API|DIFY|FASTGPT|LOBECHAT|OPENWEBUI|OLLAMA)'
|
|
r'[_\s]*(?:(?:URL|HOST|BASE|ENDPOINT|API|SERVER|ADDRESS|ADDR|PROXY|DOMAIN)[_\s]*){1,2}'
|
|
r'\s*[=:]\s*["\']?(https?://[^\s"\']{10,200})',
|
|
re.IGNORECASE)),
|
|
|
|
# ── proxy providers ─────────────────────────────────────────────────────
|
|
('SOAX', re.compile(r'(?:soax|SOAX)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([A-Za-z0-9_-]{20,50})(?![A-Za-z0-9])')),
|
|
('PROXY_SOCKS5', re.compile(r'socks5://(?:[^:@]+:[^@]+@)?[^\s"\'<>]+:\d{2,5}')),
|
|
('PROXY_HTTP', re.compile(r'https?://(?:[^:@]+:[^@]+@)?[^\s"\'<>]+:\d{2,5}')),
|
|
('DAYTONA', re.compile(r'dtn_[a-f0-9]{64}')),
|
|
('WEBSHARE', re.compile(r'(?:webshare)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})', re.IGNORECASE)),
|
|
('IPROYAL', re.compile(r'(?:iproyal|ip_royal)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([a-zA-Z0-9_-]{20,50})', re.IGNORECASE)),
|
|
('BRIGHTDATA', re.compile(r'(?:brightdata|luminati|brd)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([a-f0-9]{32})', re.IGNORECASE)),
|
|
|
|
# ── messaging / social ──────────────────────────────────────────────────
|
|
('TELEGRAM', re.compile(r'\b\d{8,10}:AA[A-Za-z0-9_\-]{33}\b')),
|
|
('DISCORD', re.compile(r'mfa\.[A-Za-z0-9_\-]{24,120}(?![A-Za-z0-9_-])')),
|
|
('DISCORD', re.compile(r'(?<![A-Za-z0-9])[MN][A-Za-z\d]{23,}\.[\w-]{6}\.[\w-]{27}(?![\w-])')),
|
|
|
|
# ── major AI providers ──────────────────────────────────────────────────
|
|
('OPENAI', re.compile(r'sk-proj-[A-Za-z0-9_-]{60,200}'
|
|
r'|sk-svcacct-[A-Za-z0-9_-]{60,200}'
|
|
r'|sk-None-[A-Za-z0-9_-]{20,200}'
|
|
r'|"sk-[A-Za-z0-9]{20}iv|"sk-[A-Za-z0-9]{20}proj')),
|
|
('ANTHROPIC', re.compile(r'sk-ant-api03-[A-Za-z0-9_-]{20,120}')),
|
|
('ANTHROPIC_OAT', re.compile(r'sk-ant-oat01-[A-Za-z0-9_-]{40,120}')),
|
|
('GOOGLE', re.compile(r'(?:GOOGLE|google|GCP|gcp)[_ ]?(?:API|APPLICATION_CREDENTIALS|AI)[_ ]?(?:KEY|TOKEN|CREDENTIALS|FILE)?[\'"]?\s*[=:]\s*[\'"]?(AIza[A-Za-z0-9_-]{35})')),
|
|
('GOOGLE', re.compile(r'AIza[A-Za-z0-9_-]{35}')),
|
|
|
|
# ── v4 traffic & infra ──────────────────────────────────────────────────
|
|
('ALIYUN', re.compile(r'LTAI[A-Za-z0-9]{12,20}')),
|
|
('AWS', re.compile(r'AKIA[A-Z0-9]{16}')),
|
|
('CLOUDFLARE', re.compile(r'(?:cf_|CF_)[A-Za-z0-9_-]{40,50}')),
|
|
('STRIPE', re.compile(r'sk_live_[A-Za-z0-9]{24,}')),
|
|
('GITHUB_PAT', re.compile(r'ghp_[A-Za-z0-9]{36,}')),
|
|
('GITHUB_OAUTH', re.compile(r'gho_[A-Za-z0-9]{36}')),
|
|
('GITHUB_USER', re.compile(r'ghu_[A-Za-z0-9]{36,45}')),
|
|
('GITHUB_SERVER', re.compile(r'ghs_[A-Za-z0-9]{36,45}')),
|
|
('GITHUB_REFRESH', re.compile(r'ghr_[A-Za-z0-9]{76,90}')),
|
|
('GITHUB_FINE', re.compile(r'github_pat_[A-Za-z0-9_]{82}')),
|
|
('SLACK_BOT', re.compile(r'xoxb-[0-9]{8,12}-[0-9]{8,12}-[A-Za-z0-9]{20,24}')),
|
|
('SLACK_USER', re.compile(r'xoxp-[0-9]{8,12}-[0-9]{8,12}-[0-9]{8,12}-[A-Za-z0-9]{20,30}')),
|
|
('SLACK_APP', re.compile(r'xapp-[0-9]-[A-Za-z0-9-]{20,}')),
|
|
('LINEAR', re.compile(r'lin_api_[A-Za-z0-9]{40}')),
|
|
('SENTRY', re.compile(r'sntrys_[A-Za-z0-9_\-]{60,}')),
|
|
('FIGMA', re.compile(r'figd_[A-Za-z0-9]{40}')),
|
|
('POSTMAN', re.compile(r'PMAK-[A-Za-z0-9]{24}-[A-Za-z0-9]{43}')),
|
|
('NOTION_BARE', re.compile(r'secret_[A-Za-z0-9]{43}')),
|
|
('UPSTASH', re.compile(r'upstash_[A-Za-z0-9]{30,40}')),
|
|
('NPM', re.compile(r'npm_[A-Za-z0-9]{36}')),
|
|
('PYPI', re.compile(r'pypi-[A-Za-z0-9_-]{50,}')),
|
|
('SENDGRID', re.compile(r'SG\.[A-Za-z0-9_-]{22}\.[A-Za-z0-9_-]{43}')),
|
|
('TWILIO', re.compile(r'(?:twilio|TWILIO)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([a-f0-9]{32})', re.IGNORECASE)),
|
|
('NOTION', re.compile(r'(?:notion|NOTION)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?(secret_[A-Za-z0-9]{43})', re.IGNORECASE)),
|
|
('PINECONE', re.compile(r'(?:pinecone|PINECONE)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})', re.IGNORECASE)),
|
|
('WANDB', re.compile(r'(?:wandb|WANDB)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([a-f0-9]{40})', re.IGNORECASE)),
|
|
('RUNPOD', re.compile(r'(?:runpod|RUNPOD)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([A-Za-z0-9_-]{30,50})', re.IGNORECASE)),
|
|
('TWILIO_SK', re.compile('SK[0-9a-f]{32}')),
|
|
('GITLAB_PAT', re.compile('glpat-[0-9a-zA-Z_-]{20,}')),
|
|
('PG_URI', re.compile('postgres(?:ql)?://[^\\s\\"\'<>]{1,64}:[^\\s\\"\'@<>]{4,}@[^\\s\\"\'<>]+')),
|
|
('MYSQL_URI', re.compile('mysql://[^\\s\\"\'<>]{1,64}:[^\\s\\"\'@<>]{4,}@[^\\s\\"\'<>]+')),
|
|
('MONGO_URI', re.compile('mongodb(?:\\+srv)?://[^\\s\\"\'<>]{1,64}:[^\\s\\"\'@<>]{4,}@[^\\s\\"\'<>]+')),
|
|
('REDIS_URI', re.compile('rediss?://:[^\\s\\"\'@<>]{4,}@[^\\s\\"\'<>]+')),
|
|
('HTTPS_AUTH', re.compile('https://[^\\s\\"\'/:@<>]{1,64}:[^\\s\\"\'@/<>]{4,}@[^\\s\\"\'<>]+')),
|
|
('AZURE_STORAGE', re.compile(r'DefaultEndpointsProtocol=https;AccountName=[a-z0-9][a-z0-9-]{1,22}[a-z0-9];AccountKey=[A-Za-z0-9+/]{86}==')),
|
|
|
|
# ── v6 full-stack wave ──────────────────────────────────────────────────
|
|
('NEON', re.compile(r'napi_[A-Za-z0-9]{30,60}')),
|
|
('DROPBOX', re.compile(r'sl\.[A-Za-z0-9_-]{100,150}')),
|
|
('TWITTER', re.compile(r'AAAAAAAAA[A-Za-z0-9%]{50,90}')),
|
|
('BREVO', re.compile(r'xkeysib-[A-Za-z0-9]{16,64}-[A-Za-z0-9]{16,64}')),
|
|
('RESEND', re.compile(r're_[A-Za-z0-9]{28,50}')),
|
|
('MAILGUN', re.compile(r'\bkey-[a-f0-9]{32}\b')),
|
|
('SHOPIFY', re.compile(r'shp(?:at|ca|ss|pa|ua)_[a-fA-F0-9]{32}')),
|
|
('SQUARE', re.compile(r'sq0atp-[A-Za-z0-9_-]{22,60}')),
|
|
('SQUARE_SECRET', re.compile(r'sq0csp-[A-Za-z0-9_-]{40,60}')),
|
|
('STRIPE_WEBHOOK', re.compile(r'whsec_[A-Za-z0-9]{32,64}')),
|
|
('STRIPE_RESTRICTED', re.compile(r'rk_live_[A-Za-z0-9]{24,99}')),
|
|
('DIGITALOCEAN', re.compile(r'do[po]_v1_[a-f0-9]{64}')),
|
|
('DOCKER', re.compile(r'dckr_pat_[A-Za-z0-9_-]{30,}')),
|
|
('KAKAO', re.compile(r'KakaoAK [0-9a-f]{32}')),
|
|
('RUBYGEMS_PAT', re.compile(r'rubygems_[A-Za-z0-9]{30,}')),
|
|
('SONARQUBE', re.compile(r'squ_[A-Za-z0-9]{40,}')),
|
|
('GRAFANA', re.compile(r'glc_[A-Za-z0-9_-]{40,}')),
|
|
('LANGSMITH', re.compile(r'lsv2_(?:pt|sk)_[A-Za-z0-9]{40,}')),
|
|
('TAVILY', re.compile(r'tvly-[A-Za-z0-9]{26,48}')),
|
|
('FIRECRAWL', re.compile(r'fc-[a-f0-9]{32}')),
|
|
('NETLIFY', re.compile(r'nfp_[A-Za-z0-9]{40,46}')),
|
|
('RENDER', re.compile(r'rnd_[A-Za-z0-9]{32,}')),
|
|
('MAPBOX_SECRET', re.compile(r'sk\.eyJ[A-Za-z0-9_\-]{50,}')),
|
|
('HUBSPOT', re.compile(r'pat-(?:na|eu)\d-[A-Za-z0-9]{30,}')),
|
|
('AIRTABLE', re.compile(r'pat[A-Za-z0-9]{8,20}\.[A-Za-z0-9]{40,}')),
|
|
('SENTRY_DSN', re.compile(r'https://[a-f0-9]{32}@[a-z0-9.-]*sentry\.io/\d+')),
|
|
('PRIVATE_KEY', re.compile(r'-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]{0,6000}?-----END [A-Z ]*PRIVATE KEY-----')),
|
|
('MAILCHIMP', re.compile(r'\b[a-f0-9]{32}-us\d{1,2}\b')),
|
|
('ACCTOKEN', re.compile(r'access_token\$production\$[a-z0-9]{16}\$[a-f0-9]{32}')),
|
|
|
|
# ── v7 PatternForge wave ────────────────────────────────────────────────
|
|
('JINA', re.compile(r'(?<![A-Za-z0-9_])jina_[A-Fa-f0-9]{20,50}(?![A-Za-z0-9])')),
|
|
('VOYAGE', re.compile(r'(?<![A-Za-z0-9_-])pa-[A-Za-z0-9_-]{20,60}(?![A-Za-z0-9_-])')),
|
|
('BRAVE_SEARCH', re.compile(r'(?<![A-Za-z0-9])BSA[A-Za-z0-9_-]{20,80}(?![A-Za-z0-9_-])')),
|
|
('BFL', re.compile(r'(?<![A-Za-z0-9_])bfl_[A-Za-z0-9_-]{20,60}(?![A-Za-z0-9_-])')),
|
|
('DEEPINFRA', re.compile(r'(?<![A-Za-z0-9_])di_[A-Za-z0-9]{30,80}(?![A-Za-z0-9])')),
|
|
('LANGFUSE', re.compile(r'(?<![A-Za-z0-9_-])(?:pk-lf-|sk-lf-)[A-Za-z0-9_-]{20,80}(?![A-Za-z0-9_-])')),
|
|
('PINECONE_V2', re.compile(r'(?<![A-Za-z0-9_])pcsk_[A-Za-z0-9_-]{30,80}(?![A-Za-z0-9_-])')),
|
|
|
|
# ── v7 env-context-gated patterns ───────────────────────────────────────
|
|
('BASETEN', re.compile(r'(?:BASETEN|baseten)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([A-Za-z0-9_-]{20,60})', re.IGNORECASE)),
|
|
('SERPER', re.compile(r'(?:SERPER|serper)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?(sp_[A-Za-z0-9_-]{20,60})', re.IGNORECASE)),
|
|
('SERPAPI', re.compile(r'(?:SERPAPI|serpapi)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([A-Za-z0-9]{30,80})', re.IGNORECASE)),
|
|
('EXA', re.compile(r'(?:EXA|exa)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([A-Za-z0-9_-]{20,60})', re.IGNORECASE)),
|
|
('RUNWAY', re.compile(r'(?:RUNWAY|runway|RUNWAYML)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([A-Za-z0-9_-]{20,60})', re.IGNORECASE)),
|
|
('PIKA', re.compile(r'(?:PIKA|pika)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([A-Za-z0-9_-]{20,60})', re.IGNORECASE)),
|
|
('SUNO', re.compile(r'(?:SUNO|suno)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?(sk-[A-Za-z0-9_-]{20,60})', re.IGNORECASE)),
|
|
('FLYIO', re.compile(r'(?:FLY|fly)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([A-Za-z0-9_-]{30,80})', re.IGNORECASE)),
|
|
('TENCENT', re.compile(r'(?:TENCENT|tencent|HUNYUAN|hunyuan)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([A-Za-z0-9_-]{20,60})', re.IGNORECASE)),
|
|
('SAMBANOVA', re.compile(r'(?:SAMBANOVA|sambanova)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([A-Za-z0-9_-]{20,60})', re.IGNORECASE)),
|
|
('UPSTAGE', re.compile(r'(?:UPSTAGE|upstage)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([A-Za-z0-9_-]{20,60})', re.IGNORECASE)),
|
|
('LEPTON', re.compile(r'(?:LEPTON|lepton)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([A-Za-z0-9_-]{20,60})', re.IGNORECASE)),
|
|
('NEARAI', re.compile(r'(?:NEARAI|nearai|NEAR_AI)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([A-Za-z0-9_-]{20,60})', re.IGNORECASE)),
|
|
('ALEPH_ALPHA', re.compile(r'(?:ALEPH_ALPHA|aleph_alpha|ALEPHALPHA)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([A-Za-z0-9_-]{20,60})', re.IGNORECASE)),
|
|
('AI21', re.compile(r'(?:AI21|ai21)[_a-z]{0,15}["\']?\s*[=:]\s*["\']?([A-Za-z0-9_-]{20,60})', re.IGNORECASE)),
|
|
|
|
# ── unified-wave from goth-key-ripper ───────────────────────────────────
|
|
('GRAFANA_SA', re.compile(r'\bglsa_[A-Za-z0-9_\-]{20,}\b')),
|
|
('NEWRELIC', re.compile(r'\bNRAK-[A-Za-z0-9]{27}\b')),
|
|
('PULUMI', re.compile(r'\bpul-[a-f0-9]{40}\b')),
|
|
('GOOGLE_CLOUD_OAUTH', re.compile(r'\bya29\.[0-9A-Za-z\-_]{50,}\b')),
|
|
('FIREBASE_SERVER', re.compile(r'\bAAAA[A-Za-z0-9\-_]{40,}:[A-Za-z0-9\-_]{40,}\b')),
|
|
('PAYPAL', re.compile(r'\bAccessToken\$production\$[a-z0-9]{16}\$[a-f0-9]{32}\b')),
|
|
('TWILIO_AC', re.compile(r'\bAC[0-9a-f]{32}\b')),
|
|
('COINBASE', re.compile(r'\bcb-[A-Za-z0-9_\-]{20,}\b')),
|
|
('DISCORD_WEBHOOK', re.compile(r'\bhttps://discord(?:app)?\.com/api/webhooks/[0-9]+/[A-Za-z0-9_\-]+\b')),
|
|
('SLACK_WEBHOOK', re.compile(r'\bhttps://hooks\.slack\.com/services/[A-Z0-9]{9}/[A-Z0-9]{9}/[A-Za-z0-9]{24}\b')),
|
|
('SALESFORCE', re.compile(r'\b00D[A-Za-z0-9]{12,}![A-Za-z0-9_\-]{20,}\b')),
|
|
('NPM_RC_AUTH', re.compile(r'_auth\s*=\s*[A-Za-z0-9+/=]{20,}')),
|
|
|
|
# ── ApiScanExtract port (2026-10-19): precise-prefix E-regex families ──
|
|
('AWS_STS', re.compile(r'\bFwoGZXIvYXdzE[A-Za-z0-9+/=]{100,}')),
|
|
('NUGET', re.compile(r'\boy2[a-zA-Z0-9]{43}\b')),
|
|
('PLANETSCALE', re.compile(r'\bpscale_[A-Za-z0-9_\-]{20,}\b')),
|
|
('AZURE_SAS', re.compile(r'(?:\?|&)sv=\d{4}-\d{2}-\d{2}[^\s"\'<>]*sig=[^\s"\'<>]+')),
|
|
('GITLAB_RUNNER', re.compile(r'\bGR1348941[A-Za-z0-9_\-]{20,}\b')),
|
|
('SOURCEGRAPH', re.compile(r'\bsgp_[a-f0-9]{40}\b')),
|
|
('APIFY', re.compile(r'\bapify_api_[A-Za-z0-9]{36}\b')),
|
|
]
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# ENV_VARS — 305+ environment → provider mappings
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
_ENV_VARS = [
|
|
# core AI providers
|
|
('DEEPSEEK_REASONER_API_KEY', 'DEEPSEEK'), ('ALIYUN_DASHSCOPE_API_KEY', 'DASHSCOPE'),
|
|
('CLAUDE_CODE_ACCESS_TOKEN', 'ANTHROPIC_OAT'), ('STABLE_DIFFUSION_API_KEY', 'STABILITY'),
|
|
('CLAUDE_CODE_OAUTH_TOKEN', 'ANTHROPIC_OAT'), ('QWEN_DASHSCOPE_API_KEY', 'DASHSCOPE'),
|
|
('TONGYI_QIANWEN_API_KEY', 'DASHSCOPE'), ('DEEPSEEK_CODER_API_KEY', 'DEEPSEEK'),
|
|
('ALIBABA_CLOUD_API_KEY', 'DASHSCOPE'), ('DASHSCOPE_LLM_API_KEY', 'DASHSCOPE'),
|
|
('DEEPSEEK_ACCESS_TOKEN', 'DEEPSEEK'), ('DEEPSEEK_CHAT_API_KEY', 'DEEPSEEK'),
|
|
('ANTHROPIC_OAUTH_TOKEN', 'ANTHROPIC_OAT'), ('HUGGINGFACE_API_TOKEN', 'HUGGINGFACE'),
|
|
('ALIBABACLOUD_API_KEY', 'DASHSCOPE'), ('ALIYUN_CLOUD_API_KEY', 'DASHSCOPE'),
|
|
('ALIBABA_QWEN_API_KEY', 'DASHSCOPE'), ('DEEPSEEK_PRO_API_KEY', 'DEEPSEEK'),
|
|
('NVIDIA_BUILD_API_KEY', 'NVIDIA'), ('OPEN_ROUTER_API_KEY', 'OPENROUTER'),
|
|
('ALIBABA_LLM_API_KEY', 'DASHSCOPE'), ('DASHSCOPE_API_TOKEN', 'DASHSCOPE'),
|
|
('ALIYUN_QWEN_API_KEY', 'DASHSCOPE'), ('DEEPSEEK_API_SECRET', 'DEEPSEEK'),
|
|
('DEEPSEEK_V3_API_KEY', 'DEEPSEEK'), ('DEEPSEEK_V4_API_KEY', 'DEEPSEEK'),
|
|
('DEEPSEEK_R1_API_KEY', 'DEEPSEEK'),
|
|
|
|
# short forms (widest coverage)
|
|
('OPENAI_API_KEY', 'OPENAI'), ('CLAUDE_API_KEY', 'ANTHROPIC'),
|
|
('GOOGLE_API_KEY', 'GOOGLE'), ('GEMINI_API_KEY', 'GOOGLE'),
|
|
('PERPLEXITY_KEY', 'PERPLEXITY'), ('NVIDIA_API_KEY', 'NVIDIA'),
|
|
('ZHIPU_API_KEY', 'ZAI'), ('DASHSCOPE_KEY', 'DASHSCOPE'),
|
|
('ANTHROPIC_KEY', 'ANTHROPIC'), ('MISTRAL_TOKEN', 'MISTRAL'),
|
|
('REPLICATE_KEY', 'REPLICATE'), ('FIREWORKS_KEY', 'FIREWORKS'),
|
|
('STABILITY_KEY', 'STABILITY'),
|
|
('Z_AI_API_KEY', 'ZAI'), ('GLM4_API_KEY', 'ZAI'), ('GLM5_API_KEY', 'ZAI'),
|
|
('BIGMODEL_KEY', 'ZAI'), ('KIMI_API_KEY', 'KIMI'),
|
|
('FUGU_API_KEY', 'SAKANA'), ('FISH_API_KEY', 'SAKANA'),
|
|
('QWEN_API_KEY', 'DASHSCOPE'), ('DEEPSEEK_KEY', 'DEEPSEEK'),
|
|
('2CAPTCHA_KEY', 'TWOCAPTCHA'), ('OPENAI_TOKEN', 'OPENAI'),
|
|
('TOGETHER_KEY', 'TOGETHER'), ('PPLX_API_KEY', 'PERPLEXITY'),
|
|
('HF_API_TOKEN', 'HUGGINGFACE'), ('ZAI_API_KEY', 'ZAI'),
|
|
('GLM_API_KEY', 'ZAI'), ('ZHIPUAI_KEY', 'ZAI'),
|
|
('CHATGLM_KEY', 'ZAI'), ('ALI_API_KEY', 'DASHSCOPE'),
|
|
('CAPTCHA_KEY', 'TWOCAPTCHA'), ('GCP_API_KEY', 'GOOGLE'),
|
|
('MISTRAL_KEY', 'MISTRAL'), ('NIM_API_KEY', 'NVIDIA'),
|
|
('SAKANA_KEY', 'SAKANA'), ('OR_API_KEY', 'OPENROUTER'),
|
|
('OPENROUTER', 'OPENROUTER'), ('QWEN_TOKEN', 'DASHSCOPE'),
|
|
('DS_API_KEY', 'DEEPSEEK'), ('OPENAI_KEY', 'OPENAI'),
|
|
('CLAUDE_KEY', 'ANTHROPIC'), ('NVIDIA_KEY', 'NVIDIA'),
|
|
('ZHIPU_KEY', 'ZAI'), ('Z_AI_KEY', 'ZAI'),
|
|
('OR_TOKEN', 'OPENROUTER'), ('QWEN_KEY', 'DASHSCOPE'),
|
|
('PPLX_KEY', 'PERPLEXITY'), ('HF_TOKEN', 'HUGGINGFACE'),
|
|
('ZAI_KEY', 'ZAI'),
|
|
|
|
# medium forms
|
|
('TWO_CAPTCHA_API_KEY', 'TWOCAPTCHA'), ('TOGETHER_AI_API_KEY', 'TOGETHER'),
|
|
('REPLICATE_API_TOKEN', 'REPLICATE'), ('FIREWORKS_API_TOKEN', 'FIREWORKS'),
|
|
('OPENROUTER_API_KEY', 'OPENROUTER'), ('ALIYUN_LLM_API_KEY', 'DASHSCOPE'),
|
|
('TWOCAPTCHA_API_KEY', 'TWOCAPTCHA'),
|
|
('OPENAI_PROJECT_KEY', 'OPENAI'), ('CLAUDE_OAUTH_TOKEN', 'ANTHROPIC_OAT'),
|
|
('MISTRAL_AI_API_KEY', 'MISTRAL'), ('PERPLEXITY_API_KEY', 'PERPLEXITY'),
|
|
('SAKANA_AI_API_KEY', 'SAKANA'), ('DASHSCOPE_API_KEY', 'DASHSCOPE'),
|
|
('BAILIAN_API_TOKEN', 'DASHSCOPE'), ('RUCAPTCHA_API_KEY', 'TWOCAPTCHA'),
|
|
('OPENAI_SECRET_KEY', 'OPENAI'), ('ANTHROPIC_API_KEY', 'ANTHROPIC'),
|
|
('GOOGLE_AI_API_KEY', 'GOOGLE'), ('MISTRALAI_API_KEY', 'MISTRAL'),
|
|
('REPLICATE_API_KEY', 'REPLICATE'), ('FIREWORKS_API_KEY', 'FIREWORKS'),
|
|
('STABILITY_API_KEY', 'STABILITY'), ('HUGGINGFACE_TOKEN', 'HUGGINGFACE'),
|
|
('BIGMODEL_API_KEY', 'ZAI'), ('MOONSHOT_API_KEY', 'KIMI'),
|
|
('OPENROUTER_TOKEN', 'OPENROUTER'), ('ALICLOUD_API_KEY', 'DASHSCOPE'),
|
|
('MODELSCOPE_TOKEN', 'DASHSCOPE'), ('DEEPSEEK_API_KEY', 'DEEPSEEK'),
|
|
('2CAPTCHA_API_KEY', 'TWOCAPTCHA'),
|
|
('ANTHROPIC_APIKEY', 'ANTHROPIC'), ('GOOGLE_API_TOKEN', 'GOOGLE'),
|
|
('TOGETHER_API_KEY', 'TOGETHER'), ('PERPLEXITY_TOKEN', 'PERPLEXITY'),
|
|
('ZHIPUAI_API_KEY', 'ZAI'), ('CHATGLM_API_KEY', 'ZAI'),
|
|
('OPEN_ROUTER_KEY', 'OPENROUTER'), ('ALIBABA_API_KEY', 'DASHSCOPE'),
|
|
('DASHSCOPE_TOKEN', 'DASHSCOPE'), ('BAILIAN_API_KEY', 'DASHSCOPE'),
|
|
('QIANWEN_API_KEY', 'DASHSCOPE'),
|
|
('SEEDANCE_API_KEY', 'ARK'), ('ARK_API_KEY', 'ARK'),
|
|
('VOLCENGINE_API_KEY', 'ARK'), ('ARK_API_TOKEN', 'ARK'),
|
|
('DOUBAO_API_KEY', 'ARK'), ('BYTEDANCE_API_KEY', 'ARK'),
|
|
('FAL_KEY', 'FAL'), ('FAL_API_KEY', 'FAL'), ('FAL_AI_API_KEY', 'FAL'),
|
|
('QWEN2_5_API_KEY', 'DASHSCOPE'), ('DEEPSEEK_SECRET', 'DEEPSEEK'),
|
|
('CAPTCHA_API_KEY', 'TWOCAPTCHA'), ('TWO_CAPTCHA_KEY', 'TWOCAPTCHA'),
|
|
('ANTHROPIC_TOKEN', 'ANTHROPIC'), ('MISTRAL_API_KEY', 'MISTRAL'),
|
|
('TOGETHER_AI_KEY', 'TOGETHER'), ('REPLICATE_TOKEN', 'REPLICATE'),
|
|
('FIREWORKS_TOKEN', 'FIREWORKS'), ('STABILITY_TOKEN', 'STABILITY'),
|
|
('BIGMODEL_TOKEN', 'ZAI'), ('SAKANA_API_KEY', 'SAKANA'),
|
|
('OPENROUTER_KEY', 'OPENROUTER'), ('ALIYUN_API_KEY', 'DASHSCOPE'),
|
|
('TONGYI_API_KEY', 'DASHSCOPE'), ('DEEPSEEK_TOKEN', 'DEEPSEEK'),
|
|
('2CAPTCHA_TOKEN', 'TWOCAPTCHA'),
|
|
|
|
('HUGGINGFACEHUB_API_TOKEN', 'HUGGINGFACE'), ('FIREWORKS_AI_API_KEY', 'FIREWORKS'),
|
|
('NVIDIA_NIM_API_KEY', 'NVIDIA'), ('MINIMAX_API_KEY', 'MINIMAX'),
|
|
('GROQ_LLM_API_KEY', 'GROQ'), ('NOVITA_API_KEY', 'NOVITA'),
|
|
('COHERE_API_KEY', 'COHERE'), ('ELEVENLABS_API_KEY', 'ELEVENLABS'),
|
|
('GROQ_API_KEY', 'GROQ'), ('XI_API_KEY', 'ELEVENLABS'),
|
|
('XAI_API_KEY', 'GROK'), ('GROQ_TOKEN', 'GROQ'),
|
|
('NVIDIA_NIM', 'NVIDIA'),
|
|
|
|
# captcha solvers
|
|
('CAPMONSTER_API_KEY', 'CAPMONSTER'), ('CAPMONSTER_KEY', 'CAPMONSTER'),
|
|
('EZSOLVER_API_KEY', 'EZSOLVER'), ('NOPECHA_API_KEY', 'NOPECHA'),
|
|
('AZCAPTCHA_API_KEY', 'AZCAPTCHA'), ('YESSOLVER_API_KEY', 'YESSOLVER'),
|
|
|
|
# proxy
|
|
('SOAX_API_KEY', 'SOAX'), ('SOAX_TOKEN', 'SOAX'),
|
|
('WEBSHARE_API_KEY', 'WEBSHARE'), ('BRIGHTDATA_API_KEY', 'BRIGHTDATA'),
|
|
('DAYTONA_API_KEY', 'DAYTONA'),
|
|
|
|
# traffic & infra
|
|
('AWS_ACCESS_KEY_ID', 'AWS'), ('AWS_SECRET_ACCESS_KEY', 'AWS'),
|
|
('AZURE_CLIENT_SECRET', 'AZURE'),
|
|
('CLOUDFLARE_API_TOKEN', 'CLOUDFLARE'), ('CF_API_TOKEN', 'CLOUDFLARE'),
|
|
('DIGITALOCEAN_TOKEN', 'DIGITALOCEAN'), ('DO_TOKEN', 'DIGITALOCEAN'),
|
|
('VULTR_API_KEY', 'VULTR'), ('LINODE_TOKEN', 'LINODE'),
|
|
('HEROKU_API_KEY', 'HEROKU'), ('VERCEL_TOKEN', 'VERCEL'),
|
|
('NETLIFY_AUTH_TOKEN', 'NETLIFY'),
|
|
('RUNPOD_API_KEY', 'RUNPOD'), ('MODAL_TOKEN', 'MODAL'),
|
|
('LAMBDA_API_KEY', 'LAMBDA'),
|
|
('PINECONE_API_KEY', 'PINECONE'), ('WEAVIATE_API_KEY', 'WEAVIATE'),
|
|
('QDRANT_API_KEY', 'QDRANT'), ('WANDB_API_KEY', 'WANDB'),
|
|
('LANGCHAIN_API_KEY', 'LANGCHAIN'), ('LANGSMITH_API_KEY', 'LANGCHAIN'),
|
|
|
|
# messaging
|
|
('DISCORD_BOT_TOKEN', 'DISCORD'), ('DISCORD_TOKEN', 'DISCORD'),
|
|
('SLACK_BOT_TOKEN', 'SLACK'),
|
|
('NOTION_API_KEY', 'NOTION'), ('NOTION_TOKEN', 'NOTION'),
|
|
('SENDGRID_API_KEY', 'SENDGRID'), ('MAILGUN_API_KEY', 'MAILGUN'),
|
|
('TWILIO_AUTH_TOKEN', 'TWILIO'), ('STRIPE_SECRET_KEY', 'STRIPE'),
|
|
|
|
# package registries
|
|
('NPM_TOKEN', 'NPM'), ('PYPI_TOKEN', 'PYPI'), ('GITHUB_TOKEN', 'GITHUB'),
|
|
|
|
# generic
|
|
('JWT_SECRET', 'GENERIC_SECRET'), ('SESSION_SECRET', 'GENERIC_SECRET'),
|
|
|
|
# v6 SaaS
|
|
('TAVILY_API_KEY', 'TAVILY'), ('FIRECRAWL_API_KEY', 'FIRECRAWL'),
|
|
('RESEND_API_KEY', 'RESEND'), ('BREVO_API_KEY', 'BREVO'),
|
|
('SENDINBLUE_API_KEY', 'BREVO'),
|
|
('SHOPIFY_ACCESS_TOKEN', 'SHOPIFY'), ('SHOPIFY_ADMIN_ACCESS_TOKEN', 'SHOPIFY'),
|
|
('SONAR_TOKEN', 'SONARQUBE'), ('SONARQUBE_TOKEN', 'SONARQUBE'),
|
|
('GRAFANA_TOKEN', 'GRAFANA'), ('GRAFANA_API_TOKEN', 'GRAFANA'),
|
|
('RUBYGEMS_AUTH_TOKEN', 'RUBYGEMS_PAT'),
|
|
('NEON_API_KEY', 'NEON'),
|
|
('TWITTER_BEARER_TOKEN', 'TWITTER'),
|
|
('MAPBOX_ACCESS_TOKEN', 'MAPBOX_SECRET'), ('MAPBOX_TOKEN', 'MAPBOX_SECRET'),
|
|
('DOCKER_HUB_TOKEN', 'DOCKER'),
|
|
('MAILCHIMP_API_KEY', 'MAILCHIMP'), ('MAILCHIMP_KEY', 'MAILCHIMP'),
|
|
('NETLIFY_TOKEN', 'NETLIFY'),
|
|
|
|
# v7 PatternForge
|
|
('BASETEN_API_KEY', 'BASETEN'), ('DEEPINFRA_API_KEY', 'DEEPINFRA'),
|
|
('DEEPINFRA_TOKEN', 'DEEPINFRA'),
|
|
('MODAL_TOKEN_ID', 'MODAL'), ('MODAL_TOKEN_SECRET', 'MODAL'),
|
|
('FLY_API_TOKEN', 'FLYIO'), ('FLYIO_API_TOKEN', 'FLYIO'),
|
|
('SERPER_API_KEY', 'SERPER'), ('SERPAPI_API_KEY', 'SERPAPI'),
|
|
('SERPAPI_KEY', 'SERPAPI'),
|
|
('JINA_API_KEY', 'JINA'), ('VOYAGE_API_KEY', 'VOYAGE'),
|
|
('VOYAGEAI_API_KEY', 'VOYAGE'),
|
|
('BRAVE_SEARCH_API_KEY', 'BRAVE_SEARCH'), ('BRAVE_API_KEY', 'BRAVE_SEARCH'),
|
|
('EXA_API_KEY', 'EXA'),
|
|
('RUNWAYML_API_SECRET', 'RUNWAY'), ('RUNWAY_API_KEY', 'RUNWAY'),
|
|
('PIKA_API_KEY', 'PIKA'), ('SUNO_API_KEY', 'SUNO'),
|
|
('BFL_API_KEY', 'BFL'), ('BLACK_FOREST_LABS_API_KEY', 'BFL'),
|
|
('TENCENT_API_KEY', 'TENCENT'), ('HUNYUAN_API_KEY', 'TENCENT'),
|
|
('HUNYUAN_SECRET_ID', 'TENCENT'), ('HUNYUAN_SECRET_KEY', 'TENCENT'),
|
|
('SAMBANOVA_API_KEY', 'SAMBANOVA'), ('UPSTAGE_API_KEY', 'UPSTAGE'),
|
|
('LEPTON_API_TOKEN', 'LEPTON'), ('LEPTON_API_KEY', 'LEPTON'),
|
|
('NEARAI_API_KEY', 'NEARAI'), ('NEAR_AI_API_KEY', 'NEARAI'),
|
|
('LANGFUSE_PUBLIC_KEY', 'LANGFUSE'), ('LANGFUSE_SECRET_KEY', 'LANGFUSE'),
|
|
('LANGFUSE_API_KEY', 'LANGFUSE'),
|
|
('ALEPH_ALPHA_API_KEY', 'ALEPH_ALPHA'), ('AI21_API_KEY', 'AI21'),
|
|
('STEPIFUN_API_KEY', 'STEPFUN'), ('BAICHUAN_API_KEY', 'BAICHUAN'),
|
|
('ZEROONE_API_KEY', 'LEADERS'), ('LEADERS_API_KEY', 'LEADERS'),
|
|
|
|
# ── CN-ENV wave (2026-10-11): Chinese AI providers & gateways ──────────
|
|
('BAIDU_API_KEY', 'BAIDU'), ('BAIDU_SECRET_KEY', 'BAIDU'),
|
|
('QIANFAN_AK', 'BAIDU'), ('QIANFAN_SK', 'BAIDU'),
|
|
('YI_API_KEY', 'YI'), ('01AI_API_KEY', 'YI'),
|
|
('STEPFUN_API_KEY', 'STEPFUN'),
|
|
('IFLYTEK_API_KEY', 'IFLYTEK'), ('IFLYTEK_API_SECRET', 'IFLYTEK'),
|
|
('IFLYTEK_APP_ID', 'IFLYTEK'),
|
|
('SPARK_API_KEY', 'IFLYTEK'), ('SPARK_API_SECRET', 'IFLYTEK'),
|
|
('ONEAPI_CHANNEL_KEY', 'ONEAPI'), ('NEWAPI_CHANNEL_KEY', 'ONEAPI'),
|
|
('ONE_API_KEY', 'ONEAPI'),
|
|
('DIFY_MODEL_KEY', 'DIFY'), ('DIFY_API_KEY', 'DIFY'),
|
|
('DIFY_OPENAI_KEY', 'DIFY'),
|
|
('FASTGPT_KEY', 'FASTGPT'), ('FASTGPT_API_KEY', 'FASTGPT'),
|
|
('LOBECHAT_OPENAI_KEY', 'LOBECHAT'), ('LOBECHAT_API_KEY', 'LOBECHAT'),
|
|
('OPENWEBUI_KEY', 'OPENWEBUI'), ('OPENWEBUI_API_KEY', 'OPENWEBUI'),
|
|
('MODELSCOPE_API_KEY', 'MODELSCOPE'), ('MODELSCOPE_ACCESS_TOKEN', 'MODELSCOPE'),
|
|
('OPENI_API_KEY', 'OPENI'), ('OPENI_ACCESS_TOKEN', 'OPENI'),
|
|
('MAXKB_API_KEY', 'MAXKB'),
|
|
('COZE_API_KEY', 'COZE'), ('COZE_BOT_KEY', 'COZE'),
|
|
('AGENTSCOPE_API_KEY', 'AGENTSCOPE'), ('AGENTSCOPE_MODEL_KEY', 'AGENTSCOPE'),
|
|
('METAGPT_API_KEY', 'METAGPT'),
|
|
('CHATGPT_NEXT_API_KEY', 'CHATNEXT'), ('CHATNEXT_API_KEY', 'CHATNEXT'),
|
|
|
|
# ── unified-wave from goth-key-ripper ──────────────────────────────────
|
|
('PULUMI_ACCESS_TOKEN', 'PULUMI'),
|
|
('NEW_RELIC_API_KEY', 'NEWRELIC'), ('NEWRELIC_API_KEY', 'NEWRELIC'),
|
|
('NR_API_KEY', 'NEWRELIC'),
|
|
('GRAFANA_SERVICE_ACCOUNT_TOKEN', 'GRAFANA_SA'),
|
|
('GOOGLE_OAUTH_ACCESS_TOKEN', 'GOOGLE_CLOUD_OAUTH'),
|
|
('FIREBASE_SERVER_KEY', 'FIREBASE_SERVER'),
|
|
('TWILIO_ACCOUNT_SID', 'TWILIO_AC'),
|
|
('SALESFORCE_SESSION_ID', 'SALESFORCE'), ('SALESFORCE_SECRET', 'SALESFORCE'),
|
|
('COINBASE_API_KEY', 'COINBASE'), ('COINBASE_SECRET', 'COINBASE'),
|
|
('DISCORD_WEBHOOK_URL', 'DISCORD_WEBHOOK'),
|
|
('SLACK_WEBHOOK_URL', 'SLACK_WEBHOOK'),
|
|
('PAYPAL_ACCESS_TOKEN', 'PAYPAL'),
|
|
('DATADOG_API_KEY', 'DATADOG'), ('DATADOG_APP_KEY', 'DATADOG'),
|
|
('HUBSPOT_API_KEY', 'HUBSPOT'), ('INTERCOM_API_KEY', 'INTERCOM'),
|
|
('CONFLUENT_API_KEY', 'CONFLUENT'), ('CONFLUENT_CLOUD_API_KEY', 'CONFLUENT'),
|
|
('ELASTIC_API_KEY', 'ELASTIC_CLOUD'), ('ELASTIC_CLOUD_API_KEY', 'ELASTIC_CLOUD'),
|
|
('FASTLY_API_KEY', 'FASTLY'), ('LINODE_API_KEY', 'LINODE'),
|
|
('MONGODB_ATLAS_API_KEY', 'MONGODB_ATLAS'), ('OKTA_API_TOKEN', 'OKTA'),
|
|
('OKTA_CLIENT_TOKEN', 'OKTA'),
|
|
# ── ApiScanExtract port (2026-10-19): env-context-gated, low-FP ──
|
|
('HEYGEN_API_KEY', 'HEYGEN'), ('APIFY_TOKEN', 'APIFY'),
|
|
('APIFY_API_TOKEN', 'APIFY'), ('SOURCEGRAPH_TOKEN', 'SOURCEGRAPH'),
|
|
('SRC_ACCESS_TOKEN', 'SOURCEGRAPH'), ('PROMPTLAYER_API_KEY', 'PROMPTLAYER'),
|
|
('WORDWARE_API_KEY', 'WORDWARE'), ('ASTRA_DB_APPLICATION_TOKEN', 'ASTRA'),
|
|
('ASTRA_TOKEN', 'ASTRA'), ('DEEPGRAM_API_KEY', 'DEEPGRAM'),
|
|
('ASSEMBLYAI_API_KEY', 'ASSEMBLYAI'), ('GLADIA_API_KEY', 'GLADIA'),
|
|
('LEONARDO_API_KEY', 'LEONARDO'), ('SYNTHESIA_API_KEY', 'SYNTHESIA'),
|
|
('CLIPDROP_API_KEY', 'CLIPDROP'), ('SCRAPINGBEE_API_KEY', 'SCRAPINGBEE'),
|
|
('ZENROWS_API_KEY', 'ZENROWS'), ('CREWAI_API_KEY', 'CREWAI'),
|
|
('COMPOSIO_API_KEY', 'COMPOSIO'), ('N8N_API_KEY', 'N8N'),
|
|
('FLOWISE_API_KEY', 'FLOWISE'), ('LANGFLOW_API_KEY', 'LANGFLOW'),
|
|
('GRETEL_API_KEY', 'GRETEL'), ('LABELBOX_API_KEY', 'LABELBOX'),
|
|
('MOSTLY_AI_API_KEY', 'MOSTLY_AI'), ('HELICONE_API_KEY', 'HELICONE'),
|
|
('BRAINTRUST_API_KEY', 'BRAINTRUST'), ('HUMANLOOP_API_KEY', 'HUMANLOOP'),
|
|
('REV_ACCESS_TOKEN', 'REV'), ('SPEECHMATICS_API_KEY', 'SPEECHMATICS'),
|
|
('UPSTASH_VECTOR_TOKEN', 'UPSTASH'), ('PORTKEY_API_KEY', 'PORTKEY'),
|
|
('LUMA_API_KEY', 'LUMA'),
|
|
]
|
|
|
|
_VAR2PROV = dict(_ENV_VARS)
|
|
_ALT = '|'.join(dict(_ENV_VARS))
|
|
ENV_RE = re.compile(
|
|
'\\b(' + _ALT + ')\\b\\s*[=:,]\\s*[\'"]?([A-Za-z0-9_\\-.:$]{20,200})',
|
|
re.IGNORECASE)
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# FILTERS — garbage / template / placeholder rejection
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
GARBAGE = ("xxxx", "xxx", "your", "YOUR", "EXAMPLE", "example", "n0tr3al",
|
|
"placeholder", "CHANGEME", "changeme", "dummy", "DUMMY",
|
|
"abcdefghijklmnopqrst", "1234567890abcdef", "test-key",
|
|
"sample", "SAMPLE", "insert", "replace", "aaaaaa", "redacted",
|
|
"masked", "censored", "your_api_key", "replace_with",
|
|
"fill_in", "undefined", "mock_", "stub_", "todo", "fixme",
|
|
"paste", "none", "null", "apikey", "my_api_key", "fake_",
|
|
"demo_", "test_key", "sk-xxx", "sk-your")
|
|
|
|
_PLACEHOLDER_WORDS = (
|
|
'your_', 'your-', 'xxxxx', 'placeholder', 'example', 'dummy',
|
|
'fake_', 'fake-', 'test_key', 'testkey', 'sample', 'demo_',
|
|
'replace', 'insert', 'paste', 'todo', 'changeme', 'change_me',
|
|
'put_here', 'add_here', 'fill_', 'enter_', 'set_', 'get_',
|
|
'api_key_here', '_here', 'key_here', 'token_here', 'none',
|
|
'null', 'undefined', 'secret_key', 'my_api_key', 'actualkey',
|
|
'apikey', 'sk-xxx', 'sk-your', 'xxxxxxxx')
|
|
|
|
|
|
def _is_placeholder(val):
|
|
"""Check if value looks like a placeholder (x-heavy, low variety, etc.)."""
|
|
low = val.lower()
|
|
if any(w in low for w in _PLACEHOLDER_WORDS):
|
|
return True
|
|
if low.count('x') > len(val) * 0.4:
|
|
return True
|
|
if len(set(low)) <= 2:
|
|
return True
|
|
if val.isupper() and '_' in val and len(val) < 40:
|
|
return True
|
|
return False
|
|
|
|
|
|
def _is_template(key):
|
|
"""Template junk that survives _is_junk — paired duplicates (aabbcc...) and
|
|
monotone ramps (abcdef..., 12345678). Calibrated on 6000 real DB keys: 0
|
|
legit keys flagged."""
|
|
if len(key) > 160:
|
|
return False
|
|
body = key
|
|
for pfx in ("sk-sp-", "sk-ws-", "sk-or-v1-", "sk-ant-api03-", "sk-", "pk-lf-",
|
|
"sk-lf-", "csk-", "nvapi-", "r8_", "fish_"):
|
|
if key.startswith(pfx):
|
|
body = key[len(pfx):]
|
|
break
|
|
if len(body) < 8:
|
|
return False
|
|
if len(body) % 2 == 0 and all(body[i] == body[i + 1]
|
|
for i in range(0, len(body), 2)):
|
|
return True
|
|
run = 1
|
|
for i in range(1, len(body)):
|
|
if ord(body[i]) - ord(body[i - 1]) in (1, -1):
|
|
run += 1
|
|
if run >= 20:
|
|
return True
|
|
else:
|
|
run = 1
|
|
return False
|
|
|
|
|
|
def _is_junk(key):
|
|
"""Reject placeholder/template keys (paired, ramps, repeats, stubs)."""
|
|
if not key:
|
|
return True
|
|
if len(set(key)) < 4:
|
|
return True
|
|
if all(c in "_-.*xX0" for c in key):
|
|
return True
|
|
for blk in (2, 3, 4):
|
|
if len(key) % blk == 0 and key[:blk] * (len(key) // blk) == key:
|
|
return True
|
|
if _is_template(key):
|
|
return True
|
|
low = key.lower()
|
|
if "xxxx" in low or "yyyy" in low:
|
|
return True
|
|
if "insert_your" in low or "your_token" in low or "your_api_key" in low:
|
|
return True
|
|
if "placeholder" in low:
|
|
return True
|
|
if "<" in low and ">" in low:
|
|
return True
|
|
if "npm_" in low and low.count("x") > len(low) * 0.3:
|
|
return True
|
|
return False
|
|
|
|
|
|
def _is_discord_jwt(t):
|
|
"""Discord bot token first segment = base64(numeric snowflake user id).
|
|
Real tokens decode to 15-25 digits; random base64 blobs do not."""
|
|
head = t.split(".", 1)[0]
|
|
try:
|
|
d = base64.b64decode(head + "=" * (-len(head) % 4), validate=False)
|
|
except Exception:
|
|
return False
|
|
return d.isdigit() and 15 <= len(d) <= 25
|
|
|
|
|
|
def _is_https_auth_junk(val):
|
|
"""HTTPS_AUTH anchoring — reject placeholder/doc URLs."""
|
|
if not val.startswith("https://"):
|
|
return True
|
|
rest = val[8:]
|
|
if "@" not in rest:
|
|
return True
|
|
cred, _, host = rest.partition("@")
|
|
user, _, pw = cred.partition(":")
|
|
ul, pl = user.lower(), pw.lower()
|
|
if "$" in cred or "{" in cred or "}" in cred or "<" in cred:
|
|
return True
|
|
PH_USER = {"admin", "user", "test", "root", "guest", "foo", "bar", "baz",
|
|
"username", "login", "demo", "sample", "nobody", "user1"}
|
|
PH_PW = {"admin", "password", "pass", "passwd", "secret", "test", "guest",
|
|
"123456", "1234", "12345", "123", "changeme", "user", "root",
|
|
"demo", "sample", "pwd", "hunter2", "letmein", "welcome",
|
|
"qwerty", "abc123", "password1", "admin123", "token"}
|
|
if ul in PH_USER and pl in PH_PW:
|
|
return True
|
|
if ul == pl and len(ul) < 24:
|
|
return True
|
|
if pl in PH_PW:
|
|
return True
|
|
hl = host.lower().split("/")[0].split(":")[0]
|
|
if hl in ("localhost", "localhost.localdomain", "hostname", "your-host",
|
|
"myserver", "server", "host", "foo.com", "bar.com", "baz.com"):
|
|
return True
|
|
if hl.endswith((".local", ".internal", ".invalid", ".test", ".localhost")):
|
|
return True
|
|
if hl.startswith(("127.", "10.", "192.168.", "0.")):
|
|
return True
|
|
if hl.startswith("172."):
|
|
try:
|
|
if 16 <= int(hl.split(".")[1]) <= 31:
|
|
return True
|
|
except (IndexError, ValueError):
|
|
pass
|
|
if hl in ("example.com", "example.org", "example.net") or hl.endswith(
|
|
(".example.com", ".example.org", ".example.net")):
|
|
return True
|
|
if "." not in hl:
|
|
return True
|
|
return False
|
|
|
|
|
|
def _is_pem_junk(val):
|
|
"""PRIVATE_KEY structural validation: body must base64-decode and carry
|
|
plausible structure (openssh-key-v1 magic or ASN.1 SEQUENCE 0x30)."""
|
|
head = val.split("-----")[1].upper() if val.count("-----") >= 2 else ""
|
|
if "ENCRYPTED" in head:
|
|
return True
|
|
if "Proc-Type:" in val or "DEK-Info:" in val:
|
|
return True
|
|
try:
|
|
body = val.split("-----", 2)[2] if val.count("-----") >= 2 else ""
|
|
body = body.split("-----")[0]
|
|
raw = base64.b64decode("".join(body.split()), validate=True)
|
|
except Exception:
|
|
return True
|
|
if len(raw) < 24:
|
|
return True
|
|
if "OPENSSH" in val:
|
|
return not raw.startswith(b"openssh-key-v1\x00")
|
|
return raw[0] != 0x30
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# BRACKET EXTRACTION — key between [], {}, (), <>
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
BRACKET_RE = re.compile(r"""['"]([A-Za-z0-9/+=\.\-_]{20,300})['"]""")
|
|
BARE_LINE_RE = re.compile(r'(?:^|\s)([A-Za-z0-9/+=_\-]{20,80})(?:\s|$)')
|
|
|
|
_BRACKET_MAP = {
|
|
'[': re.compile(r'\[([^\]]{20,300})\]'),
|
|
'{': re.compile(r'\{([^\}]{20,300})\}'),
|
|
'(': re.compile(r'\(([^\)]{20,300})\)'),
|
|
'<': re.compile(r'<([^>]{20,300})>'),
|
|
}
|
|
|
|
|
|
def _scan_brackets(text):
|
|
"""Pre-scan for keys inside bracket pairs, then match against regex
|
|
patterns. One pass through text, no overlap, dedup by key value."""
|
|
hits = []
|
|
seen = set()
|
|
for br, rx in _BRACKET_MAP.items():
|
|
for m in rx.finditer(text or ""):
|
|
inner = m.group(1)
|
|
for prov, prx in PATTERNS:
|
|
for km in prx.findall(inner):
|
|
val = _flat(km)
|
|
if len(val) >= 20 and val not in seen and not _is_junk(val):
|
|
seen.add(val)
|
|
hits.append((prov, val, m.start()))
|
|
for m in BRACKET_RE.finditer(text or ""):
|
|
inner = m.group(1)
|
|
if inner in seen or len(inner) < 20:
|
|
continue
|
|
for prov, prx in PATTERNS:
|
|
for km in prx.findall(inner):
|
|
val = _flat(km)
|
|
if len(val) >= 20 and val not in seen and not _is_junk(val):
|
|
seen.add(val)
|
|
hits.append((prov, val, m.start()))
|
|
return hits
|
|
|
|
|
|
def _entropy(data):
|
|
"""Shannon entropy in bits per character. Faster than Counter-based."""
|
|
if not data:
|
|
return 0.0
|
|
n = len(data)
|
|
freq = {}
|
|
for b in data:
|
|
freq[b] = freq.get(b, 0) + 1
|
|
return -sum((f / n) * math.log2(f / n) for f in freq.values())
|
|
|
|
|
|
def _fingerprint(key):
|
|
"""SHA-256 truncated to 8 hex chars for anonymous match-grouping."""
|
|
return hashlib.sha256(key.encode()).hexdigest()[:8]
|
|
|
|
|
|
_MASK_RE = re.compile(r'^(.{4}).*(.{4})$')
|
|
_MASK_TINY = re.compile(r'^(.{2})(.*)(.{2})$')
|
|
|
|
|
|
def _mask(key):
|
|
"""Mask key: show first 4 and last 4 chars. Short keys show first/last 2."""
|
|
if len(key) <= 12:
|
|
m = _MASK_TINY.match(key)
|
|
if m:
|
|
return m.group(1) + "*" * (len(key) - 4) + m.group(3)
|
|
return "*" * len(key)
|
|
m = _MASK_RE.match(key)
|
|
if m:
|
|
return m.group(1) + "*" * (len(key) - 8) + m.group(2)
|
|
return "*" * len(key)
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# CORE SCANNER
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
def _dedup(out):
|
|
"""Deduplicate and filter junk from (prov, key) list."""
|
|
seen_vals = set()
|
|
res = []
|
|
for p, k in out:
|
|
k = k.strip().strip("\"'`,;")
|
|
if not k or len(k) < 20 or k in seen_vals:
|
|
continue
|
|
if any(g in k for g in GARBAGE) or _is_junk(k) or _is_placeholder(k):
|
|
continue
|
|
seen_vals.add(k)
|
|
res.append((p, k))
|
|
return res
|
|
|
|
|
|
def scan_text(text):
|
|
"""Yield (prov, key) from PATTERNS + ENV assignments + EXTRA + PHP."""
|
|
out = []
|
|
for prov, rx in PATTERNS:
|
|
for m in rx.findall(text or ""):
|
|
val = _flat(m)
|
|
if prov == "DISCORD" and not val.startswith("mfa.") \
|
|
and not _is_discord_jwt(val):
|
|
continue
|
|
if prov == "HTTPS_AUTH" and _is_https_auth_junk(val):
|
|
continue
|
|
if prov == "PRIVATE_KEY" and _is_pem_junk(val):
|
|
continue
|
|
out.append((prov, val))
|
|
for m in ENV_RE.finditer(text or ""):
|
|
out.append((_VAR2PROV.get(m.group(1).upper(), "ENV"), m.group(2)))
|
|
_PHP_RE = re.compile(
|
|
r"""define\s*\(\s*['"]([A-Za-z_][A-Za-z0-9_]*?)['"]\s*,\s*['"]([A-Za-z0-9_\-.:$]{20,200})['"]\s*\)""",
|
|
re.IGNORECASE)
|
|
for m in _PHP_RE.finditer(text or ""):
|
|
out.append((_VAR2PROV.get(m.group(1).upper(), "ENV"), m.group(2)))
|
|
for _name, _rx in EXTRA_PATTERNS.items():
|
|
try:
|
|
for _m in _rx.findall(text or ""):
|
|
out.append((str(_name).upper()[:32], _flat(_m)))
|
|
except Exception:
|
|
continue
|
|
return _dedup(out)
|
|
|
|
|
|
def scan_text_brackets(text):
|
|
"""Extended scan: bracket pre-scan + main scan, merged and deduped."""
|
|
main = scan_text(text)
|
|
bracket = _scan_brackets(text)
|
|
seen = {k for _, k in main}
|
|
for prov, key, _pos in bracket:
|
|
if key not in seen and not _is_junk(key):
|
|
seen.add(key)
|
|
main.append((prov, key))
|
|
return main
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# ENRICH — full structured output
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
def enrich(provider, key):
|
|
"""Enrich a single hit with entropy, fingerprint, masked value, ts."""
|
|
return {
|
|
"provider": provider,
|
|
"key": key,
|
|
"entropy": round(_entropy(key), 2),
|
|
"masked": _mask(key),
|
|
"fingerprint": _fingerprint(key),
|
|
"len": len(key),
|
|
"ts": int(time.time()),
|
|
}
|
|
|
|
|
|
def enrich_all(hits):
|
|
return [enrich(prov, key) for prov, key in hits]
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# VALIDATION_META — live API validation endpoints (34 providers)
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
VALIDATION_META = {
|
|
'OPENAI': {'endpoint': 'https://api.openai.com/v1/models',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'ANTHROPIC': {'endpoint': 'https://api.anthropic.com/v1/messages',
|
|
'headers': {'x-api-key': '{key}', 'anthropic-version': '2023-06-01'},
|
|
'type': 'header'},
|
|
'GITHUB_FINE': {'endpoint': 'https://api.github.com/user',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'GITLAB_PAT': {'endpoint': 'https://gitlab.com/api/v4/user',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'TWITTER': {'endpoint': 'https://api.twitter.com/2/users/me',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'SLACK_BOT': {'endpoint': 'https://slack.com/api/auth.test',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'TELEGRAM': {'endpoint': 'https://api.telegram.org/bot{key}/getMe',
|
|
'headers': {}, 'type': 'bearer'},
|
|
'STRIPE': {'endpoint': 'https://api.stripe.com/v1/charges',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'SENDGRID': {'endpoint': 'https://api.sendgrid.com/v3/scopes',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'DIGITALOCEAN': {'endpoint': 'https://api.digitalocean.com/v2/account',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'CLOUDFLARE': {'endpoint': 'https://api.cloudflare.com/client/v4/user/tokens/verify',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'GOOGLE': {'endpoint': 'https://generativelanguage.googleapis.com/v1/models',
|
|
'headers': {'x-goog-api-key': '{key}'}, 'type': 'header'},
|
|
'DROPBOX': {'endpoint': 'https://api.dropboxapi.com/2/users/get_current_account',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'NEWRELIC': {'endpoint': 'https://api.newrelic.com/v2/applications.json',
|
|
'headers': {'Api-Key': '{key}'}, 'type': 'header'},
|
|
'PULUMI': {'endpoint': 'https://api.pulumi.com/api/user',
|
|
'headers': {'Authorization': 'token {key}'}, 'type': 'token'},
|
|
'GRAFANA_SA': {'endpoint': 'https://grafana.com/api/org',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'GOOGLE_CLOUD_OAUTH': {'endpoint': 'https://www.googleapis.com/oauth2/v1/tokeninfo?access_token={key}',
|
|
'headers': {}, 'type': 'url_param'},
|
|
'FIREBASE_SERVER': {'endpoint': 'https://fcm.googleapis.com/fcm/send',
|
|
'headers': {'Authorization': 'key={key}'}, 'type': 'header'},
|
|
'TWILIO_AC': {'endpoint': 'https://api.twilio.com/2010-04-01/Accounts/{key}.json',
|
|
'headers': {}, 'type': 'url_path'},
|
|
'COINBASE': {'endpoint': 'https://api.coinbase.com/v2/user',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'DASHSCOPE': {'endpoint': 'https://dashscope.aliyuncs.com/api/v1/models',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'DEEPSEEK': {'endpoint': 'https://api.deepseek.com/v1/models',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'KIMI': {'endpoint': 'https://api.moonshot.cn/v1/models',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'ZAI': {'endpoint': 'https://open.bigmodel.cn/api/paas/v4/models',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'GROQ': {'endpoint': 'https://api.groq.com/openai/v1/models',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'PERPLEXITY': {'endpoint': 'https://api.perplexity.ai/models',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'HUGGINGFACE': {'endpoint': 'https://huggingface.co/api/whoami-v2',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'TOGETHER': {'endpoint': 'https://api.together.xyz/v1/models',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'REPLICATE': {'endpoint': 'https://api.replicate.com/v1/models',
|
|
'headers': {'Authorization': 'Token {key}'}, 'type': 'token'},
|
|
'FIRECRAWL': {'endpoint': 'https://api.firecrawl.dev/v1/scrape',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'TAVILY': {'endpoint': 'https://api.tavily.com/search',
|
|
'headers': {}, 'type': 'url_param'},
|
|
'HEROKU': {'endpoint': 'https://api.heroku.com/account',
|
|
'headers': {'Authorization': 'Bearer {key}', 'Accept': 'application/vnd.heroku+json; version=3'},
|
|
'type': 'bearer'},
|
|
'SUPABASE': {'endpoint': 'https://api.supabase.com/v1/projects',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
'FEATHERLESS': {'endpoint': 'https://api.featherless.ai/v1/models',
|
|
'headers': {'Authorization': 'Bearer {key}'}, 'type': 'bearer'},
|
|
}
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# CLI
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
def build_cli():
|
|
ap = argparse.ArgumentParser(
|
|
description="KeyHunter v11 — Universal Secret Parser")
|
|
ap.add_argument("files", nargs="*", help="Files to scan")
|
|
ap.add_argument("--extract-only", action="store_true",
|
|
help="Only extract flat (provider,key) pairs")
|
|
ap.add_argument("--validate", action="store_true",
|
|
help="Validate hits against live API endpoints")
|
|
ap.add_argument("--brackets", action="store_true",
|
|
help="Enable bracket pre-scan for embedded keys")
|
|
ap.add_argument("-o", "--output", help="Output file (JSON)")
|
|
ap.add_argument("--json", action="store_true", default=True,
|
|
help="JSON output (default)")
|
|
ap.add_argument("--quiet", "-q", action="store_true",
|
|
help="Silence status output")
|
|
return ap
|
|
|
|
|
|
def main():
|
|
ap = build_cli()
|
|
args = ap.parse_args()
|
|
scanner = scan_text_brackets if args.brackets else scan_text
|
|
all_hits = []
|
|
|
|
if not sys.stdin.isatty():
|
|
text = sys.stdin.read()
|
|
all_hits.extend(scanner(text))
|
|
for fp in args.files:
|
|
try:
|
|
with open(fp, "r", encoding="utf-8", errors="replace") as fh:
|
|
text = fh.read()
|
|
except OSError as e:
|
|
print(f"read {fp}: {e}", file=sys.stderr)
|
|
continue
|
|
all_hits.extend(scanner(text))
|
|
|
|
if not args.extract_only and not args.validate:
|
|
enriched = enrich_all(all_hits)
|
|
data = {"total": len(enriched), "hits": enriched}
|
|
elif args.extract_only:
|
|
data = {prov: key for prov, key in all_hits}
|
|
else:
|
|
data = {"total": len(all_hits), "hits": [{"provider": p, "key": k}
|
|
for p, k in all_hits]}
|
|
|
|
if args.output:
|
|
with open(args.output, "w", encoding="utf-8") as fh:
|
|
json.dump(data, fh, indent=2, ensure_ascii=False)
|
|
if not args.quiet:
|
|
print(f"Wrote {len(all_hits)} hits → {args.output}", file=sys.stderr)
|
|
else:
|
|
json.dump(data, sys.stdout, indent=2, ensure_ascii=False)
|
|
sys.stdout.write("\n")
|
|
|
|
|
|
def extract_all_keys(text):
|
|
"""[(provider, key)] — ENV context first (names trump patterns), then
|
|
PATTERNS, KIMI pledge, and extra patterns."""
|
|
out = []
|
|
for m in ENV_RE.finditer(text or ""):
|
|
out.append((_VAR2PROV.get(m.group(1).upper(), "ENV"), m.group(2)))
|
|
for prov, rx in PATTERNS:
|
|
for m in rx.findall(text or ""):
|
|
out.append((prov, _flat(m)))
|
|
for m in KIMI_PATTERN.finditer(text or ""):
|
|
out.append(("KIMI", m.group(0)))
|
|
for _name, _rx in EXTRA_PATTERNS.items():
|
|
try:
|
|
for _m in _rx.findall(text or ""):
|
|
out.append((str(_name).upper()[:32], _flat(_m)))
|
|
except Exception:
|
|
continue
|
|
return _dedup(out)
|
|
|
|
|
|
def _flat(m):
|
|
"""findall yields str, or tuple when the pattern has groups."""
|
|
if isinstance(m, (tuple, list)):
|
|
for part in m:
|
|
if isinstance(part, str) and len(part) >= 20:
|
|
return part
|
|
return ""
|
|
return m
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# COMPAT LAYER — legacy key_patterns.py exports
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
_by_prov = {}
|
|
for _prov, _rx in PATTERNS:
|
|
_by_prov.setdefault(_prov, []).append(_rx)
|
|
|
|
def _merge_alts(rxs):
|
|
parts = []
|
|
for rx in rxs:
|
|
p = rx.pattern
|
|
if rx.flags & re.IGNORECASE:
|
|
p = "(?i:%s)" % p
|
|
parts.append("(?:%s)" % p)
|
|
return re.compile("|".join(parts))
|
|
|
|
KEY_PATTERNS = {p: (rs[0] if len(rs) == 1 else _merge_alts(rs))
|
|
for p, rs in _by_prov.items()}
|
|
|
|
KIMI_PATTERN = re.compile(r'sk-[A-Za-z0-9]{48}')
|
|
|
|
EXTRA_PATTERNS = {}
|
|
try:
|
|
from patterns_extra import EXTRA_PATTERNS as _EP
|
|
EXTRA_PATTERNS = dict(_EP)
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# SELF-CHECK
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
if __name__ == "__main__":
|
|
import hashlib as _hl
|
|
|
|
def _body(seed, n, alpha="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"):
|
|
"""Deterministic pseudo-random body of length n using chars from alpha.
|
|
Uses raw sha256 digest bytes (not hexdigest), so output is mixed-case
|
|
with the full alpha range — avoids accidental 32-char lowercase-hex
|
|
matches against ELEVENLABS/other patterns."""
|
|
out, i = "", 0
|
|
while len(out) < n:
|
|
for b in _hl.sha256(f"{seed}:{i}".encode()).digest():
|
|
out += alpha[b % len(alpha)]
|
|
if len(out) == n:
|
|
return out
|
|
i += 1
|
|
return out
|
|
|
|
# Safe value for ENV context tests — survives all filters
|
|
SV = _body("envsafe", 32)
|
|
|
|
env_tests = [
|
|
("DASHSCOPE_API_KEY", "DASHSCOPE"),
|
|
("MOONSHOT_API_KEY", "KIMI"), # uses sk- prefix via PATTERNS, ENV maps to KIMI
|
|
("ANTHROPIC_API_KEY", "ANTHROPIC"),
|
|
("2CAPTCHA_API_KEY", "TWOCAPTCHA"),
|
|
("SOAX_API_KEY", "SOAX"),
|
|
("OPENAI_API_KEY", "OPENAI"),
|
|
("GOOGLE_API_KEY", "GOOGLE"),
|
|
("AWS_ACCESS_KEY_ID", "AWS"),
|
|
("SLACK_BOT_TOKEN", "SLACK"),
|
|
("DISCORD_BOT_TOKEN", "DISCORD"),
|
|
("HEROKU_API_KEY", "HEROKU"),
|
|
("OPENROUTER", "OPENROUTER"),
|
|
("PERPLEXITY_API_KEY", "PERPLEXITY"),
|
|
("ZHIPUAI_API_KEY", "ZAI"),
|
|
]
|
|
|
|
all_ok = True
|
|
for env_name, expected in env_tests:
|
|
text = f"{env_name}={SV}"
|
|
hits = scan_text(text)
|
|
providers = {p for p, _ in hits}
|
|
if expected not in providers:
|
|
print(f"FAIL ENV: expected {expected} for {env_name}, got {providers}")
|
|
all_ok = False
|
|
|
|
# ENDPOINT_CN regression guard: bare URL must NOT match
|
|
cn_bare = scan_text("see https://api.openai.com/v1 in the docs")
|
|
if [p for p, _ in cn_bare if p == "ENDPOINT_CN"]:
|
|
print("FAIL ENDPOINT_CN: bare URL falsely matched")
|
|
all_ok = False
|
|
# Positive: ONE_API_URL=... must match
|
|
cn_pos = scan_text("ONE_API_URL=http://gw.internal:3000/v1/chat")
|
|
if not [p for p, _ in cn_pos if p == "ENDPOINT_CN"]:
|
|
print("FAIL ENDPOINT_CN: ONE_API_URL missed")
|
|
all_ok = False
|
|
|
|
# Shared charsets for body generation
|
|
HEX = "0123456789abcdef"
|
|
ALNUM = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
ALNUM_DASH = ALNUM + "_-"
|
|
UPPER_DIGIT = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
DIGIT = "0123456789"
|
|
LOWERALNUM = "0123456789abcdefghijklmnopqrstuvwxyz"
|
|
|
|
# ── Direct prefix pattern matches ────────────────────────────────────────
|
|
# NOTE: KIMI regex (sk-[A-Za-z0-9]{48}) is identical to SILICONFLOW at line 52;
|
|
# PATTERNS loop always claims sk-+48 matches for SILICONFLOW first. KIMI tested
|
|
# via ENV (MOONSHOT_API_KEY → KIMI) above.
|
|
direct_tests = [
|
|
("OPENAI", f"sk-proj-{_body('openai', 80, ALNUM_DASH)}"),
|
|
("ANTHROPIC", f"sk-ant-api03-{_body('ant', 50, ALNUM_DASH)}"),
|
|
("OPENROUTER", f"sk-or-v1-{_body('or', 52, HEX)}"),
|
|
("GITHUB_PAT", f"ghp_{_body('ghp', 40, ALNUM)}"),
|
|
("SLACK_BOT", f"xoxb-{_body('sb1', 10, DIGIT)}"
|
|
f"-{_body('sb2', 10, DIGIT)}"
|
|
f"-{_body('sb3', 24, ALNUM)}"),
|
|
("AWS", f"AKIA{_body('aws', 16, UPPER_DIGIT)}"),
|
|
("GOOGLE", f"AIza{_body('goog', 35, ALNUM_DASH)}"),
|
|
("TELEGRAM", f"{_body('tg1', 10, DIGIT)}"
|
|
f":AA{_body('tg2', 33, ALNUM_DASH)}"),
|
|
("SUPABASE", f"sbp-{_body('sbp', 40, HEX)}"),
|
|
("HEROKU", f"HRKU-AA{_body('hrku', 58, ALNUM_DASH)}"),
|
|
("PERPLEXITY", f"pplx-{_body('pplx', 56, LOWERALNUM)}"),
|
|
("ZAI", f"{_body('zai', 32, HEX)}.{_body('zai2', 12, ALNUM)}"),
|
|
("DISCORD", f"mfa.{_body('disc', 40, ALNUM_DASH)}"),
|
|
]
|
|
|
|
for expected, text in direct_tests:
|
|
hits = scan_text(text)
|
|
providers = {p for p, _ in hits}
|
|
if expected not in providers:
|
|
print(f"FAIL DIRECT: expected {expected}, got {providers}")
|
|
all_ok = False
|
|
|
|
# Enrich smoke test (safe value, no GARBAGE substring)
|
|
enriched = enrich_all([("OPENAI", _body("enrich", 40))])
|
|
assert len(enriched) == 1
|
|
assert enriched[0]["provider"] == "OPENAI"
|
|
assert "fingerprint" in enriched[0]
|
|
assert 0 < enriched[0]["entropy"] < 8
|
|
|
|
# Bracket extraction smoke test — use sk-or-v1- prefix so pattern matches
|
|
bracket_body = f"sk-or-v1-{_body('bracket', 52, HEX)}"
|
|
bracket_text = f'config: {{"api_key": "{bracket_body}"}}'
|
|
bracket_hits = scan_text_brackets(bracket_text)
|
|
assert any(p == "OPENROUTER" for p, _ in bracket_hits), \
|
|
f"bracket scan missed OPENROUTER: {bracket_hits}"
|
|
|
|
# EXTRA_PATTERNS smoke test — gitlab session cookie (hex only)
|
|
if EXTRA_PATTERNS:
|
|
gl_body = _body("gl", 32, HEX)
|
|
gl_text = f"_gitlab_session={gl_body}"
|
|
gl_hits = scan_text(gl_text)
|
|
gl_providers = {p for p, _ in gl_hits}
|
|
if not gl_providers:
|
|
print("FAIL EXTRA: _gitlab_session not detected by scan_text")
|
|
all_ok = False
|
|
else:
|
|
print("WARN: patterns_extra.py not found — skipping EXTRA_PATTERNS test")
|
|
|
|
total = len(env_tests) + len(direct_tests)
|
|
if all_ok:
|
|
print(f"SELF-CHECK PASS: {total}/{total}, enrich OK, brackets OK")
|
|
else:
|
|
print("SELF-CHECK FAIL")
|
|
sys.exit(1)
|
|
|
|
if len(sys.argv) > 1:
|
|
main() |