Skip to content

fix(updater): escape manifest-derived paths in the Windows update script - #580

Merged
kdroidFilter merged 1 commit into
nucleus-2.5from
security/windows-update-powershell-escape
Aug 19, 2026
Merged

fix(updater): escape manifest-derived paths in the Windows update script#580
kdroidFilter merged 1 commit into
nucleus-2.5from
security/windows-update-powershell-escape

Conversation

@kdroidFilter

Copy link
Copy Markdown
Collaborator

Security fix — F-6 (audit 2026-08-19)

Severity: Medium · Module: updater-runtime

Problem

The url field from the remote update manifest is used verbatim as the local artifact file name and then interpolated into single-quoted PowerShell strings with no escaping:

"Start-Process '${file.absolutePath}' -ArgumentList '/S', '--updated' -Wait"
"Remove-Item '$artifactPath' -Force -ErrorAction SilentlyContinue"

A hostile or compromised manifest can put a ' in the name to break out of the quoting and inject PowerShell that runs at install time. The macOS and Linux script builders already escape via quoteForShell(); the Windows builder had no equivalent.

Fix

A psSingleQuote() helper doubles every ' — the PowerShell escape for a literal quote inside a single-quoted string — and every manifest-derived value (artifact path, script path, installer path, relaunch launcher) is passed through it. Ordinary Windows paths are unchanged; a ' can no longer terminate the string and start a new command.

Test

WindowsUpdateScriptTest gains a case feeding a name containing ev'il; Start-Process calc.exe # and asserting the quote is doubled in the generated script and the raw break-out sequence never appears.

Note (out of scope here): path traversal via a ../-containing manifest name is a separate concern in the staging-path construction (NucleusUpdater / FileSelector) and is best fixed there with a filename charset allow-list; this PR closes the injection (RCE) vector only.

Verified locally: :updater-runtime:ktlintCheck :updater-runtime:detekt :updater-runtime:test all pass.

🤖 Generated with Claude Code

The Windows self-update PowerShell interpolates the downloaded artifact path
into single-quoted strings (Remove-Item, Start-Process). That path is built
from the `url` field of the remote update manifest, so a hostile or
compromised manifest could embed a single quote to break out of the quoting
and inject PowerShell that runs at install time. The macOS and Linux script
builders already quote via quoteForShell(); the Windows builder did not.

Every manifest-derived value is now passed through psSingleQuote(), which
doubles single quotes — the PowerShell escape for a literal quote inside a
single-quoted string — closing the injection while leaving ordinary Windows
paths unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@kdroidFilter
kdroidFilter merged commit 72e4260 into nucleus-2.5 Aug 19, 2026
4 checks passed
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