Last active
September 9, 2025 09:20
-
-
Save tonidy/e32479e2b2be8e26b8026a5f62665d9a to your computer and use it in GitHub Desktop.
Script to Check Vulnerable Packages
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 | |
| set -euo pipefail | |
| # Story: https://jdstaerk.substack.com/p/we-just-found-malicious-code-in-the | |
| # Enhanced from: | |
| # https://github.com/AndrewMohawk/RandomScripts/blob/main/scan_for_deps_qix-2025-08-09.sh | |
| # ===== Vulnerable package list ===== | |
| VULNS="[email protected] | |
| [email protected] | |
| [email protected] | |
| [email protected] | |
| [email protected] | |
| [email protected] | |
| [email protected] | |
| [email protected] | |
| [email protected] | |
| [email protected] | |
| [email protected] | |
| [email protected] | |
| [email protected] | |
| [email protected] | |
| [email protected] | |
| [email protected] | |
| [email protected] | |
| [email protected] | |
| [email protected]" | |
| # ===== Arguments ===== | |
| SCAN_ROOT="." | |
| USE_LOCKFILE=0 | |
| for arg in "$@"; do | |
| case "$arg" in | |
| --lockfile) USE_LOCKFILE=1 ;; | |
| *) SCAN_ROOT="$arg" ;; | |
| esac | |
| done | |
| echo "π Scanning in: $SCAN_ROOT (lockfile fallback: $([[ $USE_LOCKFILE -eq 1 ]] && echo ON || echo OFF))" | |
| echo "================================================" | |
| TOTAL_AFFECTED=0 | |
| TOTAL_SCANNED=0 | |
| # === Helpers === | |
| match_vulns() { grep -Fxf <(printf '%s\n' "$VULNS") || true; } | |
| # ---- Parsers: lockfiles ---- | |
| # npm package-lock.json (v1 & v2+) | |
| scan_package_lock() { | |
| jq -r ' | |
| if has("packages") then | |
| .packages | |
| | to_entries[] | |
| | select(.value.version != null) | |
| | "\(.value.name // (.key|sub("^node_modules/";"")))@\(.value.version)" | |
| else | |
| def walkdeps($k): | |
| to_entries[] | |
| | "\(.key)@\(.value.version)", | |
| ( .value.dependencies? // {} | walkdeps(.key) ); | |
| (.dependencies // {}) | walkdeps("") | |
| end | |
| ' "$1" 2>/dev/null | sort -u | |
| } | |
| # pnpm pnpm-lock.yaml | |
| scan_pnpm_lock() { | |
| awk ' | |
| $0 ~ /^packages:/ {inpk=1; next} | |
| inpk && $0 ~ /^[[:space:]]*\/.*\/[0-9][^:]*:/ { | |
| line=$0 | |
| gsub(/^[[:space:]]*\/|:.*/,"",line) | |
| n=split(line, a, "/") | |
| if (n>=2) { | |
| ver=a[n]; a[n]="" | |
| name=line; sub("/"ver"$","",name) | |
| print name"@"ver | |
| } | |
| } | |
| ' "$1" | sort -u | |
| } | |
| # yarn yarn.lock (v1 & simple berry) | |
| scan_yarn_lock() { | |
| awk ' | |
| /^[^[:space:]].*:?$/ { key=$0; sub(/:$/,"",key); inkey=1; next } | |
| inkey && $1=="version" { | |
| ver=$2; gsub(/"/,"",ver) | |
| name=key; gsub(/^"/,"",name); gsub(/"$/,"",name) | |
| n=split(name, parts, /, /) | |
| for (i=1; i<=n; i++) { | |
| k=parts[i] | |
| lastat=0 | |
| for (j=1; j<=length(k); j++) if (substr(k,j,1)=="@") lastat=j | |
| if (lastat>1) { pkg=substr(k,1,lastat-1); print pkg"@"ver } | |
| } | |
| inkey=0 | |
| } | |
| ' "$1" | sort -u | |
| } | |
| # deno deno.lock (v2/v3); extract npm entries | |
| scan_deno_lock() { | |
| jq -r ' | |
| if has("packages") then | |
| (.packages.npm // {}) | to_entries[]? | .key | |
| elif has("npm") then | |
| (.npm.packages // .npm) | to_entries[]? | .key | |
| else empty end | |
| ' "$1" 2>/dev/null \ | |
| | sed -E 's/^npm://g' \ | |
| | sort -u | |
| } | |
| # bun bun.lock (plaintext, v1.1+) | |
| scan_bun_lock() { | |
| local lock="$1" | |
| # Format example: | |
| # [packages] | |
| # [email protected] = { ... } | |
| awk ' | |
| /^\[packages\]/ {inpk=1; next} | |
| inpk && /^[^[:space:]].* =/ { | |
| key=$1 | |
| gsub(/=.*/,"",key) | |
| gsub(/"/,"",key) | |
| print key | |
| } | |
| ' "$lock" | sort -u | |
| } | |
| # bun bun.lockb (legacy binary format) | |
| scan_bun_lockb() { | |
| local dir="$1" | |
| if ! command -v bun >/dev/null 2>&1; then | |
| echo "(bun not installed; skipping bun.lockb)" >&2 | |
| return 0 | |
| fi | |
| (cd "$dir" && bun pm ls --json 2>/dev/null || true) \ | |
| | jq -r '.. | objects | select(has("name") and has("version")) | "\(.name)@\(.version)"' \ | |
| | sort -u | |
| } | |
| # === Main loop === | |
| while IFS= read -r pkg; do | |
| dir="$(dirname "$pkg")" | |
| TOTAL_SCANNED=$((TOTAL_SCANNED+1)) | |
| echo "π Project: $dir" | |
| if [[ -d "$dir/node_modules" ]]; then | |
| installed=$( | |
| (cd "$dir" && npm ls --all --depth=Infinity --json 2>/dev/null || true) \ | |
| | jq -r '.. | objects | select(has("name") and has("version")) | "\(.name)@\(.version)"' \ | |
| | sort -u | |
| ) | |
| hits=$(printf '%s\n' "$installed" | match_vulns) | |
| if [[ -n "$hits" ]]; then | |
| echo " β Vulnerable (installed):" | |
| printf '%s\n' "$hits" | sed 's/^/ - /' | |
| TOTAL_AFFECTED=$((TOTAL_AFFECTED+1)) | |
| else | |
| echo " β Safe (installed)" | |
| fi | |
| else | |
| echo " β No node_modules" | |
| if [[ $USE_LOCKFILE -eq 1 ]]; then | |
| has_any=0 | |
| if [[ -f "$dir/package-lock.json" ]]; then | |
| has_any=1 | |
| lhits=$(scan_package_lock "$dir/package-lock.json" | match_vulns) | |
| if [[ -n "$lhits" ]]; then | |
| echo " β Vulnerable (package-lock.json):" | |
| printf '%s\n' "$lhits" | sed 's/^/ - /' | |
| TOTAL_AFFECTED=$((TOTAL_AFFECTED+1)) | |
| else | |
| echo " β Safe in package-lock.json" | |
| fi | |
| fi | |
| if [[ -f "$dir/yarn.lock" ]]; then | |
| has_any=1 | |
| yhits=$(scan_yarn_lock "$dir/yarn.lock" | match_vulns) | |
| if [[ -n "$yhits" ]]; then | |
| echo " β Vulnerable (yarn.lock):" | |
| printf '%s\n' "$yhits" | sed 's/^/ - /' | |
| TOTAL_AFFECTED=$((TOTAL_AFFECTED+1)) | |
| else | |
| echo " β Safe in yarn.lock" | |
| fi | |
| fi | |
| if [[ -f "$dir/pnpm-lock.yaml" ]]; then | |
| has_any=1 | |
| phits=$(scan_pnpm_lock "$dir/pnpm-lock.yaml" | match_vulns) | |
| if [[ -n "$phits" ]]; then | |
| echo " β Vulnerable (pnpm-lock.yaml):" | |
| printf '%s\n' "$phits" | sed 's/^/ - /' | |
| TOTAL_AFFECTED=$((TOTAL_AFFECTED+1)) | |
| else | |
| echo " β Safe in pnpm-lock.yaml" | |
| fi | |
| fi | |
| if [[ -f "$dir/deno.lock" ]]; then | |
| has_any=1 | |
| dhits=$(scan_deno_lock "$dir/deno.lock" | match_vulns) | |
| if [[ -n "$dhits" ]]; then | |
| echo " β Vulnerable (deno.lock):" | |
| printf '%s\n' "$dhits" | sed 's/^/ - /' | |
| TOTAL_AFFECTED=$((TOTAL_AFFECTED+1)) | |
| else | |
| echo " β Safe in deno.lock" | |
| fi | |
| fi | |
| if [[ -f "$dir/bun.lock" ]]; then | |
| has_any=1 | |
| blhits=$(scan_bun_lock "$dir/bun.lock" | match_vulns) | |
| if [[ -n "$blhits" ]]; then | |
| echo " β Vulnerable (bun.lock):" | |
| printf '%s\n' "$blhits" | sed 's/^/ - /' | |
| TOTAL_AFFECTED=$((TOTAL_AFFECTED+1)) | |
| else | |
| echo " β Safe in bun.lock" | |
| fi | |
| fi | |
| if [[ -f "$dir/bun.lockb" ]]; then | |
| has_any=1 | |
| bpkgs=$(scan_bun_lockb "$dir" || true) | |
| bhits=$(printf '%s\n' "$bpkgs" | match_vulns) | |
| if [[ -n "$bhits" ]]; then | |
| echo " β Vulnerable (bun.lockb):" | |
| printf '%s\n' "$bhits" | sed 's/^/ - /' | |
| TOTAL_AFFECTED=$((TOTAL_AFFECTED+1)) | |
| else | |
| echo " β Safe in bun.lockb" | |
| fi | |
| fi | |
| [[ $has_any -eq 0 ]] && echo " (no lockfile found)" | |
| else | |
| echo " (hint: use --lockfile to check from lockfiles)" | |
| fi | |
| fi | |
| echo "------------------------------------------------" | |
| done < <(find "$SCAN_ROOT" -name package.json -not -path "*/node_modules/*" | sort) | |
| echo "π Summary: $TOTAL_AFFECTED of $TOTAL_SCANNED projects contain vulnerable versions." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment