Skip to content

fix(install): put mise's Node on PATH and refuse Node older than 22.6 - #245

Merged
ralyodio merged 1 commit into
masterfrom
fix/installer-node-path
Sep 26, 2026
Merged

ralyodio merged 1 commit into
masterfrom
fix/installer-node-path

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

What

  1. Bare machines install again. With no Node.js, install.sh bootstraps mise and mise use -g node@lts installs Node 24 correctly. Before this PR, ensure_mise_path only added mise's own bin dir (~/.local/bin) to PATH and never mise's shims, so the next check found no node and every bare box, as root or as a sudo user, ended with Failed to install Node.js via mise. and exit 1. Now:

    • mise's shims dir (${MISE_DATA_DIR:-${XDG_DATA_HOME:-~/.local/share}/mise}/shims) goes on PATH for the rest of the run, so the global install runs with mise's npm.
    • mise reshim runs after the install. npm links threatcrush into mise's version-stamped node dir, and without a reshim it gets no shim.
    • PATH for new shells: the conservative option. The script had no convention for editing rc files. So it writes to exactly one file, ~/.profile: one marked export PATH="<shims>:$PATH" line, appended only if the line isn't already there, and only when this run bootstrapped Node itself. It then prints exactly what it added. A user who already had a usable Node.js is never touched. bash skips ~/.profile when ~/.bash_profile or ~/.bash_login exists, and zsh reads ~/.zprofile. Those files are the user's own, so in those cases the installer leaves them alone and prints the line to add. Every path also prints the line to paste into the current shell, because curl | sh can't change its parent's PATH.
    • If mise use fails, the installer now shows mise's output. Before, the failure either exited silently under set -e or went through a mise install fallback. That fallback never activated a version, so the shims had nothing to run.
  2. Node.js older than 22.6 is refused before anything is installed. On Ubuntu's apt install nodejs npm (18.19.1), npm only printed EBADENGINE warnings, the installer said ✓ ThreatCrush 0.13.7 installed successfully!, and threatcrush daemon then segfaulted in better-sqlite3.

    • apps/cli/package.json now declares "engines": { "node": ">=22.6" }. @profullstack/hqtui 0.5 needs >=22.6 and better-sqlite3 13 needs >=22.
    • The installer checks the same minimum. It stops with exit 1 and a message naming the found version and path, the required version, how to upgrade (nvm / mise / nodejs.org), and the alternative: uninstall and re-run, and the installer bootstraps Node itself.
    • It does not quietly bootstrap a second Node with mise in that case. Doing so would put mise's node ahead of the user's own in every login shell.
  3. A CLI that doesn't run is no longer reported as installed. If threatcrush --version fails after install, the installer prints its output and exits 1. Before, it printed ✓ ThreatCrush unknown installed successfully!.

  4. shellcheck can read the file again. A directive written # shellcheck disable=SC2086 -- … is a parse error (SC1073) that stopped shellcheck from checking anything. The trailing comment now starts with #.

  5. Tests exercise the script instead of grepping it. install-script.test.ts used to check that the script contained mise use -g node@lts. The new tests run the real install.sh under /bin/sh (dash) in a temp HOME. PATH holds only real coreutils plus stubs: a curl that serves a fake mise installer and fails any other URL, a fake mise that installs a fake node/npm and writes shims, and node/npm stubs that report a chosen version and record their argv. They cover:

    • bare box → npm install + success;
    • a new login shell (. ~/.profile) runs threatcrush;
    • .bash_profile / .bash_login / zsh left untouched, and the printed line works;
    • re-runs add the line once;
    • Node 18 → exit 1, no install, no mise;
    • the accept/refuse boundary derived from engines.node, so the installer and the package can't drift apart;
    • existing Node ≥ 22.6 → used as is, no rc edits.

    Two install-docs.test.ts assertions that pinned the old README sentence were removed rather than re-pinned.

  6. Docs. README.md and apps/cli/README.md now state the Node.js 22.6+ requirement and what the installer does when Node is missing or too old. /docs doesn't mention a Node version, so it is unchanged.

How verified

Each container was fresh and started with --init. The branch was mounted read-only and run as sh /src/apps/web/public/install.sh; the live URL was not used. npm's latest was 0.13.9 at run time (0.13.8/0.13.9 were published at 19:19 UTC today), so the installer installed 0.13.9, not 0.13.7.

  • ubuntu:24.04, root, only curl ca-certificates: installer exit 0.
    → Installing Node.js LTS with mise...
      Bootstrapped Node.js: v24.21.0
    ✓ ThreatCrush 0.13.9 installed successfully!
      Node.js v24.21.0 was set up with mise. Added to /root/.profile for new login shells:
        # Added by the ThreatCrush installer: Node.js and threatcrush, installed with mise
        export PATH="/root/.local/share/mise/shims:$PATH"
    
    Then bash -lc 'command -v threatcrush; threatcrush --version' → /root/.local/share/mise/shims/threatcrush, 0.13.9. bash -lc 'threatcrush start && threatcrush status' → ✓ threatcrushd started, Status: ● RUNNING, Version: 0.13.9, Mode: system, exit 0.
  • ubuntu:24.04, non-root tcuser (bash, passwordless sudo), only curl ca-certificates sudo: su - tcuser -c 'sh …/install.sh' exited 0 and added the same line to /home/tcuser/.profile. Then su - tcuser -c 'threatcrush --version' → 0.13.9, and su - tcuser -c 'threatcrush start && threatcrush status' → Status: ● RUNNING, Mode: user, exit 0.
  • ubuntu:24.04 + apt install nodejs npm (v18.19.1): installer exit 1.
    ✗ ThreatCrush needs Node.js 22.6 or newer; found v18.19.1 at /usr/bin/node.
      Nothing was installed: on this Node.js the daemon cannot load its database.
    
    (followed by the upgrade options). Afterwards threatcrush was not on PATH, npm ls -g had no threatcrush, mise had not been installed, and ~/.profile was unchanged.
    Following the message (apt remove nodejs npm, re-run) bootstrapped v24.21.0 and printed ✓ ThreatCrush 0.13.9 installed successfully!. A new bash -lc shell then gave 0.13.9 and Status: ● RUNNING.
  • node:22-bookworm (v22.23.3 at /usr/local/bin/node), root: same flow as before this PR: npm install, ✓ ThreatCrush 0.13.9 installed successfully!, exit 0. mise was not installed and no ~/.profile line was written. bash -lc 'threatcrush start && threatcrush status' → Status: ● RUNNING.
  • Tests: against the unmodified install.sh from master, the new install-script.test.ts has 8 failing tests; against this branch, 19/19 pass. pnpm --filter @profullstack/threatcrush-web test: 55 files, 484 tests passed (re-run after rebasing onto 42ed31d). pnpm --dir apps/web exec tsc --noEmit exits 0. On master it reported TS2769 (NODE_ENV missing from the hermetic env) in this test file.
  • shellcheck 0.11.0 -s sh: the only remaining finding is SC2024 on the pre-existing sudo npm … >"$LOG", where writing the log as the invoking user is intended.

On a machine with no Node.js the installer bootstrapped mise and
`mise use -g node@lts` installed Node 24, but only mise's own bin dir was
ever added to PATH, never its shims. The next check could not see node, so
every bare box, as root or as a sudo user, ended with "Failed to install
Node.js via mise." and exit 1. mise's shims dir now goes on PATH for the rest
of the run, `mise reshim` gives threatcrush a shim once npm has installed it,
and new login shells find it through one marked line appended to ~/.profile.
That line is written only when this run bootstrapped Node itself. When the
login shell reads ~/.bash_profile, ~/.bash_login or ~/.zprofile instead, the
installer leaves those files alone and prints the line to add.

On an existing Node.js older than 22.6 (Ubuntu's apt nodejs is 18.19.1), npm
only printed EBADENGINE warnings and the installer reported success. The
daemon then segfaulted loading better-sqlite3. The CLI now declares
engines.node >=22.6 (@profullstack/hqtui needs 22.6, better-sqlite3 13 needs
22), and the installer stops before installing anything. Its message names
the version it found, the version required, and how to upgrade. If
`threatcrush --version` fails after install, the installer now reports a
failure instead of "ThreatCrush unknown installed successfully!".

The mise test used to grep the script for `mise use -g node@lts`. It now
runs the real script under sh against stub node, npm, mise and curl.
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

13 finding(s)

HIGH/CRITICAL: 1 | MEDIUM: 6 | LOW: 6

Severity Rule Location
HIGH secret-aws-access-key prd/0003-detect-hardcoded-secrets-before-they-are-committed-or-served.md:126
MEDIUM js-open-redirect apps/web/src/app/auth/login/page.tsx:50
MEDIUM js-unescaped-html-sink apps/web/src/app/hire/page.tsx:104
MEDIUM js-unescaped-html-sink apps/web/src/app/hire/page.tsx:108
MEDIUM js-open-redirect apps/web/src/components/funding/FundingClient.tsx:97
MEDIUM js-unescaped-html-sink apps/web/src/components/GuideReader.tsx:265
MEDIUM js-uninitialized-buffer packages/scan/src/node-rules.ts:456
LOW secret-generic-credential apps/web/src/app/api/auth/refresh/route.ts:17
LOW secret-generic-credential PRD.md:269
LOW tls-verification-disabled prd/0004-find-dangerous-code-patterns-without-pretending-to-be-a-compiler.md:121
LOW tls-verification-disabled prd/0004-find-dangerous-code-patterns-without-pretending-to-be-a-compiler.md:122
LOW sh-remote-script-execution scripts/smoke-test.sh:47
LOW secret-aws-access-key scripts/smoke-test.sh:112

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit 4f947b4 into master Sep 26, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant