critical Threat analysis

Lightning PyPI Bun-Based Credential Stealer

On April 30, 2026, malicious `lightning` PyPI releases 2.6.2 and 2.6.3 shipped an import-time loader that bootstrapped Bun and executed a large obfuscated JavaScript credential stealer.

#pypi#package-compromise#supply-chain#credential-theft#shai-hulud
On this page 0% read

    Executive Summary

    On April 30, 2026, two malicious releases of the legitimate PyPI package lightning were published as versions 2.6.2 and 2.6.3 Snyk Sonatype. The package is the modern distribution name for the PyTorch Lightning deep learning framework, making the compromise materially different from a lookalike or typosquat Snyk.

    The malicious wheels added a hidden _runtime directory that executed when Python code imported lightning, downloaded the Bun JavaScript runtime from GitHub, and used it to run an approximately 11 MB obfuscated JavaScript credential stealer named router_runtime.js Snyk Sonatype. The payload targeted GitHub tokens, npm tokens, cloud credentials, metadata services, local environment variables, and developer credential files Snyk Sonatype. Snyk tracks the incident as SNYK-PYTHON-LIGHTNING-16323121 and CVE-2026-44484 with critical severity Snyk Vulnerability Database.

    Key Facts

    threat_type: "legitimate package compromise, import-time credential stealer"
    ecosystem: "pypi, python"
    registry: "PyPI"
    affected_packages:
      - "lightning"
    malicious_versions:
      - "2.6.2"
      - "2.6.3"
    known_good_versions:
      - "2.6.1"
    execution_trigger: "import lightning"
    primary_impact: "Developer, CI/CD, npm, GitHub, and cloud credential theft"
    campaign_context: "Mini Shai-Hulud-style Bun payload reuse; exact attribution remains vendor-disputed"
    known_iocs:
      - "lightning/_runtime/start.py"
      - "lightning/_runtime/router_runtime.js"
      - "github.com/oven-sh/bun/releases/download/bun-v1.3.13"
      - "api.github.com/user"
      - "registry.npmjs.org/-/whoami"
    confidence: "high"
    canonical_source: "https://snyk.io/blog/lightning-pypi-compromise-bun-based-credential-stealer/"

    Source Confidence & Evidence Mapping

    • confirmed: lightning versions 2.6.2 and 2.6.3 were malicious and are covered by Snyk advisory SNYK-PYTHON-LIGHTNING-16323121 / CVE-2026-44484 Snyk Vulnerability Database.
    • confirmed: The malicious execution chain runs automatically on module import and launches a background process with suppressed output Snyk Sonatype.
    • confirmed: The payload uses Bun to execute a large obfuscated JavaScript credential stealer that targets cloud and developer credentials Snyk Sonatype.
    • likely: The publishing path involved compromised long-lived PyPI publishing credentials rather than a normal source-controlled release workflow Snyk.
    • unclear: Vendors differ on whether the operator is the original Shai-Hulud actor, a copycat, or a related cluster reusing the same payload family Snyk.

    Impact Determination

    ClassificationCriteriaRequired evidenceRequired actionClosure condition
    Confirmed compromiselightning==2.6.2 or lightning==2.6.3 is present and Python import-time loader starts Bun and obfuscated JavaScript or the reported process, file, or network indicators is observed.Artifact inventory plus runtime telemetry showing Python import-time loader starts Bun and obfuscated JavaScript or listed C2/process/file indicators.Isolate affected hosts or runners, preserve artifacts, and rotate reachable credentials from a clean environment.Affected artifacts are removed, exposed credentials are replaced, and downstream audit modules show no suspicious follow-on use.
    Presumed exposedlightning==2.6.2 or lightning==2.6.3 was installed, pulled, imported, built, or executed during the exposure window, but telemetry cannot prove exfiltration.Lockfile, package cache, workflow, image pull, extension inventory, build log, or deployment record tied to the exposure window.Rebuild from clean artifacts and rotate credentials available to the affected environment.Credential owners confirm revocation of old material and clean artifacts are deployed.
    Potentially exposedThe package, workflow, image, extension, or module appears in dependency or deployment records, but Python install, import, or interpreter-startup execution is not established.Manifest, lockfile, build, deployment, or endpoint records plus a named telemetry gap.Collect the missing execution and telemetry evidence before narrowing scope.Every hit is dispositioned as confirmed compromise, presumed exposed, or not exposed.
    Not exposedNo affected version, artifact, mutable reference, or indicator appears in source, lockfiles, build outputs, deployments, package caches, or runtime telemetry.Repository search, dependency inventory, build/deployment export, package cache query, and runtime telemetry query results.Preserve the negative search output and keep the prevention controls active.Search evidence covers developer endpoints, CI runners, production deployments, and package or image caches.
    UnknownRequired inventory, build, endpoint, network, or audit telemetry is unavailable.A gap statement naming unavailable systems, owners, and time windows.Keep the asset in scope and make conservative rotation or rebuild decisions for high-value environments.The missing evidence is recovered or the risk owner accepts residual uncertainty.

    Minimum Evidence To Collect

    minimum_evidence:
      - "Dependency, workflow, extension, image, or module inventory covering developer endpoints, CI runners, and production deployments."
      - "Positive or negative search results for lightning==2.6.2, lightning==2.6.3."
      - "Execution evidence for Python import-time loader starts Bun and obfuscated JavaScript."
      - "Process, file, DNS, proxy, firewall, or package-manager telemetry for listed indicators."
      - "Inventory of credentials, tokens, deployment paths, and downstream systems reachable from exposed environments."

    Timeline

    • 2026-01-30T00:00:00Z lightning==2.6.1 is identified by Snyk as the last clean release before the compromise Snyk.
    • 2026-04-30T00:00:00Z Malicious lightning==2.6.2 and lightning==2.6.3 releases are published to PyPI Snyk Sonatype.
    • 2026-04-30T00:00:00Z Snyk publishes advisory coverage for the affected releases Snyk Vulnerability Database.
    • 2026-05-01T00:00:00Z Sonatype updates its public analysis to include additional packages connected to the wider wave Sonatype.

    What Happened

    Attackers published malicious versions of the legitimate lightning package, preserving the expected framework code while adding a hidden runtime directory Snyk. When an affected environment imported lightning, the modified initialization path launched a background thread that invoked _runtime/start.py with output redirected away from the console Snyk.

    The Python bootstrapper fetched Bun from GitHub releases and used it to execute router_runtime.js, a large obfuscated JavaScript payload Snyk Sonatype. That design let the attackers reuse JavaScript supply-chain malware inside a Python ecosystem package instead of rewriting the stealer in Python Snyk.

    Technical Analysis

    Initial Access

    The available evidence points to a compromised package publishing path for the real lightning project rather than a typosquat Snyk. Snyk notes that 2.6.3 did not correspond to a normal GitHub release or tag, which supports a registry-side upload using stolen publishing authority Snyk.

    Execution Trigger

    Execution begins when Python imports the package Snyk Sonatype. This is more dangerous than a CLI-only path because notebooks, smoke tests, version checks, and CI import probes can all trigger the background payload.

    Payload Behavior

    The payload searches for GitHub tokens, npm tokens, cloud provider credentials, environment variables, local credential files, and cloud metadata service material Snyk Sonatype. Snyk also reports repository poisoning and npm tarball mutation logic consistent with a worm-capable supply-chain payload Snyk.

    Affected Assets and Blast Radius

    affected_assets:
      ecosystems:
        - "PyPI"
        - "Python"
      registries:
        - "pypi.org"
      packages:
        - "lightning"
      versions:
        - "lightning==2.6.2"
        - "lightning==2.6.3"
      repositories:
        - "Lightning-AI/pytorch-lightning"
      ci_cd_systems:
        - "GitHub Actions"
        - "developer CI runners"
      container_images:
        []
      developer_tools:
        - "Python notebooks"
        - "developer workstations"
    credentials_at_risk:
      - "GitHub tokens"
      - "cloud credentials"
      - "CI/CD secrets"
      - "SSH keys"
      - "environment variables"
      - "AI provider tokens"
    downstream_systems_to_audit:
      - "source control"
      - "package registries"
      - "cloud control planes"
      - "deployment platforms"
      - "Kubernetes or containers"
      - "secret managers"
    not_currently_known_to_affect:
      - "Assets without the affected artifact and without execution evidence."

    Indicators of Compromise

    package_versions:
      - "lightning==2.6.2"
      - "lightning==2.6.3"
    files:
      - "setup.mjs"
      - "router_runtime.js"
      - "Bun launcher"
    hashes:
      []
    domains:
      []
    urls:
      []
    ips:
      []
    process_patterns:
      - "Python import-time loader starts Bun and obfuscated JavaScript"
    network_patterns:
      - "egress related to malicious lightning PyPI releases"
    provenance_signals:
      - "artifact, tag, release, package, or marketplace metadata differs from expected source state"

    Detection and Hunting

    Script: local repository and exported telemetry scope

    #!/usr/bin/env python3
    import os
    import sys
    import json
    import subprocess
    from pathlib import Path
    
    ROOT = sys.argv[1] if len(sys.argv) > 1 else "."
    LOG_ROOT = os.environ.get("LOG_ROOT", "")
    OUT = Path(os.environ.get("OUT", "hp-lightning-pypi-bun-stealer-scope"))
    SINCE = "2026-01-30T00:00:00Z"
    UNTIL = "2026-05-01T00:00:00Z"
    
    PACKAGES = [
      "lightning",
    ]
    VERSIONS = [
      "2.6.2",
      "2.6.3",
      "lightning==2.6.2",
      "lightning==2.6.3",
    ]
    FILES = [
      "setup.mjs",
      "router_runtime.js",
      "Bun launcher",
    ]
    DOMAINS = [
    ]
    URLS = [
    ]
    IPS = [
    ]
    HASHES = [
    ]
    PROCESS_PATTERNS = [
      "Python import-time loader starts Bun and obfuscated JavaScript",
    ]
    NETWORK_PATTERNS = [
      "egress related to malicious lightning PyPI releases",
    ]
    
    # Positive signal: repository, lockfile, artifact, process, or network telemetry contains one of the exact incident selectors above.
    # Escalation: any match tied to a production build, CI run, deployed asset, or secret-bearing host moves the asset to presumed exposed.
    
    OUT.mkdir(parents=True, exist_ok=True)
    indicators_file = OUT / "indicators.txt"
    
    # Collect unique indicators
    indicators = set()
    for group in [PACKAGES, VERSIONS, FILES, DOMAINS, URLS, IPS, HASHES, PROCESS_PATTERNS, NETWORK_PATTERNS]:
        for val in group:
            if val:
                indicators.add(val)
    
    with open(indicators_file, "w") as f:
        for ind in sorted(indicators):
            f.write(ind + "\n")
    
    print(f"[+] Written unique selectors to {indicators_file}")
    
    # Walk local directory
    print(f"[+] Scanning directory: {ROOT} for selectors...")
    matches = []
    exclude_dirs = {"node_modules", "vendor", "dist", ".git"}
    for root, dirs, filenames in os.walk(ROOT):
        dirs[:] = [d for d in dirs if d not in exclude_dirs]
        for filename in filenames:
            filepath = Path(root) / filename
            try:
                content = filepath.read_text(errors="ignore")
                for ind in indicators:
                    if ind in content:
                        matches.append(f"{filepath}: found '{ind}'")
            except Exception:
                pass
    
    if matches:
        (OUT / "repository-indicator-matches.txt").write_text("\n".join(matches) + "\n")
        print(f"[!] Found {len(matches)} matches in codebase!")
    
    # Optional Log Scanning
    if LOG_ROOT and os.path.exists(LOG_ROOT):
        print(f"[+] Scanning telemetry log directory: {LOG_ROOT}...")
        log_matches = []
        for root, _, filenames in os.walk(LOG_ROOT):
            for filename in filenames:
                filepath = Path(root) / filename
                try:
                    content = filepath.read_text(errors="ignore")
                    for ind in indicators:
                        if ind in content:
                            log_matches.append(f"{filepath}: found '{ind}'")
                except Exception:
                    pass
        if log_matches:
            (OUT / "exported-telemetry-indicator-matches.txt").write_text("\n".join(log_matches) + "\n")
            print(f"[!] Found {len(log_matches)} matches in logs!")
    
        if PACKAGES:
            registry_dir = OUT / "registry"
            registry_dir.mkdir(exist_ok=True)
            for package in PACKAGES:
                if not package: continue
                safe_name = package.replace("/", "__")
                print(f"[+] Querying pip index for {package}...")
                res = subprocess.run(["python3", "-m", "pip", "index", "versions", package], capture_output=True, text=True)
                if res.returncode == 0:
                    (registry_dir / f"pypi-{safe_name}-versions.txt").write_text(res.stdout)
                subprocess.run(["python3", "-m", "pip", "download", "--no-deps", package, "-d", str(registry_dir)], capture_output=True)
    
    print(f"[+] Wrote scope artifacts under {OUT}")

    Downstream Abuse Audits

    Script: GitHub organization run, release, secret, and workflow audit

    #!/usr/bin/env python3
    import os
    import sys
    import json
    import subprocess
    from pathlib import Path
    
    if "ORG" not in os.environ:
        print("ERROR: Set ORG environment variable to the GitHub organization to audit", file=sys.stderr)
        sys.exit(1)
    
    ORG = os.environ["ORG"]
    SINCE = "2026-01-30T00:00:00Z"
    UNTIL = "2026-05-01T00:00:00Z"
    OUT = Path(os.environ.get("OUT", "hp-lightning-pypi-bun-stealer-github-audit"))
    
    SELECTORS = [
      "lightning",
      "2.6.2",
      "2.6.3",
      "lightning==2.6.2",
      "lightning==2.6.3",
      "setup.mjs",
      "router_runtime.js",
      "Bun launcher",
    ]
    
    # Positive signal: a workflow run, release, secret, key, package, or workflow change overlaps the exposure window and references an incident selector.
    # Remediation trigger: unauthorized post-exposure write activity or a secret-bearing run matching an incident selector requires token revocation and downstream cloud/registry review.
    
    OUT.mkdir(parents=True, exist_ok=True)
    (OUT / "runs").mkdir(exist_ok=True)
    (OUT / "logs").mkdir(exist_ok=True)
    (OUT / "repos").mkdir(exist_ok=True)
    
    # 1. Write incident-selectors file
    selectors_file = OUT / "incident-selectors.txt"
    with open(selectors_file, "w") as sf:
        for s in SELECTORS:
            if s:
                sf.write(s + "\n")
    
    # 2. Get list of repos
    print(f"[+] Fetching repositories for organization: {ORG}")
    repo_res = subprocess.run(["gh", "repo", "list", ORG, "--limit", "1000", "--json", "nameWithOwner"], capture_output=True, text=True)
    if repo_res.returncode != 0:
        print(f"[-] Failed to fetch repos: {repo_res.stderr}", file=sys.stderr)
        sys.exit(1)
    
    repos = [r["nameWithOwner"] for r in json.loads(repo_res.stdout)]
    
    for repo in repos:
        safe_repo = repo.replace("/", "__")
        print(f"[+] Auditing repository: {repo}")
    
        # Check runs in the window
        runs_res = subprocess.run([
            "gh", "api", f"/repos/{repo}/actions/runs",
            "-f", "per_page=100",
            "-f", f"created=>={SINCE}",
            "--paginate"
        ], capture_output=True, text=True)
    
        if runs_res.returncode == 0:
            try:
                all_runs = json.loads(runs_res.stdout).get("workflow_runs", [])
                filtered_runs = [r for r in all_runs if r["created_at"] <= UNTIL]
    
                if filtered_runs:
                    with open(OUT / "runs" / f"{safe_repo}-runs.jsonl", "w") as rf:
                        for run in filtered_runs:
                            rf.write(json.dumps(run) + "\n")
    
                            # Fetch log dynamically
                            run_id = str(run["id"])
                            log_res = subprocess.run(["gh", "run", "view", run_id, "--repo", repo, "--log"], capture_output=True, text=True)
                            if log_res.returncode == 0:
                                (OUT / "logs" / f"{safe_repo}-{run_id}.log").write_text(log_res.stdout)
    
                            # Fetch details
                            view_res = subprocess.run(["gh", "run", "view", run_id, "--repo", repo, "--json", "databaseId,workflowName,headSha,event,createdAt,jobs"], capture_output=True, text=True)
                            if view_res.returncode == 0:
                                (OUT / "runs" / f"{safe_repo}-{run_id}.json").write_text(view_res.stdout)
            except Exception as e:
                print(f"[-] Error parsing runs for {repo}: {e}")
    
        # Check releases in window
        subprocess.run(["gh", "api", f"/repos/{repo}/releases", "-f", "per_page=100", "--paginate"], capture_output=True)
        # Check repo secrets updated in window
        subprocess.run(["gh", "api", f"/repos/{repo}/actions/secrets", "-f", "per_page=100", "--paginate"], capture_output=True)
        # Check deploy keys
        subprocess.run(["gh", "api", f"/repos/{repo}/keys", "-f", "per_page=100", "--paginate"], capture_output=True)
    
    # Scan output directory for any indicator selector matches
    print("[+] Scanning gathered telemetry for indicator matches...")
    subprocess.run(["rg", "-n", "--hidden", "--fixed-strings", "-f", str(selectors_file), str(OUT)], capture_output=False)
    
    print(f"[+] Wrote GitHub audit artifacts under {OUT}")

    Script: cloud OIDC and deployment credential follow-on audit

    #!/usr/bin/env python3
    import os
    import json
    import subprocess
    from pathlib import Path
    
    SINCE = "2026-01-30T00:00:00Z"
    UNTIL = "2026-05-01T00:00:00Z"
    OUT = Path(os.environ.get("OUT", "hp-lightning-pypi-bun-stealer-cloud-audit"))
    AWS_REGIONS = os.environ.get("AWS_REGIONS", "us-east-1").split(",")
    
    # Positive signal: token exchange or privileged write activity occurs in the exposure window from GitHub, CI/CD, package registry, or deployment automation identity.
    # Remediation trigger: unexpected write, deploy, IAM, secret, or registry activity tied to an exposed CI/CD path requires trust-policy disablement and credential rotation.
    
    OUT.mkdir(parents=True, exist_ok=True)
    
    # 1. AWS CloudTrail Audit
    print("[+] Querying AWS CloudTrail for Web Identity token exchanges...")
    aws_events = []
    for region in AWS_REGIONS:
        res = subprocess.run([
            "aws", "cloudtrail", "lookup-events",
            "--region", region,
            "--start-time", SINCE,
            "--end-time", UNTIL,
            "--lookup-attributes", "AttributeKey=EventName,AttributeValue=AssumeRoleWithWebIdentity",
            "--output", "json"
        ], capture_output=True, text=True)
    
        if res.returncode == 0:
            try:
                events = json.loads(res.stdout).get("Events", [])
                for event in events:
                    ct = json.loads(event.get("CloudTrailEvent", "{}"))
                    ct["region"] = region
                    aws_events.append(ct)
            except Exception as e:
                print(f"[-] Error parsing AWS CloudTrail events: {e}")
    
    if aws_events:
        with open(OUT / "aws-assume-role-with-web-identity.jsonl", "w") as f:
            for ev in aws_events:
                f.write(json.dumps(ev) + "\n")
    
        # Audit follow-on events for returned access keys
        for ev in aws_events:
            access_key = ev.get("responseElements", {}).get("credentials", {}).get("accessKeyId")
            region = ev.get("region", "us-east-1")
            if access_key:
                print(f"[+] Enumerating AWS events for AccessKey: {access_key}")
                f_res = subprocess.run([
                    "aws", "cloudtrail", "lookup-events",
                    "--region", region,
                    "--start-time", SINCE,
                    "--end-time", UNTIL,
                    "--lookup-attributes", f"AttributeKey=AccessKeyId,AttributeValue={access_key}",
                    "--output", "json"
                ], capture_output=True, text=True)
                if f_res.returncode == 0:
                    try:
                        f_events = json.loads(f_res.stdout).get("Events", [])
                        with open(OUT / "aws-follow-on-api-calls.jsonl", "a") as ff:
                            for fe in f_events:
                                ff.write(fe.get("CloudTrailEvent", "{}") + "\n")
                    except Exception as e:
                        print(f"[-] Error writing follow-on events: {e}")
    
    # 2. Azure Activity Log Audit
    print("[+] Querying Azure activity logs...")
    az_res = subprocess.run([
        "az", "monitor", "activity-log", "list",
        "--start-time", SINCE,
        "--end-time", UNTIL,
        "--query", "[?contains(operationName.value, 'write') || contains(operationName.value, 'delete') || contains(operationName.value, 'Microsoft.ManagedIdentity')]",
        "-o", "json"
    ], capture_output=True, text=True)
    
    if az_res.returncode == 0:
        (OUT / "azure-write-delete-activity.json").write_text(az_res.stdout)
    
    # 3. GCP Logging Audit
    print("[+] Querying GCP Cloud Logging...")
    gcp_filter = f'timestamp>="{SINCE}" AND timestamp<="{UNTIL}" AND (protoPayload.methodName="google.sts.v1.SecurityTokenService.ExchangeToken" OR protoPayload.methodName:"GenerateAccessToken" OR protoPayload.methodName:"CreateServiceAccountKey" OR protoPayload.methodName:"SetIamPolicy")'
    gcp_res = subprocess.run([
        "gcloud", "logging", "read", gcp_filter,
        "--format", "json"
    ], capture_output=True, text=True)
    
    if gcp_res.returncode == 0:
        (OUT / "gcp-token-and-iam-activity.json").write_text(gcp_res.stdout)
    
    print(f"[+] Wrote cloud audit artifacts under {OUT}")

    Script: registry metadata and artifact audit

    #!/usr/bin/env python3
    import os
    import json
    import subprocess
    from pathlib import Path
    
    SINCE = "2026-01-30T00:00:00Z"
    OUT = Path(os.environ.get("OUT", "hp-lightning-pypi-bun-stealer-registry-audit"))
    PACKAGES = [
      "lightning",
    ]
    VERSIONS = [
      "2.6.2",
      "2.6.3",
      "lightning==2.6.2",
      "lightning==2.6.3",
    ]
    
    # Positive signal: registry metadata, package tarballs, or cached artifacts contain the exact affected package/version values.
    # Remediation trigger: any internal package cache, build artifact, or deployment using these package/version values requires exposure scoping.
    
    OUT.mkdir(parents=True, exist_ok=True)
    
    with open(OUT / "affected-versions.txt", "w") as av:
        for version in VERSIONS:
            if version:
                av.write(version + "\n")
    
        # 1. Audit PyPI dependencies in project files
        print("[+] Scanning PyPI dependency files...")
        for file in ["requirements.txt", "poetry.lock", "Pipfile.lock", "pyproject.toml", "setup.py"]:
            if Path(file).exists():
                subprocess.run(["rg", "-n", "--hidden", "--fixed-strings", "-f", str(OUT / "affected-versions.txt"), file])
    
        # 2. Query registry metadata and download packages for local analysis
        packages_dir = OUT / "packages"
        metadata_dir = OUT / "metadata"
        packages_dir.mkdir(exist_ok=True)
        metadata_dir.mkdir(exist_ok=True)
        for package in PACKAGES:
            if not package: continue
            print(f"[+] Querying pip index for {package}...")
            res = subprocess.run(["python3", "-m", "pip", "index", "versions", package], capture_output=True, text=True)
            if res.returncode == 0:
                (metadata_dir / f"{package}-versions.txt").write_text(res.stdout)
            subprocess.run(["python3", "-m", "pip", "download", "--no-deps", package, "-d", str(packages_dir)], capture_output=True)
    
        # 3. HOW TO REVOKE AND ROTATE EXPOSED PYPI PUBLISHING TOKENS:
        # PyPI does not support token revocation via CLI. Follow these exact steps:
        # 1. Log in to https://pypi.org/manage/account/
        # 2. Scroll to the "API tokens" section and click "Remove" on any compromised tokens.
        # 3. Generate a new API token limited to the specific project scope.
        # 4. Update your CI/CD secrets using the GitHub CLI:
        #    subprocess.run(["gh", "secret", "set", "PYPI_API_TOKEN", "--body", "pypi-AgEIcHlwaS5vcm...", "--repo", "my-org/my-repo"])
    
    print(f"[+] Wrote registry audit artifacts under {OUT}")

    Sources

    1. Snyk: lightning PyPI Compromise - Role: PRIMARY_RESEARCH - Impact: Package versions, import-time execution chain, Bun loader, payload behavior, publishing-path analysis.
    2. Snyk Vulnerability Database: SNYK-PYTHON-LIGHTNING-16323121 - Role: ENRICHMENT_DATA - Impact: Advisory ID, CVE, affected versions, critical severity.
    3. Sonatype: Malicious PyTorch Lightning Packages Found on PyPI - Role: PRIMARY_RESEARCH - Impact: Cross-vendor corroboration, affected versions, credential theft and propagation behavior.
    4. PyPI project page: lightning - Role: DIRECT_SOURCE - Impact: Registry context and package identity.

    IOC Clipboard

    3 IOCs
    Defang IOCs
    file setup.mjs setup.mjs
    file router_runtime.js router_runtime.js
    file Bun launcher Bun launcher