# Mini Shai-Hulud / TanStack + Mistral AI supply-chain audit script

> Source: <https://gist.github.com/beowolx2/a3ceeb18d1f1cec977d5cc6eaf41c96a>
> Published: 2026-05-12 13:55:14+00:00

| #!/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 <<USAGE | |
| Usage: $(basename "$0") [--exposure|--include-lockfiles] [--no-exposure] [DIR ...] | |
| Run without arguments to scan likely local locations. Pass one or more directories | |
| to scan only those paths. | |
| Environment: | |
| INCLUDE_LOCKFILE_SCAN=true Scan dependency and lockfiles for affected TanStack/Mistral/PyPI versions. Default: true. | |
| EXIT_ON_MEDIUM=true Exit non-zero for MEDIUM exposure-only findings. Default: false. | |
| FD_THREADS=1 Threads used by fd/fdfind when available. | |
| PRINT_FINDING_LIMIT=200 Max findings printed to stdout. | |
| USAGE | |
| } | |
| main() { | |
| local root | |
| while [[ "$#" -gt 0 ]]; do | |
| case "$1" in | |
| --help|-h) | |
| usage | |
| exit 0 | |
| ;; | |
| --include-lockfiles|--exposure) | |
| INCLUDE_LOCKFILE_SCAN=true | |
| shift | |
| ;; | |
| --no-exposure) | |
| INCLUDE_LOCKFILE_SCAN=false | |
| shift | |
| ;; | |
| --*) | |
| printf 'Unknown option: %s\n' "$1" >&2 | |
| usage >&2 | |
| exit 2 | |
| ;; | |
| *) | |
| add_scan_root "$1" | |
| shift | |
| ;; | |
| esac | |
| done | |
| [[ "${#SCAN_ROOTS[@]}" -eq 0 ]] && add_default_scan_roots | |
| if [[ "${#SCAN_ROOTS[@]}" -eq 0 ]]; then | |
| printf 'No valid scan roots provided.\n' >&2 | |
| exit 2 | |
| fi | |
| printf '%s----------------------------------------%s\n' "$YELLOW" "$NC" | |
| printf '%s Mini Shai-Hulud / TanStack + Mistral AI Audit%s\n' "$YELLOW" "$NC" | |
| printf '%s----------------------------------------%s\n' "$YELLOW" "$NC" | |
| printf 'Version: %s\n' "$SCRIPT_VERSION" | |
| if [[ -n "$FILE_FINDER" ]]; then | |
| printf 'File search: %s (threads=%s)\n' "$FILE_FINDER" "$FD_THREADS" | |
| else | |
| printf 'File search: find\n' | |
| fi | |
| printf 'Dependency/lockfile exposure scan: %s\n' "$INCLUDE_LOCKFILE_SCAN" | |
| printf 'Scan roots:\n' | |
| for root in "${SCAN_ROOTS[@]}"; do | |
| printf ' - %s\n' "$root" | |
| done | |
| scan_candidate_files | |
| scan_system_persistence_paths | |
| scan_processes | |
| scan_process_environments | |
| scan_network_connections | |
| scan_lockfiles_for_exposure | |
| print_summary | |
| if [[ "$(actionable_count)" -gt 0 ]]; then | |
| exit 1 | |
| fi | |
| if [[ "$EXIT_ON_MEDIUM" == "true" && "$(medium_count)" -gt 0 ]]; then | |
| exit 1 | |
| fi | |
| exit 0 | |
| } | |
| main "$@" |
