Skip to content

fix(security): harden clerk init dependency-install spawn#302

Merged
rafa-thayto merged 2 commits into
mainfrom
rafaelthayto/aie-969-clerk-inits-pnpm-add-step-autoloads-attacker-pnpmfilecjs
May 21, 2026
Merged

fix(security): harden clerk init dependency-install spawn#302
rafa-thayto merged 2 commits into
mainfrom
rafaelthayto/aie-969-clerk-inits-pnpm-add-step-autoloads-attacker-pnpmfilecjs

Conversation

@rafa-thayto
Copy link
Copy Markdown
Contributor

@rafa-thayto rafa-thayto commented May 21, 2026

Summary

clerk init spawns the user's package manager in attacker-controlled cwd to install the framework SDK. Two cwd-reachable code-exec primitives were live on that spawn:

  1. pnpm .pnpmfile.cjs autoload — pnpm require()s .pnpmfile.cjs from cwd at startup before resolving any package. Top-level statements execute at full user privilege. Verified live: a sentinel .pnpmfile.cjs writes /tmp/PWNED while clerk init reports a normal install. This is AIE-969.
  2. Lifecycle scripts — every PM (npm/yarn/pnpm/bun) runs preinstall/install/postinstall from the project's package.json on 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_FLAGS is the single source of truth — both the SDK install (heuristics.installSdk/installDeps) and the clerk init --starter bootstrap install (bootstrap.installDependencies) consume it, so the two install surfaces can't drift.

globalInstallCommand (the clerk update upgrade-hint message) is intentionally decoupled — that string is copy-pasted by users to install the trusted clerk package globally, and lifecycle scripts are how some PMs link the binary into PATH.

Test plan

  • Empirical exploit reproduction: baseline pnpm add in attacker cwd fires the sentinel .pnpmfile.cjs; with the flags applied, the same install succeeds without the sentinel firing.
  • bun run format / lint / typecheck clean
  • bun run test for the affected files (package-manager.test.ts, installer.test.ts, bootstrap.test.ts): 77/77 pass
  • Full bun run test: no new failures (pre-existing env-related failures unchanged at 122)
  • CI runs all unit + e2e suites on push

Fixes AIE-969.

`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-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 21, 2026

🦋 Changeset detected

Latest commit: ed3359f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
clerk Patch

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

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 21, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR hardens the clerk init command to prevent arbitrary code execution from untrusted working directories. It introduces PM_INSTALL_HARDENING_FLAGS constant that maps each package manager to security-focused CLI flags (--ignore-scripts for all; --ignore-pnpmfile additionally for pnpm). These flags are integrated into install commands used by the bootstrap registry, global install commands are refactored to use explicit command templates instead of deriving from a shared helper, and tests validate the hardening behavior. A changeset documents the patch release.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main security hardening change to the clerk init dependency-install process.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description clearly relates to the changeset, detailing security fixes for attacker-controlled code execution in clerk init's dependency-install step via pnpm .pnpmfile.cjs autoload and lifecycle scripts.

✏️ 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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rafa-thayto rafa-thayto enabled auto-merge (squash) May 21, 2026 12:42
@rafa-thayto rafa-thayto merged commit b581d5f into main May 21, 2026
10 checks passed
@rafa-thayto rafa-thayto deleted the rafaelthayto/aie-969-clerk-inits-pnpm-add-step-autoloads-attacker-pnpmfilecjs branch May 21, 2026 14:58
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.

2 participants