#!/usr/bin/env python3
"""
L2 Scope Enumeration for jenkins.camel-soft.com
Auto-approved (read-only API enumeration).
Reads session cookies from /tmp/camel_cookies.txt.
"""
import json
import sys
import urllib.request
import urllib.error
import ssl

BASE = "https://jenkins.camel-soft.com"
COOKIE_FILE = "/tmp/camel_cookies.txt"

# Load cookies
cookies = {}
with open(COOKIE_FILE) as f:
    for line in f:
        line = line.strip()
        if not line or line.startswith('# Netscape') or line.startswith('# https') or line.startswith('# This file'):
            continue
        if line.startswith('#HttpOnly_'):
            line = line[len('#HttpOnly_'):]
        parts = line.split('\t')
        if len(parts) >= 7:
            cookies[parts[5]] = parts[6]

cookie_str = "; ".join(f"{k}={v}" for k, v in cookies.items())
print(f"[*] Using cookies: {list(cookies.keys())}", file=sys.stderr)

ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

ENDPOINTS = [
    # Core system
    ("System Info", "/api/json?pretty=true"),
    # Jobs
    ("Jobs (root)", "/api/json?tree=jobs[name,url,color,buildable,disabled,inQueue,description]{0,2000}"),
    # Nodes / agents
    ("Nodes/Agents", "/computer/api/json?pretty=true"),
    # Users
    ("Users (people)", "/people/api/json?pretty=true"),
    ("AsynchPeople", "/asynchPeople/api/json?pretty=true"),
    # Credentials
    ("Credentials Store", "/credentials/api/json?pretty=true"),
    ("System Credentials", "/systemCredentials/api/json?pretty=true"),
    ("Configure Credentials", "/manage/credentials/api/json?pretty=true"),
    # Plugins
    ("Plugins", "/pluginManager/api/json?pretty=true&depth=2"),
    # Security
    ("Security Realm", "/whoAmI/api/json?pretty=true"),
    ("Configure Security", "/manage/configure/api/json?pretty=true"),
    # Views
    ("Views", "/api/json?tree=views[name,url]{0,100}"),
    # System config
    ("System Config", "/manage/api/json?pretty=true"),
    # Artifacts
    ("Artifacts (root)", "/api/json?tree=jobs[name,buildable]{0,100}"),
    # Build queue
    ("Build Queue", "/queue/api/json?pretty=true"),
    # Users admin
    ("User admin", "/user/admin/api/json?pretty=true"),
    # Script console status
    ("Script Console", "/scriptText"),
    # Groovy script approval
    ("Script Approval", "/scriptApproval/api/json?pretty=true"),
    # Checks
    ("Checks", "/checks/api/json?pretty=true"),
]

results = {}

for label, path in ENDPOINTS:
    url = BASE + path
    req = urllib.request.Request(url)
    req.add_header("Cookie", cookie_str)
    req.add_header("Accept", "application/json,text/html,*/*")
    try:
        with urllib.request.urlopen(req, timeout=25, context=ctx) as resp:
            status = resp.status
            ctype = resp.headers.get("Content-Type", "")
            body = resp.read().decode("utf-8", errors="replace")
            # Truncate huge responses
            if len(body) > 50000:
                body = body[:50000] + f"\n... [TRUNCATED, full={len(body)} chars]"
            results[label] = {
                "status": status,
                "content_type": ctype,
                "body": body
            }
            print(f"[+] {label}: {status} ({ctype[:40]}) {len(body)} bytes", file=sys.stderr)
    except urllib.error.HTTPError as e:
        results[label] = {
            "status": e.code,
            "content_type": e.headers.get("Content-Type", ""),
            "body": f"HTTP {e.code}: {e.reason}"
        }
        print(f"[-] {label}: HTTP {e.code} {e.reason}", file=sys.stderr)
    except Exception as e:
        results[label] = {"status": -1, "error": str(e)}
        print(f"[!] {label}: ERROR {e}", file=sys.stderr)

# Save full results
with open("l2_results.json", "w") as f:
    json.dump(results, f, indent=2, ensure_ascii=False)
print("\n[*] Results saved to l2_results.json", file=sys.stderr)

# Print key findings summary
print("\n" + "="*70)
print("L2 SCOPE ENUMERATION SUMMARY")
print("="*70)

for label, data in results.items():
    status = data.get("status", "?")
    body = data.get("body", "")
    if status == 200 and body.startswith("{"):
        try:
            j = json.loads(body)
            if label == "System Info":
                print(f"\n## {label}")
                print(f"  mode: {j.get('mode')}")
                print(f"  numExecutors: {j.get('numExecutors')}")
                print(f"  description: {j.get('description')}")
                print(f"  jobs count: {len(j.get('jobs', []))}")
                print(f"  views count: {len(j.get('views', []))}")
                print(f"  assignedLabels: {j.get('assignedLabels', [])}")
                print(f"  nodeDescription: {j.get('nodeDescription')}")
            elif label.startswith("Jobs"):
                jobs = j.get("jobs", [])
                print(f"\n## {label} ({len(jobs)} jobs)")
                for job in jobs[:50]:
                    print(f"  - {job.get('name')} [{job.get('color', '?')}] buildable={job.get('buildable')} {job.get('url', '')}")
                if len(jobs) > 50:
                    print(f"  ... and {len(jobs)-50} more")
            elif label == "Nodes/Agents":
                computers = j.get("computer", [])
                print(f"\n## {label} ({len(computers)} nodes)")
                for c in computers:
                    print(f"  - {c.get('displayName')} offline={c.get('offline')} executors={c.get('numExecutors')} {c.get('description','')}")
            elif label.startswith("Users") or label == "AsynchPeople":
                users = j.get("users", [])
                print(f"\n## {label} ({len(users)} users)")
                for u in users[:30]:
                    print(f"  - {u.get('user',{}).get('fullName', u.get('user',{}).get('id','?'))}")
            elif "Credentials" in label:
                domains = j.get("credentials", j).get('domains', [])
                print(f"\n## {label}")
                if isinstance(domains, dict):
                    for dname, creds in domains.items():
                        print(f"  Domain: {dname} ({len(creds)} creds)")
                        for c in creds[:20]:
                            desc = c.get('description', '')
                            ctype = c.get('typeName', c.get('type', '?'))
                            sid = c.get('id', '?')
                            print(f"    [{sid}] {ctype}: {desc}")
                else:
                    print(f"  domains: {domains}")
            elif label == "Plugins":
                plugins = j.get("plugins", [])
                print(f"\n## {label} ({len(plugins)} plugins)")
                for p in plugins[:80]:
                    print(f"  - {p.get('shortName')} {p.get('version')} enabled={p.get('enabled')}")
                if len(plugins) > 80:
                    print(f"  ... and {len(plugins)-80} more")
            elif label == "Security realm":
                print(f"\n## {label}: {json.dumps(j, indent=2)}")
            elif label == "Build Queue":
                items = j.get("items", [])
                print(f"\n## {label} ({len(items)} queued)")
            elif label == "User admin":
                print(f"\n## {label}: {json.dumps(j, indent=2)[:500]}")
            else:
                print(f"\n## {label}: {body[:300]}")
        except json.JSONDecodeError:
            print(f"\n## {label} (non-JSON, status {status}): {body[:300]}")
    elif status == 200:
        print(f"\n## {label} (HTML, status 200): {body[:200]}")
    else:
        print(f"\n## {label}: status={status} body={body[:200]}")
