Files
keyhunter-parser/tests/test_batch2_patterns.py

143 lines
6.3 KiB
Python

"""Batch-2 fixture tests (no network). Run: python tests/test_batch2_patterns.py
Batch-2 final scope: HEROKU + SUPABASE in canon src/key_patterns.py PATTERNS
(fleet_update ships canon; patterns_extra is excluded from wave payload).
REJECTED families asserted absent (doctrine: one label per key shape):
CLERK — sk_(live|test)_ shape owned by canon STRIPE; second label =
mislabel race on every real Stripe live key.
BAIDU — bce-v3/ALTAK-{id}/{sig} is a per-request HMAC signature,
not a reusable credential.
Shape-ownership gate: each pos fixture must match EXACTLY ONE PATTERNS entry.
Coupling gate: emitted prov must resolve in eternal_v10 (allowlist/PROVIDERS),
else UNKNOWN -> DEAD terminal (KH_AWS graveyard lesson). Static parse only —
eternal_v10 has module-level side effects + live HTTP in validate().
"""
import os
import sys
HERE = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, os.path.join(os.path.dirname(HERE), "src"))
sys.path.insert(0, os.path.dirname(HERE))
FAILS = []
def check(name, cond, detail=""):
if cond:
print(f"ok {name}")
else:
print(f"FAIL {name} {detail}")
FAILS.append(name)
import key_patterns as K # noqa: E402 (canon, src tree)
import patterns_extra as PE # noqa: E402
CANON = dict(K.PATTERNS)
P = PE.EXTRA_PATTERNS
# ---- fixtures (synthetic, valid shapes) ----
HEROKU_POS = "token=HRKU-AA" + ("aB3xY7zK9mQ2wR5t" * 4)[:58] + " done"
HEROKU_NEG1 = "token=HRKU-BB" + ("aB3xY7zK9mQ2wR5t" * 4)[:58]
HEROKU_NEG2 = "token=HRKU-AA" + "aB3xY7zK9m" # too short
SUPABASE_POS = 'SB_TOKEN="sbp-1234567890abcdef1234567890abcdef12345678"'
SUPABASE_NEG1 = "sbp-1234567890abcdef1234567890abcdef1234567" # 39 hex
SUPABASE_NEG2 = "sbo-1234567890abcdef1234567890abcdef12345678" # wrong prefix
CLERK_SHAPE = "sk_" + "live_" + "aB3xY7zK9mQ2wR5tN8vJ1cL4hF6dG0sA"
BAIDU_SHAPE = "BCE_AK=bce-v3/ALTAK-aB3xY7zK9m/0123456789abcdef0123456789abcdef"
SPECS = [
("HEROKU", HEROKU_POS, [HEROKU_NEG1, HEROKU_NEG2]),
("SUPABASE", SUPABASE_POS, [SUPABASE_NEG1, SUPABASE_NEG2]),
]
for name, pos, negs in SPECS:
rx = CANON.get(name)
if rx is None:
check(f"{name}:in-canon", False, "missing from PATTERNS")
continue
check(f"{name}:pos", rx.search(pos) is not None, f"no match in {pos[:40]}")
for i, neg in enumerate(negs):
check(f"{name}:neg{i}", rx.search(neg) is None, f"false match in {neg[:40]}")
# shape ownership: exactly one canon entry may match the pos fixture
owners = [n for n, r in CANON.items() if r.search(pos)]
check(f"{name}:shape-owned-once", owners == [name],
f"pos fixture matched by {owners} — one label per key shape")
# rejected families: no canon label, no extra label
check("rejected:CLERK-not-in-canon", "CLERK" not in CANON)
check("rejected:clerk-not-in-extras", "clerk" not in P)
check("rejected:BAIDU-not-in-canon", "BAIDU" not in CANON)
check("rejected:baidu-not-in-extras", "baidu" not in P)
# clerk shape must stay owned by STRIPE alone
stripe_owners = [n for n, r in CANON.items() if r.search(CLERK_SHAPE)]
check("rejected:clerk-shape-stripe-only", "STRIPE" in stripe_owners
and "CLERK" not in stripe_owners, f"owners={stripe_owners}")
# baidu shape must not emit a credential label from canon batch-2
baidu_owners = [n for n, r in CANON.items()
if n in ("HEROKU", "SUPABASE") and r.search(BAIDU_SHAPE)]
check("rejected:baidu-no-batch2-hit", not baidu_owners, f"owners={baidu_owners}")
# extras regression: kh_* intact, count floor
check("regress:kh_google", "kh_google" in P and
P["kh_google"].search("AIzaSy" + "A1234567890abcdefghijklmnopqrstuv"[:33]) is not None)
check("regress:kh_grok", "kh_grok" in P and
P["kh_grok"].search("xai-" + "aB3x" * 12) is not None)
check("regress:count", len(P) >= 17, f"EXTRA_PATTERNS shrank: {len(P)}")
# scan_text end-to-end (junk-фильтр + first-wins dedup): высокоэнтропийные
# нециклические фикстуры должны пройти весь тракт и выдать prov-лейблы.
import random as _rnd
_r = _rnd.Random(20261006)
_hex40 = "".join(_r.choice("0123456789abcdef") for _ in range(40))
_alnum58 = "".join(_r.choice("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-") for _ in range(58))
SB_E2E = 'SB_TOKEN="sbp-' + _hex40 + '"'
HK_E2E = "HEROKU_API_KEY=HRKU-AA" + _alnum58
for _fix, _want in ((SB_E2E, "SUPABASE"), (HK_E2E, "HEROKU")):
_got = K.scan_text(_fix)
_provs = {p for p, _ in _got}
check(f"scan_text:{_want}", _want in _provs, f"provs={sorted(_provs)}")
_v = [v for p, v in _got if p == _want]
check(f"scan_text:{_want}-value", bool(_v) and _v[0] in _fix, f"vals={_v}")
# coupling gate: batch-2 provs resolvable in eternal_v10 (allowlist or PROVIDERS)
import ast as _ast
import re as _re
_allow, _provs = None, set()
for _p in (os.path.join(os.path.dirname(HERE), "src", "eternal_v10.py"),
os.path.join(os.path.dirname(HERE), "eternal_v10.py")):
if os.path.exists(_p):
_src = open(_p, encoding="utf-8").read()
_m = _re.search(r"if prov_name in \[([^\]]+)\]", _src)
if _m:
_allow = {s.strip().strip("'\"") for s in _m.group(1).split(",")}
try:
for _node in _ast.walk(_ast.parse(_src)):
if isinstance(_node, (_ast.Assign, _ast.AnnAssign)):
_tgt = _node.targets if isinstance(_node, _ast.Assign) else [_node.target]
for _t in _tgt:
if (isinstance(_t, _ast.Name) and _t.id == "PROVIDERS"
and isinstance(_node.value, _ast.Dict)):
for _k in _node.value.keys:
if isinstance(_k, _ast.Constant) and isinstance(_k.value, str):
_provs.add(_k.value)
except SyntaxError:
pass
break
if _allow is None and not _provs:
check("coupling:allowlist-found", False, "eternal_v10 allowlist not parsed (no PROVIDERS)")
else:
for _name in ("HEROKU", "SUPABASE"):
_resolved = (_allow and _name in _allow) or _name in _provs
check(f"coupling:{_name}-resolvable",
_resolved,
f"{_name} absent -> UNKNOWN->DEAD destroys finds")
print()
if FAILS:
print(f"BATCH2 FAILURES: {len(FAILS)}: {FAILS}")
sys.exit(1)
print("BATCH2 ALL GREEN")