{"vulnerability": "CVE-2018-7408", "sightings": [{"uuid": "81e430ee-79a0-4bc0-83a0-3150ed44ba6b", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "CVE-2018-7408", "type": "seen", "source": "https://gist.github.com/steig/ddd6193b319e8b70af8f2659034a7922", "content": "#!/usr/bin/env bash\nset -euo pipefail\n\n# supply-chain-audit.sh \u2014 Developer supply chain security audit\n#\n# Merged edition: Joe Petrini's broad ecosystem coverage + additions for\n# MCP servers, curated known-bad-packages, gh CLI scopes, shell history secrets,\n# registry-hijack lockfile scans, and CI-friendly JSON/filter output.\n#\n# Usage:\n#   curl -fsSL  | bash\n#   bash supply-chain-audit.sh ~/code            # scan specific dirs\n#   bash supply-chain-audit.sh --fix             # generate remediation script\n#   bash supply-chain-audit.sh --json            # one finding per line as JSON\n#   bash supply-chain-audit.sh --only npm,mcp    # run only these groups\n#   bash supply-chain-audit.sh --skip homebrew   # skip groups\n#   bash supply-chain-audit.sh --no-prompt       # never ask, never scan\n#   bash supply-chain-audit.sh --list-groups     # show all group IDs\n#\n# Verify before piping to bash:\n#   curl -fsSL  -o /tmp/a.sh\n#   shasum -a 256 /tmp/a.sh  # compare to published sha256\n#   bash /tmp/a.sh\n\nVERSION=\"2.0.0\"\n\n# \u2500\u2500 Configuration \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# Directories to scan for dependency manifests, lockfiles, and workflow files.\n# Override with: SCAN_DIRS=\"/path/one /path/two\" ./supply-chain-audit.sh\n# Or pass as arguments: ./supply-chain-audit.sh ~/code ~/work/projects\n\nSCAN_DIRS=\"${SCAN_DIRS:-}\"\n\n# Max depth for file searches within scan directories (default: 5)\nSCAN_DEPTH=\"${SCAN_DEPTH:-5}\"\n\nDEFAULT_SCAN_DIRS=(\"$HOME/code\" \"$HOME/projects\" \"$HOME/src\" \"$HOME/dev\")\n\n# \u2500\u2500 Colors &amp; Output \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nRED=$'\\033[0;31m'\nYELLOW=$'\\033[0;33m'\nGREEN=$'\\033[0;32m'\nBLUE=$'\\033[0;34m'\nCYAN=$'\\033[0;36m'\nBOLD=$'\\033[1m'\nDIM=$'\\033[2m'\nNC=$'\\033[0m'\n\nCRIT_COUNT=0\nWARN_COUNT=0\nPASS_COUNT=0\nINFO_COUNT=0\n\nFINDINGS=()\n\n# CLI-controlled output modes (set in main())\nJSON_MODE=${JSON_MODE:-0}\nQUIET=${QUIET:-0}\nONLY_GROUPS=\"\"\nSKIP_GROUPS=\"\"\nCURRENT_GROUP=\"\"\n\n# Check if a group should be processed (called by section())\ngroup_enabled() {\n  local g=\"$1\"\n  if [[ -n \"$ONLY_GROUPS\" ]]; then\n    [[ \",$ONLY_GROUPS,\" == *\",$g,\"* ]] || return 1\n  fi\n  if [[ -n \"$SKIP_GROUPS\" ]]; then\n    [[ \",$SKIP_GROUPS,\" == *\",$g,\"* ]] &amp;&amp; return 1\n  fi\n  return 0\n}\n\n# JSON helper \u2014 escape strings without jq dep\n_j_esc() { printf '%s' \"$1\" | sed 's/\\\\/\\\\\\\\/g; s/\"/\\\\\"/g; s/\t/\\\\t/g'; }\n\n_emit_json() {\n  # args: severity tool message fix\n  printf '{\"severity\":\"%s\",\"group\":\"%s\",\"message\":\"%s\",\"fix\":\"%s\"}\\n' \\\n    \"$1\" \"$(_j_esc \"$2\")\" \"$(_j_esc \"$3\")\" \"$(_j_esc \"$4\")\"\n}\n\ncrit()  {\n  ((++CRIT_COUNT)); FINDINGS+=(\"CRIT|$1|$2|${3:-}\")\n  if [[ $JSON_MODE -eq 1 ]]; then _emit_json critical \"$1\" \"$2\" \"${3:-}\"; return; fi\n  [[ $QUIET -eq 1 ]] &amp;&amp; { printf \"  ${RED}\u2717 CRITICAL${NC}  %s\\n\" \"$2\"; [[ -n \"${3:-}\" ]] &amp;&amp; printf \"      ${DIM}fix:${NC} %s\\n\" \"$3\"; return; }\n  printf \"  ${RED}\u2717 CRITICAL${NC}  %s\\n\" \"$2\"\n  [[ -n \"${3:-}\" ]] &amp;&amp; printf \"      ${DIM}fix:${NC} %s\\n\" \"$3\"\n}\nwarn()  {\n  ((++WARN_COUNT)); FINDINGS+=(\"WARN|$1|$2|${3:-}\")\n  if [[ $JSON_MODE -eq 1 ]]; then _emit_json warning \"$1\" \"$2\" \"${3:-}\"; return; fi\n  [[ $QUIET -eq 1 ]] &amp;&amp; { printf \"  ${YELLOW}\u26a0 WARNING${NC}   %s\\n\" \"$2\"; [[ -n \"${3:-}\" ]] &amp;&amp; printf \"      ${DIM}fix:${NC} %s\\n\" \"$3\"; return; }\n  printf \"  ${YELLOW}\u26a0 WARNING${NC}   %s\\n\" \"$2\"\n  [[ -n \"${3:-}\" ]] &amp;&amp; printf \"      ${DIM}fix:${NC} %s\\n\" \"$3\"\n}\npass()  {\n  ((++PASS_COUNT)); FINDINGS+=(\"PASS|$1|$2|\")\n  if [[ $JSON_MODE -eq 1 ]]; then _emit_json pass \"$1\" \"$2\" \"\"; return; fi\n  [[ $QUIET -eq 1 ]] &amp;&amp; return\n  printf \"  ${GREEN}\u2713 OK${NC}        %s\\n\" \"$2\"\n}\ninfo()  {\n  ((++INFO_COUNT)); FINDINGS+=(\"INFO|$1|$2|${3:-}\")\n  if [[ $JSON_MODE -eq 1 ]]; then _emit_json info \"$1\" \"$2\" \"${3:-}\"; return; fi\n  [[ $QUIET -eq 1 ]] &amp;&amp; return\n  printf \"  ${BLUE}\u2139 INFO${NC}      %s\\n\" \"$2\"\n}\n\nsection() {\n  # If second arg provided, use as machine-readable group; else derive from\n  # first word lowercased so existing call sites work unchanged.\n  if [[ $# -ge 2 &amp;&amp; -n \"$2\" ]]; then\n    CURRENT_GROUP=\"$2\"\n  else\n    CURRENT_GROUP=\"$(printf '%s' \"$1\" | awk '{print tolower($1)}')\"\n  fi\n  [[ $JSON_MODE -eq 1 ]] &amp;&amp; return\n  [[ $QUIET -eq 1 ]] &amp;&amp; return\n  printf \"\\n${BOLD}${CYAN}\u2501\u2501\u2501 %s${NC}\\n\" \"$1\"\n}\n\n# group-gated wrapper: skip an audit function entirely if its group is filtered\ngroup_run() {\n  local group=\"$1\"; shift\n  group_enabled \"$group\" || return 0\n  \"$@\"\n}\n\ninstalled() {\n  command -v \"$1\" &amp;&gt;/dev/null\n}\n\nresolve_scan_dirs() {\n  local provided_dirs=()\n  local invalid_dirs=()\n  local arg\n\n  for arg in \"$@\"; do\n    if [[ -d \"$arg\" ]]; then\n      provided_dirs+=(\"$arg\")\n    else\n      invalid_dirs+=(\"$arg\")\n    fi\n  done\n\n  if [[ ${#invalid_dirs[@]} -gt 0 ]]; then\n    printf \"  %sIgnoring non-directory path(s): %s%s\\n\" \"$YELLOW\" \"${invalid_dirs[*]}\" \"$NC\" &gt;&amp;2\n  fi\n\n  if [[ ${#provided_dirs[@]} -gt 0 ]]; then\n    CODE_DIRS=(\"${provided_dirs[@]}\")\n    return 0\n  fi\n\n  if [[ -n \"$SCAN_DIRS\" ]]; then\n    read -ra CODE_DIRS &lt;&lt;&lt; \"$SCAN_DIRS\"\n    return 0\n  fi\n\n  # --no-prompt or --json bypass interaction entirely\n  if [[ \"${AUDIT_NO_PROMPT:-0}\" -eq 1 || \"$JSON_MODE\" -eq 1 ]]; then\n    CODE_DIRS=(\"${DEFAULT_SCAN_DIRS[@]}\")\n  # Try /dev/tty so curl|bash works (stdin is the script, but tty still exists)\n  elif [[ -r /dev/tty &amp;&amp; -w /dev/tty ]]; then\n    local defaults_display=\"${DEFAULT_SCAN_DIRS[*]}\"\n    local response\n    local response_lc\n\n    printf \"\\nNo scan directories were provided.\\n\"\n    printf \"Default scan directories: %s\\n\" \"$defaults_display\"\n    printf \"Press Enter to use defaults, type custom paths, or q to quit: \"\n    read -r response &lt; /dev/tty\n    response_lc=$(printf \"%s\" \"$response\" | tr '[:upper:]' '[:lower:]')\n\n    case \"$response_lc\" in\n      \"\"|\"y\"|\"yes\")\n        CODE_DIRS=(\"${DEFAULT_SCAN_DIRS[@]}\")\n        ;;\n      \"q\"|\"quit\"|\"exit\")\n        printf \"Audit cancelled. Pass paths explicitly or set SCAN_DIRS to run non-interactively.\\n\" &gt;&amp;2\n        return 2\n        ;;\n      *)\n        read -ra CODE_DIRS &lt;&lt;&lt; \"$response\"\n        ;;\n    esac\n  else\n    printf \"  %sNo scan directories provided and no tty available; using defaults. Pass DIRS or set SCAN_DIRS to override.%s\\n\" \"$YELLOW\" \"$NC\" &gt;&amp;2\n    CODE_DIRS=(\"${DEFAULT_SCAN_DIRS[@]}\")\n  fi\n\n  local missing=()\n  local existing=()\n  local dir\n  for dir in \"${CODE_DIRS[@]}\"; do\n    if [[ -d \"$dir\" ]]; then\n      existing+=(\"$dir\")\n    else\n      missing+=(\"$dir\")\n    fi\n  done\n\n  if [[ ${#missing[@]} -gt 0 ]]; then\n    printf \"  %sSkipping missing scan path(s): %s%s\\n\" \"$YELLOW\" \"${missing[*]}\" \"$NC\" &gt;&amp;2\n  fi\n\n  CODE_DIRS=(\"${existing[@]}\")\n\n  if [[ ${#CODE_DIRS[@]} -eq 0 ]]; then\n    printf \"No valid scan directories selected. Pass existing directories or set SCAN_DIRS.\\n\" &gt;&amp;2\n    return 2\n  fi\n}\n\n# \u2500\u2500 npm \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_npm() {\n  section \"npm ($(npm --version 2&gt;/dev/null || echo 'unknown'))\"\n\n  local ignore_scripts\n  ignore_scripts=$(npm config get ignore-scripts 2&gt;/dev/null || echo \"undefined\")\n  if [[ \"$ignore_scripts\" == \"true\" ]]; then\n    pass \"npm\" \"ignore-scripts is enabled globally\"\n  else\n    crit \"npm\" \"Lifecycle scripts run on install (preinstall/postinstall)\" \\\n      \"npm config set ignore-scripts true\"\n  fi\n\n  local audit_setting\n  audit_setting=$(npm config get audit 2&gt;/dev/null || echo \"true\")\n  if [[ \"$audit_setting\" == \"true\" ]]; then\n    pass \"npm\" \"npm audit is enabled\"\n  else\n    warn \"npm\" \"npm audit is disabled\" \\\n      \"npm config set audit true\"\n  fi\n\n  local package_lock\n  package_lock=$(npm config get package-lock 2&gt;/dev/null || echo \"true\")\n  if [[ \"$package_lock\" == \"true\" ]]; then\n    pass \"npm\" \"package-lock is enabled\"\n  else\n    warn \"npm\" \"package-lock is disabled \u2014 installs are non-deterministic\" \\\n      \"npm config set package-lock true\"\n  fi\n\n  # Check for custom registries (potential dependency confusion)\n  local registry\n  registry=$(npm config get registry 2&gt;/dev/null || echo \"\")\n  if [[ \"$registry\" == \"https://registry.npmjs.org/\" ]]; then\n    pass \"npm\" \"Using official npm registry\"\n  else\n    info \"npm\" \"Custom registry: $registry \u2014 verify this is trusted\" \"\"\n  fi\n\n\t  # Check for .npmrc files with custom registries in common locations\n\t  if [[ -f \"$HOME/.npmrc\" ]]; then\n\t    local custom_registries\n\t    custom_registries=$(grep -E \"^registry=|^@.*:registry=\" \"$HOME/.npmrc\" 2&gt;/dev/null | grep -v \"registry.npmjs.org\" || true)\n\t    if [[ -n \"$custom_registries\" ]]; then\n\t      info \"npm\" \"Custom registries in ~/.npmrc \u2014 verify these are trusted\" \"\"\n\t    fi\n\n\t    if grep -qiE '^\\s*strict-ssl\\s*=\\s*false' \"$HOME/.npmrc\" 2&gt;/dev/null; then\n\t      crit \"npm\" \"strict-ssl=false in ~/.npmrc \u2014 TLS certificate validation is disabled\" \\\n\t        \"npm config set strict-ssl true\"\n\t    fi\n\n\t    if grep -qiE '(_authToken|_auth|username|password)\\s*=' \"$HOME/.npmrc\" 2&gt;/dev/null; then\n\t      warn \"npm\" \"Credentials or tokens appear in ~/.npmrc \u2014 prefer environment variables or scoped automation tokens\" \\\n\t        \"Review ~/.npmrc and move tokens out of persistent dotfiles where possible\"\n\t    fi\n\n\t    if grep -qiE '^\\s*always-auth\\s*=\\s*true' \"$HOME/.npmrc\" 2&gt;/dev/null; then\n\t      info \"npm\" \"always-auth=true in ~/.npmrc \u2014 tokens may be sent to every matching registry request\" \\\n\t        \"Scope tokens to the smallest possible registry and package namespace\"\n\t    fi\n\t  fi\n\t}\n\n# \u2500\u2500 pnpm \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_pnpm() {\n  section \"pnpm ($(pnpm --version 2&gt;/dev/null || echo 'unknown'))\"\n\n  local ignore_scripts\n  ignore_scripts=$(pnpm config get ignore-scripts 2&gt;/dev/null || echo \"undefined\")\n  if [[ \"$ignore_scripts\" == \"true\" ]]; then\n    pass \"pnpm\" \"ignore-scripts is enabled\"\n  else\n    crit \"pnpm\" \"Lifecycle scripts run on install\" \\\n      \"pnpm config set ignore-scripts true\"\n  fi\n\n  info \"pnpm\" \"Consider using pnpm.onlyBuiltDependencies in package.json to allowlist specific packages\" \\\n    \"Add to package.json: { \\\"pnpm\\\": { \\\"onlyBuiltDependencies\\\": [\\\"esbuild\\\"] } }\"\n}\n\n# \u2500\u2500 yarn \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_yarn() {\n  local yarn_ver\n  yarn_ver=$(yarn --version 2&gt;/dev/null || echo \"unknown\")\n  section \"Yarn ($yarn_ver)\"\n\n  if [[ \"$yarn_ver\" == 1.* ]]; then\n    warn \"yarn\" \"Yarn Classic (v1) \u2014 consider upgrading to Yarn Berry (v3+) for better security\" \"\"\n  fi\n  info \"yarn\" \"Check .yarnrc.yml for enableScripts: false in project roots\" \\\n    \"Add to .yarnrc.yml: enableScripts: false\"\n}\n\n# \u2500\u2500 bun \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_bun() {\n  section \"Bun ($(bun --version 2&gt;/dev/null || echo 'unknown'))\"\n  pass \"bun\" \"Bun disables lifecycle scripts by default \u2014 secure by design\"\n  info \"bun\" \"Use --trust to allowlist specific packages that need scripts\" \"\"\n}\n\n# \u2500\u2500 pip \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_pip() {\n  local pip_cmd=\"${1:-pip3}\"\n  section \"$pip_cmd ($(${pip_cmd} --version 2&gt;/dev/null | awk '{print $2}' || echo 'unknown'))\"\n\n  # Check for require-hashes in pip config\n  local pip_conf=\"$HOME/.config/pip/pip.conf\"\n  local pip_conf_alt=\"$HOME/Library/Application Support/pip/pip.conf\"\n  local has_require_hashes=false\n\n  for conf in \"$pip_conf\" \"$pip_conf_alt\"; do\n    if [[ -f \"$conf\" ]] &amp;&amp; grep -qi \"require-hashes\" \"$conf\" 2&gt;/dev/null; then\n      has_require_hashes=true\n    fi\n  done\n\n  if $has_require_hashes; then\n    pass \"$pip_cmd\" \"require-hashes is configured\"\n  else\n    warn \"$pip_cmd\" \"require-hashes not set \u2014 packages installed without hash verification\" \\\n      \"Add to ~/.config/pip/pip.conf under [global]: require-hashes = true\"\n  fi\n\n\t  # Check for trusted-host (this DISABLES TLS verification)\n\t  for conf in \"$pip_conf\" \"$pip_conf_alt\"; do\n\t    if [[ -f \"$conf\" ]] &amp;&amp; grep -qi \"trusted-host\" \"$conf\" 2&gt;/dev/null; then\n\t      crit \"$pip_cmd\" \"trusted-host found in pip config \u2014 this DISABLES TLS verification for that host\" \\\n\t        \"Remove trusted-host from $conf unless it's an internal HTTP-only mirror\"\n\t    fi\n\t    if [[ -f \"$conf\" ]] &amp;&amp; grep -qiE \"^\\s*extra-index-url\\s*=\" \"$conf\" 2&gt;/dev/null; then\n\t      warn \"$pip_cmd\" \"extra-index-url found in pip config \u2014 vulnerable to dependency confusion if package names overlap\" \\\n\t        \"Prefer a single index-url proxy that mirrors public packages and hosts private packages\"\n\t    fi\n\t    if [[ -f \"$conf\" ]] &amp;&amp; grep -qiE \"^\\s*index-url\\s*=\\s*http://\" \"$conf\" 2&gt;/dev/null; then\n\t      crit \"$pip_cmd\" \"pip index-url uses HTTP \u2014 package metadata and downloads can be intercepted\" \\\n\t        \"Use an HTTPS package index or trusted internal TLS endpoint\"\n\t    fi\n\t  done\n\n  # Check if pip-audit is available\n  if installed pip-audit; then\n    pass \"$pip_cmd\" \"pip-audit is installed\"\n  else\n    info \"$pip_cmd\" \"pip-audit not installed \u2014 recommended for vulnerability scanning\" \\\n      \"${pip_cmd} install pip-audit\"\n  fi\n}\n\n# \u2500\u2500 uv \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_uv() {\n  section \"uv ($(uv --version 2&gt;/dev/null | awk '{print $2}' || echo 'unknown'))\"\n  pass \"uv\" \"uv generates lockfiles with hashes by default \u2014 more secure than pip\"\n  pass \"uv\" \"uv does not run setup.py by default \u2014 uses wheel metadata\"\n  info \"uv\" \"Set require-hashes = true in uv.toml for extra safety\" \\\n    \"Add to uv.toml: require-hashes = true\"\n}\n\n# \u2500\u2500 conda \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_conda() {\n  section \"conda ($(conda --version 2&gt;/dev/null | awk '{print $2}' || echo 'unknown'))\"\n\n  local condarc=\"$HOME/.condarc\"\n\n  if [[ -f \"$condarc\" ]]; then\n    if grep -q \"channel_priority: strict\" \"$condarc\" 2&gt;/dev/null; then\n      pass \"conda\" \"channel_priority is strict\"\n    else\n      warn \"conda\" \"channel_priority not set to strict \u2014 risk of channel hijacking\" \\\n        \"Add to ~/.condarc: channel_priority: strict\"\n    fi\n\n    if grep -q \"ssl_verify: false\" \"$condarc\" 2&gt;/dev/null; then\n      crit \"conda\" \"SSL verification is disabled\" \\\n        \"Set ssl_verify: true in ~/.condarc\"\n    else\n      pass \"conda\" \"SSL verification is enabled\"\n    fi\n\n    if grep -q \"auto_update_conda: false\" \"$condarc\" 2&gt;/dev/null; then\n      pass \"conda\" \"Auto-update of conda is disabled\"\n    else\n      warn \"conda\" \"conda auto-updates itself \u2014 could pull compromised binary\" \\\n        \"Add to ~/.condarc: auto_update_conda: false\"\n    fi\n  else\n    warn \"conda\" \"No ~/.condarc found \u2014 using defaults (flexible channel priority)\" \\\n      \"Create ~/.condarc with: channel_priority: strict\"\n  fi\n}\n\n# \u2500\u2500 cargo \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_cargo() {\n  section \"Cargo ($(cargo --version 2&gt;/dev/null | awk '{print $2}' || echo 'unknown'))\"\n\n  if installed cargo-audit; then\n    pass \"cargo\" \"cargo-audit is installed\"\n  else\n    warn \"cargo\" \"cargo-audit not installed \u2014 no vulnerability scanning\" \\\n      \"cargo install cargo-audit\"\n  fi\n\n  if installed cargo-vet; then\n    pass \"cargo\" \"cargo-vet is installed (supply chain audit)\"\n  else\n    info \"cargo\" \"cargo-vet not installed \u2014 Mozilla's supply chain review tool\" \\\n      \"cargo install cargo-vet\"\n  fi\n\n  warn \"cargo\" \"build.rs scripts run with full system access \u2014 no stable sandboxing exists\" \\\n    \"Review build.rs in dependencies: find ~/.cargo/registry -name build.rs | head -20\"\n}\n\n# \u2500\u2500 go \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_go() {\n  section \"Go ($(go version 2&gt;/dev/null | awk '{print $3}' || echo 'unknown'))\"\n\n  local gosumdb\n  gosumdb=$(go env GOSUMDB 2&gt;/dev/null || echo \"\")\n  if [[ \"$gosumdb\" == \"off\" ]]; then\n    crit \"go\" \"GOSUMDB=off \u2014 public module checksum verification is disabled\" \\\n      \"go env -w GOSUMDB=sum.golang.org\"\n  else\n    pass \"go\" \"GOSUMDB is enabled (${gosumdb:-default})\"\n  fi\n\n  local gonosumdb\n  gonosumdb=$(go env GONOSUMDB 2&gt;/dev/null || echo \"\")\n  if [[ -z \"$gonosumdb\" ]]; then\n    pass \"go\" \"GONOSUMDB is empty \u2014 modules use checksum database unless private\"\n  elif [[ \"$gonosumdb\" == \"*\" ]]; then\n    crit \"go\" \"GONOSUMDB=* \u2014 ALL checksum verification is disabled\" \\\n      \"go env -w GONOSUMDB=''\"\n  else\n    info \"go\" \"GONOSUMDB=$gonosumdb \u2014 matching modules skip public checksum verification\" \"\"\n  fi\n\n  local goproxy\n  goproxy=$(go env GOPROXY 2&gt;/dev/null || echo \"\")\n  if [[ \"$goproxy\" == \"direct\" ]]; then\n    warn \"go\" \"GOPROXY=direct \u2014 bypasses module proxy caching and checksum mirror workflow\" \\\n      \"go env -w GOPROXY=https://proxy.golang.org,direct\"\n  else\n    pass \"go\" \"GOPROXY is set to ${goproxy:-default}\"\n  fi\n\n  local goflags\n  goflags=$(go env GOFLAGS 2&gt;/dev/null || echo \"\")\n  if [[ \"$goflags\" == *\"-mod=readonly\"* || \"$goflags\" == *\"-mod=vendor\"* ]]; then\n    pass \"go\" \"GOFLAGS includes -mod=readonly or -mod=vendor\"\n  else\n    warn \"go\" \"GOFLAGS does not enforce read-only module graph\" \\\n      \"go env -w GOFLAGS='-mod=readonly'\"\n  fi\n\n  if installed govulncheck; then\n    pass \"go\" \"govulncheck is installed\"\n  else\n    info \"go\" \"govulncheck not installed \u2014 official Go vulnerability checker\" \\\n      \"go install golang.org/x/vuln/cmd/govulncheck@latest\"\n  fi\n}\n\n# \u2500\u2500 homebrew \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_homebrew() {\n  section \"Homebrew ($(brew --version 2&gt;/dev/null | head -1 | awk '{print $2}' || echo 'unknown'))\"\n\n  # Check for third-party taps\n  local taps\n  taps=$(brew tap 2&gt;/dev/null || echo \"\")\n  local third_party\n  third_party=$(echo \"$taps\" | grep -v \"^homebrew/\" || true)\n  if [[ -n \"$third_party\" ]]; then\n    local tap_count\n    tap_count=$(echo \"$third_party\" | wc -l | tr -d ' ')\n    warn \"homebrew\" \"$tap_count third-party tap(s) installed \u2014 verify these are trusted\" \\\n      \"Review: brew tap | grep -v homebrew/\"\n    while IFS= read -r tap; do\n      [[ -n \"$tap\" ]] &amp;&amp; info \"homebrew\" \"  Third-party tap: $tap\" \"\"\n    done &lt;&lt;&lt; \"$third_party\"\n  else\n    pass \"homebrew\" \"No third-party taps installed\"\n  fi\n\n  if [[ \"${HOMEBREW_NO_AUTO_UPDATE:-0}\" == \"1\" ]]; then\n    pass \"homebrew\" \"HOMEBREW_NO_AUTO_UPDATE=1 \u2014 manual update control\"\n  else\n    info \"homebrew\" \"Auto-update is enabled (default) \u2014 consider manual control\" \\\n      \"export HOMEBREW_NO_AUTO_UPDATE=1  # add to ~/.zshrc\"\n  fi\n\n  if [[ \"${HOMEBREW_NO_ANALYTICS:-0}\" == \"1\" ]]; then\n    pass \"homebrew\" \"Analytics are disabled\"\n  else\n    warn \"homebrew\" \"Analytics are enabled \u2014 sends telemetry to Homebrew\" \\\n      \"export HOMEBREW_NO_ANALYTICS=1  # add to ~/.zshrc\"\n  fi\n\n  local cask_opts=\"${HOMEBREW_CASK_OPTS:-}\"\n  if [[ \"$cask_opts\" == *\"--no-quarantine\"* ]]; then\n    crit \"homebrew\" \"HOMEBREW_CASK_OPTS contains --no-quarantine \u2014 bypasses macOS Gatekeeper\" \\\n      \"Remove --no-quarantine from HOMEBREW_CASK_OPTS in ~/.zshrc\"\n  else\n    pass \"homebrew\" \"Cask quarantine is enabled (Gatekeeper active)\"\n  fi\n}\n\n# \u2500\u2500 VS Code \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_vscode() {\n  local code_cmd=\"\"\n  if installed code; then\n    code_cmd=\"code\"\n  elif installed code-insiders; then\n    code_cmd=\"code-insiders\"\n  fi\n\n  section \"VS Code ($code_cmd)\"\n\n  local settings_file=\"$HOME/Library/Application Support/Code/User/settings.json\"\n  if [[ \"$code_cmd\" == \"code-insiders\" ]]; then\n    settings_file=\"$HOME/Library/Application Support/Code - Insiders/User/settings.json\"\n  fi\n\n  if [[ ! -f \"$settings_file\" ]]; then\n    info \"vscode\" \"No settings.json found at expected path\" \"\"\n    return\n  fi\n\n  # Helper to read a VS Code setting from JSONC\n  _vscode_setting() {\n    python3 -c \"\nimport json, re\nwith open('$settings_file') as f:\n    content = f.read()\n    content = re.sub(r'//.*$', '', content, flags=re.MULTILINE)\n    content = re.sub(r'/\\*.*?\\*/', '', content, flags=re.DOTALL)\n    content = re.sub(r',\\s*([}\\]])', r'\\1', content)\n    try:\n        d = json.loads(content)\n        print(d.get('$1', 'NOT_SET'))\n    except: print('PARSE_ERROR')\n\" 2&gt;/dev/null || echo \"PARSE_ERROR\"\n  }\n\n  # Check autoUpdate\n  local auto_update\n  auto_update=$(_vscode_setting \"extensions.autoUpdate\")\n\n  if [[ \"$auto_update\" == \"false\" || \"$auto_update\" == \"False\" ]]; then\n    pass \"vscode\" \"Extension auto-update is disabled\"\n  elif [[ \"$auto_update\" == \"PARSE_ERROR\" ]]; then\n    info \"vscode\" \"Could not parse settings.json \u2014 check manually\" \"\"\n  else\n    crit \"vscode\" \"Extension auto-update is enabled \u2014 compromised updates install silently\" \\\n      \"Add to VS Code settings.json: \\\"extensions.autoUpdate\\\": false\"\n  fi\n\n  # Check autoCheckUpdates\n  local auto_check\n  auto_check=$(_vscode_setting \"extensions.autoCheckUpdates\")\n\n  if [[ \"$auto_check\" == \"false\" || \"$auto_check\" == \"False\" ]]; then\n    pass \"vscode\" \"Extension auto-check for updates is disabled\"\n  elif [[ \"$auto_check\" != \"PARSE_ERROR\" ]]; then\n    warn \"vscode\" \"Extension auto-check for updates is enabled\" \\\n      \"Add to VS Code settings.json: \\\"extensions.autoCheckUpdates\\\": false\"\n  fi\n\n  # Check task.allowAutomaticTasks\n  local auto_tasks\n  auto_tasks=$(_vscode_setting \"task.allowAutomaticTasks\")\n\n  if [[ \"$auto_tasks\" == \"off\" ]]; then\n    pass \"vscode\" \"Automatic task execution is disabled\"\n  elif [[ \"$auto_tasks\" != \"PARSE_ERROR\" ]]; then\n    crit \"vscode\" \"Automatic tasks enabled \u2014 .vscode/tasks.json can execute code on folder open\" \\\n      \"Add to VS Code settings.json: \\\"task.allowAutomaticTasks\\\": \\\"off\\\"\"\n  fi\n\n  # Check workspace trust\n  local workspace_trust\n  workspace_trust=$(_vscode_setting \"security.workspace.trust.enabled\")\n\n  if [[ \"$workspace_trust\" == \"false\" || \"$workspace_trust\" == \"False\" ]]; then\n    crit \"vscode\" \"Workspace Trust is disabled \u2014 all folders are trusted\" \\\n      \"Set in VS Code settings.json: \\\"security.workspace.trust.enabled\\\": true\"\n  else\n    pass \"vscode\" \"Workspace Trust is enabled (or default)\"\n  fi\n\n  # Count installed extensions\n  if [[ -n \"$code_cmd\" ]]; then\n    local ext_count\n    ext_count=$($code_cmd --list-extensions 2&gt;/dev/null | wc -l | tr -d ' ')\n    info \"vscode\" \"$ext_count extensions installed \u2014 review periodically\" \\\n      \"$code_cmd --list-extensions\"\n  fi\n}\n\n# \u2500\u2500 Docker \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_docker() {\n  section \"Docker ($(docker --version 2&gt;/dev/null | awk '{print $3}' | tr -d ',' || echo 'unknown'))\"\n\n  if [[ \"${DOCKER_CONTENT_TRUST:-0}\" == \"1\" ]]; then\n    info \"docker\" \"DOCKER_CONTENT_TRUST=1 is set, but Docker Content Trust is legacy and has limited modern coverage\" \\\n      \"Prefer digest-pinned images plus cosign/notation verification for critical images\"\n  else\n    info \"docker\" \"Docker Content Trust is not enabled; use digest pinning and modern signature verification instead\" \\\n      \"Pin Dockerfiles with @sha256 and verify important images with cosign or notation\"\n  fi\n\n  # Check credential storage\n  local docker_config=\"$HOME/.docker/config.json\"\n  if [[ -f \"$docker_config\" ]]; then\n    local creds_store\n    creds_store=$(python3 -c \"\nimport json\nwith open('$docker_config') as f:\n    d = json.load(f)\n    print(d.get('credsStore', 'NONE'))\n\" 2&gt;/dev/null || echo \"UNKNOWN\")\n\n    if [[ \"$creds_store\" == \"osxkeychain\" || \"$creds_store\" == \"desktop\" ]]; then\n      pass \"docker\" \"Credentials stored in macOS Keychain ($creds_store)\"\n    elif [[ \"$creds_store\" == \"NONE\" ]]; then\n      warn \"docker\" \"No credential store configured \u2014 credentials may be in plaintext\" \\\n        \"Add to ~/.docker/config.json: \\\"credsStore\\\": \\\"osxkeychain\\\"\"\n    else\n      info \"docker\" \"Credential store: $creds_store\" \"\"\n    fi\n\n    # Check for stored auth tokens in plaintext\n    local has_auths\n    has_auths=$(python3 -c \"\nimport json\nwith open('$docker_config') as f:\n    d = json.load(f)\n    auths = d.get('auths', {})\n    has_auth = any('auth' in v for v in auths.values() if isinstance(v, dict))\n    print('yes' if has_auth else 'no')\n\" 2&gt;/dev/null || echo \"unknown\")\n\n    if [[ \"$has_auths\" == \"yes\" ]]; then\n      crit \"docker\" \"Plaintext credentials found in ~/.docker/config.json\" \\\n        \"Use a credential store: docker-credential-osxkeychain\"\n    fi\n  fi\n}\n\n# \u2500\u2500 Git Hooks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_git() {\n  section \"Git ($(git --version 2&gt;/dev/null | awk '{print $3}' || echo 'unknown'))\"\n\n  # Check global hooks path\n  local hooks_path\n  hooks_path=$(git config --global core.hooksPath 2&gt;/dev/null || echo \"NOT_SET\")\n  if [[ \"$hooks_path\" == \"NOT_SET\" || -z \"$hooks_path\" ]]; then\n    info \"git\" \"No global core.hooksPath set \u2014 repos use their own .git/hooks/\" \\\n      \"git config --global core.hooksPath ~/.git-hooks  # to control hook execution\"\n  else\n    pass \"git\" \"Global hooks path: $hooks_path\"\n  fi\n\n  # Check for filter drivers (clean/smudge can execute arbitrary code)\n  local filters\n  filters=$(git config --global --get-regexp 'filter\\..*\\.(clean|smudge)' 2&gt;/dev/null || true)\n  if [[ -n \"$filters\" ]]; then\n    warn \"git\" \"Git filter drivers configured \u2014 these execute on checkout/staging\" \\\n      \"Review: git config --global --get-regexp filter\"\n  fi\n\n  # Check safe.directory\n  local safe_dirs\n  safe_dirs=$(git config --global --get-all safe.directory 2&gt;/dev/null || true)\n  if [[ \"$safe_dirs\" == \"*\" ]]; then\n    crit \"git\" \"safe.directory is set to * \u2014 trusts ALL directories\" \\\n      \"git config --global --unset-all safe.directory &amp;&amp; add specific paths\"\n  elif [[ -n \"$safe_dirs\" ]]; then\n    info \"git\" \"safe.directory has explicit entries \u2014 review periodically\" \"\"\n  fi\n}\n\n# \u2500\u2500 GitHub Actions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_github_actions() {\n  section \"GitHub Actions (local workflow files)\"\n\n  local code_dirs=(\"${CODE_DIRS[@]}\" \".\")\n  local unpinned_count=0\n  local scanned_files=0\n\n  for base in \"${code_dirs[@]}\"; do\n    [[ -d \"$base\" ]] || continue\n    while IFS= read -r workflow; do\n      ((++scanned_files))\n      local unpinned\n      unpinned=$(python3 - \"$workflow\" &lt;&lt;'PY' 2&gt;/dev/null || true\nimport re\nimport sys\n\nworkflow = sys.argv[1]\nbad = []\nuses_re = re.compile(r\"^\\s*uses:\\s*['\\\"]?([^'\\\"\\s#]+)\")\nsha_re = re.compile(r\"^[a-fA-F0-9]{40}$\")\n\nwith open(workflow, encoding=\"utf-8\", errors=\"ignore\") as fh:\n    for lineno, line in enumerate(fh, 1):\n        match = uses_re.search(line)\n        if not match:\n            continue\n\n        spec = match.group(1)\n        if spec.startswith((\"./\", \"../\")):\n            continue\n\n        if spec.startswith(\"docker://\"):\n            if \"@sha256:\" not in spec:\n                bad.append(f\"{lineno}:{line.rstrip()}\")\n            continue\n\n        if \"@\" not in spec:\n            bad.append(f\"{lineno}:{line.rstrip()}\")\n            continue\n\n        ref = spec.rsplit(\"@\", 1)[1]\n        if not sha_re.fullmatch(ref):\n            bad.append(f\"{lineno}:{line.rstrip()}\")\n\n        if len(bad) &gt;= 5:\n            break\n\nprint(\"\\n\".join(bad))\nPY\n)\n      if [[ -n \"$unpinned\" ]]; then\n        ((++unpinned_count))\n        if [[ $unpinned_count -le 3 ]]; then\n          local rel_path=\"${workflow#\"$HOME\"/}\"\n          warn \"github-actions\" \"Unpinned action refs in ~/$rel_path (tags, branches, or docker tags)\" \\\n            \"Pin to SHA: npm install -g pin-github-action &amp;&amp; pin-github-action $workflow\"\n        fi\n      fi\n    done &lt; &lt;(find \"$base\" -maxdepth \"$SCAN_DEPTH\" \\( -path '*/.github/workflows/*.yml' -o -path '*/.github/workflows/*.yaml' \\) 2&gt;/dev/null || true)\n  done\n\n  if [[ $scanned_files -eq 0 ]]; then\n    info \"github-actions\" \"No workflow files found in common code directories\" \"\"\n  elif [[ $unpinned_count -eq 0 ]]; then\n    pass \"github-actions\" \"All $scanned_files workflow files use pinned references\"\n  else\n    [[ $unpinned_count -gt 3 ]] &amp;&amp; warn \"github-actions\" \"...and $((unpinned_count - 3)) more files with unpinned actions\" \"\"\n    info \"github-actions\" \"Use pin-github-action to auto-pin: npx pin-github-action \" \"\"\n  fi\n}\n\n# \u2500\u2500 Chrome Extensions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_chrome() {\n  section \"Chrome Extensions\"\n\n  local chrome_ext_dir=\"$HOME/Library/Application Support/Google/Chrome/Default/Extensions\"\n  if [[ ! -d \"$chrome_ext_dir\" ]]; then\n    info \"chrome\" \"Chrome extensions directory not found\" \"\"\n    return\n  fi\n\n  local ext_count=0\n  local high_perm_count=0\n\n  for dir in \"$chrome_ext_dir\"/*/; do\n    [[ -d \"$dir\" ]] || continue\n    ((++ext_count))\n    local manifest\n    manifest=$(find \"$dir\" -name \"manifest.json\" -maxdepth 2 2&gt;/dev/null | head -1)\n    if [[ -f \"$manifest\" ]]; then\n      local has_all_urls\n      has_all_urls=$(python3 -c \"\nimport json\ntry:\n    m = json.load(open('$manifest'))\n    perms = m.get('permissions', []) + m.get('host_permissions', [])\n    perms_str = ' '.join(str(p) for p in perms)\n    if '' in perms_str or '*://*/*' in perms_str or 'cookies' in perms_str:\n        print('yes')\n    else:\n        print('no')\nexcept: print('error')\n\" 2&gt;/dev/null || echo \"error\")\n      [[ \"$has_all_urls\" == \"yes\" ]] &amp;&amp; ((++high_perm_count))\n    fi\n  done\n\n  info \"chrome\" \"$ext_count extensions installed\" \"\"\n  if [[ $high_perm_count -gt 0 ]]; then\n    warn \"chrome\" \"$high_perm_count extension(s) have broad permissions (, cookies, or *://*/*)\" \\\n      \"Audit at chrome://extensions \u2014 review permissions for each extension\"\n  else\n    pass \"chrome\" \"No extensions with broad permissions detected\"\n  fi\n}\n\n# \u2500\u2500 Ruby/Bundler \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_ruby() {\n  section \"Ruby/Bundler ($(ruby --version 2&gt;/dev/null | awk '{print $2}' || echo 'unknown'))\"\n\n  if installed bundle; then\n    if installed bundler-audit || gem list -i bundler-audit &amp;&gt;/dev/null; then\n      pass \"ruby\" \"bundler-audit is installed\"\n    else\n      info \"ruby\" \"bundler-audit not installed \u2014 vulnerability scanning for gems\" \\\n        \"gem install bundler-audit\"\n    fi\n\n    local frozen\n    frozen=$(bundle config get frozen 2&gt;/dev/null | grep -o 'true\\|false' | head -1 || echo \"unknown\")\n    if [[ \"$frozen\" == \"true\" ]]; then\n      pass \"ruby\" \"Bundler frozen mode is enabled\"\n    else\n      warn \"ruby\" \"Bundler frozen mode is not set \u2014 Gemfile.lock can mutate\" \\\n        \"bundle config set --global frozen true\"\n    fi\n  fi\n}\n\n# \u2500\u2500 Dependency Pinning Audit \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_dependency_pinning() {\n  section \"Dependency Pinning (project files)\"\n\n  local code_dirs=(\"${CODE_DIRS[@]}\")\n  # \u2500\u2500 Python: requirements.txt \u2500\u2500\n  local py_unpinned_files=0\n  local py_unpinned_total=0\n  local py_no_hash_files=0\n  local py_files_checked=0\n\n  for base in \"${code_dirs[@]}\"; do\n    [[ -d \"$base\" ]] || continue\n    while IFS= read -r reqfile; do\n      ((++py_files_checked))\n      local rel=\"${reqfile#\"$HOME\"/}\"\n\n      # Check for unpinned deps (lines without == that aren't comments/flags/blanks)\n      local unpinned\n      unpinned=$(grep -cE '^[a-zA-Z][a-zA-Z0-9_.-]*\\s*($|[&gt;/dev/null || true)\n      unpinned=\"${unpinned//[^0-9]/}\"\n      unpinned=\"${unpinned:-0}\"\n      if [[ $unpinned -gt 0 ]]; then\n        ((++py_unpinned_files))\n        py_unpinned_total=$((py_unpinned_total + unpinned))\n        if [[ $py_unpinned_files -le 3 ]]; then\n          warn \"deps-python\" \"$unpinned unpinned dep(s) in ~/$rel\" \\\n            \"Pin with ==: pip-compile --generate-hashes $reqfile\"\n        fi\n      fi\n\n      # Check for missing hashes\n      if ! grep -q -- '--hash=' \"$reqfile\" 2&gt;/dev/null; then\n        ((++py_no_hash_files))\n      fi\n    done &lt; &lt;(find \"$base\" -maxdepth \"$SCAN_DEPTH\" -name \"requirements*.txt\" -not -path \"*/node_modules/*\" -not -path \"*/.venv/*\" -not -path \"*/venv/*\" -not -path \"*/.tox/*\" 2&gt;/dev/null || true)\n  done\n\n  if [[ $py_files_checked -gt 0 ]]; then\n    if [[ $py_unpinned_files -gt 0 ]]; then\n      [[ $py_unpinned_files -gt 3 ]] &amp;&amp; warn \"deps-python\" \"...and $((py_unpinned_files - 3)) more requirements files with unpinned deps\" \"\"\n      crit \"deps-python\" \"$py_unpinned_total unpinned Python dep(s) across $py_unpinned_files file(s) \u2014 builds can pull compromised versions\" \\\n        \"Use pip-compile --generate-hashes or pin all deps with ==\"\n    else\n      pass \"deps-python\" \"All $py_files_checked requirements file(s) have pinned versions\"\n    fi\n    if [[ $py_no_hash_files -gt 0 ]]; then\n      warn \"deps-python\" \"$py_no_hash_files requirements file(s) without --hash verification\" \\\n        \"pip-compile --generate-hashes requirements.in\"\n    fi\n  fi\n\n  # \u2500\u2500 Python: pyproject.toml loose constraints \u2500\u2500\n  local pyproj_loose=0\n  local pyproj_checked=0\n  for base in \"${code_dirs[@]}\"; do\n    [[ -d \"$base\" ]] || continue\n    while IFS= read -r pyproj; do\n      ((++pyproj_checked))\n      local rel=\"${pyproj#\"$HOME\"/}\"\n      # Look for dependencies with &gt;= or ~= or &gt; or * (loose constraints)\n      local loose\n\t      loose=$(python3 - \"$pyproj\" &lt;&lt;'PY' 2&gt;/dev/null || echo \"0\"\nimport re\nimport sys\n\npath = sys.argv[1]\ntry:\n    try:\n        import tomllib\n    except ModuleNotFoundError:\n        import tomli as tomllib\n\n    with open(path, \"rb\") as fh:\n        data = tomllib.load(fh)\n\n    specs = []\n\n    def add_spec(value):\n        if isinstance(value, str):\n            specs.append(value)\n        elif isinstance(value, list):\n            for item in value:\n                add_spec(item)\n        elif isinstance(value, dict):\n            version = value.get(\"version\")\n            if version is not None:\n                add_spec(str(version))\n            for nested in value.values():\n                if isinstance(nested, (list, dict)):\n                    add_spec(nested)\n\n    project = data.get(\"project\", {})\n    add_spec(project.get(\"dependencies\", []))\n    add_spec(project.get(\"optional-dependencies\", {}))\n    add_spec(data.get(\"dependency-groups\", {}))\n\n    poetry = data.get(\"tool\", {}).get(\"poetry\", {})\n    add_spec(poetry.get(\"dependencies\", {}))\n    add_spec(poetry.get(\"group\", {}))\n    add_spec(poetry.get(\"dev-dependencies\", {}))\n\n    loose = 0\n    for spec in specs:\n        s = str(spec).strip()\n        if not s or s.lower().startswith(\"python\"):\n            continue\n        if re.search(r\"(^|[&lt;&gt;=!,\\s])([&gt;~^*]|&gt;=|&gt;|!=)|\\*\", s):\n            loose += 1\n        elif re.match(r\"^[A-Za-z0-9_.-]+$\", s):\n            loose += 1\n\n    print(loose)\nexcept Exception:\n    print(0)\nPY\n)\n      loose=\"${loose//[^0-9]/}\"\n      loose=\"${loose:-0}\"\n      if [[ $loose -gt 0 ]]; then\n        ((++pyproj_loose))\n        if [[ $pyproj_loose -le 2 ]]; then\n          warn \"deps-python\" \"$loose loosely pinned dep(s) in ~/$rel\" \\\n            \"Consider pinning to exact versions in production dependencies\"\n        fi\n      fi\n    done &lt; &lt;(find \"$base\" -maxdepth \"$SCAN_DEPTH\" -name \"pyproject.toml\" -not -path \"*/node_modules/*\" -not -path \"*/.venv/*\" 2&gt;/dev/null || true)\n  done\n\n  # \u2500\u2500 JavaScript: missing lockfiles \u2500\u2500\n  local js_no_lock=0\n  local js_checked=0\n  local js_unpinned_files=0\n\n  for base in \"${code_dirs[@]}\"; do\n    [[ -d \"$base\" ]] || continue\n    while IFS= read -r pkgjson; do\n      local dir\n      dir=$(dirname \"$pkgjson\")\n      local rel=\"${dir#\"$HOME\"/}\"\n      ((++js_checked))\n\n      # Check for ANY lockfile\n      if [[ ! -f \"$dir/package-lock.json\" &amp;&amp; ! -f \"$dir/yarn.lock\" &amp;&amp; ! -f \"$dir/pnpm-lock.yaml\" &amp;&amp; ! -f \"$dir/bun.lockb\" &amp;&amp; ! -f \"$dir/bun.lock\" ]]; then\n        ((++js_no_lock))\n        if [[ $js_no_lock -le 3 ]]; then\n          crit \"deps-js\" \"No lockfile in ~/$rel \u2014 builds are non-deterministic\" \\\n            \"cd ~/$rel &amp;&amp; npm install  # generates package-lock.json\"\n        fi\n      fi\n\n      # Check for dangerous version ranges: *, latest, &gt;=, or no range at all\n      local dangerous\n      dangerous=$(python3 -c \"\nimport json\ntry:\n    pkg = json.load(open('$pkgjson'))\n    count = 0\n    for section in ['dependencies', 'devDependencies']:\n        deps = pkg.get(section, {})\n        for name, ver in deps.items():\n            v = str(ver).strip()\n            if v in ('*', 'latest', '') or v.startswith('&gt;=') or v.startswith('&gt;'):\n                count += 1\n    print(count)\nexcept: print(0)\n\" 2&gt;/dev/null || echo \"0\")\n      dangerous=\"${dangerous//[^0-9]/}\"\n      dangerous=\"${dangerous:-0}\"\n      if [[ $dangerous -gt 0 ]]; then\n        ((++js_unpinned_files))\n        if [[ $js_unpinned_files -le 3 ]]; then\n          warn \"deps-js\" \"$dangerous wildcard/unpinned dep(s) in ~/$rel/package.json (*, latest, &gt;=)\" \\\n            \"Pin to specific semver ranges: npm pkg fix\"\n        fi\n      fi\n    done &lt; &lt;(find \"$base\" -maxdepth \"$SCAN_DEPTH\" -name \"package.json\" -not -path \"*/node_modules/*\" -not -path \"*/.next/*\" -not -path \"*/dist/*\" -not -path \"*/.turbo/*\" 2&gt;/dev/null || true)\n  done\n\n  if [[ $js_checked -gt 0 ]]; then\n    if [[ $js_no_lock -gt 0 ]]; then\n      [[ $js_no_lock -gt 3 ]] &amp;&amp; crit \"deps-js\" \"...and $((js_no_lock - 3)) more JS projects without lockfiles\" \"\"\n    else\n      pass \"deps-js\" \"All $js_checked JS project(s) have lockfiles\"\n    fi\n    if [[ $js_unpinned_files -gt 3 ]]; then\n      warn \"deps-js\" \"...and $((js_unpinned_files - 3)) more package.json files with wildcard deps\" \"\"\n    fi\n  fi\n\n  # \u2500\u2500 Rust: Cargo.toml without Cargo.lock \u2500\u2500\n  local cargo_no_lock=0\n  local cargo_checked=0\n  for base in \"${code_dirs[@]}\"; do\n    [[ -d \"$base\" ]] || continue\n    while IFS= read -r cargotoml; do\n      local dir\n      dir=$(dirname \"$cargotoml\")\n      # Only check if it looks like a binary project (has [[bin]] or default main.rs)\n      if [[ -f \"$dir/src/main.rs\" ]] || grep -q '\\[\\[bin\\]\\]' \"$cargotoml\" 2&gt;/dev/null; then\n        ((++cargo_checked))\n        if [[ ! -f \"$dir/Cargo.lock\" ]]; then\n          ((++cargo_no_lock))\n          local rel=\"${dir#\"$HOME\"/}\"\n          if [[ $cargo_no_lock -le 2 ]]; then\n            warn \"deps-rust\" \"No Cargo.lock in ~/$rel \u2014 binary builds are non-deterministic\" \\\n              \"cd ~/$rel &amp;&amp; cargo generate-lockfile &amp;&amp; git add Cargo.lock\"\n          fi\n        fi\n      fi\n    done &lt; &lt;(find \"$base\" -maxdepth \"$SCAN_DEPTH\" -name \"Cargo.toml\" -not -path \"*/target/*\" 2&gt;/dev/null || true)\n  done\n\n  if [[ $cargo_checked -gt 0 &amp;&amp; $cargo_no_lock -eq 0 ]]; then\n    pass \"deps-rust\" \"All $cargo_checked Rust binary project(s) have Cargo.lock\"\n  fi\n\n  # \u2500\u2500 Go: go.mod without go.sum \u2500\u2500\n  local go_no_sum=0\n  local go_checked=0\n  for base in \"${code_dirs[@]}\"; do\n    [[ -d \"$base\" ]] || continue\n    while IFS= read -r gomod; do\n      local dir\n      dir=$(dirname \"$gomod\")\n      ((++go_checked))\n      if [[ ! -f \"$dir/go.sum\" ]]; then\n        ((++go_no_sum))\n        local rel=\"${dir#\"$HOME\"/}\"\n        if [[ $go_no_sum -le 2 ]]; then\n          warn \"deps-go\" \"No go.sum in ~/$rel \u2014 module checksums not tracked\" \\\n            \"cd ~/$rel &amp;&amp; go mod tidy\"\n        fi\n      fi\n    done &lt; &lt;(find \"$base\" -maxdepth \"$SCAN_DEPTH\" -name \"go.mod\" -not -path \"*/vendor/*\" 2&gt;/dev/null || true)\n  done\n\n  if [[ $go_checked -gt 0 &amp;&amp; $go_no_sum -eq 0 ]]; then\n    pass \"deps-go\" \"All $go_checked Go project(s) have go.sum\"\n  fi\n\n  # \u2500\u2500 Docker: Dockerfiles with unpinned base images \u2500\u2500\n  local docker_unpinned=0\n  local docker_checked=0\n  for base in \"${code_dirs[@]}\"; do\n    [[ -d \"$base\" ]] || continue\n    while IFS= read -r dockerfile; do\n      ((++docker_checked))\n\t      # Check external FROM images without @sha256 digest pinning.\n\t      # Multi-stage aliases are allowed, but external base images with \"AS\" still need a digest.\n\t      local real_unpinned\n\t      real_unpinned=$(python3 - \"$dockerfile\" &lt;&lt;'PY' 2&gt;/dev/null || echo \"0\"\nimport re\nimport sys\n\npath = sys.argv[1]\nstages = set()\nunpinned = 0\nfrom_re = re.compile(r\"^\\s*FROM\\s+(.+)$\", re.IGNORECASE)\n\nwith open(path, encoding=\"utf-8\", errors=\"ignore\") as fh:\n    for raw in fh:\n        line = raw.split(\"#\", 1)[0].strip()\n        match = from_re.match(line)\n        if not match:\n            continue\n\n        tokens = match.group(1).split()\n        while tokens and tokens[0].startswith(\"--\"):\n            tokens.pop(0)\n        if not tokens:\n            continue\n\n        image = tokens[0]\n        lower_tokens = [token.lower() for token in tokens]\n        if \"as\" in lower_tokens:\n            idx = lower_tokens.index(\"as\")\n            if idx + 1 &lt; len(tokens):\n                stages.add(tokens[idx + 1])\n\n        if image == \"scratch\" or image in stages:\n            continue\n        if \"@sha256:\" not in image:\n            unpinned += 1\n\nprint(unpinned)\nPY\n)\n      real_unpinned=\"${real_unpinned//[^0-9]/}\"\n      real_unpinned=\"${real_unpinned:-0}\"\n      if [[ $real_unpinned -gt 0 ]]; then\n        ((++docker_unpinned))\n        local rel=\"${dockerfile#\"$HOME\"/}\"\n        if [[ $docker_unpinned -le 3 ]]; then\n          warn \"deps-docker\" \"Unpinned base image(s) in ~/$rel \u2014 tags are mutable\" \\\n            \"Pin by digest: FROM image@sha256:abc123...\"\n        fi\n      fi\n    done &lt; &lt;(find \"$base\" -maxdepth \"$SCAN_DEPTH\" \\( -name \"Dockerfile\" -o -name \"Dockerfile.*\" -o -name \"*.dockerfile\" \\) -not -path \"*/node_modules/*\" 2&gt;/dev/null || true)\n  done\n\n  if [[ $docker_checked -gt 0 ]]; then\n    if [[ $docker_unpinned -eq 0 ]]; then\n      pass \"deps-docker\" \"All $docker_checked Dockerfile(s) use digest-pinned base images\"\n    else\n      [[ $docker_unpinned -gt 3 ]] &amp;&amp; warn \"deps-docker\" \"...and $((docker_unpinned - 3)) more Dockerfiles with unpinned images\" \"\"\n    fi\n  fi\n\n  # Summary line\n  local total_scanned=$((py_files_checked + js_checked + cargo_checked + go_checked + docker_checked + pyproj_checked))\n  if [[ $total_scanned -eq 0 ]]; then\n    info \"deps\" \"No dependency files found in common code directories ($HOME/code, etc.)\" \"\"\n  else\n    info \"deps\" \"Scanned $total_scanned dependency manifest(s) across ${#code_dirs[@]} directories\" \"\"\n\t  fi\n\t}\n\n# \u2500\u2500 Secrets &amp; Local Credentials \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_local_credentials() {\n  section \"Local Credentials &amp; Token Surfaces\"\n\n  local credential_files=(\n    \"$HOME/.npmrc\"\n    \"$HOME/.pypirc\"\n    \"$HOME/.netrc\"\n    \"$HOME/.gem/credentials\"\n    \"$HOME/.cargo/credentials\"\n    \"$HOME/.cargo/credentials.toml\"\n    \"$HOME/.docker/config.json\"\n    \"$HOME/.config/gh/hosts.yml\"\n    \"$HOME/.aws/credentials\"\n    \"$HOME/.config/gcloud/application_default_credentials.json\"\n  )\n\n  local found=0\n  for file in \"${credential_files[@]}\"; do\n    [[ -f \"$file\" ]] || continue\n    ((++found))\n\n    case \"$file\" in\n      \"$HOME/.docker/config.json\")\n        if grep -q '\"auth\"' \"$file\" 2&gt;/dev/null; then\n          crit \"credentials\" \"Docker config stores registry auth material in plaintext/base64 form\" \\\n            \"Configure Docker credential helpers and remove auth entries from ~/.docker/config.json\"\n        else\n          info \"credentials\" \"Docker config exists; no inline auth field detected\" \"\"\n        fi\n        ;;\n      *)\n        if grep -qiE '(token|password|secret|_auth|machine|aws_access_key_id|private_key)' \"$file\" 2&gt;/dev/null; then\n          warn \"credentials\" \"Credential-like material found in ${file#\"$HOME\"/}\" \\\n            \"Review permissions and move long-lived secrets to a keychain, password manager, or short-lived auth flow\"\n        else\n          info \"credentials\" \"Credential file exists: ${file#\"$HOME\"/}\" \"\"\n        fi\n        ;;\n    esac\n\n    local mode\n    mode=$(stat -f \"%Lp\" \"$file\" 2&gt;/dev/null || echo \"\")\n    if [[ -n \"$mode\" &amp;&amp; \"$mode\" != \"600\" &amp;&amp; \"$mode\" != \"400\" ]]; then\n      warn \"credentials\" \"${file#\"$HOME\"/} permissions are $mode, not owner-only\" \\\n        \"chmod 600 \\\"$file\\\"\"\n    fi\n  done\n\n  local ssh_unencrypted=0\n  if [[ -d \"$HOME/.ssh\" ]]; then\n    local key\n    for key in \"$HOME\"/.ssh/id_*; do\n      [[ -f \"$key\" ]] || continue\n      [[ \"$key\" == *.pub || \"$key\" == *known_hosts* || \"$key\" == *config ]] &amp;&amp; continue\n      if ssh-keygen -y -P \"\" -f \"$key\" &gt;/dev/null 2&gt;&amp;1; then\n        ((++ssh_unencrypted))\n      fi\n    done\n  fi\n\n  if [[ $ssh_unencrypted -gt 0 ]]; then\n    warn \"credentials\" \"$ssh_unencrypted SSH private key(s) appear to have no passphrase\" \\\n      \"Rotate or add passphrases with: ssh-keygen -p -f ~/.ssh/id_ed25519\"\n  fi\n\n  if [[ $found -eq 0 &amp;&amp; $ssh_unencrypted -eq 0 ]]; then\n    pass \"credentials\" \"No common plaintext credential files found\"\n  fi\n}\n\n# \u2500\u2500 Project Attack Patterns \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_project_attack_patterns() {\n  section \"Project Attack Patterns\"\n\n  local code_dirs=(\"${CODE_DIRS[@]}\")\n  local remote_install_files=0\n  local env_secret_files=0\n  local devcontainer_unpinned=0\n  local compose_unpinned=0\n  local broad_actions_permissions=0\n  local git_submodule_files=0\n  local git_filter_files=0\n\n  for base in \"${code_dirs[@]}\"; do\n    [[ -d \"$base\" ]] || continue\n\n    while IFS= read -r file; do\n      if grep -qiE '(curl|wget)[^|;&amp;]*(\\||&gt;|bash|sh)|bash\\s+&lt;\\(|sh\\s+&lt;\\(' \"$file\" 2&gt;/dev/null; then\n        ((++remote_install_files))\n        if [[ $remote_install_files -le 3 ]]; then\n          warn \"patterns\" \"Remote install script pattern in ${file#\"$HOME\"/}\" \\\n            \"Download, pin, verify checksum/signature, then execute reviewed scripts\"\n        fi\n      fi\n    done &lt; &lt;(find \"$base\" -maxdepth \"$SCAN_DEPTH\" \\( -name \"Makefile\" -o -name \"*.sh\" -o -name \"*.bash\" -o -name \"*.zsh\" -o -name \"Dockerfile*\" -o -name \"*.md\" \\) -not -path \"*/node_modules/*\" -not -path \"*/.git/*\" 2&gt;/dev/null || true)\n\n    while IFS= read -r envfile; do\n      if grep -qiE '(API_KEY|TOKEN|SECRET|PASSWORD|PRIVATE_KEY|ACCESS_KEY)\\s*=' \"$envfile\" 2&gt;/dev/null; then\n        ((++env_secret_files))\n        if [[ $env_secret_files -le 3 ]]; then\n          warn \"patterns\" \"Secret-like values in ${envfile#\"$HOME\"/}\" \\\n            \"Keep real secrets out of repo files; use .env.example placeholders and secret managers\"\n        fi\n      fi\n    done &lt; &lt;(find \"$base\" -maxdepth \"$SCAN_DEPTH\" \\( -name \".env\" -o -name \".env.*\" \\) -not -name \".env.example\" -not -path \"*/node_modules/*\" -not -path \"*/.git/*\" 2&gt;/dev/null || true)\n\n    while IFS= read -r devcontainer; do\n      if grep -qiE '\"image\"\\s*:\\s*\"[^\"]+:(latest|main|master|edge|dev)\"|\"image\"\\s*:\\s*\"[^\"]+\"(,|$)' \"$devcontainer\" 2&gt;/dev/null &amp;&amp; ! grep -q '@sha256:' \"$devcontainer\" 2&gt;/dev/null; then\n        ((++devcontainer_unpinned))\n        if [[ $devcontainer_unpinned -le 3 ]]; then\n          warn \"patterns\" \"Devcontainer image is not digest-pinned in ${devcontainer#\"$HOME\"/}\" \\\n            \"Pin devcontainer images with @sha256 and review devcontainer features\"\n        fi\n      fi\n    done &lt; &lt;(find \"$base\" -maxdepth \"$SCAN_DEPTH\" \\( -name \"devcontainer.json\" -o -path \"*/.devcontainer/devcontainer.json\" \\) 2&gt;/dev/null || true)\n\n    while IFS= read -r compose; do\n      if grep -qiE 'image:\\s*[^@[:space:]]+:(latest|main|master|edge|dev)\\b|image:\\s*[^@[:space:]]+\\s*$' \"$compose\" 2&gt;/dev/null; then\n        ((++compose_unpinned))\n        if [[ $compose_unpinned -le 3 ]]; then\n          warn \"patterns\" \"Docker Compose image may be tag-pinned or implicit latest in ${compose#\"$HOME\"/}\" \\\n            \"Pin Compose images by digest for reproducible local services\"\n        fi\n      fi\n    done &lt; &lt;(find \"$base\" -maxdepth \"$SCAN_DEPTH\" \\( -name \"docker-compose.yml\" -o -name \"docker-compose.yaml\" -o -name \"compose.yml\" -o -name \"compose.yaml\" \\) -not -path \"*/node_modules/*\" 2&gt;/dev/null || true)\n\n    while IFS= read -r workflow; do\n      if grep -qiE '^\\s*permissions:\\s*(write-all|read-all)|^\\s*contents:\\s*write|^\\s*id-token:\\s*write' \"$workflow\" 2&gt;/dev/null; then\n        ((++broad_actions_permissions))\n        if [[ $broad_actions_permissions -le 3 ]]; then\n          warn \"patterns\" \"Broad GitHub Actions permissions in ${workflow#\"$HOME\"/}\" \\\n            \"Set least-privilege permissions per workflow/job and require OIDC only where needed\"\n        fi\n      fi\n    done &lt; &lt;(find \"$base\" -maxdepth \"$SCAN_DEPTH\" \\( -path '*/.github/workflows/*.yml' -o -path '*/.github/workflows/*.yaml' \\) 2&gt;/dev/null || true)\n\n    while IFS= read -r submodule; do\n      ((++git_submodule_files))\n      warn \"patterns\" \"Git submodules found in ${submodule#\"$HOME\"/} \u2014 submodule refs and URLs need review\" \\\n        \"Review .gitmodules URLs, pin submodule commits, and prefer HTTPS/SSH over git://\"\n    done &lt; &lt;(find \"$base\" -maxdepth \"$SCAN_DEPTH\" -name \".gitmodules\" 2&gt;/dev/null || true)\n\n    while IFS= read -r attrs; do\n      if grep -qiE 'filter=|textconv|diff=.*command' \"$attrs\" 2&gt;/dev/null; then\n        ((++git_filter_files))\n        warn \"patterns\" \"Git attributes can invoke filters/diff drivers in ${attrs#\"$HOME\"/}\" \\\n          \"Review filter, textconv, and diff driver config before opening untrusted repos\"\n      fi\n    done &lt; &lt;(find \"$base\" -maxdepth \"$SCAN_DEPTH\" -name \".gitattributes\" -not -path \"*/.git/*\" 2&gt;/dev/null || true)\n  done\n\n  [[ $remote_install_files -gt 3 ]] &amp;&amp; warn \"patterns\" \"...and $((remote_install_files - 3)) more files with remote install patterns\" \"\"\n  [[ $env_secret_files -gt 3 ]] &amp;&amp; warn \"patterns\" \"...and $((env_secret_files - 3)) more env files with secret-like values\" \"\"\n  [[ $devcontainer_unpinned -gt 3 ]] &amp;&amp; warn \"patterns\" \"...and $((devcontainer_unpinned - 3)) more unpinned devcontainer files\" \"\"\n  [[ $compose_unpinned -gt 3 ]] &amp;&amp; warn \"patterns\" \"...and $((compose_unpinned - 3)) more Compose files with unpinned images\" \"\"\n\n  local total_patterns=$((remote_install_files + env_secret_files + devcontainer_unpinned + compose_unpinned + broad_actions_permissions + git_submodule_files + git_filter_files))\n  if [[ $total_patterns -eq 0 ]]; then\n    pass \"patterns\" \"No high-risk project attack patterns found in scanned directories\"\n  fi\n}\n\n# \u2500\u2500 Additional Ecosystems \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_additional_ecosystems() {\n  section \"Additional Ecosystems\"\n\n  local code_dirs=(\"${CODE_DIRS[@]}\")\n  local composer_projects=0\n  local composer_no_lock=0\n  local gradle_projects=0\n  local gradle_no_lock=0\n  local nuget_projects=0\n  local terraform_files=0\n  local terraform_loose=0\n  local swift_projects=0\n  local swift_no_resolved=0\n  local dart_projects=0\n  local dart_no_lock=0\n\n  for base in \"${code_dirs[@]}\"; do\n    [[ -d \"$base\" ]] || continue\n\n    while IFS= read -r composer; do\n      ((++composer_projects))\n      local dir\n      dir=$(dirname \"$composer\")\n      if [[ ! -f \"$dir/composer.lock\" ]]; then\n        ((++composer_no_lock))\n        warn \"deps-php\" \"composer.json without composer.lock in ${dir#\"$HOME\"/}\" \\\n          \"Run composer update intentionally, commit composer.lock, and deploy with composer install\"\n      fi\n    done &lt; &lt;(find \"$base\" -maxdepth \"$SCAN_DEPTH\" -name \"composer.json\" -not -path \"*/vendor/*\" 2&gt;/dev/null || true)\n\n    while IFS= read -r gradle; do\n      ((++gradle_projects))\n      local dir\n      dir=$(dirname \"$gradle\")\n      if [[ ! -f \"$dir/gradle.lockfile\" &amp;&amp; ! -d \"$dir/gradle/dependency-locks\" ]]; then\n        ((++gradle_no_lock))\n        info \"deps-jvm\" \"Gradle project without dependency locking in ${dir#\"$HOME\"/}\" \\\n          \"Enable dependencyLocking and commit generated lockfiles\"\n      fi\n    done &lt; &lt;(find \"$base\" -maxdepth \"$SCAN_DEPTH\" \\( -name \"build.gradle\" -o -name \"build.gradle.kts\" \\) -not -path \"*/.gradle/*\" 2&gt;/dev/null || true)\n\n    while IFS= read -r nuget; do\n      ((++nuget_projects))\n      info \"deps-dotnet\" \"NuGet project detected in ${nuget#\"$HOME\"/}\" \\\n        \"Use packages.lock.json with RestoreLockedMode=true and trusted package sources\"\n    done &lt; &lt;(find \"$base\" -maxdepth \"$SCAN_DEPTH\" \\( -name \"*.csproj\" -o -name \"packages.config\" -o -name \"Directory.Packages.props\" \\) -not -path \"*/bin/*\" -not -path \"*/obj/*\" 2&gt;/dev/null || true)\n\n    while IFS= read -r tf; do\n      ((++terraform_files))\n      if grep -qE 'version\\s*=\\s*\"(&gt;=|&gt;|~&gt;|.*\\*)' \"$tf\" 2&gt;/dev/null; then\n        ((++terraform_loose))\n        warn \"deps-iac\" \"Loose Terraform/OpenTofu provider constraint in ${tf#\"$HOME\"/}\" \\\n          \"Pin providers tightly and commit .terraform.lock.hcl\"\n      fi\n    done &lt; &lt;(find \"$base\" -maxdepth \"$SCAN_DEPTH\" -name \"*.tf\" -not -path \"*/.terraform/*\" 2&gt;/dev/null || true)\n\n    while IFS= read -r swift; do\n      ((++swift_projects))\n      local dir\n      dir=$(dirname \"$swift\")\n      if [[ ! -f \"$dir/Package.resolved\" ]]; then\n        ((++swift_no_resolved))\n        warn \"deps-swift\" \"Swift Package.swift without Package.resolved in ${dir#\"$HOME\"/}\" \\\n          \"Run swift package resolve and commit Package.resolved for apps/tools\"\n      fi\n    done &lt; &lt;(find \"$base\" -maxdepth \"$SCAN_DEPTH\" -name \"Package.swift\" -not -path \"*/.build/*\" 2&gt;/dev/null || true)\n\n    while IFS= read -r pubspec; do\n      ((++dart_projects))\n      local dir\n      dir=$(dirname \"$pubspec\")\n      if [[ ! -f \"$dir/pubspec.lock\" ]]; then\n        ((++dart_no_lock))\n        warn \"deps-dart\" \"pubspec.yaml without pubspec.lock in ${dir#\"$HOME\"/}\" \\\n          \"Commit pubspec.lock for apps; use locked installs in CI\"\n      fi\n    done &lt; &lt;(find \"$base\" -maxdepth \"$SCAN_DEPTH\" -name \"pubspec.yaml\" -not -path \"*/.dart_tool/*\" 2&gt;/dev/null || true)\n  done\n\n  [[ $composer_projects -gt 0 &amp;&amp; $composer_no_lock -eq 0 ]] &amp;&amp; pass \"deps-php\" \"All $composer_projects Composer project(s) have composer.lock\"\n  [[ $gradle_projects -gt 0 &amp;&amp; $gradle_no_lock -eq 0 ]] &amp;&amp; pass \"deps-jvm\" \"All $gradle_projects Gradle project(s) appear to use dependency locking\"\n  [[ $terraform_files -gt 0 &amp;&amp; $terraform_loose -eq 0 ]] &amp;&amp; pass \"deps-iac\" \"No loose Terraform provider constraints detected\"\n  [[ $swift_projects -gt 0 &amp;&amp; $swift_no_resolved -eq 0 ]] &amp;&amp; pass \"deps-swift\" \"All $swift_projects Swift package(s) have Package.resolved\"\n  [[ $dart_projects -gt 0 &amp;&amp; $dart_no_lock -eq 0 ]] &amp;&amp; pass \"deps-dart\" \"All $dart_projects Dart/Flutter project(s) have pubspec.lock\"\n\n  if [[ $composer_projects -eq 0 &amp;&amp; $gradle_projects -eq 0 &amp;&amp; $nuget_projects -eq 0 &amp;&amp; $terraform_files -eq 0 &amp;&amp; $swift_projects -eq 0 &amp;&amp; $dart_projects -eq 0 ]]; then\n    info \"ecosystems\" \"No PHP, JVM, .NET, Terraform, Swift, or Dart manifests found in scanned directories\" \"\"\n  fi\n}\n\n# \u2500\u2500 macOS Security Baseline \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_macos() {\n  section \"macOS Security Baseline\"\n\n  # System Integrity Protection\n  local sip_status\n  sip_status=$(csrutil status 2&gt;/dev/null || echo \"unknown\")\n  if [[ \"$sip_status\" == *\"enabled\"* ]]; then\n    pass \"macos\" \"System Integrity Protection (SIP) is enabled\"\n  else\n    crit \"macos\" \"SIP is disabled \u2014 system files are unprotected\" \\\n      \"Reboot to Recovery Mode \u2192 csrutil enable\"\n  fi\n\n  # Gatekeeper\n  local gk_status\n  gk_status=$(spctl --status 2&gt;/dev/null || echo \"unknown\")\n  if [[ \"$gk_status\" == *\"assessments enabled\"* ]]; then\n    pass \"macos\" \"Gatekeeper is enabled\"\n  else\n    crit \"macos\" \"Gatekeeper is disabled \u2014 unsigned apps can run freely\" \\\n      \"sudo spctl --master-enable\"\n  fi\n\n  # FileVault\n  local fv_status\n  fv_status=$(fdesetup status 2&gt;/dev/null || echo \"unknown\")\n  if [[ \"$fv_status\" == *\"On\"* ]]; then\n    pass \"macos\" \"FileVault disk encryption is enabled\"\n  else\n    warn \"macos\" \"FileVault is not enabled \u2014 disk is not encrypted\" \\\n      \"System Settings \u2192 Privacy &amp; Security \u2192 FileVault \u2192 Turn On\"\n  fi\n\n  # Firewall\n  local fw_status\n  fw_status=$(/usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate 2&gt;/dev/null || echo \"unknown\")\n  if [[ \"$fw_status\" == *\"enabled\"* ]]; then\n    pass \"macos\" \"Application firewall is enabled\"\n  else\n    warn \"macos\" \"Application firewall is disabled\" \\\n      \"sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate on\"\n  fi\n}\n\n# \u2500\u2500 Vulnerability Scanners \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_scanners() {\n  section \"Vulnerability Scanners (meta-tools)\"\n\n  local has_scanner=false\n\n  if installed osv-scanner; then\n    pass \"scanners\" \"osv-scanner is installed \u2014 multi-ecosystem CVE scanner\"\n    has_scanner=true\n  else\n    info \"scanners\" \"osv-scanner not installed (Google, free, widest coverage)\" \\\n      \"brew install osv-scanner  # or: go install github.com/google/osv-scanner/v2/cmd/osv-scanner@latest\"\n  fi\n\n  if installed trivy; then\n    pass \"scanners\" \"trivy is installed \u2014 vulns + secrets + IaC scanning\"\n    has_scanner=true\n  else\n    info \"scanners\" \"trivy not installed (Aqua, free, vuln + secrets + misconfig)\" \\\n      \"brew install trivy\"\n  fi\n\n  if installed grype; then\n    pass \"scanners\" \"grype is installed \u2014 vulnerability scanner\"\n    has_scanner=true\n  else\n    info \"scanners\" \"grype not installed (Anchore, free, image + filesystem)\" \"\"\n  fi\n\n\t  if installed syft; then\n\t    pass \"scanners\" \"syft is installed \u2014 SBOM generation\"\n\t  else\n\t    info \"scanners\" \"syft not installed \u2014 generates SBOMs for compliance/auditing\" \\\n\t      \"brew install syft\"\n\t  fi\n\n  if installed cosign; then\n    pass \"scanners\" \"cosign is installed \u2014 container/artifact signature verification\"\n  else\n    info \"scanners\" \"cosign not installed \u2014 recommended for verifying signed containers and artifacts\" \\\n      \"brew install cosign\"\n  fi\n\n  if installed gitleaks; then\n    pass \"scanners\" \"gitleaks is installed \u2014 repository secret scanning\"\n  else\n    info \"scanners\" \"gitleaks not installed \u2014 catches committed secrets before push\" \\\n      \"brew install gitleaks\"\n  fi\n\n  if installed pre-commit; then\n    pass \"scanners\" \"pre-commit is installed \u2014 useful for local policy gates\"\n  else\n    info \"scanners\" \"pre-commit not installed \u2014 useful for secret and lint hooks before commit\" \\\n      \"brew install pre-commit\"\n  fi\n\n  if ! $has_scanner; then\n    warn \"scanners\" \"No multi-ecosystem vulnerability scanner installed\" \\\n      \"brew install osv-scanner trivy\"\n  fi\n}\n\n# \u2500\u2500 MCP Servers (Claude/Cursor) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# AI coding tools accept MCP server definitions that can run arbitrary code or\n# call arbitrary HTTP endpoints. A single rogue MCP added to ~/.claude.json gives\n# the attacker tool-call access to your sessions.\n\naudit_mcp_servers() {\n  section \"MCP Servers (~/.claude.json)\" \"mcp_servers\"\n\n  local claude_json=\"$HOME/.claude.json\"\n  if [[ ! -f \"$claude_json\" ]]; then\n    info \"mcp\" \"No ~/.claude.json \u2014 Claude Code not configured here\"\n    return\n  fi\n\n  local servers=\"\"\n  if installed jq; then\n    servers=\"$(jq -r '\n      .mcpServers // {} | to_entries[]\n      | \"\\(.key)\\t\\(.value.type // \"stdio\")\\t\\(.value.url // .value.command // \"?\")\"\n    ' \"$claude_json\" 2&gt;/dev/null || true)\"\n  fi\n\n  if [[ -z \"$servers\" ]]; then\n    pass \"mcp\" \"No MCP servers configured\"\n    return\n  fi\n\n  # Known-safe URLs/commands. Anything else gets flagged for manual review.\n  local safe_pattern='^(https://(api\\.anthropic\\.com|dash\\.brain-ai\\.dev|.*\\.githubusercontent\\.com|mcp\\.openai\\.com)|/nix/store/|/usr/local/|/opt/homebrew/|npx |node |uvx |python )'\n\n  while IFS=$'\\t' read -r name typ target; do\n    [[ -z \"$name\" ]] &amp;&amp; continue\n    if printf '%s' \"$target\" | grep -qE \"$safe_pattern\"; then\n      pass \"mcp\" \"MCP $name [$typ] \u2192 $target\"\n    else\n      crit \"mcp\" \"Unrecognized MCP server: $name [$typ] \u2192 $target\" \\\n        \"Inspect ~/.claude.json; remove with: jq 'del(.mcpServers.\\\"$name\\\")' ~/.claude.json | sponge ~/.claude.json\"\n    fi\n  done &lt;&lt;&lt; \"$servers\"\n}\n\n# \u2500\u2500 Known-bad packages (offline lockfile match) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# Curated list of compromised packages from 2024\u20132026. Matches against\n# package-lock.json / pnpm-lock.yaml / uv.lock without hitting the network.\n# Update the lists below as new supply-chain incidents are disclosed.\n\naudit_known_bad_pkgs() {\n  section \"Known-bad package signatures\" \"known_bad_pkgs\"\n\n  local KNOWN_BAD_NPM\n  KNOWN_BAD_NPM=$(cat &lt;&lt;'NPMBAD'\n@ctrl/tinycolor|*|GHSA-7vfx-9hwp-c2x4|critical\n@nx/devkit|17.3.0|GHSA-cxm3-wv7p-998c|critical\n@nx/devkit|20.5.0|GHSA-cxm3-wv7p-998c|critical\n@nx/devkit|21.5.0|GHSA-cxm3-wv7p-998c|critical\nnx|17.3.0|GHSA-cxm3-wv7p-998c|critical\nnx|20.5.0|GHSA-cxm3-wv7p-998c|critical\nnx|21.5.0|GHSA-cxm3-wv7p-998c|critical\nngx-bootstrap|18.1.4|shai-hulud-2025|critical\nngx-toastr|19.0.2|shai-hulud-2025|critical\n@crowdstrike/falcon-shoelace|0.4.2|shai-hulud-2025|critical\nevent-stream|3.3.6|CVE-2018-1000620|critical\nua-parser-js|0.7.29|CVE-2021-44906|critical\nua-parser-js|0.8.0|CVE-2021-44906|critical\nua-parser-js|1.0.0|CVE-2021-44906|critical\nrc|1.2.9|GHSA-g2q5-5433-rhrf|critical\nrc|1.3.9|GHSA-g2q5-5433-rhrf|critical\nrc|2.3.9|GHSA-g2q5-5433-rhrf|critical\ncoa|2.0.3|GHSA-73qr-pfmq-6rp6|critical\ncoa|2.0.4|GHSA-73qr-pfmq-6rp6|critical\ncoa|2.1.1|GHSA-73qr-pfmq-6rp6|critical\nnode-ipc|10.1.1|CVE-2022-23812|critical\nnode-ipc|10.1.2|CVE-2022-23812|critical\nnode-ipc|11.0.0|CVE-2022-23812|critical\nnode-ipc|11.1.0|CVE-2022-23812|critical\nflatmap-stream|*|CVE-2018-16487|critical\neslint-scope|3.7.2|CVE-2018-7408|critical\nNPMBAD\n  )\n\n  local KNOWN_BAD_PY\n  KNOWN_BAD_PY=$(cat &lt;&lt;'PYBAD'\nctx|*|CVE-2022-29217|critical\nphpass|*|typosquat|critical\nPYBAD\n  )\n\n  # Match helper: glob \"*\" wildcards, else exact-equal\n  _kbp_match() {\n    local glob=\"$1\" v=\"$2\"\n    [[ \"$glob\" == \"*\" ]] &amp;&amp; return 0\n    # shellcheck disable=SC2053\n    [[ \"$v\" == $glob ]]\n  }\n\n  # Extract pkg+version pairs from a lockfile\n  _kbp_extract_npm() {\n    local lf=\"$1\"\n    if installed jq; then\n      jq -r '(.packages // {}) | to_entries[]\n        | select(.key != \"\")\n        | \"\\(.key | sub(\"^node_modules/\"; \"\") | sub(\".*/node_modules/\"; \"\"))\\t\\(.value.version // \"\")\"' \"$lf\" 2&gt;/dev/null\n    fi\n  }\n  _kbp_extract_pnpm() {\n    local lf=\"$1\"\n    grep -oE \"/[@a-zA-Z0-9_./-]+@[0-9][a-zA-Z0-9.+_-]*\" \"$lf\" 2&gt;/dev/null \\\n      | sed 's|^/||' \\\n      | awk -F'@' '{ if (NF==2) print $1 \"\\t\" $2; else if (NF==3) print \"@\" $2 \"\\t\" $3 }'\n  }\n  _kbp_extract_uv() {\n    local lf=\"$1\"\n    awk '\n      /^\\[\\[package\\]\\]/ { in_pkg=1; name=\"\"; ver=\"\"; next }\n      /^\\[/ &amp;&amp; !/^\\[\\[package\\]\\]/ { in_pkg=0 }\n      in_pkg &amp;&amp; /^name *=/ { gsub(/^name *= *|[\"[:space:]]+/, \"\", $0); name=$0 }\n      in_pkg &amp;&amp; /^version *=/ { gsub(/^version *= *|[\"[:space:]]+/, \"\", $0); ver=$0; if(name &amp;&amp; ver) print name \"\\t\" ver; name=\"\"; ver=\"\" }\n    ' \"$lf\" 2&gt;/dev/null\n  }\n\n  local hits=0\n  declare -A reported=()\n\n  _kbp_scan() {\n    local lf=\"$1\" extractor=\"$2\" bad_list=\"$3\"\n    local project; project=\"$(dirname \"$lf\")\"\n    while IFS=$'\\t' read -r name ver; do\n      [[ -z \"$name\" || -z \"$ver\" ]] &amp;&amp; continue\n      while IFS='|' read -r bn bv cve sev; do\n        [[ -z \"$bn\" ]] &amp;&amp; continue\n        if [[ \"$name\" == \"$bn\" ]] &amp;&amp; _kbp_match \"$bv\" \"$ver\"; then\n          local key=\"$project|$name@$ver\"\n          [[ -n \"${reported[$key]:-}\" ]] &amp;&amp; continue\n          reported[$key]=1\n          crit \"known_bad\" \"$name@$ver ($cve) in $project\" \\\n            \"cd $project &amp;&amp; (pnpm up $name@latest || npm i $name@latest)\"\n          hits=$((hits+1))\n        fi\n      done &lt;&lt;&lt; \"$bad_list\"\n    done &lt; &lt;(\"$extractor\" \"$lf\")\n  }\n\n  local dir\n  for dir in \"${CODE_DIRS[@]}\"; do\n    [[ -d \"$dir\" ]] || continue\n    while IFS= read -r -d '' lf; do _kbp_scan \"$lf\" _kbp_extract_npm  \"$KNOWN_BAD_NPM\"; done \\\n      &lt; &lt;(find \"$dir\" -maxdepth \"$SCAN_DEPTH\" -name package-lock.json -not -path '*/node_modules/*' -not -path '*/.git/*' -print0 2&gt;/dev/null)\n    while IFS= read -r -d '' lf; do _kbp_scan \"$lf\" _kbp_extract_pnpm \"$KNOWN_BAD_NPM\"; done \\\n      &lt; &lt;(find \"$dir\" -maxdepth \"$SCAN_DEPTH\" -name pnpm-lock.yaml  -not -path '*/node_modules/*' -not -path '*/.git/*' -print0 2&gt;/dev/null)\n    while IFS= read -r -d '' lf; do _kbp_scan \"$lf\" _kbp_extract_uv   \"$KNOWN_BAD_PY\";  done \\\n      &lt; &lt;(find \"$dir\" -maxdepth \"$SCAN_DEPTH\" -name uv.lock          -not -path '*/.venv/*'        -not -path '*/.git/*' -print0 2&gt;/dev/null)\n  done\n\n  if [[ $hits -eq 0 ]]; then\n    pass \"known_bad\" \"No known-bad packages matched in any scanned lockfile\"\n  fi\n}\n\n# \u2500\u2500 Lockfile registry-hijack check \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n# Catches tarball substitution: lockfile claims the official name+version but\n# 'resolved' points to a hostile mirror or arbitrary URL.\n\naudit_registry_hijack() {\n  section \"Lockfile registry-hijack\" \"registry_hijack\"\n  local hits=0\n  local dir\n  for dir in \"${CODE_DIRS[@]}\"; do\n    [[ -d \"$dir\" ]] || continue\n    while IFS= read -r -d '' lf; do\n      while IFS= read -r url; do\n        url=\"${url#*\\\"}\"; url=\"${url%\\\"*}\"\n        [[ -z \"$url\" ]] &amp;&amp; continue\n        if ! printf '%s' \"$url\" | grep -qE '^(https://registry\\.npmjs\\.org/|https://registry\\.yarnpkg\\.com/|git\\+ssh://|git\\+https://|github:|file:)'; then\n          crit \"registry\" \"Non-official tarball in ${lf#$dir/}: $url\" \\\n            \"Verify intent; if not, rm lockfile and regenerate from a clean install\"\n          hits=$((hits+1))\n          [[ $hits -ge 15 ]] &amp;&amp; return\n        fi\n      done &lt; &lt;(grep -E '\"resolved\":' \"$lf\" 2&gt;/dev/null | grep -oE '\"https?://[^\"]+\"')\n    done &lt; &lt;(find \"$dir\" -maxdepth \"$SCAN_DEPTH\" -name package-lock.json -not -path '*/node_modules/*' -not -path '*/.git/*' -print0 2&gt;/dev/null)\n  done\n  [[ $hits -eq 0 ]] &amp;&amp; pass \"registry\" \"All package-lock.json resolutions point to official registries\"\n}\n\n# \u2500\u2500 gh CLI scope audit \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_gh_scopes() {\n  section \"GitHub CLI auth\" \"gh_scopes\"\n  if ! installed gh; then\n    info \"gh\" \"gh CLI not installed\"\n    return\n  fi\n  local stat\n  if ! stat=\"$(gh auth status 2&gt;&amp;1)\"; then\n    info \"gh\" \"gh not logged in\"\n    return\n  fi\n  local scopes\n  scopes=\"$(printf '%s' \"$stat\" | grep -i 'scopes:' | head -1 | sed 's/.*scopes://; s/ //g')\"\n  if [[ -z \"$scopes\" ]]; then\n    info \"gh\" \"Could not parse gh auth scopes\"\n    return\n  fi\n  if printf '%s' \"$scopes\" | grep -qE \"delete_repo|admin:org|admin:enterprise\"; then\n    crit \"gh\" \"gh token has dangerous scopes: $scopes\" \\\n      \"gh auth refresh --scopes repo,read:org   # downgrade\"\n  elif printf '%s' \"$scopes\" | grep -qE \"workflow\"; then\n    warn \"gh\" \"gh token has 'workflow' scope: $scopes\" \\\n      \"If you don't push workflows from CLI: gh auth refresh --scopes repo,read:org\"\n  else\n    pass \"gh\" \"gh token scopes look minimal: $scopes\"\n  fi\n}\n\n# \u2500\u2500 Shell history secret-pattern scan \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\naudit_shell_history() {\n  section \"Shell history secrets\" \"shell_history\"\n  local files=(\"$HOME/.zsh_history\" \"$HOME/.bash_history\" \"$HOME/.local/share/fish/fish_history\")\n  local patterns='AKIA[0-9A-Z]{16}|ghp_[A-Za-z0-9]{30,}|gho_[A-Za-z0-9]{30,}|ghs_[A-Za-z0-9]{30,}|sk-[A-Za-z0-9]{32,}|xoxb-[0-9]+-[0-9]+-[A-Za-z0-9]+|eyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}'\n  local total=0\n  local f\n  for f in \"${files[@]}\"; do\n    [[ -f \"$f\" ]] || continue\n    local n\n    # grep -c always prints a count to stdout (0 when no match) but exits 1\n    # on zero matches. Don't add `|| echo 0` \u2014 it produces \"0\\n0\" \u2192 arithmetic error.\n    n=\"$(grep -cE \"$patterns\" \"$f\" 2&gt;/dev/null)\" || n=0\n    if [[ \"${n:-0}\" -gt 0 ]]; then\n      warn \"history\" \"$n probable-secret hit(s) in ${f/#$HOME/~}\" \\\n        \"Rotate any matching credentials; trim history: grep -vE '' $f &gt; $f.clean &amp;&amp; mv $f.clean $f\"\n      total=$((total+n))\n    fi\n  done\n  [[ $total -eq 0 ]] &amp;&amp; pass \"history\" \"No obvious secret patterns in shell history\"\n}\n\n# \u2500\u2500 Report Generation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ngenerate_html_report() {\n  local output_file=\"${1:-/tmp/supply-chain-audit-report.html}\"\n  local timestamp\n  timestamp=$(date \"+%Y-%m-%d %H:%M\")\n\n  cat &gt; \"$output_file\" &lt;&lt; 'HTMLHEAD'\n\n\n\n\n\nSupply Chain Security Audit\n\n  * { box-sizing: border-box; }\n  body { margin: 0; background: #0f172a; color: #e2e8f0; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; }\n  .min-h-screen { min-height: 100vh; }\n  .p-4 { padding: 1rem; }\n  .max-w-4xl { max-width: 56rem; }\n  .mx-auto { margin-left: auto; margin-right: auto; }\n  .mb-8 { margin-bottom: 2rem; }\n  .mb-4 { margin-bottom: 1rem; }\n  .mb-3 { margin-bottom: 0.75rem; }\n  .mb-2 { margin-bottom: 0.5rem; }\n  .mt-4 { margin-top: 1rem; }\n  .mt-1 { margin-top: 0.25rem; }\n  .flex { display: flex; }\n  .gap-4 { gap: 1rem; }\n  .p-5 { padding: 1.25rem; }\n  .pl-4 { padding-left: 1rem; }\n  .py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; }\n  .py-6 { padding-top: 1.5rem; padding-bottom: 1.5rem; }\n  .px-3 { padding-left: 0.75rem; padding-right: 0.75rem; }\n  .py-1 { padding-top: 0.25rem; padding-bottom: 0.25rem; }\n  .rounded { border-radius: 0.25rem; }\n  .rounded-full { border-radius: 9999px; }\n  .text-center { text-align: center; }\n  .text-3xl { font-size: 1.875rem; line-height: 2.25rem; }\n  .text-lg { font-size: 1.125rem; line-height: 1.75rem; }\n  .text-sm { font-size: 0.875rem; line-height: 1.25rem; }\n  .text-xs { font-size: 0.75rem; line-height: 1rem; }\n  .font-bold { font-weight: 700; }\n  .font-semibold { font-weight: 600; }\n  .text-white { color: #fff; }\n  .text-slate-400 { color: #94a3b8; }\n  .text-slate-600 { color: #475569; }\n  .card { background: #1e293b; border: 1px solid #334155; border-radius: 0.75rem; }\n  .crit { border-left: 3px solid #ef4444; background: #7f1d1d20; }\n  .warn { border-left: 3px solid #f59e0b; background: #78350f20; }\n  .pass { border-left: 3px solid #22c55e; }\n  .info { border-left: 3px solid #3b82f6; }\n  code { background: #0f172a; padding: 2px 6px; border-radius: 4px; font-size: 0.85em; }\n  .badge-red { background: #991b1b; color: #fca5a5; }\n  .badge-yellow { background: #854d0e; color: #fde68a; }\n  .badge-green { background: #166534; color: #86efac; }\n  .badge-blue { background: #1e3a5f; color: #93c5fd; }\n  @media (min-width: 768px) { .md\\:p-8 { padding: 2rem; } }\n\n\n\n\n\nHTMLHEAD\n\n  # Header with stats\n  cat &gt;&gt; \"$output_file\" &lt;&lt; EOF\n\n\n  \nSupply Chain Security Audit\n  \n$(hostname) &middot; $timestamp &middot; v$VERSION\n  \n\n    $CRIT_COUNT Critical\n    $WARN_COUNT Warnings\n    $PASS_COUNT Passed\n    $INFO_COUNT Info\n  \n\nEOF\n\n  # Findings\n  local current_tool=\"\"\n  for finding in \"${FINDINGS[@]}\"; do\n    IFS='|' read -r severity tool message fix &lt;&lt;&lt; \"$finding\"\n    if [[ \"$tool\" != \"$current_tool\" ]]; then\n      [[ -n \"$current_tool\" ]] &amp;&amp; echo \"\" &gt;&gt; \"$output_file\"\n      echo \"\n\" &gt;&gt; \"$output_file\"\n      echo \"\n$tool\" &gt;&gt; \"$output_file\"\n      current_tool=\"$tool\"\n    fi\n\n    local css_class=\"info\"\n    local label=\"INFO\"\n    case \"$severity\" in\n      CRIT) css_class=\"crit\"; label=\"CRITICAL\" ;;\n      WARN) css_class=\"warn\"; label=\"WARNING\" ;;\n      PASS) css_class=\"pass\"; label=\"OK\" ;;\n      INFO) css_class=\"info\"; label=\"INFO\" ;;\n    esac\n\n    # Escape HTML\n    message=$(echo \"$message\" | sed 's/&amp;/\\&amp;/g; s/&lt;/\\&lt;/g; s/&gt;/\\&gt;/g')\n    fix=$(echo \"$fix\" | sed 's/&amp;/\\&amp;/g; s/&lt;/\\&lt;/g; s/&gt;/\\&gt;/g')\n\n    echo \"\n\" &gt;&gt; \"$output_file\"\n    echo \"\n$label: $message\" &gt;&gt; \"$output_file\"\n    [[ -n \"$fix\" ]] &amp;&amp; echo \"\n$fix\" &gt;&gt; \"$output_file\"\n    echo \"\" &gt;&gt; \"$output_file\"\n  done\n  [[ -n \"$current_tool\" ]] &amp;&amp; echo \"\" &gt;&gt; \"$output_file\"\n\n  # Footer\n  cat &gt;&gt; \"$output_file\" &lt;&lt; 'HTMLFOOT'\n\n\n  supply-chain-audit.sh\n\n\nHTMLFOOT\n\n  echo \"$output_file\"\n}\n\n# \u2500\u2500 Main \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nmain() {\n  local do_fix=false\n  local no_prompt=0\n  local list_groups=0\n\n  # Parse arguments: paths go to SCAN_DIRS, plus flags for new features\n  local extra_dirs=()\n  local i=0\n  local args=(\"$@\")\n  while [[ $i -lt $# ]]; do\n    arg=\"${args[$i]}\"\n    case \"$arg\" in\n      --fix)           do_fix=true ;;\n      --json)          JSON_MODE=1 ;;\n      --quiet|-q)      QUIET=1 ;;\n      --no-prompt)     no_prompt=1 ;;\n      --list-groups)   list_groups=1 ;;\n      --only)          i=$((i+1)); ONLY_GROUPS=\"${args[$i]}\" ;;\n      --skip)          i=$((i+1)); SKIP_GROUPS=\"${args[$i]}\" ;;\n      --version)       echo \"supply-chain-audit.sh $VERSION\"; return 0 ;;\n      --help|-h)\n        echo \"Usage: supply-chain-audit.sh [OPTIONS] [DIRS...]\"\n        echo \"\"\n        echo \"Scans developer tools and dependency files for supply chain risks.\"\n        echo \"If no DIRS or SCAN_DIRS are provided in an interactive shell, the script\"\n        echo \"prompts before using the default scan directories.\"\n        echo \"\"\n        echo \"Arguments:\"\n        echo \"  DIRS        Directories to scan for dependency files (default: ~/code ~/projects ~/src ~/dev)\"\n        echo \"\"\n        echo \"Options:\"\n        echo \"  --fix       Generate a remediation script\"\n        echo \"  --help      Show this help\"\n        echo \"\"\n        echo \"Environment:\"\n        echo \"  SCAN_DIRS   Space-separated directories to scan (overrides defaults)\"\n        echo \"  SCAN_DEPTH  Max search depth within directories (default: 5)\"\n        echo \"\"\n        echo \"Examples:\"\n        echo \"  ./supply-chain-audit.sh                    # prompt before scanning defaults\"\n        echo \"  ./supply-chain-audit.sh ~/work ~/repos     # scan specific dirs\"\n        echo \"  SCAN_DEPTH=3 ./supply-chain-audit.sh       # shallow scan\"\n        return 0\n        ;;\n      *) extra_dirs+=(\"$arg\") ;;\n    esac\n    i=$((i+1))\n  done\n\n  if [[ $list_groups -eq 1 ]]; then\n    cat &lt; \"$fix_file\" &lt;&lt; 'FIXHEADER'\n#!/usr/bin/env bash\n# Auto-generated remediation script \u2014 review before running!\nset -euo pipefail\necho \"Supply Chain Security Remediation\"\necho \"Review each command before uncommenting and running.\"\necho \"\"\nFIXHEADER\n\n  for finding in \"${FINDINGS[@]}\"; do\n    IFS='|' read -r severity tool message fix &lt;&lt;&lt; \"$finding\"\n    [[ -z \"$fix\" ]] &amp;&amp; continue\n    [[ \"$severity\" != \"CRIT\" &amp;&amp; \"$severity\" != \"WARN\" ]] &amp;&amp; continue\n    {\n      echo \"# [$severity] $tool: $message\"\n      echo \"# $fix\"\n      echo \"\"\n    } &gt;&gt; \"$fix_file\"\n  done\n\n  chmod +x \"$fix_file\"\n  printf \"\\n  %sFix script: %s%s\\n\" \"$YELLOW\" \"$fix_file\" \"$NC\"\n  printf \"  %sReview and uncomment commands before running.%s\\n\" \"$DIM\" \"$NC\"\n}\n\nmain \"$@\"\n", "creation_timestamp": "2026-05-20T15:46:13.000000Z"}]}