fix(security): harden clerk init dependency-install spawn#302
Conversation
`clerk init` spawns the project's package manager in attacker-controlled cwd to install the framework SDK. On pnpm this autoloaded `.pnpmfile.cjs` from cwd at install startup, executing arbitrary JS via `require()` before any package resolved. Every PM additionally runs lifecycle scripts (`preinstall`/`install`/`postinstall`) from the project's `package.json`. Pass `--ignore-pnpmfile` (pnpm) and `--ignore-scripts` (all PMs) to the install spawn so a cloned-then-`clerk init`'d attacker repo can't gain arbitrary code-exec at install time. Decouple `globalInstallCommand` from `pmInstallCommand` — the upgrade-Clerk hint message is a copy-pasteable instruction to the user and must not inherit the hardening flags (lifecycle scripts are how some PMs link the binary into PATH). Fixes AIE-969.
- Extract PM_INSTALL_HARDENING_FLAGS as single source of truth so the `clerk init --starter` install path in bootstrap-registry.ts shares the flags with the SDK install in heuristics.ts. Without this, the two PM_INSTALL_COMMANDS tables drift independently and the `--starter` path loses the hardening. - Fix `satisfies` constraint on installer.ts GLOBAL_UPDATE_COMMANDS to bind against Installer (not PackageManager) — those types are independently maintained, and the lookup is called with an Installer. - Replace per-PM for-loops in the test with `test.each(PACKAGE_MANAGERS)` so each PM becomes its own named test case. - Drop the AIE-969 ticket reference from the test comment (ticket refs belong in PR description, not source).
🦋 Changeset detectedLatest commit: ed3359f The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
📝 WalkthroughWalkthroughThis PR hardens the Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
clerk initspawns the user's package manager in attacker-controlledcwdto install the framework SDK. Two cwd-reachable code-exec primitives were live on that spawn:.pnpmfile.cjsautoload — pnpmrequire()s.pnpmfile.cjsfrom cwd at startup before resolving any package. Top-level statements execute at full user privilege. Verified live: a sentinel.pnpmfile.cjswrites/tmp/PWNEDwhileclerk initreports a normal install. This is AIE-969.npm/yarn/pnpm/bun) runspreinstall/install/postinstallfrom the project'spackage.jsonon every install, which the attacker controls in the cloned repo.The fix passes
--ignore-pnpmfile(pnpm) and--ignore-scripts(all package managers) to the install spawn, closing both primitives.PM_INSTALL_HARDENING_FLAGSis the single source of truth — both the SDK install (heuristics.installSdk/installDeps) and theclerk init --starterbootstrap install (bootstrap.installDependencies) consume it, so the two install surfaces can't drift.globalInstallCommand(theclerk updateupgrade-hint message) is intentionally decoupled — that string is copy-pasted by users to install the trustedclerkpackage globally, and lifecycle scripts are how some PMs link the binary into PATH.Test plan
pnpm addin attacker cwd fires the sentinel.pnpmfile.cjs; with the flags applied, the same install succeeds without the sentinel firing.bun run format/lint/typecheckcleanbun run testfor the affected files (package-manager.test.ts,installer.test.ts,bootstrap.test.ts): 77/77 passbun run test: no new failures (pre-existing env-related failures unchanged at 122)Fixes AIE-969.