144 lines
7.8 KiB
Python
144 lines
7.8 KiB
Python
import key_patterns as kp
|
|
|
|
names = [p for p, _ in kp.PATTERNS]
|
|
old = ['ZAI', 'KIMI', 'SAKANA', 'OPENROUTER', 'DASHSCOPE', 'MINIMAX', 'DEEPSEEK',
|
|
'TWOCAPTCHA', 'CAPSOLVER', 'ANTICAPTCHA', 'WEBSHARE', 'IPROYAL', 'BRIGHTDATA',
|
|
'TELEGRAM', 'DISCORD', 'OPENAI', 'ANTHROPIC', 'ANTHROPIC_OAT', 'GOOGLE',
|
|
'MISTRAL', 'TOGETHER', 'REPLICATE', 'SILICONFLOW', 'FIREWORKS', 'GROQ',
|
|
'COHERE', 'NOVITA', 'PERPLEXITY', 'ELEVENLABS', 'STABILITY', 'ALIYUN',
|
|
'NVIDIA', 'HUGGINGFACE']
|
|
missing = [p for p in old if p not in names]
|
|
assert not missing, f'missing: {missing}'
|
|
assert 'GROK' in names, 'GROK missing'
|
|
print('PATTERNS entries:', len(names), '(was 36), distinct provs:', len(set(names)))
|
|
|
|
new_env = ['FIREWORKS_AI_API_KEY', 'NOVITA_API_KEY', 'GROQ_API_KEY', 'GROQ_TOKEN',
|
|
'GROQ_LLM_API_KEY', 'NVIDIA_NIM_API_KEY', 'NVIDIA_NIM', 'MINIMAX_API_KEY',
|
|
'COHERE_API_KEY', 'ELEVENLABS_API_KEY', 'XI_API_KEY',
|
|
'HUGGINGFACEHUB_API_TOKEN', 'XAI_API_KEY']
|
|
env_set = set(v for v, _ in kp._ENV_VARS)
|
|
for v in new_env:
|
|
assert v in env_set, f'{v} missing'
|
|
assert set(kp._ALT.split('|')) == env_set, '_ALT != _ENV_VARS set'
|
|
print('ENV vars:', len(kp._ENV_VARS), '(was 142), _ALT in sync')
|
|
|
|
text = (
|
|
'XAI_API_KEY=xai-Abc123' + '9' * 40 + '\n'
|
|
'GROQ_API_KEY=gsk_' + 'a' * 48 + '\n'
|
|
'NOVITA_API_KEY=sk_OneRealNovitaStyleKeyValue1234\n'
|
|
'FIREWORKS_API_KEY=fw_' + 'b' * 48 + '\n'
|
|
# BOTH sides were wrong here, and the smoke read green for the wrong reason:
|
|
# (a) the value regex was nvapi-{80,120} while real keys are prefix+64 - it
|
|
# matched 0/263 real rows and 4/4 run-ons in data/keys.db (now {40,80}
|
|
# plus a negative lookahead); (b) the line below carries an env-var name,
|
|
# so it is labelled through ENV_VARS and never exercises that regex.
|
|
# 'n'*90 was also dropped by the low-entropy filter, masking both facts.
|
|
# The bare-value and run-on assertions after this block are the real cover.
|
|
'NVIDIA_NIM_API_KEY=nvapi-' + 'Kx7Qm2Zp9Rt4Wv6Yb1Nc8Hj3Lf5Sd0Ga' * 2 + '\n'
|
|
'nvapi-' + 'Zt8Rk3Vm6Qp1Xw9Yb1Nc8Hj3Lf5Sd0Ga' * 2 + '\n'
|
|
'nvapi-' + 'Kx7Qm2Zp9Rt4Wv6Yb1Nc8Hj3Lf5Sd0Ga' * 3 + '\n'
|
|
'MINIMAX_API_KEY=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.aaaa.aaaa\n'
|
|
'COHERE_API_KEY=cohere' + 'aBcDeFgH' * 5 + '\n'
|
|
'ELEVENLABS_API_KEY=' + 'eA1b2C3d' * 4 + '\n'
|
|
'XI_API_KEY=xi' + 'aBcDeFgHiJk' * 3 + '\n'
|
|
'HUGGINGFACEHUB_API_TOKEN=hf_' + 'g' * 34 + '\n'
|
|
'GROQ_TOKEN=gsk_' + 'h' * 50 + '\n'
|
|
'old: TOGETHER_API_KEY=tgp-' + 'c' * 45 + '\n'
|
|
'MISTRAL_API_KEY=' + 'm1A2b3C4' * 4 + '\n'
|
|
'pplx-' + 'd' * 56 + '\n'
|
|
'DEEPSEEK_API_KEY=sk-' + 'aB3cD5eF' * 4 + '\n'
|
|
)
|
|
res = kp.scan_text(text)
|
|
for r in res:
|
|
print(r)
|
|
provs = [p for p, _ in res]
|
|
for expect in ['GROK', 'GROQ', 'NOVITA', 'FIREWORKS', 'NVIDIA', 'MINIMAX', 'COHERE',
|
|
'ELEVENLABS', 'HUGGINGFACE', 'TOGETHER', 'MISTRAL', 'PERPLEXITY', 'DEEPSEEK']:
|
|
assert expect in provs, f'scan missed {expect}'
|
|
assert ('GROK', 'xai-Abc123' + '9' * 40) in res
|
|
assert ('FIREWORKS', 'fw_' + 'b' * 48) in res
|
|
# bare value (no env name) => this is the only assertion covering the value regex
|
|
NV_BARE = 'nvapi-' + 'Zt8Rk3Vm6Qp1Xw9Yb1Nc8Hj3Lf5Sd0Ga' * 2
|
|
assert ('NVIDIA', NV_BARE) in res, 'value regex missed a real 64-char nvapi key'
|
|
assert not any(v.startswith('nvapi-') and len(v) - 6 > 80 for _, v in res), \
|
|
'run-on junk matched: {40,80} + negative lookahead must reject it'
|
|
print('OK')
|
|
# ---- v6 wave: new provider patterns (must exist + match samples) ----
|
|
v6 = {
|
|
'NEON': 'napi_' + 'A1b2C3' * 7,
|
|
'DROPBOX': 'sl.' + 'AbCdEfGhIjKlMnOp' * 8,
|
|
'TWITTER': 'AAAAAAAAAA' + 'aBcDeFgHiJkLmNoPqRsTuVwXyZ01234' * 2,
|
|
'BREVO': 'xkeysib-' + 'aB1' * 9 + '-' + 'cD2' * 9,
|
|
'RESEND': 're_' + 'aB1cD2eF3' * 4,
|
|
'MAILGUN': 'key-' + 'a1b2c3d4' * 4,
|
|
'SHOPIFY': 'shpat_' + 'a1b2c3d4' * 4,
|
|
'SQUARE': 'sq0atp-' + 'aB1cD2eF3g' * 4,
|
|
'STRIPE_WEBHOOK': 'whsec_' + 'aB1cD2eF3' * 4,
|
|
'STRIPE_RESTRICTED': 'rk_live_' + 'aB1cD2eF3' * 4,
|
|
'DIGITALOCEAN': 'dop_v1_' + 'a1b2c3d4' * 8,
|
|
'DOCKER': 'dckr_pat_' + 'aB1cD2eF3' * 4,
|
|
'KAKAO': 'KakaoAK ' + 'a1b2c3d4' * 4,
|
|
'RUBYGEMS_PAT': 'rubygems_' + 'aB1cD2eF3' * 4,
|
|
'SONARQUBE': 'squ_' + 'aB1cD2eF3' * 5,
|
|
'GRAFANA': 'glc_' + 'aB1cD2eF3' * 5,
|
|
'LANGSMITH': 'lsv2_pt_' + 'aB1cD2eF3' * 5,
|
|
'TAVILY': 'tvly-' + 'aB1cD2eF3' * 4,
|
|
'FIRECRAWL': 'fc-' + 'a1b2c3d4' * 4,
|
|
'NETLIFY': 'nfp_' + 'aB1cD2eF3' * 5,
|
|
'RENDER': 'rnd_' + 'aB1cD2eF3' * 4,
|
|
'MAPBOX_SECRET': 'sk.eyJ' + 'aB1cD2eF3' * 6,
|
|
'HUBSPOT': 'pat-na1-' + 'aB1cD2eF3' * 4,
|
|
'AIRTABLE': 'pat' + 'aB1cD2eF3' + '.' + 'aB1cD2eF3' * 5,
|
|
'SENTRY_DSN': 'https://' + 'a1b2c3d4' * 4 + '@o12345.ingest.sentry.io/67890',
|
|
'PRIVATE_KEY': '-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEA' + 'aB1cD2eF3' * 10 + '\n-----END RSA PRIVATE KEY-----',
|
|
}
|
|
for prov, sample in v6.items():
|
|
assert prov in names, f'v6 pattern missing: {prov}'
|
|
got = kp.scan_text('ctx ' + sample + ' ctx')
|
|
assert (prov, sample) in got, f'{prov} sample not matched: {got[:3]}'
|
|
print('v6 patterns:', len(v6), 'all matched')
|
|
|
|
v6_env = ['TAVILY_API_KEY', 'FIRECRAWL_API_KEY', 'RESEND_API_KEY', 'BREVO_API_KEY',
|
|
'SENDINBLUE_API_KEY', 'SHOPIFY_ACCESS_TOKEN', 'SHOPIFY_ADMIN_ACCESS_TOKEN',
|
|
'SONAR_TOKEN', 'SONARQUBE_TOKEN', 'GRAFANA_TOKEN', 'GRAFANA_API_TOKEN',
|
|
'RUBYGEMS_AUTH_TOKEN', 'NEON_API_KEY', 'TWITTER_BEARER_TOKEN',
|
|
'MAPBOX_ACCESS_TOKEN', 'MAPBOX_TOKEN', 'DOCKER_HUB_TOKEN', 'NETLIFY_TOKEN']
|
|
env_set = set(v for v, _ in kp._ENV_VARS)
|
|
missing_env = [v for v in v6_env if v not in env_set]
|
|
assert not missing_env, f'v6 env missing: {missing_env}'
|
|
for v, exp in [('TAVILY_API_KEY', 'TAVILY'), ('FIRECRAWL_API_KEY', 'FIRECRAWL'),
|
|
('SONAR_TOKEN', 'SONARQUBE'), ('NEON_API_KEY', 'NEON')]:
|
|
assert kp._VAR2PROV[v] == exp, f'{v} -> {kp._VAR2PROV[v]}'
|
|
print('v6 env vars added, _ALT in sync')
|
|
|
|
|
|
# --- v7.5: Discord snowflake gate fixtures (bare regex + base64-snowflake gate) ---
|
|
import base64 as _b64
|
|
import random as _rnd
|
|
import string as _string
|
|
|
|
_head_ok = _b64.b64encode(b"123456789012345678").decode() # 18 digits -> 'M...' head
|
|
_tail27 = "Z9kQ2mXpL7vR4tW1nB6cD8eF0gH" # 27 chars
|
|
_real_tok = _head_ok + ".aB3xYz." + _tail27
|
|
_blob_head = _b64.b64encode(b"5ure-but-not-a-snowflake-id!").decode() # 'N...' head, non-digit decode
|
|
_blob_tok = _blob_head + ".aB3xYz." + _tail27
|
|
assert any(p == "DISCORD" for p, _ in kp.scan_text("token " + _real_tok)), \
|
|
"real-shaped discord token must pass snowflake gate"
|
|
assert not any(k == _blob_tok for _, k in kp.scan_text("blob " + _blob_tok)), \
|
|
"base64 blob must FAIL snowflake gate"
|
|
_mfa = "mfa." + "".join(_rnd.choice(_string.ascii_letters + _string.digits) for _ in range(40))
|
|
assert any(p == "DISCORD" for p, _ in kp.scan_text(_mfa)), "bare mfa still hits"
|
|
# GARBAGE-in-scan_text pin: sequential-alphabet body IS silently dropped (real assert)
|
|
assert kp.scan_text("mfa.abcdefghijklmnopqrstuvwx123456") == [], \
|
|
"GARBAGE must drop sequential-alphabet mfa body"
|
|
# realistic high-entropy mfa (40-char body, within {24,80}) must hit
|
|
_mfa2 = "mfa." + "".join(_rnd.choice(_string.ascii_letters + _string.digits) for _ in range(40))
|
|
assert any(p == "DISCORD" for p, _ in kp.scan_text("t=" + _mfa2)), "realistic mfa must hit"
|
|
print("v7.5+v7.6 discord gate fixtures OK")
|
|
|
|
# v7.10: mfa bound raised {24,120} - real 84-char tails must round-trip full length
|
|
_mfa84 = "mfa." + "".join(_rnd.choice(_string.ascii_letters + _string.digits) for _ in range(84))
|
|
_hits84 = [k for p, k in kp.scan_text(_mfa84) if p == "DISCORD"]
|
|
assert any(len(k) == 88 for k in _hits84), f"mfa 84-char tail truncated: {[len(k) for k in _hits84]}"
|
|
print("mfa-84 round-trip OK")
|