Mini Shai-Hulud / TanStack + Mistral AI supply-chain audit script A developer has released an open-source supply-chain audit script, "Mini Shai-Hulud," designed to detect the TanStack Router and Mistral AI supply-chain attacks. The bash script checks for specific SHA hashes, malicious commits, and known command-and-control infrastructure associated with the incidents. It scans project directories and lockfiles for indicators of compromise, including trojanized tarballs and persistence mechanisms. | /usr/bin/env bash | | | | | | Mini Shai-Hulud / TanStack + Mistral AI supply-chain audit script | | | | | | chmod +x tanstack mistral audit public.sh | | | ./tanstack mistral audit public.sh | | | sudo ./tanstack mistral audit public.sh | | | ./tanstack mistral audit public.sh /path/to/project /tmp | | | set -uo pipefail | | | RED='' | | | GREEN='' | | | YELLOW='' | | | NC='' | | | if -t 1 && -z "${NO COLOR:-}" ; then | | | RED=$'\033 0;31m' | | | GREEN=$'\033 0;32m' | | | YELLOW=$'\033 1;33m' | | | NC=$'\033 0m' | | | fi | | | SCRIPT VERSION="2026-05-12-public" | | | ROUTER INIT SHA="ab4fcadaec49c03278063dd269ea5eef82d24f2124a8e15d7b90f2fa8601266c" | | | TANSTACK RUNNER SHA="2ec78d556d696e208927cc503d48e4b5eb56b31abc2870c2ed2e98d6be27fc96" | | | TANSTACK SETUP PACKAGE SHA="7c12d8614c624c70d6dd6fc2ee289332474abaa38f70ebe2cdef064923ca3a9b" | | | SETUP MJS SHA="2258284d65f63829bd67eaba01ef6f1ada2f593f9bbe41678b2df360bd90d3df" | | | TANSTACK TROJANIZED TARBALL SHA="1e8538c6e0563d50da0f2e097e979ebd5294ce1defe01d0b9fe361ba3bed1898" | | | MALICIOUS COMMIT="79ac49eedf774dd4b0cfa308722bc463cfe5885c" | | | MALICIOUS SETUP SPEC="github:tanstack/router ${MALICIOUS COMMIT}" | | | SESSION RECIPIENT ID="05f9e609d79eed391015e11380dee4b5c9ead0b6e2e7f0134e6e51767a87323026" | | | PYPI MISTRAL C2="83.142.209.194" | | | PYPI MISTRAL ENV MARKER="MISTRAL INIT" | | | PYPI PAYLOAD DOMAIN="git-tanstack.com" | | | PYPI PAYLOAD URL PATH="tmp/transformers.pyz" | | | PYPI SECOND STAGE="transformers.pyz" | | | PYPI SECOND STAGE ABS="/tmp/transformers.pyz" | | | PYPI PERSISTENCE SCRIPT="pgmonitor.py" | | | PYPI PERSISTENCE SERVICE="pgsql-monitor.service" | | | GUARDRAILS AI 0101 SDIST SHA="8491b17dc16f31c27f290b3b1e0f2e8866cc775828590e90376ecfb0cc1f8d9c" | | | GUARDRAILS AI 0101 WHEEL SHA="b76c800a685c0376a668170b000ba1e5a5ac7daeb714a6af97eac2d31d9a8dbc" | | | MISTRALAI 246 WHEEL SHA="6dbaa43bf2f3c0d3cddbca74967e952da563fb974c1ef9d4ecbb2e58e41fe81b" | | | MISTRALAI 246 STAGE1 INIT SHA="2a314ea8be337e1ca9ec833ed13ed854d9fd38bce0a519cf288f3bec8d9e6f30" | | | MISTRALAI 246 TRANSFORMERS PYZ SHA="eab7964658dd6ad28cb581d979837cfbf4690c582e0c507e965060defbb89d1b" | | | MISTRALAI 246 STAGE2 MAIN SHA="59811e589b0caa202e63a07c61129d58443fe8911545fdb3ed9264f8628d68e5" | | | MISTRALAI 246 STAGE2 AGGREGATE SHA="eeee34f2db5cb3eb5e5877c93e2250d44128c470d24fad38885bbfdab452de09" | | | MISTRALAI 246 STAGE2 CONFIG SHA="714152c0489c3d8d9196305efa12a6850e5d460ab62ff3be91caf3fb852ec793" | | | MISTRALAI 246 STAGE2 ENTRYPOINT SHA="e9d0fefa8efc50e4fa9a84375780bef9590dfd60968757a35158ba0d45e265b1" | | | MISTRALAI 246 STAGE2 ROULETTE SHA="5774ad8f2594f6f1c34cdfb2511dc4728db7b5f4089765600d6c74a353de69a3" | | | MISTRALAI 246 STAGE2 COLLECTORS AWS SHA="03cdeb175f6f3d79aeca171841f83d10fff83143b2bc61a22c15f1ccfe484f27" | | | MISTRALAI 246 STAGE2 COLLECTORS AZURE SHA="3e0f0fbf089adc0255775cb65b2e8dfc18028bb288376080d7a90a2448b00e92" | | | MISTRALAI 246 STAGE2 COLLECTORS FILESYSTEM SHA="50d88c81cac859cb0f446a30229b64336ef4f682a1ed12e1f4cfa03758d57042" | | | MISTRALAI 246 STAGE2 COLLECTORS GCP SHA="4c37f2239888b689d67a193bbd7864d33641c9343574412470b4f882aefbff5c" | | | MISTRALAI 246 STAGE2 COLLECTORS KUBERNETES SHA="ed81603bb88d8060deb46b474a515d49bb71ca0832a3f6d854b4b886bff25842" | | | MISTRALAI 246 STAGE2 COLLECTORS PASSWORDS SHA="630333cc3950387605ad09a528506053e4ff36cee08547b8424958d742a5eac7" | | | MISTRALAI 246 STAGE2 COLLECTORS VAULT SHA="0eb70f94b82aa24ae8f69d6644f17ca1173b1318a0d30af142d8f76a5175aa49" | | | MISTRALAI 246 STAGE2 UTILITIES AWS SIGNER SHA="3b48fb6375ca66dba4b38c001a635b53a4e9ebee233a4ff3ac6352674f4ddc38" | | | MISTRALAI 246 STAGE2 UTILITIES CRYPTO SHA="831d54a730b2a141ed97ce7a892cec9066c29c9f4094ad6d2ba3e92af97edb51" | | | INCLUDE LOCKFILE SCAN="${INCLUDE LOCKFILE SCAN:-true}" | | | EXIT ON MEDIUM="${EXIT ON MEDIUM:-false}" | | | PRINT FINDING LIMIT="${PRINT FINDING LIMIT:-200}" | | | FD THREADS="${FD THREADS:-1}" | | | SCAN ROOTS= | | | FILES SCANNED=0 | | | LOCKFILES SCANNED=0 | | | FILE FINDER='' | | | if command -v fdfind /dev/null 2 &1; then | | | FILE FINDER='fdfind' | | | elif command -v fd /dev/null 2 &1; then | | | FILE FINDER='fd' | | | fi | | | ISSUES FILE="$ mktemp "${TMPDIR:-/tmp}/tanstack mistral audit.XXXXXX" " || exit 2 | | | trap 'rm -f "$ISSUES FILE"' EXIT | | | sanitize field { | | | printf '%s' "${1:-}" | tr '\t\n\r' ' ' | cut -c 1-1200 | | | } | | | print header { | | | printf '\n%s=== %s ===%s\n' "$YELLOW" "$1" "$NC" | | | } | | | print clean { | | | printf '%s + CLEAN:%s %s\n' "$GREEN" "$NC" "$1" | | | } | | | print warning { | | | printf '%s ? WARNING:%s %s\n' "$YELLOW" "$NC" "$1" | | | } | | | add issue { | | | printf '%s\t%s\t%s\t%s\n' \ | | | "$ sanitize field "$1" " \ | | | "$ sanitize field "$2" " \ | | | "$ sanitize field "$3" " \ | | | "$ sanitize field "$4" " "$ISSUES FILE" | | | } | | | issue count { | | | wc -l < "$ISSUES FILE" | tr -d ' ' | | | } | | | count severity { | | | local severity="$1" | | | awk -F '\t' -v s="$severity" '$1 == s { c++ } END { print c + 0 }' "$ISSUES FILE" | | | } | | | actionable count { | | | awk -F '\t' '$1 == "CRITICAL" || $1 == "HIGH" { c++ } END { print c + 0 }' "$ISSUES FILE" | | | } | | | medium count { | | | count severity "MEDIUM" | | | } | | | path is under { | | | local child="$1" | | | local parent="$2" | | | "$child" == "$parent" && return 0 | | | if "$parent" == "/" ; then | | | "$child" == / | | | return | | | fi | | | "$child" == "$parent"/ | | | } | | | add scan root { | | | local candidate="$1" | | | local resolved existing | | | local kept= | | | -d "$candidate" || { | | | print warning "Skipping missing directory: $candidate" | | | return | | | } | | | resolved="$ cd "$candidate" 2 /dev/null && pwd -P " || { | | | print warning "Skipping unreadable directory: $candidate" | | | return | | | } | | | if "${ SCAN ROOTS @ }" -gt 0 ; then | | | for existing in "${SCAN ROOTS @ }"; do | | | if path is under "$resolved" "$existing"; then | | | return | | | fi | | | if path is under "$existing" "$resolved"; then | | | kept+= "$existing" | | | fi | | | done | | | fi | | | if "${ kept @ }" -gt 0 ; then | | | SCAN ROOTS= "${kept @ }" "$resolved" | | | else | | | SCAN ROOTS= "$resolved" | | | fi | | | } | | | add scan root if exists { | | | -d "$1" && add scan root "$1" | | | } | | | add global package roots { | | | local path | | | for path in \ | | | /usr/local/lib/node modules \ | | | /usr/lib/node modules \ | | | /opt/homebrew/lib/node modules \ | | | /Library/Python/ /site-packages \ | | | /opt/homebrew/lib/python /site-packages \ | | | /usr/local/lib/python /site-packages \ | | | /usr/local/lib/python /dist-packages \ | | | /usr/lib/python /site-packages \ | | | /usr/lib/python /dist-packages \ | | | /opt/ /venv/lib/python /site-packages \ | | | /opt/ /.venv/lib/python /site-packages \ | | | /srv/ /venv/lib/python /site-packages \ | | | /srv/ /.venv/lib/python /site-packages; do | | | add scan root if exists "$path" | | | done | | | } | | | add default scan roots { | | | local cwd os uid | | | cwd="$ pwd -P 2 /dev/null || pwd " | | | os="$ uname -s 2 /dev/null || printf unknown " | | | uid="$ id -u 2 /dev/null || printf 1 " | | | if "$uid" == "0" ; then | | | if "$os" == "Darwin" ; then | | | add scan root if exists /Users | | | add scan root if exists /var/root | | | add scan root if exists /usr/local/lib/node modules | | | add scan root if exists /opt/homebrew/lib/node modules | | | else | | | add scan root if exists /home | | | add scan root if exists /root | | | fi | | | -n "${HOME:-}" && "$HOME" = "/" && add scan root if exists "$HOME" | | | else | | | -n "${HOME:-}" && "$HOME" = "/" && add scan root if exists "$HOME" | | | fi | | | case "$cwd" in | | | /|/private|/System|/Library|/Applications|/usr|/usr/local|/opt|/opt/homebrew ;; | | | add scan root if exists "$cwd" ;; | | | esac | | | add scan root if exists /tmp | | | add scan root if exists /var/tmp | | | add scan root if exists /private/tmp | | | add scan root if exists /private/var/tmp | | | add global package roots | | | } | | | hash file { | | | if command -v sha256sum /dev/null 2 &1; then | | | sha256sum "$1" 2 /dev/null | awk '{print $1}' | | | elif command -v shasum /dev/null 2 &1; then | | | shasum -a 256 "$1" 2 /dev/null | awk '{print $1}' | | | elif command -v openssl /dev/null 2 &1; then | | | openssl dgst -sha256 "$1" 2 /dev/null | awk '{print $NF}' | | | else | | | printf 'sha256-unavailable' | | | fi | | | } | | | file has { | | | grep -Fq -- "$2" "$1" 2 /dev/null | | | } | | | file has regex { | | | grep -Eq -- "$2" "$1" 2 /dev/null | | | } | | | json get string { | | | local file="$1" | | | local key="$2" | | | sed -nE "s/. \"${key}\" :space: : :space: \" ^\" \". /\1/p" "$file" 2 /dev/null | head -n 1 | | | } | | | file has pkg version nearby { | | | local file="$1" | | | local package ere="$2" | | | local version ere="$3" | | | awk -v pkg="$package ere" -v ver="$version ere" ' | | | BEGIN { found = 0 } | | | $0 ~ pkg { | | | if $0 ~ ver { found = 1; exit } | | | for i = 0; i < 18 && getline line 0; i++ { | | | if line ~ ver { found = 1; exit } | | | if line ~ pkg && line ~ ver { found = 1; exit } | | | } | | | } | | | END { exit found ? 0 : 1 } | | | ' "$file" 2 /dev/null | | | } | | | file has any malware marker { | | | local file="$1" | | | -f "$file" || return 1 | | | file has "$file" "filev2.getsession.org" || | | | file has "$file" "api.masscan.cloud" || | | | file has "$file" "git-tanstack.com" || | | | file has "$file" "seed1.getsession.org" || | | | file has "$file" "seed2.getsession.org" || | | | file has "$file" "seed3.getsession.org" || | | | file has "$file" "$SESSION RECIPIENT ID" || | | | file has "$file" "IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner" || | | | file has "$file" "svksjrhjkcejg" || | | | file has "$file" "EveryBoiWeBuildIsAWormyBoi" || | | | file has "$file" "A Mini Shai-Hulud has Appeared" || | | | file has "$file" "Shai-Hulud: Here We Go Again" || | | | file has "$file" "PUSH UR T3MPRR" || | | | file has "$file" "bun run tanstack runner.js" || | | | file has "$file" "$MALICIOUS SETUP SPEC" || | | | { file has "$file" "@tanstack/setup" && file has "$file" "$MALICIOUS COMMIT"; } | | | } | | | pypi loader is malicious { | | | local file="$1" | | | -f "$file" || return 1 | | | { file has "$file" "$PYPI MISTRAL ENV MARKER" && \ | | | { file has "$file" "$PYPI MISTRAL C2" || file has "$file" "$PYPI SECOND STAGE" || file has "$file" "$PYPI PAYLOAD DOMAIN"; }; } || | | | { file has "$file" "$PYPI MISTRAL C2" && file has "$file" "$PYPI SECOND STAGE"; } || | | | { file has "$file" "$PYPI PAYLOAD DOMAIN" && file has "$file" "$PYPI SECOND STAGE"; } || | | | { file has "$file" "$PYPI SECOND STAGE ABS" && \ | | | { file has "$file" "subprocess" || file has "$file" "Popen" || file has "$file" "curl" || file has "$file" "urlopen"; }; } | | | } | | | pypi payload is malicious { | | | local file="$1" | | | -f "$file" || return 1 | | | { file has "$file" "$PYPI MISTRAL ENV MARKER" && \ | | | { file has "$file" "$PYPI MISTRAL C2" || file has "$file" "$PYPI SECOND STAGE" || file has "$file" "$PYPI PAYLOAD DOMAIN"; }; } || | | | { file has "$file" "$PYPI MISTRAL C2" && \ | | | { file has "$file" "$PYPI SECOND STAGE" || file has "$file" "$PYPI PERSISTENCE SCRIPT" || file has "$file" "$PYPI PERSISTENCE SERVICE" || file has "$file" "PUSH UR T3MPRR"; }; } || | | | { file has "$file" "$PYPI PAYLOAD DOMAIN" && file has "$file" "$PYPI SECOND STAGE"; } || | | | { file has "$file" "$PYPI SECOND STAGE" && file has "$file" "$PYPI PERSISTENCE SCRIPT" && file has "$file" "$PYPI PERSISTENCE SERVICE"; } || | | | { file has "$file" "FIRESCALE" && file has "$file" "$PYPI MISTRAL C2"; } || | | | { file has "$file" "PUSH UR T3MPRR" && file has "$file" "RunForCover.mp3"; } | | | } | | | pypi service points to monitor { | | | local file="$1" | | | -f "$file" || return 1 | | | file has "$file" "$PYPI PERSISTENCE SCRIPT" && \ | | | { file has "$file" "$PYPI PERSISTENCE SERVICE" || file has "$file" "ExecStart" || file has "$file" "systemd"; } | | | } | | | pypi metadata compromised package { | | | local file="$1" | | | -f "$file" || return 1 | | | if file has "$file" "Name: mistralai" && file has "$file" "Version: 2.4.6"; then | | | printf 'mistralai==2.4.6' | | | return 0 | | | fi | | | if file has "$file" "Name: guardrails-ai" && file has "$file" "Version: 0.10.1"; then | | | printf 'guardrails-ai==0.10.1' | | | return 0 | | | fi | | | return 1 | | | } | | | node metadata compromised package { | | | local file="$1" | | | local name version | | | -f "$file" || return 1 | | | name="$ json get string "$file" name " | | | version="$ json get string "$file" version " | | | case "${name}:${version}" in | | | '@mistralai/mistralai:2.2.2'|'@mistralai/mistralai:2.2.3'|'@mistralai/mistralai:2.2.4'|'@mistralai/mistralai-azure:1.7.1'|'@mistralai/mistralai-azure:1.7.2'|'@mistralai/mistralai-azure:1.7.3'|'@mistralai/mistralai-gcp:1.7.1'|'@mistralai/mistralai-gcp:1.7.2'|'@mistralai/mistralai-gcp:1.7.3' | | | printf '%s@%s' "$name" "$version" | | | return 0 | | | ;; | | | esac | | | return 1 | | | } | | | exposure marker for depfile { | | | local file="$1" | | | -f "$file" || return 1 | | | if file has pkg version nearby "$file" '@mistralai/mistralai-azure ^A-Za-z0-9 .- |$ ' '1 . 7 . 123 '; then | | | printf '@mistralai/mistralai-azure affected version marker' | | | return 0 | | | fi | | | if file has pkg version nearby "$file" '@mistralai/mistralai-gcp ^A-Za-z0-9 .- |$ ' '1 . 7 . 123 '; then | | | printf '@mistralai/mistralai-gcp affected version marker' | | | return 0 | | | fi | | | if file has pkg version nearby "$file" '@mistralai/mistralai ^A-Za-z0-9 .- |$ ' '2 . 2 . 234 '; then | | | printf '@mistralai/mistralai affected version marker' | | | return 0 | | | fi | | | if file has pkg version nearby "$file" ' ^| ^A-Za-z0-9 ./@- mistralai ^A-Za-z0-9 ./- |$ ' '2 . 4 . 6' || \ | | | file has regex "$file" ' ^| ^A-Za-z0-9 ./@- mistralai ^A-Za-z0-9 ./- | :space: + ^ \r\n 2 . 4 . 6'; then | | | printf 'mistralai==2.4.6 marker' | | | return 0 | | | fi | | | if file has pkg version nearby "$file" ' ^| ^A-Za-z0-9 .- guardrails-ai ^A-Za-z0-9 .- |$ ' '0 . 10 . 1' || \ | | | file has pkg version nearby "$file" ' ^| ^A-Za-z0-9 .- guardrails ai ^A-Za-z0-9 .- |$ ' '0 . 10 . 1' || \ | | | file has regex "$file" ' ^| ^A-Za-z0-9 .- guardrails - ai ^A-Za-z0-9 .- | :space: + ^ \r\n 0 . 10 . 1'; then | | | printf 'guardrails-ai==0.10.1 marker' | | | return 0 | | | fi | | | return 1 | | | } | | | file hash is known malicious { | | | local file="$1" | | | local digest | | | digest="$ hash file "$file" " | | | case "$digest" in | | | "$ROUTER INIT SHA"|"$TANSTACK RUNNER SHA"|"$TANSTACK SETUP PACKAGE SHA"|"$SETUP MJS SHA"|"$TANSTACK TROJANIZED TARBALL SHA"|"$GUARDRAILS AI 0101 SDIST SHA"|"$GUARDRAILS AI 0101 WHEEL SHA"|"$MISTRALAI 246 WHEEL SHA"|"$MISTRALAI 246 STAGE1 INIT SHA"|"$MISTRALAI 246 TRANSFORMERS PYZ SHA"|"$MISTRALAI 246 STAGE2 MAIN SHA"|"$MISTRALAI 246 STAGE2 AGGREGATE SHA"|"$MISTRALAI 246 STAGE2 CONFIG SHA"|"$MISTRALAI 246 STAGE2 ENTRYPOINT SHA"|"$MISTRALAI 246 STAGE2 ROULETTE SHA"|"$MISTRALAI 246 STAGE2 COLLECTORS AWS SHA"|"$MISTRALAI 246 STAGE2 COLLECTORS AZURE SHA"|"$MISTRALAI 246 STAGE2 COLLECTORS FILESYSTEM SHA"|"$MISTRALAI 246 STAGE2 COLLECTORS GCP SHA"|"$MISTRALAI 246 STAGE2 COLLECTORS KUBERNETES SHA"|"$MISTRALAI 246 STAGE2 COLLECTORS PASSWORDS SHA"|"$MISTRALAI 246 STAGE2 COLLECTORS VAULT SHA"|"$MISTRALAI 246 STAGE2 UTILITIES AWS SIGNER SHA"|"$MISTRALAI 246 STAGE2 UTILITIES CRYPTO SHA" | | | printf '%s' "$digest" | | | return 0 | | | ;; | | | esac | | | return 1 | | | } | | | installed package has malicious setup { | | | local package json="$1" | | | "$package json" == /node modules/ /package.json || return 1 | | | file has "$package json" '"@tanstack/setup"' && file has "$package json" "$MALICIOUS SETUP SPEC" | | | } | | | lockfile has malicious setup { | | | local lockfile="$1" | | | file has "$lockfile" "@tanstack/setup" && file has "$lockfile" "$MALICIOUS COMMIT" | | | } | | | persistence script is malicious { | | | local file="$1" | | | local digest | | | -f "$file" || return 1 | | | if digest="$ file hash is known malicious "$file" "; then | | | return 0 | | | fi | | | file has any malware marker "$file" | | | } | | | find candidate files { | | | local root="$1" | | | if -n "$FILE FINDER" ; then | | | "$FILE FINDER" -0 -j "$FD THREADS" -H -I -t f \ | | | -E .git \ | | | -E .hg \ | | | -E .svn \ | | | -E target \ | | | -E .cache \ | | | -E node modules/.cache \ | | | -E .pnpm-store \ | | | -E .yarn/cache \ | | | -E .Trash \ | | | -E Library/Caches \ | | | -E Library/Developer/Xcode/DerivedData \ | | | -p ' ^|/ router init\.js|tanstack runner\.js|router runtime\.js|node modules/. /package\.json|mistralai/client/ init \.py|guardrails/ init \.py|mistralai-2\.4\.6\.dist-info/METADATA|guardrails - ai-0\.10\.1\.dist-info/METADATA|mistralai-2\.4\.6 -py3-none-any\.whl|\.tar\.gz |guardrails ai-0\.10\.1 -py3-none-any\.whl|\.tar\.gz |transformers\.pyz|pgmonitor\.py|pgsql-monitor\.service| main \.py|aggregate\.py|config\.py|entrypoint\.py|roulette\.py|collectors/ aws|azure|filesystem|gcp|kubernetes|passwords|vault \.py|utilities/ aws signer|crypto \.py|\.claude/ settings\.json|router runtime\.js|setup\.mjs |\.vscode/ tasks\.json|setup\.mjs |gh-token-monitor\.service|com\.user\.gh-token-monitor\.plist|gh-token-monitor\.sh|\.config/gh-token-monitor/token|\.github/workflows/codeql analysis\.ya?ml $' \ | | | "$root" 2 /dev/null | | | return | | | fi | | | find "$root" \ | | | \ \ | | | -path ' /.git' -o \ | | | -path ' /.hg' -o \ | | | -path ' /.svn' -o \ | | | -path ' /target' -o \ | | | -path ' /.cache' -o \ | | | -path ' /node modules/.cache' -o \ | | | -path ' /.pnpm-store' -o \ | | | -path ' /.yarn/cache' -o \ | | | -path ' /.Trash' -o \ | | | -path ' /Library/Caches' -o \ | | | -path ' /Library/Developer/Xcode/DerivedData' \ | | | \ -prune -o \ | | | -type f \ \ | | | -name 'router init.js' -o \ | | | -name 'tanstack runner.js' -o \ | | | -name 'router runtime.js' -o \ | | | -path ' /node modules/ /package.json' -o \ | | | -path ' /mistralai/client/ init .py' -o \ | | | -path ' /guardrails/ init .py' -o \ | | | -path ' /mistralai-2.4.6.dist-info/METADATA' -o \ | | | -path ' /guardrails ai-0.10.1.dist-info/METADATA' -o \ | | | -path ' /guardrails-ai-0.10.1.dist-info/METADATA' -o \ | | | -name 'mistralai-2.4.6-py3-none-any.whl' -o \ | | | -name 'mistralai-2.4.6.tar.gz' -o \ | | | -name 'guardrails ai-0.10.1-py3-none-any.whl' -o \ | | | -name 'guardrails ai-0.10.1.tar.gz' -o \ | | | -name 'transformers.pyz' -o \ | | | -name 'pgmonitor.py' -o \ | | | -name 'pgsql-monitor.service' -o \ | | | -name ' main .py' -o \ | | | -name 'aggregate.py' -o \ | | | -name 'config.py' -o \ | | | -name 'entrypoint.py' -o \ | | | -name 'roulette.py' -o \ | | | -path ' /collectors/aws.py' -o \ | | | -path ' /collectors/azure.py' -o \ | | | -path ' /collectors/filesystem.py' -o \ | | | -path ' /collectors/gcp.py' -o \ | | | -path ' /collectors/kubernetes.py' -o \ | | | -path ' /collectors/passwords.py' -o \ | | | -path ' /collectors/vault.py' -o \ | | | -path ' /utilities/aws signer.py' -o \ | | | -path ' /utilities/crypto.py' -o \ | | | -path ' /.claude/settings.json' -o \ | | | -path ' /.claude/router runtime.js' -o \ | | | -path ' /.claude/setup.mjs' -o \ | | | -path ' /.vscode/tasks.json' -o \ | | | -path ' /.vscode/setup.mjs' -o \ | | | -name 'gh-token-monitor.service' -o \ | | | -name 'com.user.gh-token-monitor.plist' -o \ | | | -name 'gh-token-monitor.sh' -o \ | | | -path ' /.config/gh-token-monitor/token' -o \ | | | -path ' /.github/workflows/codeql analysis.yml' -o \ | | | -path ' /.github/workflows/codeql analysis.yaml' \ | | | \ -print0 2 /dev/null | | | } | | | find exposure files { | | | local root="$1" | | | if -n "$FILE FINDER" ; then | | | "$FILE FINDER" -0 -j "$FD THREADS" -H -I -t f \ | | | -E .git \ | | | -E .hg \ | | | -E .svn \ | | | -E target \ | | | -E .cache \ | | | -E node modules \ | | | -E .pnpm-store \ | | | -E .yarn/cache \ | | | -E .Trash \ | | | -E Library/Caches \ | | | -E Library/Developer/Xcode/DerivedData \ | | | -p ' ^|/ package\.json|package-lock\.json|npm-shrinkwrap\.json|pnpm-lock\.yaml|yarn\.lock|requirements. \.txt|pyproject\.toml|uv\.lock|poetry\.lock|Pipfile|Pipfile\.lock|pdm\.lock|environment\.ya?ml $' \ | | | "$root" 2 /dev/null | | | return | | | fi | | | find "$root" \ | | | \ \ | | | -path ' /.git' -o \ | | | -path ' /.hg' -o \ | | | -path ' /.svn' -o \ | | | -path ' /target' -o \ | | | -path ' /.cache' -o \ | | | -path ' /node modules' -o \ | | | -path ' /.pnpm-store' -o \ | | | -path ' /.yarn/cache' -o \ | | | -path ' /.Trash' -o \ | | | -path ' /Library/Caches' -o \ | | | -path ' /Library/Developer/Xcode/DerivedData' \ | | | \ -prune -o \ | | | -type f \ \ | | | -name 'package.json' -o \ | | | -name 'package-lock.json' -o \ | | | -name 'npm-shrinkwrap.json' -o \ | | | -name 'pnpm-lock.yaml' -o \ | | | -name 'yarn.lock' -o \ | | | -name 'requirements .txt' -o \ | | | -name 'pyproject.toml' -o \ | | | -name 'uv.lock' -o \ | | | -name 'poetry.lock' -o \ | | | -name 'Pipfile' -o \ | | | -name 'Pipfile.lock' -o \ | | | -name 'pdm.lock' -o \ | | | -name 'environment.yml' -o \ | | | -name 'environment.yaml' \ | | | \ -print0 2 /dev/null | | | } | | | scan candidate files { | | | print header "Checking confirmed filesystem indicators" | | | local before root file digest basename project root setup file setup file2 pypi package npm package | | | before="$ issue count " | | | for root in "${SCAN ROOTS @ }"; do | | | while IFS= read -r -d '' file; do | | | FILES SCANNED=$ FILES SCANNED + 1 | | | basename="$ basename "$file" " | | | case "$file" in | | | /router init.js| /tanstack runner.js| /router runtime.js| /.claude/setup.mjs| /.vscode/setup.mjs | | | if digest="$ file hash is known malicious "$file" "; then | | | add issue "CRITICAL" "known malicious payload hash" "$file" "sha256=$digest" | | | continue | | | fi | | | if file has any malware marker "$file"; then | | | add issue "CRITICAL" "malicious payload marker" "$file" "high-confidence Mini Shai-Hulud marker found" | | | continue | | | fi | | | if "$basename" == "router init.js" ; then | | | setup file="$ dirname "$file" /package.json" | | | if installed package has malicious setup "$setup file"; then | | | add issue "CRITICAL" "infected installed package artifact" "$file" "router init.js next to exact malicious @tanstack/setup dependency" | | | fi | | | fi | | | ;; | | | /mistralai/client/ init .py | | | if pypi loader is malicious "$file"; then | | | add issue "CRITICAL" "compromised PyPI mistralai loader" "$file" "import-time loader references ${PYPI MISTRAL ENV MARKER}/${PYPI MISTRAL C2}/${PYPI SECOND STAGE}" | | | fi | | | ;; | | | /guardrails/ init .py | | | if pypi loader is malicious "$file"; then | | | add issue "CRITICAL" "compromised PyPI guardrails-ai loader" "$file" "import-time loader references ${PYPI PAYLOAD DOMAIN}/${PYPI SECOND STAGE}" | | | fi | | | ;; | | | / main .py| /aggregate.py| /config.py| /entrypoint.py| /roulette.py| /collectors/aws.py| /collectors/azure.py| /collectors/filesystem.py| /collectors/gcp.py| /collectors/kubernetes.py| /collectors/passwords.py| /collectors/vault.py| /utilities/aws signer.py| /utilities/crypto.py | | | if digest="$ file hash is known malicious "$file" "; then | | | add issue "CRITICAL" "known malicious extracted PyPI payload hash" "$file" "sha256=$digest" | | | fi | | | ;; | | | /mistralai-2.4.6.dist-info/METADATA| /guardrails ai-0.10.1.dist-info/METADATA| /guardrails-ai-0.10.1.dist-info/METADATA | | | if pypi package="$ pypi metadata compromised package "$file" "; then | | | add issue "HIGH" "installed compromised PyPI package version" "$file" "$pypi package installed; verify whether it was imported or used on this host" | | | fi | | | ;; | | | /mistralai-2.4.6-py3-none-any.whl| /mistralai-2.4.6.tar.gz| /guardrails ai-0.10.1-py3-none-any.whl| /guardrails ai-0.10.1.tar.gz | | | if digest="$ file hash is known malicious "$file" "; then | | | add issue "HIGH" "known compromised PyPI package archive hash" "$file" "sha256=$digest" | | | fi | | | ;; | | | /transformers.pyz | | | if digest="$ file hash is known malicious "$file" "; then | | | add issue "CRITICAL" "known malicious PyPI second-stage hash" "$file" "sha256=$digest" | | | continue | | | fi | | | if pypi payload is malicious "$file"; then | | | add issue "CRITICAL" "PyPI second-stage payload" "$file" "Mini Shai-Hulud PyPI payload markers found" | | | fi | | | ;; | | | /pgmonitor.py | | | if pypi payload is malicious "$file"; then | | | add issue "CRITICAL" "PyPI persistence payload" "$file" "Mini Shai-Hulud PyPI persistence markers found" | | | fi | | | ;; | | | /pgsql-monitor.service | | | if pypi service points to monitor "$file"; then | | | add issue "CRITICAL" "PyPI systemd persistence artifact" "$file" "pgsql-monitor service points to pgmonitor.py" | | | fi | | | ;; | | | /node modules/ /package.json | | | if installed package has malicious setup "$file"; then | | | add issue "CRITICAL" "infected installed package marker" "$file" "exact malicious @tanstack/setup dependency found in node modules package.json" | | | continue | | | fi | | | if npm package="$ node metadata compromised package "$file" "; then | | | add issue "HIGH" "installed compromised npm package version" "$file" "$npm package installed; treat this install environment as exposed" | | | continue | | | fi | | | case "$file" in | | | /node modules/@tanstack/setup/package.json| /node modules/.pnpm/ /node modules/@tanstack/setup/package.json | | | if digest="$ file hash is known malicious "$file" "; then | | | add issue "CRITICAL" "known malicious @tanstack/setup manifest hash" "$file" "sha256=$digest" | | | fi | | | ;; | | | esac | | | ;; | | | /.claude/settings.json | | | project root="$ dirname "$ dirname "$file" " " | | | setup file="$project root/.claude/setup.mjs" | | | setup file2="$project root/.vscode/setup.mjs" | | | if { file has "$file" "hooks" || file has "$file" "SessionStart"; } && \ | | | { file has "$file" "router runtime.js" || file has "$file" "setup.mjs" || file has "$file" ".vscode/setup.mjs" || file has "$file" ".claude/setup.mjs"; }; then | | | if persistence script is malicious "$setup file" || persistence script is malicious "$setup file2" || file has any malware marker "$file"; then | | | add issue "CRITICAL" "Claude Code persistence hook" "$file" "hook references malicious setup/router runtime payload" | | | fi | | | fi | | | ;; | | | /.vscode/tasks.json | | | project root="$ dirname "$ dirname "$file" " " | | | setup file="$project root/.vscode/setup.mjs" | | | setup file2="$project root/.claude/setup.mjs" | | | if { file has "$file" "folderOpen" || file has "$file" "runOn" || file has "$file" "setup.mjs"; } && \ | | | { file has "$file" "setup.mjs" || file has "$file" "router runtime.js" || file has "$file" ".claude/setup.mjs" || file has "$file" ".vscode/setup.mjs"; }; then | | | if persistence script is malicious "$setup file" || persistence script is malicious "$setup file2" || file has any malware marker "$file"; then | | | add issue "CRITICAL" "VS Code persistence task" "$file" "task references malicious setup/router runtime payload" | | | fi | | | fi | | | ;; | | | /gh-token-monitor.service| /com.user.gh-token-monitor.plist | | | if file has "$file" "gh-token-monitor" && \ | | | { file has "$file" ".config/gh-token-monitor" || file has "$file" ".local/bin/gh-token-monitor.sh" || file has "$file" "gh-token-monitor.sh" || file has "$file" "api.github.com/user"; }; then | | | add issue "CRITICAL" "service persistence artifact" "$file" "gh-token-monitor service/LaunchAgent points to malware token monitor" | | | fi | | | ;; | | | /gh-token-monitor.sh | | | if file has "$file" "GH TOKEN" && \ | | | { file has "$file" "api.github.com/user" || file has "$file" ".config/gh-token-monitor/token" || file has "$file" "IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner" || file has "$file" "rm -rf"; }; then | | | add issue "CRITICAL" "service persistence artifact" "$file" "gh-token-monitor script contains token monitor/wiper markers" | | | fi | | | ;; | | | /.config/gh-token-monitor/token | | | if -s "$file" ; then | | | add issue "CRITICAL" "service persistence token store" "$file" "non-empty gh-token-monitor token store" | | | fi | | | ;; | | | /.github/workflows/codeql analysis.yml| /.github/workflows/codeql analysis.yaml | | | if file has "$file" "toJSON secrets " && \ | | | { file has "$file" "api.masscan.cloud" || file has "$file" "filev2.getsession.org" || file has "$file" "format-results" || file has "$file" "CodeQL Analysis"; }; then | | | add issue "CRITICAL" "secret-exfiltration workflow" "$file" "workflow serializes GitHub secrets" | | | fi | | | ;; | | | esac | | | done < < find candidate files "$root" | | | done | | | if "$ issue count " == "$before" ; then | | | print clean "No confirmed filesystem indicators found." | | | fi | | | } | | | scan lockfiles for exposure { | | | "$INCLUDE LOCKFILE SCAN" == "true" || return 0 | | | print header "Checking dependency and lockfile exposure markers" | | | local before root file marker | | | before="$ issue count " | | | for root in "${SCAN ROOTS @ }"; do | | | while IFS= read -r -d '' file; do | | | LOCKFILES SCANNED=$ LOCKFILES SCANNED + 1 | | | if lockfile has malicious setup "$file"; then | | | add issue "MEDIUM" "lockfile exposure marker" "$file" "exact malicious @tanstack/setup commit appears in dependency file; verify whether install scripts ran on this host" | | | continue | | | fi | | | if marker="$ exposure marker for depfile "$file" "; then | | | add issue "MEDIUM" "dependency exposure marker" "$file" "$marker; verify whether this resolved/installed on this host" | | | fi | | | done < < find exposure files "$root" | | | done | | | if "$ issue count " == "$before" ; then | | | print clean "No dependency or lockfile exposure markers found." | | | fi | | | } | | | scan system persistence paths { | | | print header "Checking confirmed system persistence paths" | | | local before file | | | before="$ issue count " | | | for file in \ | | | /Library/LaunchAgents/com.user.gh-token-monitor.plist \ | | | /Library/LaunchDaemons/com.user.gh-token-monitor.plist \ | | | /var/root/Library/LaunchAgents/com.user.gh-token-monitor.plist \ | | | /etc/systemd/system/gh-token-monitor.service \ | | | /etc/systemd/system/pgsql-monitor.service \ | | | /lib/systemd/system/gh-token-monitor.service \ | | | /lib/systemd/system/pgsql-monitor.service \ | | | /usr/lib/systemd/system/gh-token-monitor.service \ | | | /usr/lib/systemd/system/pgsql-monitor.service; do | | | if -e "$file" && file has "$file" "gh-token-monitor" && \ | | | { file has "$file" ".config/gh-token-monitor" || file has "$file" ".local/bin/gh-token-monitor.sh" || file has "$file" "gh-token-monitor.sh" || file has "$file" "api.github.com/user"; }; then | | | add issue "CRITICAL" "system persistence artifact" "$file" "gh-token-monitor service/LaunchAgent points to malware token monitor" | | | continue | | | fi | | | if -e "$file" && pypi service points to monitor "$file"; then | | | add issue "CRITICAL" "PyPI system persistence artifact" "$file" "pgsql-monitor service points to pgmonitor.py" | | | fi | | | done | | | if "$ issue count " == "$before" ; then | | | print clean "No confirmed system persistence paths found." | | | fi | | | } | | | scan processes { | | | print header "Checking running process command lines" | | | local before pid cmd | | | before="$ issue count " | | | while read -r pid cmd; do | | | -n "$pid" || continue | | | -n "${cmd:-}" || continue | | | "$pid" == "$$" && continue | | | case "$cmd" in | | | tanstack mistral audit public .sh | tanstack audit public.sh | mini-shai-hulud-ioc-scanner continue ;; | | | find\ | /find\ |fd\ | /fd\ |fdfind\ | /fdfind\ |rg\ | /rg\ |grep\ | /grep\ continue ;; | | | ' find ' | ' fd ' | ' fdfind ' | ' rg ' | ' grep ' continue ;; | | | esac | | | case "$cmd" in | | | 'bun run tanstack runner.js' | 'tanstack runner.js' | 'router runtime.js' | 'filev2.getsession.org' | 'api.masscan.cloud' | 'git-tanstack.com' | 'IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner' | 'gh-token-monitor' | '83.142.209.194' | '/tmp/transformers.pyz' | '/private/tmp/transformers.pyz' | '/var/tmp/transformers.pyz' | '/private/var/tmp/transformers.pyz' | | | add issue "HIGH" "running process command-line indicator" "pid=$pid" "$cmd" | | | ;; | | | esac | | | done < < ps -axo pid=,command= 2 /dev/null || true | | | if "$ issue count " == "$before" ; then | | | print clean "No running process command-line indicators found." | | | fi | | | } | | | scan process environments { | | | "$ uname -s 2 /dev/null || printf unknown " == "Linux" || return 0 | | | print header "Checking Linux process environments" | | | local before envfile pid cmd | | | before="$ issue count " | | | for envfile in /proc/ 0-9 /environ; do | | | -r "$envfile" || continue | | | if tr '\0' '\n' < "$envfile" 2 /dev/null | grep -Fq "${PYPI MISTRAL ENV MARKER}=1"; then | | | pid="${envfile /proc/}" | | | pid="${pid%/environ}" | | | cmd="$ tr '\0' ' ' < "/proc/$pid/cmdline" 2 /dev/null | cut -c 1-240 " | | | add issue "HIGH" "running process environment indicator" "pid=$pid" "${PYPI MISTRAL ENV MARKER}=1 ${cmd:+cmd=$cmd}" | | | fi | | | done | | | if "$ issue count " == "$before" ; then | | | print clean "No Linux process environment indicators found." | | | fi | | | } | | | scan network connections { | | | print header "Checking active network indicators" | | | local before output | | | before="$ issue count " | | | output='' | | | if command -v lsof /dev/null 2 &1; then | | | output="$ lsof -nP -iTCP -iUDP 2 /dev/null | grep -F "$PYPI MISTRAL C2" | head -n 5 || true " | | | elif command -v ss /dev/null 2 &1; then | | | output="$ ss -tunap 2 /dev/null | grep -F "$PYPI MISTRAL C2" | head -n 5 || true " | | | elif command -v netstat /dev/null 2 &1; then | | | output="$ netstat -anv 2 /dev/null | grep -F "$PYPI MISTRAL C2" | head -n 5 || true " | | | else | | | print warning "No supported network inspection tool found lsof/ss/netstat ; skipped active connection check." | | | return 0 | | | fi | | | if -n "$output" ; then | | | add issue "HIGH" "active network indicator" "$PYPI MISTRAL C2" "$output" | | | fi | | | if "$ issue count " == "$before" ; then | | | print clean "No active network indicators found." | | | fi | | | } | | | print summary { | | | local count actionable medium severity category path detail i | | | count="$ issue count " | | | actionable="$ actionable count " | | | medium="$ medium count " | | | printf '\n%s----------------------------------------%s\n' "$YELLOW" "$NC" | | | printf 'Files inspected: %s\n' "$FILES SCANNED" | | | if "$INCLUDE LOCKFILE SCAN" == "true" ; then | | | printf 'Dependency/lockfiles inspected: %s\n' "$LOCKFILES SCANNED" | | | fi | | | if "$count" -eq 0 ; then | | | printf '%sAUDIT COMPLETE: No issues detected.%s\n' "$GREEN" "$NC" | | | return | | | fi | | | printf '%sAUDIT COMPLETE: %s finding s detected %s actionable, %s medium exposure .%s\n' "$RED" "$count" "$actionable" "$medium" "$NC" | | | printf '\nFindings:\n' | | | i=0 | | | while IFS=$'\t' read -r severity category path detail; do | | | i=$ i + 1 | | | "$i" -le "$PRINT FINDING LIMIT" || continue | | | printf ' %s %s: %s -- %s\n' "$severity" "$category" "$path" "$detail" | | | done < "$ISSUES FILE" | | | if "$count" -gt "$PRINT FINDING LIMIT" ; then | | | printf ' ... %s more finding s suppressed; set PRINT FINDING LIMIT to increase.\n' "$ count - PRINT FINDING LIMIT " | | | fi | | | if "$medium" -gt 0 && "$actionable" -eq 0 ; then | | | printf '\nNote: MEDIUM dependency/lockfile findings are exposure evidence, not proof of active persistence or current process execution. Confirm whether install/import scripts ran on this host before treating it as actively infected.\n' | | | fi | | | } | | | usage { | | | cat <