Skip to content

Fix --signTemplate double-escaping every batch after the first on Linux/macOS - #1018

Open
virzak wants to merge 1 commit into
velopack:developfrom
virzak:fix/signtemplate-double-escape
Open

virzak wants to merge 1 commit into
velopack:developfrom
virzak:fix/signtemplate-double-escape

Conversation

@virzak

@virzak virzak commented Jul 28, 2026

Copy link
Copy Markdown

--signTemplate corrupts its own command on Linux/macOS for every file after the first.

EscapeForBash(signArguments) is called inside the batching loop, so each iteration escapes an already-escaped string:

do {
    ...
    } else {
        filesToSignStr = QuoteFileArgsBash(filesToSign);
        signArguments = EscapeForBash(signArguments);   // <-- re-escapes each batch
    }

Batch 1 is correct; batch 2 has been escaped twice, batch 3 three times.

Why this isn't an edge case

A {{file}} template pins parallelism to 1, so every file is its own batch. Any template containing \, $, `, " or ' — which is most real signing commands, since credentials and URLs get quoted — fails on the second file onwards.

A {{file...}} template hits the same thing once the file count exceeds --signParallel (default 10).

Reproduction

Two files, parallelism: 1, template cp {{file}} "/tmp/out_$V.bin", driven through CodeSign.Sign on Ubuntu:

Sign() THREW: UserInfoException: Signing command failed.
Output was:
cp: cannot create regular file '"/tmp/vrepro-912b4c40/out_\$V.bin"': No such file or directory

The destination has picked up literal " and \$. The first file had already been copied correctly.

The fix

Escape once, before the loop. The filesToSignStr branch collapses to a ternary since only the quoting differs per platform now.

Tests

Three regression tests added to CodeSignTests, covering the {{file}} multi-batch path, the {{file...}} path with more files than --signParallel, and an end-to-end copy to a quoted destination across two batches.

All three fail on develop and pass with the fix:

total failed succeeded skipped
without fix 34 3 25 6
with fix 34 0 28 6

Full Velopack.Packaging.Tests run: 86 total, 61 passed, 24 skipped, 1 failed — ResourceEditTests.CommitResourcesInCorrectOrder, which fails identically on an unmodified checkout of develop and is unrelated to this change.

Also corrected one log message: the {{file...}} branch announced itself as "a single file signing template".

Found while checking whether Velopack could sign Windows packages from Linux — it can, and does, which is how the multi-batch path got exercised. Disclosure: the signing tool I was driving it with is my own, but nothing here references or depends on it.

EscapeForBash was called on signArguments inside the batching loop, so each
iteration escaped an already-escaped string. The first batch was fine and
every subsequent one was progressively mangled.

This is not an edge case: a {{file}} template forces parallelism to 1, giving
each file its own batch, so any template containing \ $ ` " or ' fails on
the second file onwards with output like

    cp: cannot create regular file '"/tmp/out_\$V.bin"': No such file or directory

A {{file...}} template hits it once the file count exceeds --signParallel.

Escaping is now done once, before the loop. Adds three regression tests, each
of which fails without the fix, and corrects the log message on the
{{file...}} branch that described itself as a single file template.

This branch has not been deployed

No deployments
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