critical Threat analysis

Malware-Slop mouse5212-super-formatter npm Package Targets AI Workspaces

Snyk and OX tracked mouse5212-super-formatter as a malicious npm package published on 2026-05-26 and removed on 2026-05-27. The package should be treated as credential theft risk for AI-assisted workspaces, Claude/Cursor context files, GitHub tokens, npm tokens, and build logs.

#npm#supply-chain#ai-tools#credential-theft#github
On this page 0% read

    Executive Summary

    mouse5212-super-formatter is a malicious npm package in the Malware-Slop cluster. Snyk lists it as critical, marks the package as malicious, credits OX researchers, and points to OX’s Malware-Slop writeup for the package-level analysis Snyk. The npm registry record shows the package was created on 2026-05-26T17:30:57.408Z, published versions 1.0.0 through 1.0.4, and was unpublished on 2026-05-27T17:53:41.301Z npm registry.

    This is a developer-workstation and CI-runner incident, not just a dependency hygiene item. Any install in an AI-assisted workspace should be scoped for GitHub, npm, cloud, and AI assistant context exposure because the public reporting names a malicious package that leaks its own GitHub private token and targets assistant-adjacent files and user-data paths OX Snyk.

    Key Facts

    event_type: "malicious npm package"
    package: "mouse5212-super-formatter"
    ecosystem: "npm"
    known_versions: ["1.0.0", "1.0.1", "1.0.2", "1.0.3", "1.0.4"]
    created: "2026-05-26T17:30:57.408Z"
    unpublished: "2026-05-27T17:53:41.301Z"
    snyk_id: "SNYK-JS-MOUSE5212SUPERFORMATTER-16895729"
    cwe: "CWE-506"
    cvss_v31: "9.8"
    cvss_v40: "9.3"
    primary_risk:
      - "developer credential theft"
      - "AI workspace context theft"
      - "GitHub and npm token exposure"
      - "CI log and package-cache exposure"

    Source Confidence & Evidence Mapping

    • confirmed: Snyk identifies mouse5212-super-formatter as a malicious npm package, lists all versions as affected, and records publication and disclosure timestamps Snyk.
    • confirmed: npm registry metadata shows versions 1.0.0 through 1.0.4 existed and were unpublished on 2026-05-27 npm registry.
    • confirmed: The npm package page currently represents the package identity and takedown state for scoping direct package references npm.
    • likely: OX’s Malware-Slop writeup is the best public source for behavior beyond registry metadata; Snyk links to that OX writeup as the package reference OX.
    • unknown: Public data reviewed here does not prove victim count, complete exfiltration destination set, or whether every package version executed the same payload.

    Impact Determination

    ClassificationCriteriaRequired evidenceHandling decision
    Confirmed compromiseThe package appears in a lockfile, package cache, install log, CI job, or endpoint telemetry and runtime evidence shows token, workspace, or file collection.Lockfile/cache hit plus process, network, shell, EDR, CI, or proxy evidence.Isolate the host or runner, preserve npm cache and logs, rotate reachable credentials from a clean environment.
    Presumed exposedThe package was installed in a developer workstation, container build, or CI runner but runtime telemetry is incomplete.package-lock.json, pnpm-lock.yaml, yarn.lock, bun.lock, npm cache, image layer, or install log.Rotate GitHub, npm, cloud, and assistant-provider tokens available to that environment.
    Potentially exposedRepositories used npm install flows during 2026-05-26 to 2026-05-27 but package resolution records are incomplete.Dependency, package-manager, proxy, CI, and private-registry logs.Reconstruct package resolution before narrowing scope.
    Not exposedNo package name, version, package-cache artifact, install log, or runtime selector appears in complete evidence.Negative search across repos, package caches, images, runners, proxy logs, and endpoint telemetry.Preserve negative evidence and enforce scoped package allow-lists.
    UnknownDependency, npm cache, runner, endpoint, or proxy telemetry is unavailable.Named telemetry gap with owner and retention window.Keep reachable credentials in scope until evidence is recovered or rotation closes the gap.

    Timeline

    • 2026-05-26T17:30:57Z: npm registry metadata records creation of mouse5212-super-formatter npm registry.
    • 2026-05-26T18:17:32Z: version 1.0.4 is the last version timestamp present in the npm registry metadata npm registry.
    • 2026-05-27T17:10:53Z: Snyk publishes SNYK-JS-MOUSE5212SUPERFORMATTER-16895729 Snyk.
    • 2026-05-27T17:53:41Z: npm registry metadata records the package as unpublished npm registry.
    • 2026-06-01: This Halting Problems refresh found no local post for mouse5212-super-formatter, Malware-Slop, or the Snyk package ID.

    Technical Analysis

    The package identity is narrow and actionable: search exactly for mouse5212-super-formatter and the known versions. Because the malicious package is unpublished, private registry mirrors, npm caches, CI artifact caches, and container image layers become more important than live registry queries.

    The operational risk is broader than the name implies. The OX reference describes Malware-Slop as a malicious npm package incident involving a leaked GitHub private token, and Snyk classifies the package as malicious with CWE-506 embedded malicious code OX Snyk. Defenders should assume that GitHub credentials, npm tokens, cloud credentials, and assistant context files reachable from the install environment may be exposed until telemetry proves otherwise.

    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-malware-slop-mouse5212-super-formatter-scope"))
    SINCE = "2026-05-26T17:30:57Z"
    UNTIL = "2026-06-01T23:59:59Z"
    
    PACKAGES = [
    ]
    VERSIONS = [
    ]
    FILES = [
    ]
    DOMAINS = [
      "security.snyk.io",
      "registry.npmjs.org",
      "www.ox.security",
      "www.npmjs.com",
    ]
    URLS = [
      "https://security.snyk.io/vuln/SNYK-JS-MOUSE5212SUPERFORMATTER-16895729",
      "https://registry.npmjs.org/mouse5212-super-formatter",
      "https://www.ox.security/blog/malware-slop-new-malicious-npm-package-leaks-its-own-github-private-token/",
      "https://www.npmjs.com/package/mouse5212-super-formatter",
    ]
    IPS = [
    ]
    HASHES = [
    ]
    PROCESS_PATTERNS = [
    ]
    NETWORK_PATTERNS = [
    ]
    
    # 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 npm view for {package}...")
                res = subprocess.run(["npm", "view", package, "name", "version", "time", "versions", "dist-tags", "maintainers", "dist.tarball", "dist.integrity", "scripts", "--json"], capture_output=True, text=True)
                if res.returncode == 0:
                    (registry_dir / f"npm-{safe_name}.json").write_text(res.stdout)
    
    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-05-26T17:30:57Z"
    UNTIL = "2026-06-01T23:59:59Z"
    OUT = Path(os.environ.get("OUT", "hp-malware-slop-mouse5212-super-formatter-github-audit"))
    
    SELECTORS = [
      "security.snyk.io",
      "registry.npmjs.org",
      "www.ox.security",
      "www.npmjs.com",
      "https://security.snyk.io/vuln/SNYK-JS-MOUSE5212SUPERFORMATTER-16895729",
      "https://registry.npmjs.org/mouse5212-super-formatter",
      "https://www.ox.security/blog/malware-slop-new-malicious-npm-package-leaks-its-own-github-private-token/",
      "https://www.npmjs.com/package/mouse5212-super-formatter",
    ]
    
    # 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-05-26T17:30:57Z"
    UNTIL = "2026-06-01T23:59:59Z"
    OUT = Path(os.environ.get("OUT", "hp-malware-slop-mouse5212-super-formatter-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-05-26T17:30:57Z"
    OUT = Path(os.environ.get("OUT", "hp-malware-slop-mouse5212-super-formatter-registry-audit"))
    PACKAGES = [
    ]
    VERSIONS = [
    ]
    
    # 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 npm dependencies in lockfiles/package.json
        print("[+] Scanning lockfiles for npm selectors...")
        for file in ["package-lock.json", "npm-shrinkwrap.json", "pnpm-lock.yaml", "yarn.lock", "package.json"]:
            if Path(file).exists():
                subprocess.run(["rg", "-n", "--hidden", "--fixed-strings", "-f", str(OUT / "affected-versions.txt"), file])
    
        # 2. Query registry metadata and fetch tarballs for local analysis
        metadata_dir = OUT / "metadata"
        tarballs_dir = OUT / "tarballs"
        metadata_dir.mkdir(exist_ok=True)
        tarballs_dir.mkdir(exist_ok=True)
        for package in PACKAGES:
            if not package: continue
            safe_name = package.replace("/", "__")
            print(f"[+] Querying npm view for {package}...")
            res = subprocess.run(["npm", "view", package, "time", "versions", "dist-tags", "maintainers", "dist.tarball", "dist.integrity", "scripts", "--json"], capture_output=True, text=True)
            if res.returncode == 0:
                (metadata_dir / f"npm-{safe_name}.json").write_text(res.stdout)
            subprocess.run(["npm", "pack", package, "--pack-destination", str(tarballs_dir)], capture_output=True)
    
        # 3. HOW TO REVOKE AND ROTATE EXPOSED NPM PUBLISHING TOKENS:
        # Revoke all compromised tokens via npm CLI:
        # subprocess.run(["npm", "token", "list"])
        # subprocess.run(["npm", "token", "revoke", "123456"])
        # Or logout to revoke the current session:
        # subprocess.run(["npm", "logout"])
        # Generate a new publishing token with MFA protection:
        # subprocess.run(["npm", "token", "create", "--read-only=false", "--cidr=0.0.0.0/0"])
    
    print(f"[+] Wrote registry audit artifacts under {OUT}")

    Remediation and Closure

    Remove the package from dependencies, purge package-manager caches, rebuild containers from clean lockfiles, and rotate credentials reachable from affected hosts or runners. Closure requires a negative search across repos, CI logs, package caches, endpoint telemetry, and registry audit logs, or a documented decision to rotate all reachable credentials where telemetry is missing.

    Sources

    1. Snyk: SNYK-JS-MOUSE5212SUPERFORMATTER-16895729
    2. npm registry metadata for mouse5212-super-formatter
    3. npm package page for mouse5212-super-formatter
    4. OX: Malware-Slop malicious npm package writeup

    IOC Clipboard

    8 IOCs
    Defang IOCs
    domain security.snyk.io security[.]snyk[.]io
    domain registry.npmjs.org registry[.]npmjs[.]org
    domain www.ox.security www[.]ox[.]security
    domain www.npmjs.com www[.]npmjs[.]com
    url https://security.snyk.io/vuln/SNYK-JS-MOUSE5212SUPERFORMATTER-16895729 hxxps://security[.]snyk[.]io/vuln/SNYK-JS-MOUSE5212SUPERFORMATTER-16895729
    url https://registry.npmjs.org/mouse5212-super-formatter hxxps://registry[.]npmjs[.]org/mouse5212-super-formatter
    url https://www.ox.security/blog/malware-slop-new-malicious-npm-package-leaks-its-own-github-private-token/ hxxps://www[.]ox[.]security/blog/malware-slop-new-malicious-npm-package-leaks-its-own-github-private-token/
    url https://www.npmjs.com/package/mouse5212-super-formatter hxxps://www[.]npmjs[.]com/package/mouse5212-super-formatter