Skip to content

fix: pass gitleaks --log-opts without a shell so Windows keeps the caret - #1676

Open
ppcvote wants to merge 1 commit into
finos:mainfrom
ppcvote:fix/gitleaks-log-opts-windows
Open

fix: pass gitleaks --log-opts without a shell so Windows keeps the caret#1676
ppcvote wants to merge 1 commit into
finos:mainfrom
ppcvote:fix/gitleaks-log-opts-windows

Conversation

@ppcvote

@ppcvote ppcvote commented Aug 13, 2026

Copy link
Copy Markdown

Description

runCommand spawned with shell: true, and the range argument was written with POSIX single quotes. With shell: true Node joins the command and its arguments into a single command line and hands it to the platform shell. cmd.exe does not treat ' as a quote character, and does treat ^ as an escape character.

Measured on Windows through the same code path, with commitFrom = abc123, commitTo = def456:

resulting argv
intended, one entry ["--log-opts='--first-parent abc123^..def456'"]
shell: true ["--log-opts='--first-parent", "abc123..def456'"]
no shell ["--log-opts='--first-parent abc123^..def456'"]

Two things go wrong at once: the argument splits at the space, and the ^ is deleted.

The caret is the part that matters. <sha>^..<sha> starts at the parent of commitFrom; <sha>..<sha> starts at commitFrom itself. Losing it narrows the range by one commit, and the commit that falls out is the first commit of the push being scanned. gitleaks then runs, exits 0, and reports no leaks, having skipped a commit that was in scope. On Windows the scan quietly covered less than it claimed.

Related Issue

Resolves #1675

Changes

Two lines in src/proxy/processors/push-action/gitleaks.ts:

  • spawn(command, args, { cwd, shell: true }) becomes spawn(command, args, { cwd })
  • the embedded '...' around the --log-opts value is removed, since with the argument passed as one array element there is nothing for a shell to re-parse

runCommand is only ever called with git and gitleaks, both native executables rather than .cmd shims, so nothing here depended on shell resolution. I verified on Windows that spawn('git', ['--version'], {}) with no shell resolves through PATH and exits 0, and the git rev-list call at line 152 passes only simple tokens, so it is unaffected either way.

Test

One test added to test/processors/gitLeaks.test.ts, which already mocks node:child_process, so no gitleaks binary is needed. It reads spawn.mock.calls[1] (call 0 being git rev-list) and asserts three things: the exact argv entry --log-opts=--first-parent abc123^..def456, that no argument contains a ', and that shell is not true.

Confirmed it discriminates by stashing the source change and re-running: 1 failed / 10 passed on the unmodified tree, 11 passed with the fix.

Full suite is unchanged: 48 failures before this branch and the same 48 after, with one extra passing test. Those failures are pre-existing on Windows and unrelated. prettier --check and eslint are clean on both touched files; the two tsc errors on main are in src/ui/views/PushDetails/components/AttestationInfo.tsx and predate this change.

Note

An asymmetry worth naming: the repository does run a Windows CI job, but nothing currently asserts on what spawn receives, so a platform-dependent quoting bug can pass every job. The test added here closes that specific gap; whether it is worth generalising is your call.

runCommand spawned with shell:true, and the range argument was written with
POSIX single quotes. With shell:true Node joins command and args into one
command line for the platform shell, and cmd.exe neither treats ' as a quote
nor leaves ^ alone. Measured through the same code path on Windows:

  intended, one entry:  ["--log-opts='--first-parent abc123^..def456'"]
  shell:true actual:    ["--log-opts='--first-parent", "abc123..def456'"]
  no shell:             ["--log-opts='--first-parent abc123^..def456'"]

The argument is split at the space, and the caret is deleted.

The caret is the part that matters. <sha>^..<sha> starts at the parent of
commitFrom; <sha>..<sha> starts at commitFrom. Losing it narrows the range by
one commit, and that commit is the first of the push. gitleaks then exits 0
having skipped a commit that was in scope, so on Windows the scan silently
covered less than it reported.

Dropping shell:true makes the argv identical on win32 and linux, at which point
the embedded quotes are unnecessary and are removed with it. runCommand is only
called with git and gitleaks, both native executables; spawn without a shell
resolves both through PATH on Windows, verified locally.

The test reads spawn.mock.calls[1] and asserts the exact argv entry, that no
argument contains a quote, and that shell is not true. It fails on the
unmodified tree and passes with the change.

resolves finos#1675
@ppcvote
ppcvote requested a review from a team as a code owner August 13, 2026 08:55
@netlify

netlify Bot commented Aug 13, 2026

Copy link
Copy Markdown

Deploy Preview for endearing-brigadeiros-63f9d0 canceled.

Name Link
🔨 Latest commit 130e95d
🔍 Latest deploy log https://app.netlify.com/projects/endearing-brigadeiros-63f9d0/deploys/6a7d869f4aac0a000822b9fd

@linux-foundation-easycla

linux-foundation-easycla Bot commented Aug 13, 2026

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

  • ✅ login: ppcvote / name: ppcvote (130e95d)

@github-actions github-actions Bot added the fix label Aug 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gitleaks scans one commit too few on Windows: shell:true corrupts the --log-opts argument

1 participant