Skip to content

staged-lint: file paths with special shell characters (e.g. parentheses) break pre-commit hook #2067

Description

@dokterbob

Summary

The staged-lint feature passes file paths to execSync as unquoted shell strings, which breaks for any path containing shell special characters. The most common real-world trigger is SvelteKit route groups, which use parentheses in directory names (e.g. src/routes/(app)/+page.svelte).

Steps to reproduce

  1. Create a project with a file inside a directory with parentheses: src/routes/(app)/+page.svelte
  2. Stage that file: git add src/routes/\(app\)/+page.svelte
  3. Run git commit — the pre-commit hook fails with:
/bin/sh: -c: line 0: syntax error near unexpected token `('
/bin/sh: -c: line 0: `git show :src/routes/(app)/+page.svelte'

Root cause

Three locations in dist/bin/cli.js (and mirror locations in dist/index.js) interpolate file paths directly into shell command strings without quoting:

// captureStagedContent — line ~6070
execSync(`git show :${file}`, ...)

// runLintCommand — line ~6127
const finalCommand = `${command} ${files.join(" ")}`;

// restageFiles — line ~6169
execSync(`git add ${files.join(" ")}`, ...)

Fix

Add a POSIX-compatible shell-quoting helper and apply it at all three sites:

function shellQuote(f) { return "'" + f.replace(/'/g, "'\\''") + "'"; }

// captureStagedContent
execSync(`git show :${shellQuote(file)}`, ...)

// runLintCommand
const quotedFiles = files.map((f) => shellQuote(f)).join(" ");
const finalCommand = command.includes("{files}")
  ? command.replace("{files}", quotedFiles)
  : `${command} ${quotedFiles}`;

// restageFiles
execSync(`git add ${files.map((f) => shellQuote(f)).join(" ")}`, ...)

Context

  • bun-git-hooks version: 0.3.1
  • Affected characters: (), spaces, $, `, !, [, ], {, }, &, ;, |, >, <, ~, *, ?
  • Common trigger: SvelteKit route groups (src/routes/(app)/..., src/routes/(auth)/...)
  • Workaround: SKIP_BUN_GIT_HOOKS=1 git commit or use bun patch to apply the fix locally

We are currently applying this as a local bun patch in our project (dokterbob/mutuvia).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions