Restructure repo into package/ and standalone/ directories

Moves automated scan scripts and setup.sh into package/.
bind-ssh-tailscale.sh remains in standalone/ as a manual-run tool.
Updates README.md setup instructions to reflect new paths.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
pdmarf
2026-04-19 14:25:11 +01:00
parent 50aa38712e
commit 7585a12b6d
6 changed files with 4 additions and 4 deletions

71
package/README-scanner.md Normal file
View File

@@ -0,0 +1,71 @@
# Next.js RCE Vulnerability Scanner
Quick scanner for CVE-2025-66478 / CVE-2025-55182 (CVSS 10.0)
## Usage
```bash
curl -o check-nextjs-rce.sh http://100.120.125.113:3000/pdm/security-tools/raw/branch/master/check-nextjs-rce.sh
chmod +x check-nextjs-rce.sh
sudo ./check-nextjs-rce.sh
```
## What it checks
- Scans all package.json files on the system
- Checks Docker containers for Next.js
- Identifies vulnerable versions (15.0-15.5.6, 16.0-16.0.6)
## Patched Versions
- Next.js 15.5.7+
- Next.js 16.0.7+
## How to Update Next.js
### For npm projects:
```bash
# Update to latest patched version
npm install next@latest
# Or specify exact version
npm install next@15.5.7
```
### For yarn projects:
```bash
# Update to latest patched version
yarn upgrade next@latest
# Or specify exact version
yarn upgrade next@15.5.7
```
### For Docker containers:
```bash
# 1. Update package.json in your project
sed -i 's/"next": "15\.[0-5]\.[0-6]"/"next": "15.5.7"/g' package.json
# 2. Rebuild Docker image
docker compose build
# 3. Restart container
docker compose down
docker compose up -d
# 4. Verify version
docker compose exec <container-name> npm list next
```
### Verify the update:
```bash
# Check installed version
npm list next
# or
yarn list next
# Verify no vulnerabilities remain
npm audit
# or
yarn audit
```

140
package/check-nextjs-rce.sh Executable file
View File

@@ -0,0 +1,140 @@
#!/bin/bash
# Next.js CVE-2025-66478 / CVE-2025-55182 Vulnerability Checker
# Checks if Next.js installations are vulnerable to critical RCE
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/config.sh"
send_telegram() {
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d chat_id="${TELEGRAM_CHAT_ID}" \
-d text="$1" \
-d parse_mode="HTML" > /dev/null || true
}
HOSTNAME=$(hostname)
echo "=== Next.js RCE Vulnerability Scanner ==="
echo "CVE-2025-66478 / CVE-2025-55182 (CVSS 10.0)"
echo ""
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
VULNERABLE=0
SAFE=0
UNKNOWN=0
# Function to check if version is vulnerable
check_version() {
local version=$1
local major=$(echo $version | cut -d. -f1)
local minor=$(echo $version | cut -d. -f2)
local patch=$(echo $version | cut -d. -f3)
# Vulnerable versions:
# 15.0.0 - 15.0.4
# 15.1.0 - 15.1.8
# 15.2.0 - 15.2.5
# 15.3.0 - 15.3.5
# 15.4.0 - 15.4.7
# 15.5.0 - 15.5.6
# 16.0.0 - 16.0.6
if [ "$major" = "15" ]; then
if [ "$minor" = "0" ] && [ "$patch" -le "4" ]; then
return 1 # Vulnerable
elif [ "$minor" = "1" ] && [ "$patch" -le "8" ]; then
return 1
elif [ "$minor" = "2" ] && [ "$patch" -le "5" ]; then
return 1
elif [ "$minor" = "3" ] && [ "$patch" -le "5" ]; then
return 1
elif [ "$minor" = "4" ] && [ "$patch" -le "7" ]; then
return 1
elif [ "$minor" = "5" ] && [ "$patch" -le "6" ]; then
return 1
fi
elif [ "$major" = "16" ]; then
if [ "$minor" = "0" ] && [ "$patch" -le "6" ]; then
return 1
fi
fi
return 0 # Safe
}
echo "Searching for Next.js installations..."
echo ""
# Method 1: Check package.json files
find / -name "package.json" -type f 2>/dev/null | while read pkg; do
next_version=$(grep -o '"next"[[:space:]]*:[[:space:]]*"[^"]*"' "$pkg" 2>/dev/null | grep -o '[0-9][0-9.]*' | head -1)
if [ -n "$next_version" ]; then
echo "Found: $pkg"
echo " Next.js version: $next_version"
if check_version "$next_version"; then
echo -e " Status: ${GREEN}SAFE${NC}"
SAFE=$((SAFE + 1))
else
echo -e " Status: ${RED}VULNERABLE${NC} - Update to 15.5.7+ or 16.0.7+"
VULNERABLE=$((VULNERABLE + 1))
fi
echo ""
fi
done
# Method 2: Check Docker containers
echo "Checking Docker containers..."
docker ps --format '{{.Names}}' 2>/dev/null | while read container; do
echo "Checking container: $container"
# Try to find Next.js version in container
next_version=$(docker exec "$container" sh -c 'cat /*/package.json 2>/dev/null | grep -o "\"next\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" | grep -o "[0-9][0-9.]*" | head -1' 2>/dev/null)
if [ -n "$next_version" ]; then
echo " Next.js version: $next_version"
if check_version "$next_version"; then
echo -e " Status: ${GREEN}SAFE${NC}"
else
echo -e " Status: ${RED}VULNERABLE${NC}"
fi
else
echo -e " Status: ${YELLOW}No Next.js found${NC}"
fi
echo ""
done
echo "=== Summary ==="
echo -e "${GREEN}Safe installations: $SAFE${NC}"
echo -e "${RED}Vulnerable installations: $VULNERABLE${NC}"
echo ""
if [ $VULNERABLE -gt 0 ]; then
echo -e "${RED}⚠️ ACTION REQUIRED${NC}"
echo "Vulnerable Next.js installations found!"
echo ""
echo "Patched versions:"
echo " - Next.js 15.0.5, 15.1.9, 15.2.6, 15.3.6, 15.4.8, 15.5.7+"
echo " - Next.js 16.0.7+"
echo ""
echo "Update command:"
echo " npm install next@latest"
echo " # or"
echo " yarn upgrade next@15.5.7"
send_telegram "🚨 <b>Vulnerable Next.js Found — CVE-2025-66478</b>
Host: <code>${HOSTNAME}</code>
Vulnerable installations: ${VULNERABLE}
Update to Next.js 15.5.7+ or 16.0.7+
Run manually: bash check-nextjs-rce.sh"
exit 1
else
echo -e "${GREEN}✓ All Next.js installations are safe${NC}"
exit 0
fi

200
package/check-npm-sudo-config.sh Executable file
View File

@@ -0,0 +1,200 @@
#!/usr/bin/env bash
# check-npm-sudo-config.sh v1.0
# Audits npm configuration on this VM for sudo-related issues and recommends fixes.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=config.sh
source "$SCRIPT_DIR/config.sh"
send_telegram() {
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d chat_id="${TELEGRAM_CHAT_ID}" \
-d text="$1" \
-d parse_mode="HTML" > /dev/null || true
}
HOSTNAME=$(hostname)
DATE=$(date)
LOGFILE="$SCRIPT_DIR/logs/npm-sudo-config-$(date +%Y%m%d).log"
mkdir -p "$SCRIPT_DIR/logs"
RED='\033[0;31m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
BOLD='\033[1m'
RESET='\033[0m'
ISSUES=0
WARNINGS=0
log() { echo "$*" | tee -a "$LOGFILE"; }
ok() { log "$(printf "${GREEN}${RESET} %s" "$*")"; }
warn() { log "$(printf "${YELLOW}${RESET} %s" "$*")"; (( WARNINGS++ )) || true; }
fail() { log "$(printf "${RED}${RESET} %s" "$*")"; (( ISSUES++ )) || true; }
rec() { log "$(printf " ${YELLOW}${RESET} %s" "$*")"; }
header() { log ""; log "=========================================="; log "$*"; log "=========================================="; }
log "=========================================="
log " npm sudo config audit"
log "=========================================="
log "Hostname : $HOSTNAME"
log "Date : $DATE"
# ── 1. npm present? ───────────────────────────────────────────────────────────
header "1. npm availability"
if ! command -v npm &>/dev/null; then
warn "npm not found in PATH — skipping remaining checks"
echo ""
echo "RESULT: 0 issue(s), 1 warning(s)"
exit 0
fi
NPM_PATH=$(command -v npm)
ok "npm found: $NPM_PATH"
# ── 2. npm prefix ─────────────────────────────────────────────────────────────
header "2. npm prefix"
PREFIX=$(npm config get prefix 2>/dev/null || echo "unknown")
log "Current prefix: $PREFIX"
if [[ "$PREFIX" == "/usr" || "$PREFIX" == "/usr/local" ]]; then
fail "npm prefix is $PREFIX (system-wide) — global installs require sudo"
rec "npm config set prefix ~/.npm-global"
rec "Add to ~/.profile: export PATH=\"\$HOME/.npm-global/bin:\$PATH\""
elif [[ "$PREFIX" == "unknown" ]]; then
warn "Could not determine npm prefix"
else
PREFIX_OWNER=$(stat -c "%U" "$PREFIX" 2>/dev/null || echo "unknown")
if [[ "$PREFIX_OWNER" == "root" ]]; then
fail "npm prefix $PREFIX is owned by root — global installs require sudo"
rec "sudo chown -R \$(whoami) $PREFIX"
rec "Or set a user-owned prefix: npm config set prefix ~/.npm-global"
else
ok "npm prefix is $PREFIX (owned by $PREFIX_OWNER)"
fi
fi
# ── 3. .npmrc ────────────────────────────────────────────────────────────────
header "3. ~/.npmrc"
if [[ -f "$HOME/.npmrc" ]]; then
log "$(cat "$HOME/.npmrc")"
NPM_PREFIX_LINE=$(grep "^prefix=" "$HOME/.npmrc" 2>/dev/null || true)
if [[ -n "$NPM_PREFIX_LINE" ]]; then
ok ".npmrc explicitly sets: $NPM_PREFIX_LINE"
else
warn ".npmrc exists but does not pin the prefix"
rec "npm config set prefix ~/.npm-global"
fi
else
warn "No ~/.npmrc — prefix is not pinned to a user directory"
rec "npm config set prefix ~/.npm-global"
fi
# ── 4. prefix/bin in PATH ─────────────────────────────────────────────────────
header "4. npm prefix bin in PATH"
if [[ "$PREFIX" != "unknown" ]]; then
PREFIX_BIN="${PREFIX}/bin"
if echo "$PATH" | tr ':' '\n' | grep -qxF "$PREFIX_BIN"; then
ok "$PREFIX_BIN is in PATH"
else
warn "$PREFIX_BIN is NOT in PATH — globally installed binaries won't run"
PROFILE_FILE="$HOME/.profile"
[[ -f "$HOME/.zshrc" ]] && PROFILE_FILE="$HOME/.zshrc"
rec "Add to $PROFILE_FILE: export PATH=\"$PREFIX_BIN:\$PATH\""
rec "Then reload: source $PROFILE_FILE"
fi
fi
# ── 5. Root-owned files in npm prefix ────────────────────────────────────────
header "5. Root-owned files in npm prefix"
if [[ -d "$PREFIX" ]]; then
ROOT_FILES=$(find "$PREFIX" -maxdepth 3 -user root 2>/dev/null | head -5 || true)
if [[ -n "$ROOT_FILES" ]]; then
fail "Root-owned files found in npm prefix (past sudo npm usage):"
echo "$ROOT_FILES"
rec "sudo chown -R \$(whoami) $PREFIX"
else
ok "No root-owned files in $PREFIX"
fi
else
ok "npm prefix directory does not exist yet (no global installs made)"
fi
# ── 6. sudo npm in shell history ─────────────────────────────────────────────
header "6. Shell history — sudo npm usage"
SUDO_NPM_FOUND=false
for hfile in "$HOME/.bash_history" "$HOME/.zsh_history"; do
if [[ -f "$hfile" ]]; then
HITS=$(grep -c "sudo npm" "$hfile" 2>/dev/null || true)
if [[ "$HITS" -gt 0 ]]; then
warn "Found $HITS occurrence(s) of \"sudo npm\" in $hfile"
SUDO_NPM_FOUND=true
fi
fi
done
$SUDO_NPM_FOUND || ok "No \"sudo npm\" in shell history"
# ── 7. npm cache ownership ───────────────────────────────────────────────────
header "7. npm cache ownership"
CACHE_DIR=$(npm config get cache 2>/dev/null || echo "$HOME/.npm")
if [[ -d "$CACHE_DIR" ]]; then
ROOT_CACHE=$(find "$CACHE_DIR" -maxdepth 2 -user root 2>/dev/null | head -3 || true)
if [[ -n "$ROOT_CACHE" ]]; then
fail "Root-owned files in npm cache ($CACHE_DIR) — will cause EACCES errors"
rec "sudo chown -R \$(whoami) $CACHE_DIR"
else
ok "npm cache ($CACHE_DIR) is user-owned"
fi
else
ok "npm cache directory does not exist yet"
fi
# ── 8. Node version manager ───────────────────────────────────────────────────
header "8. Node version manager"
if command -v n &>/dev/null; then
N_PREFIX_VAL="${N_PREFIX:-}"
if [[ -z "$N_PREFIX_VAL" ]]; then
warn "n is installed but N_PREFIX is not set — n defaults to /usr/local (requires sudo)"
rec "Add to ~/.profile: export N_PREFIX=\$HOME/.n"
rec "Add to ~/.profile: export PATH=\$PATH:\$N_PREFIX/bin"
else
ok "n is installed, N_PREFIX=$N_PREFIX_VAL"
fi
elif [[ -s "$HOME/.nvm/nvm.sh" ]] || command -v nvm &>/dev/null 2>&1; then
ok "nvm is managing Node (sudo-free by design)"
elif command -v fnm &>/dev/null; then
ok "fnm is managing Node (sudo-free by design)"
else
ok "No Node version manager detected"
fi
# ── Summary ───────────────────────────────────────────────────────────────────
header "SUMMARY"
log "Scan completed at: $(date)"
log "Log saved to : $LOGFILE"
log ""
if [[ $ISSUES -gt 0 ]]; then
printf "${RED}✗ %d issue(s) and %d warning(s) — see recommendations above${RESET}\n" "$ISSUES" "$WARNINGS"
send_telegram "⚠️ <b>npm sudo config issues</b>
Host: <code>${HOSTNAME}</code>
Issues: ${ISSUES} | Warnings: ${WARNINGS}
Run manually: bash check-npm-sudo-config.sh"
exit 1
elif [[ $WARNINGS -gt 0 ]]; then
printf "${YELLOW}⚠ Clean but %d warning(s) — see recommendations above${RESET}\n" "$WARNINGS"
exit 0
else
printf "${GREEN}✓ npm is correctly configured on %s${RESET}\n" "$HOSTNAME"
exit 0
fi

288
package/npm-security-check.sh Executable file
View File

@@ -0,0 +1,288 @@
#!/usr/bin/env bash
# npm-security-check.sh
# Scans for NPM/Node.js malware indicators on this VM.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=config.sh
source "$SCRIPT_DIR/config.sh"
send_telegram() {
curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d chat_id="${TELEGRAM_CHAT_ID}" \
-d text="$1" \
-d parse_mode="HTML" > /dev/null || true
}
HOSTNAME=$(hostname)
DATE=$(date)
LOGFILE="${1:-npm_security_check_${HOSTNAME}_$(date +%Y%m%d_%H%M%S).log}"
RED='\033[0;31m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
BOLD='\033[1m'
RESET='\033[0m'
ISSUES=0
WARNINGS=0
log() { echo "$*" | tee -a "$LOGFILE"; }
header() { log ""; log "=========================================="; log "$*"; log "=========================================="; }
ok() { log "$(printf "${GREEN}${RESET} %s" "$*")"; }
warn() { log "$(printf "${YELLOW}${RESET} %s" "$*")"; (( WARNINGS++ )) || true; }
fail() { log "$(printf "${RED}${RESET} %s" "$*")"; (( ISSUES++ )) || true; }
# ── Header ────────────────────────────────────────────────────────────────────
log "=========================================="
log " NPM / Node.js Security Check"
log "=========================================="
log "Hostname : $HOSTNAME"
log "Date : $DATE"
log "Log file : $LOGFILE"
# ── 1. Global npm packages ────────────────────────────────────────────────────
header "1. Global npm packages"
KNOWN_GOOD_GLOBALS="npm corepack"
if command -v npm &>/dev/null; then
GLOBALS=$(npm list -g --depth=0 2>/dev/null | tail -n +2 | sed 's/.*── //')
log "$GLOBALS"
# Flag anything that looks like a typosquat or known-bad package
SUSPICIOUS_PATTERNS="(plain-crypto-js|axios-[0-9]|node-fetch-[0-9]{3}|colors-js|event-stream|flatmap-stream|ua-parser-js|coa@|rc@[0-9]|nodemailer-[0-9]{3})"
HITS=$(echo "$GLOBALS" | grep -E "$SUSPICIOUS_PATTERNS" || true)
if [[ -n "$HITS" ]]; then
fail "Suspicious global package(s) found:"
log "$HITS"
else
ok "No suspicious global packages"
fi
else
warn "npm not found in PATH"
fi
# ── 2. Known malicious packages in lock files ─────────────────────────────────
header "2. Malicious package names in lock files"
BAD_PKGS=(
"plain-crypto-js"
"axios-proxy"
"node-colors"
"colors-js"
"event-stream"
"flatmap-stream"
"ua-parser-js"
"getcookies"
)
LOCKFILES=$(find / -name "package-lock.json" -o -name "yarn.lock" -o -name "pnpm-lock.yaml" \
2>/dev/null | grep -v node_modules | grep -v "\.vscode-server" | grep -v "\.cache") || true
if [[ -z "$LOCKFILES" ]]; then
warn "No lock files found to scan"
else
COUNT=$(echo "$LOCKFILES" | wc -l)
log "Scanning $COUNT lock file(s)..."
for pkg in "${BAD_PKGS[@]}"; do
MATCHES=$(echo "$LOCKFILES" | xargs grep -l "\"$pkg\"" 2>/dev/null || true)
if [[ -n "$MATCHES" ]]; then
fail "Found '$pkg' in: $MATCHES"
fi
done
ok "No known-malicious package names found"
fi
# ── 3. Node processes and their origin ────────────────────────────────────────
header "3. Running Node/Next.js processes"
NODE_PROCS=$(ps aux | grep -E "[n]ode|[n]ext-server|[n]pm|[n]px|[p]npm" | grep -v grep || true)
if [[ -z "$NODE_PROCS" ]]; then
ok "No Node.js processes running"
else
log "$NODE_PROCS"
# Check for processes running as root outside of Docker containers
ROOT_PROCS=$(echo "$NODE_PROCS" | awk '$1 == "root" {print}' || true)
if [[ -n "$ROOT_PROCS" ]]; then
# Check if each root process is inside a Docker cgroup (normal)
while IFS= read -r proc; do
PID=$(echo "$proc" | awk '{print $2}')
CMD=$(cat /proc/"$PID"/cmdline 2>/dev/null | tr '\0' ' ' || echo 'unreadable')
# Skip this script's own process
if echo "$CMD" | grep -qF "npm-security-check.sh"; then
ok "PID $PID is this script running — not a threat"
continue
fi
CGROUP=$(cat /proc/"$PID"/cgroup 2>/dev/null | grep -c "docker" || true)
if [[ "$CGROUP" -gt 0 ]]; then
ok "PID $PID runs as root but is inside a Docker container (normal)"
else
warn "PID $PID is a root Node process outside Docker — review manually"
log " Command: $CMD"
fi
done <<< "$ROOT_PROCS"
else
ok "No root-owned Node processes outside Docker"
fi
fi
# ── 4. Outbound network connections from Node processes ───────────────────────
header "4. Node process network connections"
if command -v lsof &>/dev/null; then
NODE_CONNS=$(lsof -i TCP -a -c node -a -s TCP:ESTABLISHED 2>/dev/null || true)
if [[ -n "$NODE_CONNS" ]]; then
log "$NODE_CONNS"
# Flag connections to non-443/80 ports on public IPs
UNUSUAL=$(echo "$NODE_CONNS" | awk '!/localhost|127\.0\.0|192\.168|10\.|172\.(1[6-9]|2[0-9]|3[01])\.|:443|:80|:22/ && /ESTABLISHED/' || true)
if [[ -n "$UNUSUAL" ]]; then
warn "Unusual outbound Node connections (non-standard ports or IPs):"
log "$UNUSUAL"
else
ok "Node connections look normal (443/80 or private IPs)"
fi
else
ok "No established TCP connections from node processes"
fi
else
# Fallback to ss
ALL_CONNS=$(ss -tnp 2>/dev/null | grep "node\|npm" || true)
if [[ -n "$ALL_CONNS" ]]; then
log "$ALL_CONNS"
else
ok "No Node network connections found"
fi
fi
# ── 5. Known C2 indicators ────────────────────────────────────────────────────
header "5. Known C2 / malware indicators"
# From previous axios supply-chain attack (Apr 2025 npm incident)
C2_IPS=("142.11.206.73" "185.220.101" "194.165.16")
C2_DOMAINS=("sfrclak.com" "discordapp.com/api/webhooks" "ngrok.io")
ACTIVE_CONNS=$(ss -tn 2>/dev/null || netstat -tn 2>/dev/null || true)
FOUND_C2=false
for ip in "${C2_IPS[@]}"; do
if echo "$ACTIVE_CONNS" | grep -q "$ip"; then
fail "Active connection to known C2 IP: $ip"
FOUND_C2=true
fi
done
for domain in "${C2_DOMAINS[@]}"; do
if echo "$ACTIVE_CONNS" | grep -q "$domain"; then
fail "Active connection to suspicious domain: $domain"
FOUND_C2=true
fi
done
if ! $FOUND_C2; then
ok "No connections to known C2 infrastructure"
fi
# ── 6. Suspicious processes (miners, RATs) ────────────────────────────────────
header "6. Suspicious process names"
SUSPICIOUS_PROCS="(xmrig|minerd|cpuminer|kdevtmpfsi|kinsing|/tmp/[a-z0-9]{8,}|/dev/shm/)"
# Match processes whose executable path (field 11) starts with ../ — not args containing ../
HITS=$(ps aux | grep -E "$SUSPICIOUS_PROCS" | grep -v grep || true)
DOTDOT=$(ps aux | grep -v grep | awk '$11 ~ /^\.\.\// {print}' || true)
[[ -n "$DOTDOT" ]] && HITS="$HITS
$DOTDOT"
if [[ -n "$HITS" ]]; then
fail "Suspicious processes detected:"
log "$HITS"
else
ok "No suspicious process names"
fi
# ── 7. Suspicious files in temp directories ───────────────────────────────────
header "7. Suspicious files in /tmp and /dev/shm"
for dir in /tmp /dev/shm /var/tmp; do
EXEC_FILES=$(find "$dir" -type f -executable 2>/dev/null | head -20 || true)
JS_FILES=$(find "$dir" -name "*.js" -o -name "*.mjs" 2>/dev/null | head -10 || true)
if [[ -n "$EXEC_FILES" ]]; then
warn "Executable files in $dir:"
log "$EXEC_FILES"
fi
if [[ -n "$JS_FILES" ]]; then
warn "JS files in $dir:"
log "$JS_FILES"
fi
done
ok "Temp directory scan complete"
# ── 8. npm configuration ──────────────────────────────────────────────────────
header "8. npm configuration"
if [[ -f "$HOME/.npmrc" ]]; then
log "$(cat "$HOME/.npmrc")"
# Check for non-official registry
ALT_REGISTRY=$(grep -v "^#" "$HOME/.npmrc" | grep "registry" | grep -v "registry.npmjs.org" || true)
if [[ -n "$ALT_REGISTRY" ]]; then
warn "Non-official npm registry configured: $ALT_REGISTRY"
else
ok ".npmrc uses official registry"
fi
else
warn "No .npmrc found (registry defaults to npmjs.org — acceptable)"
fi
# ── 9. Docker containers quick review ────────────────────────────────────────
header "9. Docker containers"
if command -v docker &>/dev/null && docker ps &>/dev/null 2>&1; then
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}" 2>/dev/null | tee -a "$LOGFILE"
# Flag containers with no named image (just an image ID)
UNNAMED=$(docker ps --format "{{.Names}} {{.Image}}" 2>/dev/null | awk '$2 ~ /^[0-9a-f]{12}$/' || true)
if [[ -n "$UNNAMED" ]]; then
warn "Container(s) using unnamed image IDs (verify these are known):"
log "$UNNAMED"
else
ok "All containers use named images"
fi
else
warn "Docker not available or not accessible"
fi
# ── 10. Bash history spot-check ───────────────────────────────────────────────
header "10. Bash history — suspicious patterns"
HIST_FILE="${HISTFILE:-$HOME/.bash_history}"
if [[ -f "$HIST_FILE" ]]; then
# Look for obfuscated execution patterns (not internal curl to known Tailscale IPs)
SUSPICIOUS_HIST=$(grep -E "(eval\s*\$|base64\s*-d|python.*exec|perl.*eval|/dev/tcp/|bash.*<\(curl.*[^1][^0][^0]\.)" \
"$HIST_FILE" 2>/dev/null | grep -vE "100\.[0-9]+\.[0-9]+\.[0-9]+" | tail -20 || true)
if [[ -n "$SUSPICIOUS_HIST" ]]; then
warn "Potentially suspicious history entries:"
log "$SUSPICIOUS_HIST"
else
ok "No obviously suspicious history entries"
fi
else
warn "Bash history file not found at $HIST_FILE"
fi
# ── Summary ────────────────────────────────────────────────────────────────────
header "SUMMARY"
log "Scan completed at: $(date)"
log "Results saved to : $LOGFILE"
log ""
if [[ $ISSUES -gt 0 ]]; then
log "$(printf "${RED}✗ %d issue(s) found — review output above${RESET}" "$ISSUES")"
send_telegram "🚨 <b>Security Alert — npm-security-check</b>
Host: <code>${HOSTNAME}</code>
Issues: ${ISSUES} | Warnings: ${WARNINGS}
Run manually to review: bash npm-security-check.sh"
exit 1
elif [[ $WARNINGS -gt 0 ]]; then
log "$(printf "${YELLOW}⚠ Clean but %d warning(s) — review output above${RESET}" "$WARNINGS")"
exit 0
else
log "$(printf "${GREEN}✓ All checks passed — no indicators of compromise${RESET}")"
exit 0
fi

112
package/setup.sh Executable file
View File

@@ -0,0 +1,112 @@
#!/usr/bin/env bash
# setup.sh
# Run once after cloning on any VM where you want security scanning active.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "=== Security Tools Setup ==="
echo ""
# ── Git config ────────────────────────────────────────────────────────────────
git -C "$SCRIPT_DIR" config pull.rebase false
echo "Git pull strategy set to merge."
# ── Telegram credentials ───────────────────────────────────────────────────────
if [[ -f "$SCRIPT_DIR/config.sh" ]]; then
echo "config.sh already exists — skipping credential setup."
else
echo "Enter your Telegram bot token:"
read -r BOT_TOKEN
echo "Enter your Telegram chat ID:"
read -r CHAT_ID
cat > "$SCRIPT_DIR/config.sh" <<EOF
#!/usr/bin/env bash
# Telegram notification config
TELEGRAM_BOT_TOKEN="${BOT_TOKEN}"
TELEGRAM_CHAT_ID="${CHAT_ID}"
EOF
chmod 600 "$SCRIPT_DIR/config.sh"
echo "config.sh created."
fi
# ── Make scripts executable ────────────────────────────────────────────────────
chmod +x "$SCRIPT_DIR/npm-security-check.sh"
chmod +x "$SCRIPT_DIR/check-nextjs-rce.sh"
chmod +x "$SCRIPT_DIR/check-npm-sudo-config.sh"
# ── Create logs directory ──────────────────────────────────────────────────────
mkdir -p "$SCRIPT_DIR/logs"
# ── Cron jobs ──────────────────────────────────────────────────────────────────
CRON_1="0 8 * * * $SCRIPT_DIR/npm-security-check.sh >> $SCRIPT_DIR/logs/npm-security-check-\$(date +\%Y\%m\%d).log 2>&1"
CRON_2="5 8 * * * $SCRIPT_DIR/check-nextjs-rce.sh >> $SCRIPT_DIR/logs/check-nextjs-rce-\$(date +\%Y\%m\%d).log 2>&1"
CRON_3="10 8 * * * $SCRIPT_DIR/check-npm-sudo-config.sh >> $SCRIPT_DIR/logs/check-npm-sudo-config-\$(date +\%Y\%m\%d).log 2>&1"
CRON_4="0 9 * * * find $SCRIPT_DIR/logs -name '*.log' -mtime +60 -delete"
EXISTING=$(crontab -l 2>/dev/null || true)
if echo "$EXISTING" | grep -qF "npm-security-check.sh"; then
echo "Cron job for npm-security-check.sh already registered — skipping."
else
(echo "$EXISTING"; echo "$CRON_1") | crontab -
echo "Cron job registered: npm-security-check.sh daily at 08:00."
fi
if echo "$EXISTING" | grep -qF "check-nextjs-rce.sh"; then
echo "Cron job for check-nextjs-rce.sh already registered — skipping."
else
(crontab -l 2>/dev/null; echo "$CRON_2") | crontab -
echo "Cron job registered: check-nextjs-rce.sh daily at 08:05."
fi
if echo "$EXISTING" | grep -qF "check-npm-sudo-config.sh"; then
echo "Cron job for check-npm-sudo-config.sh already registered — skipping."
else
(crontab -l 2>/dev/null; echo "$CRON_3") | crontab -
echo "Cron job registered: check-npm-sudo-config.sh daily at 08:10."
fi
if echo "$EXISTING" | grep -qF "logs -name '*.log'"; then
echo "Log cleanup cron already registered — skipping."
else
(crontab -l 2>/dev/null; echo "$CRON_4") | crontab -
echo "Cron job registered: log cleanup daily at 09:00 (60 day retention)."
fi
# ── Test Telegram ──────────────────────────────────────────────────────────────
source "$SCRIPT_DIR/config.sh"
HOSTNAME=$(hostname)
echo ""
echo "Sending test Telegram message..."
RESPONSE=$(curl -s -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d chat_id="${TELEGRAM_CHAT_ID}" \
-d text="✅ <b>Security Tools Active</b>
Host: <code>${HOSTNAME}</code>
Scripts registered and running daily at 08:00." \
-d parse_mode="HTML")
if echo "$RESPONSE" | grep -q '"ok":true'; then
echo "Test message sent to Telegram."
else
echo "Warning: Telegram message failed. Check your token and chat ID in config.sh."
fi
echo ""
echo "Setup complete. Security scans will run daily at 08:00 on ${HOSTNAME}."
# ── Initial scan ───────────────────────────────────────────────────────────────
echo ""
echo "Running initial security scan..."
bash "$SCRIPT_DIR/npm-security-check.sh" >> "$SCRIPT_DIR/logs/npm-security-check-$(date +%Y%m%d).log" 2>&1 && echo "npm-security-check: done." || echo "npm-security-check: issues found — check Telegram."
bash "$SCRIPT_DIR/check-nextjs-rce.sh" >> "$SCRIPT_DIR/logs/check-nextjs-rce-$(date +%Y%m%d).log" 2>&1 && echo "check-nextjs-rce: done." || echo "check-nextjs-rce: issues found — check Telegram."
NPM_SUDO_LOG="$SCRIPT_DIR/logs/check-npm-sudo-config-$(date +%Y%m%d).log"
echo ""
echo "--- npm sudo config audit results ---"
bash "$SCRIPT_DIR/check-npm-sudo-config.sh" 2>&1 | tee -a "$NPM_SUDO_LOG"
echo "-------------------------------------"
echo ""
echo "Initial scan complete. Check Telegram for any alerts."