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.
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-formatteras a malicious npm package, lists all versions as affected, and records publication and disclosure timestamps Snyk. - confirmed: npm registry metadata shows versions
1.0.0through1.0.4existed 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
| Classification | Criteria | Required evidence | Handling decision |
|---|---|---|---|
| Confirmed compromise | The 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 exposed | The 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 exposed | Repositories 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 exposed | No 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. |
| Unknown | Dependency, 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-formatternpm registry. - 2026-05-26T18:17:32Z: version
1.0.4is the last version timestamp present in the npm registry metadata npm registry. - 2026-05-27T17:10:53Z: Snyk publishes
SNYK-JS-MOUSE5212SUPERFORMATTER-16895729Snyk. - 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
IOC Clipboard
8 IOCssecurity.snyk.io security[.]snyk[.]io registry.npmjs.org registry[.]npmjs[.]org www.ox.security www[.]ox[.]security www.npmjs.com www[.]npmjs[.]com https://security.snyk.io/vuln/SNYK-JS-MOUSE5212SUPERFORMATTER-16895729 hxxps://security[.]snyk[.]io/vuln/SNYK-JS-MOUSE5212SUPERFORMATTER-16895729 https://registry.npmjs.org/mouse5212-super-formatter hxxps://registry[.]npmjs[.]org/mouse5212-super-formatter 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/ https://www.npmjs.com/package/mouse5212-super-formatter hxxps://www[.]npmjs[.]com/package/mouse5212-super-formatter