Skip to content

fix: upgrade cross-spawn (ReDoS), harden hook install and spawn handling#169

Merged
3rd-Eden merged 5 commits intomasterfrom
maintenance/deps-cross-spawn-hooks-2026
Apr 28, 2026
Merged

fix: upgrade cross-spawn (ReDoS), harden hook install and spawn handling#169
3rd-Eden merged 5 commits intomasterfrom
maintenance/deps-cross-spawn-hooks-2026

Conversation

@3rd-Eden
Copy link
Copy Markdown
Member

@3rd-Eden 3rd-Eden commented Apr 28, 2026

Cross-spawn upgrade + hook hardening

  • Bump cross-spawn to ^7.0.5 and which to ^4; drop unused spawn-sync.
  • Fix spawnSync result checks (use status/signal/error, not .code).
  • Treat non-zero / null close codes from npm run spawns reliably.
  • Install hook via absolute path to package hook script for Yarn PnP; chmod 0755.
  • Hook: cd to git root before require.resolve; use exec for node.
  • Install: guard gitdir parse; avoid fs.existsSync(null) on missing .git.
  • Dev: mocha 10, assume 2, nyc; engines node>=16; stub tty in tests.
  • Version 1.2.3; add package-lock.json; ignore .nyc_output.

Cleanup folded in from open PRs

This PR also takes care of three long-standing open PRs whose intent
overlaps with the cleanup. Each is implemented from scratch on top of
the new wrapper/hook code rather than rebasing the original branches.

Existing PRs superseded by this one (close on merge)

Addresses GH-167, GH-160, GH-157; mitigates GH-166 (cwd / exit handling).

- Bump cross-spawn to ^7.0.5 and which to ^4; drop unused spawn-sync.
- Fix spawnSync result checks (use status/signal/error, not .code).
- Treat non-zero / null close codes from npm run spawns reliably.
- Install hook via absolute path to package hook script for Yarn PnP; chmod 0755.
- Hook: cd to git root before require.resolve; use exec for node.
- Install: guard gitdir parse; avoid fs.existsSync(null) on missing .git.
- Dev: mocha 10, assume 2, nyc; engines node>=16; stub tty in tests.
- Version 1.2.3; add package-lock.json; ignore .nyc_output.

Addresses GH-167, GH-160, GH-157; mitigates GH-166 (cwd / exit handling).

Made-with: Cursor
Copilot AI review requested due to automatic review settings April 28, 2026 14:02
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR upgrades spawn-related dependencies to address security concerns and improves the reliability/portability of the git hook installation and hook execution (notably for Yarn PnP and inconsistent hook cwd/exit handling).

Changes:

  • Upgrade cross-spawn/which, modernize dev tooling to mocha+nyc, and bump package version/Node engine.
  • Harden hook installation and hook runtime behavior (absolute hook path, chmod 0755, cd to repo root, exec usage).
  • Add package-lock.json and ignore nyc output.

Reviewed changes

Copilot reviewed 5 out of 7 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
index.js Uses status/signal/error semantics for spawnSync results; improves close-code handling from npm run.
install.js Installs hook via absolute path (PnP-friendly), adds shell quoting, updates chmod mode, and guards gitdir parsing.
hook Ensures hook runs from repo root; uses exec for the Node invocation.
test.js Stubs tty.isatty for deterministic color/TTY-dependent behavior in tests.
package.json Dependency/devDependency updates, switches coverage tooling to nyc, adds Node engine, bumps version.
package-lock.json Newly added lockfile for dependency reproducibility.
.gitignore Ignores .nyc_output.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread install.js Outdated
Comment thread package.json Outdated
Addresses long-standing open PRs whose intent fits the cross-spawn /
hook-hardening cleanup:

* #127 - install.js: "backuped" -> "backed up".
* #148 - magit workaround: emit `unset GIT_LITERAL_PATHSPECS` in the
  generated `.git/hooks/pre-commit` wrapper, and also at the top of
  the package's `hook` script for defense-in-depth, so hooks invoked
  from emacs/magit behave the same as on the command line.
* #112 - resilience for missing pre-commit package: when a user
  switches to a branch without `node_modules`, the generated wrapper
  now exits 0 instead of failing the commit, and the `hook` script
  detects an unresolvable `pre-commit` package and skips with a
  friendly warning instead of throwing a Node module-not-found stack.

Made-with: Cursor
* getGitFolderPath previously recursed past a `.git` *file*, which made
  the existing submodule-aware gitdir parsing block unreachable -- in a
  submodule we silently walked up to the super-project's `.git` dir and
  installed the hook there instead. Return the `.git` path whether it is
  a file or directory.
* Resolve `gitdir:` pointers against the directory containing the `.git`
  file (path.dirname(git)) instead of the package root, since git stores
  those paths relative to the file. Fixes submodules whose `.git` file
  is not at the package root, and linked worktrees.
* Tighten engines.node to >=16.13.0 to match `which@4`'s minimum
  (`^16.13.0 || >=18.0.0`); the previous `>=16` allowed Node 16.0-16.12
  where `which@4` will warn/fail at install time.

Addresses copilot-pull-request-reviewer feedback on PR #169.
Supersedes the submodule-install half of #75.

Made-with: Cursor
This release contains breaking changes that warrant a major bump:

* Drops support for Node < 16.13 by introducing
  `engines.node: ">=16.13.0"` (matches `which@4`'s minimum). Previous
  releases declared no engine, so anything old enough to run them is
  now incompatible.
* Production deps make jumps that raise the floor and change result
  shapes: `cross-spawn` ^5 -> ^7 (`spawnSync` returns `status`, not
  `code`), `which` 1.2.x -> ^4. The runtime `spawn-sync` dep is dropped
  in favor of `cross-spawn.spawnSync`.
* The generated `.git/hooks/pre-commit` wrapper format is rewritten
  (single `exec bash <abs-hook> "\$@"` instead of the previous inline
  bash). Anyone parsing or scripting against the previous wrapper
  shape will see a different file.
* Hook file mode tightened from 0777 to 0755 (CIS 6.1.10).
* Submodule install location now resolves correctly to
  `<super>/.git/modules/<sub>/hooks` (previously the unreachable
  gitdir-parsing block silently caused submodule hooks to land in the
  super-project).

Minor / patch additions in the same release (carried by the same PR):

* Magit fix: generated wrapper and hook script `unset
  GIT_LITERAL_PATHSPECS` so hooks behave the same way under emacs.
* Resilient missing-package handling: a removed `pre-commit` (e.g.
  branch-switch without `node_modules`) skips with a friendly warning
  instead of breaking the commit.
* Internal: harden `index.js` spawn result checks; guard install-time
  gitdir parsing against null matches; spelling fix
  ("backuped" -> "backed up"); drop `istanbul` for `nyc`; modernize
  `mocha` and `assume` test deps.

Made-with: Cursor
@3rd-Eden 3rd-Eden merged commit a24084c into master Apr 28, 2026
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.

3 participants