@archpublicwebsite/eslint-config 1.0.13 → 1.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +20 -1
- package/eslint.config.mjs +63 -14
- package/package.json +7 -1
- package/tools/git-hooks/pre-commit.mjs +30 -0
- package/tools/security/patterns.mjs +352 -0
- package/tools/security/safe-reinstall.sh +179 -0
- package/tools/security/scan-global.sh +155 -0
- package/tools/security/scan.mjs +465 -0
- package/tools/security/test-patterns.mjs +130 -0
- package/tools/setup/install.mjs +70 -2
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# safe-reinstall.sh
|
|
4
|
+
# Clean node_modules reinstall — post-PolinRider incident (June 2026)
|
|
5
|
+
#
|
|
6
|
+
# What it does:
|
|
7
|
+
# 1. Verifies ignore-scripts=true is set (blocks postinstall hooks)
|
|
8
|
+
# 2. Scans config files for PolinRider IOCs (abort if still infected)
|
|
9
|
+
# 3. Removes all node_modules directories (including monorepo packages)
|
|
10
|
+
# 4. Clears the package manager cache (npm / pnpm / yarn)
|
|
11
|
+
# 5. Reinstalls strictly from lockfile (npm ci / pnpm --frozen-lockfile)
|
|
12
|
+
#
|
|
13
|
+
# Usage — single repo:
|
|
14
|
+
# cd /path/to/your/repo && bash safe-reinstall.sh
|
|
15
|
+
#
|
|
16
|
+
# Usage — all repos in a folder at once:
|
|
17
|
+
# for dir in ~/code/*/; do
|
|
18
|
+
# [ -f "$dir/package.json" ] && echo "── $dir" && (cd "$dir" && bash ~/safe-reinstall.sh)
|
|
19
|
+
# done
|
|
20
|
+
# =============================================================================
|
|
21
|
+
|
|
22
|
+
set -euo pipefail
|
|
23
|
+
|
|
24
|
+
CHECK_ONLY=false
|
|
25
|
+
if [[ "${1:-}" == "--check-only" ]]; then
|
|
26
|
+
CHECK_ONLY=true
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
# ── Colours ───────────────────────────────────────────────────────────────────
|
|
30
|
+
BLUE='\033[0;34m'; GREEN='\033[0;32m'
|
|
31
|
+
YELLOW='\033[1;33m'; RED='\033[0;31m'; NC='\033[0m'
|
|
32
|
+
|
|
33
|
+
step() { echo -e "\n${BLUE}[→]${NC} $1"; }
|
|
34
|
+
ok() { echo -e "${GREEN}[✓]${NC} $1"; }
|
|
35
|
+
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
|
|
36
|
+
fail() { echo -e "${RED}[✗]${NC} $1"; exit 1; }
|
|
37
|
+
hr() { echo -e "${BLUE}────────────────────────────────────────────────────${NC}"; }
|
|
38
|
+
|
|
39
|
+
hr
|
|
40
|
+
echo -e "${BLUE} Safe Reinstall · Post-PolinRider (Jun 2026)${NC}"
|
|
41
|
+
echo -e " Repo: ${YELLOW}$(basename "$PWD")${NC}"
|
|
42
|
+
hr
|
|
43
|
+
|
|
44
|
+
# ── Step 1 — Verify we are inside a JS/TS project ────────────────────────────
|
|
45
|
+
step "Checking project root..."
|
|
46
|
+
[[ -f "package.json" ]] \
|
|
47
|
+
|| fail "No package.json found. cd into the root of a JS/TS repo first."
|
|
48
|
+
ok "package.json found"
|
|
49
|
+
|
|
50
|
+
# ── Step 2 — Enforce ignore-scripts=true ─────────────────────────────────────
|
|
51
|
+
step "Verifying ignore-scripts protection..."
|
|
52
|
+
if npm config get ignore-scripts 2>/dev/null | grep -q "^true$"; then
|
|
53
|
+
ok "ignore-scripts=true is set — postinstall hooks are blocked"
|
|
54
|
+
else
|
|
55
|
+
if [[ "$CHECK_ONLY" == "true" ]]; then
|
|
56
|
+
fail "ignore-scripts is NOT set in ~/.npmrc. Run: npm config set ignore-scripts true"
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
warn "ignore-scripts is NOT set in ~/.npmrc"
|
|
60
|
+
warn "Without this, reinstalling could re-trigger the malware."
|
|
61
|
+
read -rp " Add ignore-scripts=true to ~/.npmrc now? (y/N): " CONFIRM
|
|
62
|
+
if [[ "$CONFIRM" =~ ^[Yy]$ ]]; then
|
|
63
|
+
npm config set ignore-scripts true
|
|
64
|
+
ok "ignore-scripts=true added to ~/.npmrc"
|
|
65
|
+
else
|
|
66
|
+
fail "Aborted. Add 'ignore-scripts=true' to ~/.npmrc manually and re-run."
|
|
67
|
+
fi
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# ── Step 3 — IOC scan (abort if the repo is still infected) ──────────────────
|
|
71
|
+
step "Scanning config files for PolinRider IOC markers..."
|
|
72
|
+
|
|
73
|
+
IOC_FILES=()
|
|
74
|
+
|
|
75
|
+
while IFS= read -r file; do
|
|
76
|
+
if grep -qE \
|
|
77
|
+
"9-0224-2|9-857-1|global\[.!\.\]|createRequire.*import\.meta\.url" \
|
|
78
|
+
"$file" 2>/dev/null; then
|
|
79
|
+
IOC_FILES+=("$file")
|
|
80
|
+
fi
|
|
81
|
+
done < <(find . \
|
|
82
|
+
-not -path "*/node_modules/*" \
|
|
83
|
+
-not -path "*/.git/*" \
|
|
84
|
+
\( -name "eslint.config.*" \
|
|
85
|
+
-o -name "postcss.config.*" \
|
|
86
|
+
-o -name "vite.config.*" \
|
|
87
|
+
-o -name "webpack.config.*" \) \
|
|
88
|
+
-type f 2>/dev/null)
|
|
89
|
+
|
|
90
|
+
if (( ${#IOC_FILES[@]} > 0 )); then
|
|
91
|
+
warn "IOC markers detected in ${#IOC_FILES[@]} file(s):"
|
|
92
|
+
for f in "${IOC_FILES[@]}"; do
|
|
93
|
+
echo -e " ${RED}$f${NC}"
|
|
94
|
+
done
|
|
95
|
+
echo ""
|
|
96
|
+
fail "This repo still has infected config files. Clean them first (see Security-Remediation-Runbook-2026-06.md) then re-run this script."
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
ok "No IOC markers found — config files are clean"
|
|
100
|
+
|
|
101
|
+
# ── Step 4 — Detect package manager from lockfile ────────────────────────────
|
|
102
|
+
step "Detecting package manager..."
|
|
103
|
+
|
|
104
|
+
if [[ -f "pnpm-lock.yaml" ]]; then
|
|
105
|
+
PKG_MGR="pnpm"
|
|
106
|
+
LOCKFILE="pnpm-lock.yaml"
|
|
107
|
+
INSTALL_CMD="pnpm install --frozen-lockfile"
|
|
108
|
+
command -v pnpm >/dev/null 2>&1 \
|
|
109
|
+
|| fail "pnpm not found. Install it with: npm install -g pnpm"
|
|
110
|
+
|
|
111
|
+
elif [[ -f "package-lock.json" ]]; then
|
|
112
|
+
PKG_MGR="npm"
|
|
113
|
+
LOCKFILE="package-lock.json"
|
|
114
|
+
INSTALL_CMD="npm ci"
|
|
115
|
+
|
|
116
|
+
elif [[ -f "yarn.lock" ]]; then
|
|
117
|
+
PKG_MGR="yarn"
|
|
118
|
+
LOCKFILE="yarn.lock"
|
|
119
|
+
INSTALL_CMD="yarn install --frozen-lockfile"
|
|
120
|
+
command -v yarn >/dev/null 2>&1 \
|
|
121
|
+
|| fail "yarn not found. Install it with: npm install -g yarn"
|
|
122
|
+
|
|
123
|
+
else
|
|
124
|
+
fail "No lockfile found (pnpm-lock.yaml / package-lock.json / yarn.lock). A lockfile is required to guarantee a safe reinstall."
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
ok "Package manager: $PKG_MGR · lockfile: $LOCKFILE"
|
|
128
|
+
ok "Install command: $INSTALL_CMD"
|
|
129
|
+
|
|
130
|
+
if [[ "$CHECK_ONLY" == "true" ]]; then
|
|
131
|
+
step "Checking node_modules safety status..."
|
|
132
|
+
NM_COUNT=$(find . -name "node_modules" -type d -prune 2>/dev/null | wc -l | tr -d ' ')
|
|
133
|
+
ok "node_modules directories detected: $NM_COUNT"
|
|
134
|
+
|
|
135
|
+
hr
|
|
136
|
+
echo -e "${GREEN} ✓ $(basename "$PWD") → safe checks complete (no reinstall executed)${NC}"
|
|
137
|
+
hr
|
|
138
|
+
echo ""
|
|
139
|
+
exit 0
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
# ── Step 5 — Remove all node_modules ─────────────────────────────────────────
|
|
143
|
+
step "Removing node_modules..."
|
|
144
|
+
|
|
145
|
+
NM_COUNT=$(find . -name "node_modules" -type d -prune 2>/dev/null | wc -l | tr -d ' ')
|
|
146
|
+
|
|
147
|
+
if (( NM_COUNT > 1 )); then
|
|
148
|
+
warn "Monorepo: found $NM_COUNT node_modules directories — removing all"
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
find . -name "node_modules" -type d -prune -exec rm -rf '{}' + 2>/dev/null || true
|
|
152
|
+
|
|
153
|
+
ok "Removed $NM_COUNT node_modules director$( (( NM_COUNT == 1 )) && echo 'y' || echo 'ies' )"
|
|
154
|
+
|
|
155
|
+
# ── Step 6 — Clear package manager cache ─────────────────────────────────────
|
|
156
|
+
step "Clearing $PKG_MGR cache..."
|
|
157
|
+
|
|
158
|
+
case "$PKG_MGR" in
|
|
159
|
+
pnpm) pnpm store prune 2>/dev/null || true ;;
|
|
160
|
+
npm) npm cache clean --force ;;
|
|
161
|
+
yarn) yarn cache clean ;;
|
|
162
|
+
esac
|
|
163
|
+
|
|
164
|
+
ok "$PKG_MGR cache cleared"
|
|
165
|
+
|
|
166
|
+
# ── Step 7 — Reinstall strictly from lockfile ─────────────────────────────────
|
|
167
|
+
step "Reinstalling from $LOCKFILE..."
|
|
168
|
+
echo ""
|
|
169
|
+
|
|
170
|
+
eval "$INSTALL_CMD"
|
|
171
|
+
|
|
172
|
+
echo ""
|
|
173
|
+
ok "Dependencies installed from lockfile"
|
|
174
|
+
|
|
175
|
+
# ── Done ──────────────────────────────────────────────────────────────────────
|
|
176
|
+
hr
|
|
177
|
+
echo -e "${GREEN} ✓ $(basename "$PWD") → clean reinstall complete${NC}"
|
|
178
|
+
hr
|
|
179
|
+
echo ""
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# scan-global.sh
|
|
4
|
+
# Scan global node_modules and caches for PolinRider IOC markers.
|
|
5
|
+
# Run once per machine — no need to be inside any repo.
|
|
6
|
+
#
|
|
7
|
+
# Locations scanned:
|
|
8
|
+
# • npm global node_modules (npm root -g)
|
|
9
|
+
# • All nvm node versions (~/.nvm/versions/node/*/lib/node_modules)
|
|
10
|
+
# • pnpm global node_modules (pnpm root -g)
|
|
11
|
+
# • pnpm store (pnpm store path)
|
|
12
|
+
# • npx cache (~/.npm/_npx)
|
|
13
|
+
#
|
|
14
|
+
# Usage:
|
|
15
|
+
# bash scan-global.sh
|
|
16
|
+
# =============================================================================
|
|
17
|
+
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
GREEN='\033[0;32m'; YELLOW='\033[1;33m'; RED='\033[0;31m'
|
|
21
|
+
BLUE='\033[0;34m'; GRAY='\033[0;90m'; NC='\033[0m'
|
|
22
|
+
|
|
23
|
+
step() { echo -e "\n${BLUE}[→]${NC} $1"; }
|
|
24
|
+
ok() { echo -e "${GREEN}[✓]${NC} $1"; }
|
|
25
|
+
warn() { echo -e "${YELLOW}[!]${NC} $1"; }
|
|
26
|
+
skip() { echo -e "${GRAY}[–]${NC} $1"; }
|
|
27
|
+
hr() { echo -e "${BLUE}────────────────────────────────────────────────────${NC}"; }
|
|
28
|
+
|
|
29
|
+
# IOC patterns: payload markers + C2 hosts from the incident report
|
|
30
|
+
IOC_PATTERN="9-0224-2|9-857-1|trongrid\.io|aptoslabs\.com|bsc-dataseed\.binance|publicnode\.com"
|
|
31
|
+
|
|
32
|
+
SCAN_DIRS=() # entries formatted as "label|path"
|
|
33
|
+
TOTAL_HITS=0
|
|
34
|
+
|
|
35
|
+
hr
|
|
36
|
+
echo -e "${BLUE} Global IOC Scan · Post-PolinRider (Jun 2026)${NC}"
|
|
37
|
+
echo -e " Host: $(hostname)"
|
|
38
|
+
hr
|
|
39
|
+
|
|
40
|
+
# ── Collect scan targets ──────────────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
# 1. npm global node_modules
|
|
43
|
+
step "Locating npm global node_modules..."
|
|
44
|
+
NPM_GLOBAL=$(npm root -g 2>/dev/null || echo "")
|
|
45
|
+
if [[ -n "$NPM_GLOBAL" && -d "$NPM_GLOBAL" ]]; then
|
|
46
|
+
SCAN_DIRS+=("npm global|$NPM_GLOBAL")
|
|
47
|
+
ok "$NPM_GLOBAL"
|
|
48
|
+
else
|
|
49
|
+
skip "npm global not found"
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# 2. All nvm-managed node versions
|
|
53
|
+
step "Locating nvm node_modules..."
|
|
54
|
+
NVM_BASE="${NVM_DIR:-$HOME/.nvm}/versions/node"
|
|
55
|
+
if [[ -d "$NVM_BASE" ]]; then
|
|
56
|
+
while IFS= read -r ver; do
|
|
57
|
+
nm="$ver/lib/node_modules"
|
|
58
|
+
if [[ -d "$nm" ]]; then
|
|
59
|
+
SCAN_DIRS+=("nvm $(basename "$ver")|$nm")
|
|
60
|
+
ok "$nm"
|
|
61
|
+
fi
|
|
62
|
+
done < <(find "$NVM_BASE" -maxdepth 1 -mindepth 1 -type d 2>/dev/null | sort)
|
|
63
|
+
else
|
|
64
|
+
skip "nvm not found (checked $NVM_BASE)"
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# 3. pnpm global node_modules
|
|
68
|
+
step "Locating pnpm global node_modules..."
|
|
69
|
+
if command -v pnpm >/dev/null 2>&1; then
|
|
70
|
+
PNPM_GLOBAL=$(pnpm root -g 2>/dev/null || echo "")
|
|
71
|
+
if [[ -n "$PNPM_GLOBAL" && -d "$PNPM_GLOBAL" ]]; then
|
|
72
|
+
SCAN_DIRS+=("pnpm global|$PNPM_GLOBAL")
|
|
73
|
+
ok "$PNPM_GLOBAL"
|
|
74
|
+
else
|
|
75
|
+
skip "pnpm global node_modules not found"
|
|
76
|
+
fi
|
|
77
|
+
else
|
|
78
|
+
skip "pnpm not installed"
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
# 4. pnpm store (content-addressable cache)
|
|
82
|
+
step "Locating pnpm store..."
|
|
83
|
+
if command -v pnpm >/dev/null 2>&1; then
|
|
84
|
+
PNPM_STORE=$(pnpm store path 2>/dev/null || echo "")
|
|
85
|
+
if [[ -n "$PNPM_STORE" && -d "$PNPM_STORE" ]]; then
|
|
86
|
+
SCAN_DIRS+=("pnpm store|$PNPM_STORE")
|
|
87
|
+
warn "pnpm store can be large — this may take a moment"
|
|
88
|
+
warn "$PNPM_STORE"
|
|
89
|
+
else
|
|
90
|
+
skip "pnpm store not found"
|
|
91
|
+
fi
|
|
92
|
+
else
|
|
93
|
+
skip "pnpm not installed"
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
# 5. npx cache
|
|
97
|
+
step "Locating npx cache..."
|
|
98
|
+
NPM_CACHE=$(npm config get cache 2>/dev/null || echo "$HOME/.npm")
|
|
99
|
+
NPX_CACHE="$NPM_CACHE/_npx"
|
|
100
|
+
if [[ -d "$NPX_CACHE" ]]; then
|
|
101
|
+
SCAN_DIRS+=("npx cache|$NPX_CACHE")
|
|
102
|
+
ok "$NPX_CACHE"
|
|
103
|
+
else
|
|
104
|
+
skip "npx cache not found at $NPX_CACHE"
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
# ── Run scan ──────────────────────────────────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
echo ""
|
|
110
|
+
hr
|
|
111
|
+
echo -e "${BLUE} Scanning ${#SCAN_DIRS[@]} location(s)...${NC}"
|
|
112
|
+
hr
|
|
113
|
+
|
|
114
|
+
for ENTRY in "${SCAN_DIRS[@]}"; do
|
|
115
|
+
LABEL="${ENTRY%%|*}"
|
|
116
|
+
DIR="${ENTRY##*|}"
|
|
117
|
+
|
|
118
|
+
echo -e "\n${BLUE}[→]${NC} ${LABEL}"
|
|
119
|
+
echo -e " ${GRAY}${DIR}${NC}"
|
|
120
|
+
|
|
121
|
+
HITS=$(grep -rl \
|
|
122
|
+
--include="*.js" --include="*.mjs" --include="*.cjs" \
|
|
123
|
+
-E "$IOC_PATTERN" "$DIR" 2>/dev/null || true)
|
|
124
|
+
|
|
125
|
+
if [[ -z "$HITS" ]]; then
|
|
126
|
+
ok "Clean"
|
|
127
|
+
else
|
|
128
|
+
HIT_COUNT=$(echo "$HITS" | grep -c "." || true)
|
|
129
|
+
echo -e "${RED}[✗]${NC} IOC markers found in ${HIT_COUNT} file(s):"
|
|
130
|
+
while IFS= read -r f; do
|
|
131
|
+
echo -e " ${RED}${f}${NC}"
|
|
132
|
+
PREVIEW=$(grep -m1 -oE ".{0,30}(${IOC_PATTERN}).{0,30}" "$f" 2>/dev/null || true)
|
|
133
|
+
[[ -n "$PREVIEW" ]] && echo -e " ${GRAY}→ ${PREVIEW}${NC}"
|
|
134
|
+
done <<< "$HITS"
|
|
135
|
+
TOTAL_HITS=$((TOTAL_HITS + HIT_COUNT))
|
|
136
|
+
fi
|
|
137
|
+
done
|
|
138
|
+
|
|
139
|
+
# ── Summary ───────────────────────────────────────────────────────────────────
|
|
140
|
+
|
|
141
|
+
echo ""
|
|
142
|
+
hr
|
|
143
|
+
if [[ "$TOTAL_HITS" -eq 0 ]]; then
|
|
144
|
+
echo -e "${GREEN} ✓ All ${#SCAN_DIRS[@]} location(s) clean — machine is clear${NC}"
|
|
145
|
+
else
|
|
146
|
+
echo -e "${RED} ✗ IOC markers found in ${TOTAL_HITS} file(s) across global locations${NC}"
|
|
147
|
+
echo ""
|
|
148
|
+
echo -e "${YELLOW} Recommended next steps:${NC}"
|
|
149
|
+
echo -e "${YELLOW} 1. npm cache clean --force${NC}"
|
|
150
|
+
echo -e "${YELLOW} 2. pnpm store prune (if pnpm is installed)${NC}"
|
|
151
|
+
echo -e "${YELLOW} 3. Identify and remove the flagged packages globally${NC}"
|
|
152
|
+
echo -e "${YELLOW} 4. See: Security-Remediation-Runbook-2026-06.md${NC}"
|
|
153
|
+
fi
|
|
154
|
+
hr
|
|
155
|
+
echo ""
|