Skip to content

fix(runtime-local): harden the Windows PowerShell launch path - #443

Open
Wenjix wants to merge 1 commit into
hypit-ai:mainfrom
Wenjix:security/powershell-hardening
Open

Wenjix wants to merge 1 commit into
hypit-ai:mainfrom
Wenjix:security/powershell-hardening

Conversation

@Wenjix

@Wenjix Wenjix commented Oct 27, 2026 •

Copy link
Copy Markdown

Closes #451

Summary

Three findings sharing the Windows -EncodedCommand launch path:

  • 28 (medium, CWE-88): powershellLiteral in packages/runtime-local/src/programs.ts escaped only the ASCII quote by doubling it, but Windows PowerShell accepts U+2018–U+201B as single-quote delimiters too. A typographic quote in a program command, argument, working directory, log path or environment value ended the literal early and reopened script parsing — injected statements inside the encoded Start-Process script. All five quote codepoints are now doubled (exported for the test, same convention as windowsCommandLine).
  • unlisted variant of the same boundary: $env:${name} interpolated environment names into the script unvalidated, so a name holding a quote/operator injected statements. Names now must match ^[A-Za-z_][A-Za-z0-9_]*$ — every in-repo program declaration already conforms.
  • 60 (medium, CWE-426): both startWithOwnConsole (programs.ts) and windowsCredential (packages/credential-store-local/src/windows.ts) spawned powershell.exe by bare name; CreateProcess searches the current directory first, so a planted powershell.exe in the working directory runs in place of the OS copy — with the caller's arguments, including the credential operation. A windowsPowerShell() helper now resolves the executable under %SystemRoot%\System32\WindowsPowerShell\v1.0\, falling back to the bare name only when SystemRoot is unset.

Behavior relative to main

Flow main this PR
arg/env value containing ’ ends literal early → script injection doubled → stays literal data
env name x';evil;#' injected into script throws must be a portable variable name
powershell.exe in CWD planted binary executes OS copy under SystemRoot runs

Red / green

packages/runtime-local/test/powershell-literal.test.ts (new): ASCII quote doubling preserved, and each of U+2018–U+201B doubles — on main they passed through unescaped.

Verification

  • node --import tsx --test packages/runtime-local/test/powershell-literal.test.ts packages/runtime-local/test/windows-command-line.test.ts — pass.
  • pnpm check — clean.
  • Windows PowerShell's typographic-quote tokenization is a documented 5.1 behavior; doubling is the same escape rule PowerShell applies to ASCII quotes.

Merge Danger

  • env names outside A-Za-z0-9_ (after a leading letter/underscore) now throw a clear error instead of silently interpolating.
  • With SystemRoot unset the launcher still falls back to PATH lookup, same as before.

Checklist

  • The why is documented in this description.
  • Regression test added; fails on main, passes here.
  • TypeScript check clean.
  • Windows end-to-end: managed program launch + credential-store round-trip (unchanged paths, but worth a smoke test).

Generated with Devin

Three gaps shared one script boundary:

- powershellLiteral() only escaped the ASCII quote, but Windows
  PowerShell accepts U+2018-U+201B as single-quote delimiters; a
  typographic quote in a command, path, log path or environment value
  ended the literal early and reopened script parsing inside the
  -EncodedCommand script.
- $env: names interpolated into the same script were not validated, so
  a name holding a quote or operator injected statements.
- powershell.exe spawned by bare name lets CreateProcess search the
  current directory first; a planted binary would run in place of the
  OS copy. Resolve the executable under SystemRoot instead (both here
  and in the credential-store-local launcher).

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 27, 2026 06:16

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

An organization owner can choose the repository organization as the billing source for reviews requested by members with a paid Copilot license. Learn about Copilot code review billing.

SanHsien added a commit to SanHsien/hypit that referenced this pull request Oct 28, 2026
…graphic quotes

Port of upstream PR hypit-ai#443 (issue hypit-ai#451, head 926502e); the diff does not
apply here, and this fork's credential adapter lives in credential-store-os
(upstream: credential-store-local). Three defects, all present in this fork:
- powershellLiteral doubled only the ASCII quote; Windows PowerShell also
  ends a literal at U+2018-U+201B, so a typographic quote reopened parsing
  of the -EncodedCommand script.
- $env: names went into that script unvalidated.
- startWithOwnConsole and windowsCredential spawned `powershell.exe` by bare
  name, which CreateProcess resolves from the current directory first.
Tests mock spawn/execFile, so none of them starts PowerShell; all failed on
the behaviour before the fix and pass after.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
SanHsien added a commit to SanHsien/hypit that referenced this pull request Oct 28, 2026
…i#447-hypit-ai#456; advance watermarks

PRs hypit-ai#442, hypit-ai#443 and hypit-ai#444 were ported ahead of upstream (d5afdfe, 541ab59,
5dc763c) because the fork has the same defect and it is reachable from
outside input. The rest are recorded with the fork-side evidence and a
re-review trigger in docs/fork/DECISIONS.md. Release track unchanged at
v0.3.1; watermarks PR 455, issue 456.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

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.

bug(runtime-local): typographic quotes inject into PowerShell and powershell.exe resolves from the working directory

2 participants