Created
May 12, 2026 04:52
-
-
Save nrajlekhak/84bda8dc75e9212c7699a79bb34fa4dc to your computer and use it in GitHub Desktop.
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 | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # detect-mini-shai-hulud.sh | |
| # | |
| # Scans every project under the CWD for npm packages compromised in the | |
| # "Mini Shai-Hulud" supply-chain attack (npm ecosystem, 2026). | |
| # | |
| # Compromised package list source: StepSecurity blog | |
| # https://www.stepsecurity.io/blog/mini-shai-hulud-is-back-a-self-spreading-supply-chain-attack-hits-the-npm-ecosystem | |
| # @tanstack/* patched versions cross-verified against GitHub Security Advisory: | |
| # https://github.com/TanStack/router/security/advisories/GHSA-g7cv-rxg3-hmpx | |
| # | |
| # Usage: | |
| # cd ~/projects | |
| # bash detect-mini-shai-hulud.sh | |
| # | |
| # Scans: package-lock.json, yarn.lock, pnpm-lock.yaml (skips node_modules) | |
| # Note: bun.lockb is binary — for Bun projects: bun pm ls | less | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| set -euo pipefail | |
| # ─── Compromised packages ──────────────────────────────────────────────────── | |
| # Format: "<package>|<bad_version_1>[,<bad_version_2>,...]|<fix_message>" | |
| # Add new entries here as advisories drop. | |
| VULN_LIST=( | |
| "@uipath/docsai-tool|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/packager-tool-apiworkflow|0.0.19|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/packager-tool-workflowcompiler-browser|0.0.34|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/packager-tool-functions|0.1.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/agent.sdk|0.0.18|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/filesystem|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/admin-tool|0.1.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/llmgw-tool|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@tanstack/arktype-adapter|1.166.12,1.166.15|upgrade to 1.166.16" | |
| "@tanstack/eslint-plugin-router|1.161.9,1.161.12|upgrade to 1.161.13" | |
| "@tanstack/eslint-plugin-start|0.0.4,0.0.7|upgrade to 0.0.8" | |
| "@tanstack/history|1.161.9,1.161.12|upgrade to 1.161.13" | |
| "@tanstack/nitro-v2-vite-plugin|1.154.12,1.154.15|upgrade to 1.154.16" | |
| "@tanstack/react-router|1.169.5,1.169.8|upgrade to 1.169.9" | |
| "@tanstack/react-router-devtools|1.166.16,1.166.19|upgrade to 1.166.20" | |
| "@tanstack/react-router-ssr-query|1.166.15,1.166.18|upgrade to 1.166.19" | |
| "@tanstack/react-start|1.167.68,1.167.71|upgrade to 1.167.72" | |
| "@tanstack/react-start-client|1.166.51,1.166.54|upgrade to 1.166.55" | |
| "@tanstack/react-start-rsc|0.0.47,0.0.50|upgrade to 0.0.51" | |
| "@tanstack/react-start-server|1.166.55,1.166.58|upgrade to 1.166.59" | |
| "@tanstack/router-cli|1.166.46,1.166.49|upgrade to 1.166.50" | |
| "@tanstack/router-core|1.169.5,1.169.8|upgrade to 1.169.9" | |
| "@tanstack/router-devtools|1.166.16,1.166.19|upgrade to 1.166.20" | |
| "@tanstack/router-devtools-core|1.167.6,1.167.9|upgrade to 1.167.10" | |
| "@tanstack/router-generator|1.166.45,1.166.48|upgrade to 1.166.49" | |
| "@tanstack/router-plugin|1.167.38,1.167.41|upgrade to 1.167.42" | |
| "@tanstack/router-ssr-query-core|1.168.3,1.168.6|upgrade to 1.168.7" | |
| "@tanstack/router-utils|1.161.11,1.161.14|upgrade to 1.161.15" | |
| "@tanstack/router-vite-plugin|1.166.53,1.166.56|upgrade to 1.166.57" | |
| "@tanstack/solid-router|1.169.5,1.169.8|upgrade to 1.169.9" | |
| "@tanstack/solid-router-devtools|1.166.16,1.166.19|upgrade to 1.166.20" | |
| "@tanstack/solid-router-ssr-query|1.166.15,1.166.18|upgrade to 1.166.19" | |
| "@tanstack/solid-start|1.167.65,1.167.68|upgrade to 1.167.69" | |
| "@tanstack/solid-start-client|1.166.50,1.166.53|upgrade to 1.166.54" | |
| "@tanstack/solid-start-server|1.166.54,1.166.57|upgrade to 1.166.58" | |
| "@tanstack/start-client-core|1.168.5,1.168.8|upgrade to 1.168.9" | |
| "@tanstack/start-fn-stubs|1.161.9,1.161.12|upgrade to 1.161.13" | |
| "@tanstack/start-plugin-core|1.169.23,1.169.26|upgrade to 1.169.27" | |
| "@tanstack/start-server-core|1.167.33,1.167.36|upgrade to 1.167.37" | |
| "@tanstack/start-static-server-functions|1.166.44,1.166.47|upgrade to 1.166.48" | |
| "@tanstack/start-storage-context|1.166.38,1.166.41|upgrade to 1.166.42" | |
| "@tanstack/valibot-adapter|1.166.12,1.166.15|upgrade to 1.166.16" | |
| "@tanstack/virtual-file-routes|1.161.10,1.161.13|upgrade to 1.161.14" | |
| "@tanstack/vue-router|1.169.5,1.169.8|upgrade to 1.169.9" | |
| "@tanstack/vue-router-devtools|1.166.16,1.166.19|upgrade to 1.166.20" | |
| "@tanstack/vue-router-ssr-query|1.166.15,1.166.18|upgrade to 1.166.19" | |
| "@tanstack/vue-start|1.167.61,1.167.64|upgrade to 1.167.65" | |
| "@tanstack/vue-start-client|1.166.46,1.166.49|upgrade to 1.166.50" | |
| "@tanstack/vue-start-server|1.166.50,1.166.53|upgrade to 1.166.54" | |
| "@tanstack/zod-adapter|1.166.12,1.166.15|upgrade to 1.166.16" | |
| "@draftauth/client|0.2.1,0.2.2|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@draftauth/core|0.13.1,0.13.2|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@draftlab/auth|0.24.1,0.24.2|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@draftlab/auth-router|0.5.1,0.5.2|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@draftlab/db|0.16.1,0.16.2|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@taskflow-corp/cli|0.1.24,0.1.25,0.1.26,0.1.27,0.1.28,0.1.29|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@tolka/cli|1.0.2,1.0.3,1.0.4,1.0.6|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/access-policy-sdk|0.3.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/access-policy-tool|0.3.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/agent-sdk|1.0.2|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/agent-tool|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/aops-policy-tool|0.3.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/ap-chat|1.5.7|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/api-workflow-tool|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/apollo-core|5.9.2|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/apollo-react|4.24.5|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/apollo-wind|2.16.2|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/auth|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/case-tool|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/cli|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/codedagent-tool|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/codedagents-tool|0.1.12|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/codedapp-tool|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/common|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/context-grounding-tool|0.1.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/data-fabric-tool|1.0.2|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/flow-tool|1.0.2|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/functions-tool|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/gov-tool|0.3.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/identity-tool|0.1.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/insights-sdk|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/insights-tool|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/integrationservice-sdk|1.0.2|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/integrationservice-tool|1.0.2|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/maestro-sdk|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/maestro-tool|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/orchestrator-tool|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/packager-tool-bpmn|0.0.9|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/packager-tool-case|0.0.9|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/packager-tool-connector|0.0.19|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/packager-tool-flow|0.0.19|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/packager-tool-webapp|1.0.6|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/packager-tool-workflowcompiler|0.0.16|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/platform-tool|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/project-packager|1.1.16|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/resource-tool|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/resourcecatalog-tool|0.1.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/resources-tool|0.1.11|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/robot|1.3.4|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/rpa-legacy-tool|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/rpa-tool|0.9.5|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/solution-packager|0.0.35|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/solution-tool|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/solutionpackager-sdk|1.0.11|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/solutionpackager-tool-core|0.0.34|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/tasks-tool|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/telemetry|0.0.7|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/test-manager-tool|1.0.2|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/tool-workflowcompiler|0.0.12|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/traces-tool|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/ui-widgets-multi-file-upload|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/uipath-python-bridge|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/vertical-solutions-tool|1.0.1|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/vss|0.1.6|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@uipath/widget.sdk|1.2.3|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "safe-action|0.8.3,0.8.4|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@supersurkhet/cli|0.0.2,0.0.3,0.0.4,0.0.5,0.0.6,0.0.7|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@supersurkhet/sdk|0.0.2,0.0.3,0.0.4,0.0.5,0.0.6,0.0.7|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "cmux-agent-mcp|0.1.3,0.1.4,0.1.5,0.1.6,0.1.7,0.1.8|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "git-git-git|1.0.8,1.0.9,1.0.10,1.0.12|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "git-branch-selector|1.3.3,1.3.4,1.3.5,1.3.7|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "nextmove-mcp|0.1.3,0.1.4,0.1.5,0.1.7|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@beproduct/nestjs-auth|0.1.2,0.1.3,0.1.4,0.1.5,0.1.6,0.1.7,0.1.8,0.1.9,0.1.10,0.1.11,0.1.12,0.1.13,0.1.14,0.1.15,0.1.16,0.1.17,0.1.19|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@dirigible-ai/sdk|0.6.2,0.6.3|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@ml-toolkit-ts/preprocessing|1.0.2,1.0.3|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@ml-toolkit-ts/xgboost|1.0.3,1.0.4|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "agentwork-cli|0.1.4,0.1.5|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "ml-toolkit-ts|1.0.4,1.0.5|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/airport-data|0.7.4,0.7.5,0.7.7|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/airports|0.6.2,0.6.3,0.6.5|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/airspace|0.8.1,0.8.2,0.8.4|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/airspace-data|0.5.3,0.5.4,0.5.6|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/airway-data|0.5.4,0.5.5,0.5.7|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/airways|0.4.2,0.4.3,0.4.5|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/fix-data|0.6.4,0.6.5,0.6.7|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/fixes|0.3.2,0.3.3,0.3.5|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/flight-math|0.5.4,0.5.5,0.5.7|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/flightplan|0.5.2,0.5.3,0.5.5|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/geo|0.4.4,0.4.5,0.4.7|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/icao-registry|0.5.2,0.5.3,0.5.5|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/icao-registry-data|0.8.4,0.8.5,0.8.7|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/mcp|0.9.1,0.9.2,0.9.4|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/navaid-data|0.6.4,0.6.5,0.6.7|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/navaids|0.4.2,0.4.3,0.4.5|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/notams|0.3.6,0.3.7,0.3.9|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/procedure-data|0.7.3,0.7.4,0.7.6|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/procedures|0.5.2,0.5.3,0.5.5|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/types|0.8.1,0.8.2,0.8.4|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/units|0.4.3,0.4.4,0.4.6|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@squawk/weather|0.5.6,0.5.7,0.5.9|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "wot-api|0.8.1,0.8.2,0.8.4|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "cross-stitch|1.1.3,1.1.4,1.1.6|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "ts-dna|3.0.1,3.0.2,3.0.4|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@tallyui/components|1.0.1,1.0.2,1.0.3|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@tallyui/connector-medusa|1.0.1,1.0.2,1.0.3|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@tallyui/connector-shopify|1.0.1,1.0.2,1.0.3|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@tallyui/connector-vendure|1.0.1,1.0.2,1.0.3|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@tallyui/connector-woocommerce|1.0.1,1.0.2,1.0.3|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@tallyui/core|0.2.1,0.2.2,0.2.3|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@tallyui/database|1.0.1,1.0.2,1.0.3|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@tallyui/pos|0.1.1,0.1.2,0.1.3|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@tallyui/storage-sqlite|0.2.1,0.2.2,0.2.3|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@tallyui/theme|0.2.1,0.2.2,0.2.3|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@mesadev/rest|0.28.3|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@mesadev/saguaro|0.4.22|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@mesadev/sdk|0.28.3|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@mistralai/mistralai|2.2.3,2.2.4|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@mistralai/mistralai-azure|1.7.2,1.7.3|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| "@mistralai/mistralai-gcp|1.7.2,1.7.3|remove/replace — no known clean patched version; rotate any creds touched at install" | |
| ) | |
| # ───────────────────────────────────────────────────────────────────────────── | |
| # Serialize VULN_LIST to JSON for the per-lockfile Python parser | |
| VULN_RAW=$(printf '%s\n' "${VULN_LIST[@]}") | |
| export VULN_RAW | |
| VULN_JSON=$(python3 -c ' | |
| import json, os | |
| out = {} | |
| for line in os.environ["VULN_RAW"].strip().split("\n"): | |
| pkg, bad_csv, fix = line.split("|", 2) | |
| out[pkg] = {"bad": bad_csv.split(","), "fix": fix} | |
| print(json.dumps(out)) | |
| ') | |
| export VULN_JSON | |
| NUM_PKGS=${#VULN_LIST[@]} | |
| LOCKFILES=() | |
| while IFS= read -r line; do | |
| LOCKFILES+=("$line") | |
| done < <(find . \( -name package-lock.json -o -name yarn.lock -o -name pnpm-lock.yaml \) -not -path '*/node_modules/*' 2>/dev/null | sort) | |
| if [[ ${#LOCKFILES[@]} -eq 0 ]]; then | |
| echo "No lock files found under $(pwd)" | |
| exit 0 | |
| fi | |
| echo "Scanning ${#LOCKFILES[@]} lock file(s) for $NUM_PKGS compromised packages (Mini Shai-Hulud campaign)..." | |
| echo | |
| TOTAL_HITS=0 | |
| AFFECTED_PROJECTS=() | |
| for lock in "${LOCKFILES[@]}"; do | |
| project=$(dirname "$lock" | sed 's|^\./||') | |
| base=$(basename "$lock") | |
| hits=$(LOCK_PATH="$lock" LOCK_TYPE="$base" python3 - <<'PY' | |
| import json, os, re, sys | |
| vuln = json.loads(os.environ['VULN_JSON']) | |
| path = os.environ['LOCK_PATH'] | |
| kind = os.environ['LOCK_TYPE'] | |
| found = [] # list of (pkg, version, fix) | |
| def check(pkg, ver): | |
| if pkg in vuln and ver in vuln[pkg]['bad']: | |
| found.append((pkg, ver, vuln[pkg]['fix'])) | |
| # Pre-built alternation of all watched package names for fast scanning | |
| pkg_re_alt = '|'.join(re.escape(p) for p in vuln.keys()) | |
| try: | |
| with open(path, 'r', encoding='utf-8', errors='replace') as f: | |
| content = f.read() | |
| except Exception as e: | |
| print(f"ERROR_READING:{e}", file=sys.stderr) | |
| sys.exit(0) | |
| if kind == 'package-lock.json': | |
| try: | |
| d = json.loads(content) | |
| except json.JSONDecodeError: | |
| sys.exit(0) | |
| # npm v7+ "packages" map | |
| pkg_suffix_re = re.compile(r'node_modules/(' + pkg_re_alt + r')$') | |
| for key, meta in (d.get('packages') or {}).items(): | |
| m = pkg_suffix_re.search(key) | |
| if m: | |
| check(m.group(1), meta.get('version', '')) | |
| # legacy "dependencies" tree (npm v6) | |
| def walk(deps): | |
| for name, meta in (deps or {}).items(): | |
| if name in vuln: | |
| check(name, meta.get('version', '')) | |
| walk(meta.get('dependencies')) | |
| walk(d.get('dependencies')) | |
| elif kind == 'yarn.lock': | |
| # Yarn v1 / Berry blocks | |
| blocks = re.split(r'\n(?=[^\s])', content) | |
| header_pkg_re = re.compile(r'"?(' + pkg_re_alt + r')@') | |
| ver_line_re = re.compile(r'^\s+version\s+"([^"]+)"', re.M) | |
| for block in blocks: | |
| first = block.split('\n', 1)[0] | |
| pkgs_in_header = header_pkg_re.findall(first) | |
| if not pkgs_in_header: | |
| continue | |
| ver_match = ver_line_re.search(block) | |
| if not ver_match: | |
| continue | |
| version = ver_match.group(1) | |
| for pkg in set(pkgs_in_header): | |
| check(pkg, version) | |
| elif kind == 'pnpm-lock.yaml': | |
| # pnpm: every package@version reference is captured | |
| occ_re = re.compile(r'(' + pkg_re_alt + r')@([0-9][0-9a-zA-Z.+-]*)') | |
| for m in occ_re.finditer(content): | |
| check(m.group(1), m.group(2)) | |
| seen = set() | |
| for pkg, ver, fix in found: | |
| key = (pkg, ver) | |
| if key in seen: continue | |
| seen.add(key) | |
| print(f"{pkg}|{ver}|{fix}") | |
| PY | |
| ) | |
| if [[ -n "$hits" ]]; then | |
| AFFECTED_PROJECTS+=("$project") | |
| echo "❌ $project ($base)" | |
| while IFS='|' read -r pkg ver fix; do | |
| echo " $pkg@$ver → $fix" | |
| TOTAL_HITS=$((TOTAL_HITS + 1)) | |
| done <<< "$hits" | |
| echo | |
| fi | |
| done | |
| echo "──────────────────────────────────────────────" | |
| if [[ $TOTAL_HITS -eq 0 ]]; then | |
| echo "✅ No compromised packages found in ${#LOCKFILES[@]} lock file(s)." | |
| else | |
| uniq_projects=$(printf '%s\n' "${AFFECTED_PROJECTS[@]}" | sort -u | wc -l | tr -d ' ') | |
| echo "❌ Found $TOTAL_HITS compromised dependency entries across $uniq_projects project(s)." | |
| echo | |
| echo "Remediation:" | |
| echo " 1. Upgrade/remove each affected package per the message above." | |
| echo " 2. Delete node_modules and the lock file, then reinstall." | |
| echo " 3. Rotate any secrets accessible to your dev/build/CI environment" | |
| echo " (the worm exfiltrates env vars, .env files, cloud/GitHub/npm tokens, SSH keys)." | |
| echo " 4. Audit CI logs for outbound calls to attacker-controlled GitHub repos." | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Command to look through a folder recursively through all lock files and package files. Shows affected and non affected versions