KeyHunter parser v10 + 18 scanners + tests (synthetic fixtures only, no keys/db/tokens)
This commit is contained in:
74
fleet_validate.py
Executable file
74
fleet_validate.py
Executable file
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
KF_BASE = os.path.dirname(os.path.abspath(__file__))
|
||||
os.makedirs(os.path.join(KF_BASE, "data"), exist_ok=True)
|
||||
"""Validate all tokens in gh_tokens.txt via GitHub API /user.
|
||||
Alive + scope info -> data/alive_tokens.json
|
||||
"""
|
||||
import json
|
||||
import concurrent.futures as cf
|
||||
import sys
|
||||
import time
|
||||
|
||||
import requests
|
||||
|
||||
TOKENS_FILE = os.path.join(KF_BASE, "gh_tokens.txt")
|
||||
OUT = os.path.join(KF_BASE, "data/alive_tokens.json")
|
||||
|
||||
HEADERS_BASE = {"Accept": "application/vnd.github+json",
|
||||
"X-GitHub-Api-Version": "2022-11-28"}
|
||||
|
||||
|
||||
def check(tok: str):
|
||||
try:
|
||||
r = requests.get("https://api.github.com/user",
|
||||
headers={**HEADERS_BASE, "Authorization": f"Bearer {tok}"},
|
||||
timeout=12)
|
||||
if r.status_code != 200:
|
||||
return None
|
||||
u = r.json()
|
||||
# scopes header present on some responses
|
||||
scopes = r.headers.get("x-oauth-scopes", "")
|
||||
return {"pat": tok, "login": u.get("login"), "id": u.get("id"),
|
||||
"scopes": scopes,
|
||||
"created": u.get("created_at", "")[:10],
|
||||
"suspended": bool(u.get("suspended_at"))}
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def main():
|
||||
limit = int(sys.argv[1]) if len(sys.argv) > 1 else 0
|
||||
toks = [t.strip() for t in open(TOKENS_FILE, encoding="utf-8")
|
||||
if t.strip().startswith("ghp_")]
|
||||
if limit:
|
||||
toks = toks[:limit]
|
||||
print(f"checking {len(toks)} tokens", flush=True)
|
||||
|
||||
alive = []
|
||||
done = 0
|
||||
t0 = time.time()
|
||||
with cf.ThreadPoolExecutor(max_workers=64) as ex:
|
||||
for res in ex.map(check, toks):
|
||||
done += 1
|
||||
if res:
|
||||
alive.append(res)
|
||||
if done % 250 == 0:
|
||||
print(f" [{done}/{len(toks)}] alive={len(alive)} "
|
||||
f"({time.time()-t0:.0f}s)", flush=True)
|
||||
|
||||
with open(OUT, "w", encoding="utf-8") as f:
|
||||
json.dump(alive, f, indent=1)
|
||||
|
||||
# stats
|
||||
with_repo = [a for a in alive if "repo" in a["scopes"]]
|
||||
with_repo_all = [a for a in alive if "repo" in a["scopes"] and "workflow" in a["scopes"]]
|
||||
print(f"\nTOTAL checked: {done}")
|
||||
print(f"ALIVE: {len(alive)}")
|
||||
print(f" with repo scope: {len(with_repo)}")
|
||||
print(f" with repo+workflow scope: {len(with_repo_all)}")
|
||||
print(f"saved -> {OUT}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user