Last active
April 18, 2026 11:42
-
-
Save ceilidhboy/8af1b44c737df5ccf5b26066b02d640f to your computer and use it in GitHub Desktop.
BASH script to scan for compromised axios package (v1.14.1)
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
| #!/bin/bash | |
| # by Mike Scott 18-Apr-26 | |
| COMPROMISED_AXIOS="1.14.1" | |
| COMPROMISED_AXIOS_OLD="0.30.4" | |
| COMPROMISED_PLAIN_CRYPTO="4.2.1" | |
| RED='\033[0;31m' | |
| GREEN='\033[0;32m' | |
| AMBER='\033[0;33m' | |
| YELLOW='\033[1;33m' | |
| NC='\033[0m' | |
| usage() { | |
| echo "Usage: $0 <directory>" | |
| echo "" | |
| echo "Scan for compromised axios versions in npm/yarn/pnpm lock files." | |
| echo "" | |
| echo "Arguments:" | |
| echo " directory Directory to scan (required)" | |
| echo "" | |
| echo "Options:" | |
| echo " -h, --help Show this help message" | |
| echo "" | |
| echo "Examples:" | |
| echo " $0 ~/projects # Scan ~/projects directory" | |
| echo " $0 /var/www # Scan /var/www directory" | |
| exit 1 | |
| } | |
| if [ -z "$1" ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then | |
| usage | |
| fi | |
| SCAN_DIR="$1" | |
| if [ ! -d "$SCAN_DIR" ]; then | |
| echo "Error: Directory '$SCAN_DIR' does not exist." >&2 | |
| exit 1 | |
| fi | |
| echo "=== SCANNING FOR COMPROMISED AXIOS VERSION SUPPLY CHAIN ATTACK ===" | |
| echo "Scanning directory: $SCAN_DIR" | |
| echo "" | |
| AFFECTED_COUNT=0 | |
| AT_RISK_COUNT=0 | |
| SAFE_COUNT=0 | |
| echo "=== FINDING LOCK FILES ===" | |
| LOCK_FILES=$(find "$SCAN_DIR" -maxdepth 6 \( -name "package-lock.json" -o -name "yarn.lock" -o -name "pnpm-lock.yaml" -o -name "bun.lock" \) 2>/dev/null | grep -v node_modules | grep -v .cache | grep -v .Trash | grep -v Library) | |
| LOCK_FILE_COUNT=$(echo "$LOCK_FILES" | wc -l) | |
| echo "Found $LOCK_FILE_COUNT lock files" | |
| echo "" | |
| echo "=== AFFECTED (compromised versions in lock files) ===" | |
| AFFECTED="" | |
| while IFS= read -r lockfile; do | |
| if [ -z "$lockfile" ]; then | |
| continue | |
| fi | |
| dirname=$(dirname "$lockfile") | |
| if [[ "$lockfile" == *"package-lock.json" ]]; then | |
| if grep -q "\"axios\":\[\"${COMPROMISED_AXIOS}\"" "$lockfile" 2>/dev/null; then | |
| echo -e "${RED}AFFECTED:${NC} $lockfile -> axios@${COMPROMISED_AXIOS}" | |
| AFFECTED_COUNT=$((AFFECTED_COUNT + 1)) | |
| elif grep -q "\"axios\":\[\"${COMPROMISED_AXIOS_OLD}\"" "$lockfile" 2>/dev/null; then | |
| echo -e "${RED}AFFECTED:${NC} $lockfile -> axios@${COMPROMISED_AXIOS_OLD}" | |
| AFFECTED_COUNT=$((AFFECTED_COUNT + 1)) | |
| elif grep -q "\"plain-crypto-js\":\[\"${COMPROMISED_PLAIN_CRYPTO}\"" "$lockfile" 2>/dev/null; then | |
| echo -e "${RED}AFFECTED:${NC} $lockfile -> plain-crypto-js@${COMPROMISED_PLAIN_CRYPTO}" | |
| AFFECTED_COUNT=$((AFFECTED_COUNT + 1)) | |
| fi | |
| elif [[ "$lockfile" == *"yarn.lock" ]]; then | |
| if grep -q "axios@${COMPROMISED_AXIOS}:" "$lockfile" 2>/dev/null; then | |
| echo -e "${RED}AFFECTED:${NC} $lockfile -> axios@${COMPROMISED_AXIOS}" | |
| AFFECTED_COUNT=$((AFFECTED_COUNT + 1)) | |
| elif grep -q "axios@${COMPROMISED_AXIOS_OLD}:" "$lockfile" 2>/dev/null; then | |
| echo -e "${RED}AFFECTED:${NC} $lockfile -> axios@${COMPROMISED_AXIOS_OLD}" | |
| AFFECTED_COUNT=$((AFFECTED_COUNT + 1)) | |
| elif grep -q "plain-crypto-js@${COMPROMISED_PLAIN_CRYPTO}:" "$lockfile" 2>/dev/null; then | |
| echo -e "${RED}AFFECTED:${NC} $lockfile -> plain-crypto-js@${COMPROMISED_PLAIN_CRYPTO}" | |
| AFFECTED_COUNT=$((AFFECTED_COUNT + 1)) | |
| fi | |
| elif [[ "$lockfile" == *"pnpm-lock.yaml" ]]; then | |
| if grep -q "axios@${COMPROMISED_AXIOS}:" "$lockfile" 2>/dev/null; then | |
| echo -e "${RED}AFFECTED:${NC} $lockfile -> axios@${COMPROMISED_AXIOS}" | |
| AFFECTED_COUNT=$((AFFECTED_COUNT + 1)) | |
| elif grep -q "axios@${COMPROMISED_AXIOS_OLD}:" "$lockfile" 2>/dev/null; then | |
| echo -e "${RED}AFFECTED:${NC} $lockfile -> axios@${COMPROMISED_AXIOS_OLD}" | |
| AFFECTED_COUNT=$((AFFECTED_COUNT + 1)) | |
| elif grep -q "plain-crypto-js@${COMPROMISED_PLAIN_CRYPTO}:" "$lockfile" 2>/dev/null; then | |
| echo -e "${RED}AFFECTED:${NC} $lockfile -> plain-crypto-js@${COMPROMISED_PLAIN_CRYPTO}" | |
| AFFECTED_COUNT=$((AFFECTED_COUNT + 1)) | |
| fi | |
| elif [[ "$lockfile" == *"bun.lock" ]]; then | |
| if grep -q "axios@${COMPROMISED_AXIOS};" "$lockfile" 2>/dev/null; then | |
| echo -e "${RED}AFFECTED:${NC} $lockfile -> axios@${COMPROMISED_AXIOS}" | |
| AFFECTED_COUNT=$((AFFECTED_COUNT + 1)) | |
| elif grep -q "axios@${COMPROMISED_AXIOS_OLD};" "$lockfile" 2>/dev/null; then | |
| echo -e "${RED}AFFECTED:${NC} $lockfile -> axios@${COMPROMISED_AXIOS_OLD}" | |
| AFFECTED_COUNT=$((AFFECTED_COUNT + 1)) | |
| elif grep -q "plain-crypto-js@${COMPROMISED_PLAIN_CRYPTO};" "$lockfile" 2>/dev/null; then | |
| echo -e "${RED}AFFECTED:${NC} $lockfile -> plain-crypto-js@${COMPROMISED_PLAIN_CRYPTO}" | |
| AFFECTED_COUNT=$((AFFECTED_COUNT + 1)) | |
| fi | |
| fi | |
| done <<< "$LOCK_FILES" | |
| if [ $AFFECTED_COUNT -eq 0 ]; then | |
| echo "None found" | |
| fi | |
| echo "" | |
| echo "=== PACKAGE.JSON DEPS (exact versions matching compromised, and caret ranges) ===" | |
| PACKAGE_JSON_FILES=$(find "$SCAN_DIR" -maxdepth 6 -name "package.json" 2>/dev/null | grep -v node_modules | grep -v .cache | grep -v .Trash | grep -v Library) | |
| while IFS= read -r pkgfile; do | |
| if [ -z "$pkgfile" ]; then | |
| continue | |
| fi | |
| if grep -qE "\"axios\":\s*\"1\.14\.1\"" "$pkgfile" 2>/dev/null; then | |
| VERSION=$(grep -oE "\"axios\":\s*\"[^\"]+\"" "$pkgfile" | head -1) | |
| echo -e "${AMBER}CRITICAL:${NC} $pkgfile -> $VERSION ${AMBER}(exact version - will install compromised 1.14.1)${NC}" | |
| AT_RISK_COUNT=$((AT_RISK_COUNT + 1)) | |
| elif grep -qE "\"axios\":\s*\"0\.30\.4\"" "$pkgfile" 2>/dev/null; then | |
| VERSION=$(grep -oE "\"axios\":\s*\"[^\"]+\"" "$pkgfile" | head -1) | |
| echo -e "${AMBER}CRITICAL:${NC} $pkgfile -> $VERSION ${AMBER}(exact version - will install compromised 0.30.4)${NC}" | |
| AT_RISK_COUNT=$((AT_RISK_COUNT + 1)) | |
| elif grep -qE "\"plain-crypto-js\":\s*\"4\.2\.1\"" "$pkgfile" 2>/dev/null; then | |
| VERSION=$(grep -oE "\"plain-crypto-js\":\s*\"[^\"]+\"" "$pkgfile" | head -1) | |
| echo -e "${AMBER}CRITICAL:${NC} $pkgfile -> $VERSION ${AMBER}(exact version - will install compromised 4.2.1)${NC}" | |
| AT_RISK_COUNT=$((AT_RISK_COUNT + 1)) | |
| elif grep -qE "\"axios\":\s*\"\^1\." "$pkgfile" 2>/dev/null; then | |
| VERSION=$(grep -oE "\"axios\":\s*\"[^\"]+\"" "$pkgfile" | head -1) | |
| echo -e "${GREEN}SAFE:${NC} $pkgfile -> $VERSION ${GREEN}(caret range - resolves to latest 1.x)${NC}" | |
| SAFE_COUNT=$((SAFE_COUNT + 1)) | |
| elif grep -qE "\"axios\":\s*\"\^0\." "$pkgfile" 2>/dev/null; then | |
| VERSION=$(grep -oE "\"axios\":\s*\"[^\"]+\"" "$pkgfile" | head -1) | |
| echo -e "${GREEN}SAFE:${NC} $pkgfile -> $VERSION" | |
| SAFE_COUNT=$((SAFE_COUNT + 1)) | |
| elif grep -qE "\"plain-crypto-js\":\s*\"\^" "$pkgfile" 2>/dev/null; then | |
| VERSION=$(grep -oE "\"plain-crypto-js\":\s*\"[^\"]+\"" "$pkgfile" | head -1) | |
| echo -e "${GREEN}SAFE:${NC} $pkgfile -> $VERSION" | |
| SAFE_COUNT=$((SAFE_COUNT + 1)) | |
| fi | |
| done <<< "$PACKAGE_JSON_FILES" | |
| if [ $AT_RISK_COUNT -eq 0 ] && [ $SAFE_COUNT -eq 0 ]; then | |
| echo "None found" | |
| fi | |
| echo "" | |
| echo "=== SUMMARY ===" | |
| echo "AFFECTED: $AFFECTED_COUNT project(s)" | |
| echo "AT RISK: $AT_RISK_COUNT project(s)" | |
| echo "SAFE: $SAFE_COUNT project(s)" | |
| echo "" | |
| if [ $AFFECTED_COUNT -eq 0 ] && [ $AT_RISK_COUNT -eq 0 ]; then | |
| echo -e "${GREEN}=== CLEAN ===${NC}" | |
| echo -e "${GREEN}No compromised or at-risk projects found.${NC}" | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment