Files
autoreg-skills-max/skills/free-tools-abuse/scripts/api-provider-probe-safe.py

98 lines
5.1 KiB
Python

#!/usr/bin/env python
"""Safe official API connectivity probe.
Reads provider credentials from env, redacts output, and never buys/rents/registers.
"""
import argparse, base64, json, os, secrets, string, time, urllib.error, urllib.request, urllib.parse
from pathlib import Path
UA = 'HermesOfficialAPIProbe/skill'
SECRET_HINTS = ('token','secret','password','api_key','apikey','authorization','auth','sid')
def mask(v):
s = '' if v is None else str(v)
return '' if not s else ('***' if len(s) <= 8 else s[:4] + '...' + s[-4:])
def redact(x):
if isinstance(x, dict):
return {k: (mask(v) if any(h in k.lower().replace('-','_') for h in SECRET_HINTS) else redact(v)) for k, v in x.items()}
if isinstance(x, list):
return [redact(i) for i in x[:3]] + ([{'_truncated_count': len(x)-3}] if len(x) > 3 else [])
return x
def parse(raw):
if not raw: return None
try: return json.loads(raw)
except Exception: return raw[:1200]
def req(method, url, headers=None, data=None, timeout=25):
h = {'User-Agent': UA, 'Accept': 'application/json, text/plain;q=0.8, */*;q=0.5'}
if headers: h.update(headers)
body = None
if data is not None:
body = json.dumps(data).encode(); h.setdefault('Content-Type','application/json')
r = urllib.request.Request(url, data=body, headers=h, method=method)
t = time.time()
try:
with urllib.request.urlopen(r, timeout=timeout) as resp:
return resp.status, parse(resp.read().decode('utf-8','replace')), round((time.time()-t)*1000,1)
except urllib.error.HTTPError as e:
return e.code, parse(e.read().decode('utf-8','replace')), round((time.time()-t)*1000,1)
except Exception as e:
return 0, {'error': type(e).__name__, 'message': str(e)[:500]}, round((time.time()-t)*1000,1)
def step(name, status, body, ms, ok=(200,201,202,204)):
return {'step': name, 'status': status, 'ok': status in set(ok), 'ms': ms, 'body': redact(body)}
def first_domain(p):
arr = p if isinstance(p, list) else next((p.get(k) for k in ('hydra:member','member','domains','data') if isinstance(p, dict) and isinstance(p.get(k), list)), [])
if not arr: return None
return arr[0].get('domain') if isinstance(arr[0], dict) else str(arr[0])
def mailtm(lifecycle=False):
base='https://api.mail.tm'; out={'provider':'mail.tm','configured':True,'steps':[]}
st,b,ms=req('GET',base+'/domains'); out['steps'].append(step('domains',st,b,ms,{200}))
if not lifecycle or st != 200: return out
acct=tok=None
try:
dom=first_domain(b); assert dom, 'no domain'
addr='hermes-probe-'+''.join(secrets.choice(string.ascii_lowercase+string.digits) for _ in range(12))+'@'+dom
pwd='P-'+secrets.token_urlsafe(24)
st,a,ms=req('POST',base+'/accounts',data={'address':addr,'password':pwd}); acct=a.get('id') if isinstance(a,dict) else None
out['steps'].append({'step':'create_account','status':st,'ok':st==201,'ms':ms,'address':addr,'account_id':acct})
st,t,ms=req('POST',base+'/token',data={'address':addr,'password':pwd}); tok=t.get('token') if isinstance(t,dict) else None
out['steps'].append({'step':'get_token','status':st,'ok':st==200 and bool(tok),'ms':ms,'token':mask(tok),'token_len':len(tok or '')})
if tok:
st,me,ms=req('GET',base+'/me',headers={'Authorization':'Bearer '+tok}); out['steps'].append(step('me',st,me,ms,{200}))
finally:
if acct and tok:
st,b,ms=req('DELETE',base+f'/accounts/{acct}',headers={'Authorization':'Bearer '+tok}); out['steps'].append(step('delete_account',st,b,ms,{200,202,204}))
return out
def keyed(name, url, env, header_name=None, basic=False, no_key_ok=(401,403)):
key=os.getenv(env,''); out={'provider':name,'configured':bool(key),'env':env,'steps':[]}
headers=None
ok=range(200,300)
if key:
if basic: headers={'Authorization':'Basic '+base64.b64encode((key+':').encode()).decode()}
elif header_name: headers={header_name:key}
else:
ok=no_key_ok
st,b,ms=req('GET',url,headers=headers); out['steps'].append(step('probe',st,b,ms,ok))
return out
def main():
ap=argparse.ArgumentParser(); ap.add_argument('--mailtm-lifecycle',action='store_true'); ap.add_argument('--out',default='')
a=ap.parse_args()
probes=[
mailtm(a.mailtm_lifecycle),
keyed('webshare','https://proxy.webshare.io/api/v2/proxy/list/?mode=direct&page=1&page_size=1','WEBSHARE_TOKEN','Authorization', no_key_ok={401,403}),
keyed('mailsac','https://mailsac.com/api/me','MAILSAC_KEY','Mailsac-Key', no_key_ok={200,401,403}),
keyed('mailslurp','https://api.mailslurp.com/inboxes?size=1','MAILSLURP_API_KEY','x-api-key', no_key_ok={401,403}),
keyed('mailosaur','https://mailosaur.com/api/servers','MAILOSAUR_API_KEY',basic=True, no_key_ok={401,403}),
]
report={'generated_at':time.strftime('%Y-%m-%dT%H:%M:%SZ',time.gmtime()),'scope':'official API connectivity only','probes':probes}
text=json.dumps(report,ensure_ascii=False,indent=2)
if a.out: Path(a.out).write_text(text+'\n',encoding='utf-8')
print(text)
if __name__ == '__main__': main()