Skip to content

Run difft without a shell, so paths with spaces, quotes or $(...) work - #37

Open
czj wants to merge 1 commit into
marcoroth:mainfrom
czj:spawn-without-shell
Open

czj wants to merge 1 commit into
marcoroth:mainfrom
czj:spawn-without-shell

Conversation

@czj

@czj czj commented Oct 26, 2026 •

Copy link
Copy Markdown

Hi Marco, thanks for difftastic-ruby, we use it to render inline diffs of edited texts in our back office.

While profiling it I noticed that Differ#diff_files builds a single command string and runs it with backticks. When a path contains shell metacharacters, Ruby hands that string to /bin/sh: a file named a $(touch injected).txt actually runs touch injected, a quote in a filename breaks the command, and a TMPDIR containing a space makes diff_strings return nil. This PR adds a small Difftastic.capture(*arguments) that runs IO.popen([executable, *arguments], &:read), so arguments go straight to difft with no shell involved. Difftastic.execute is left untouched for anyone calling it directly.

It also fixes a crash I hit along the way: options.compact! returns nil when nothing was removed, so Differ.new(color:, context:, background:, syntax_highlight:, tab_width:, width:) with every option set raised NoMethodError: undefined method 'join' for nil.

test/shell_test.rb covers the three cases (metacharacters in paths, a TMPDIR with a space, every option set); all three fail on main and pass here, along with the existing suite. Outputs are byte-identical to main on 600 random text pairs across inline/side-by-side, color on/off and labels containing $(...), backticks and quotes. Speed is unchanged, since Ruby already skipped the shell for plain Tempfile paths. The actual speed-up for inline diffs is a separate question, in #38.


🤖 Generated with Claude Code, but prompted, read, and verified by @czj, a caring human :)

Paths containing spaces, quotes or $(...) no longer break the diff or reach
a shell, and Differ no longer crashes when every option is set (compact!
returned nil).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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