Last active
June 7, 2026 01:07
-
-
Save beowolx2/a3ceeb18d1f1cec977d5cc6eaf41c96a to your computer and use it in GitHub Desktop.
Mini Shai-Hulud / TanStack + Mistral AI supply-chain audit script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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 "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment