When VS Code constructs a PowerShell command for a Debug Adapter Protocol runInTerminal request, environment variable values are handled differently from normal command arguments.
PowerShell command arguments already use the existing shell-specific quoting helper, which escapes embedded single quotes correctly. However, environment values are currently manually wrapped in single quotes.
For example, the environment assignment is constructed in a form equivalent to:
command += `\${env:${key}}='${value}'; `;
This works for ordinary values, but a value containing a single quote produces malformed or unintended PowerShell syntax.
Example
Given an environment value such as:
the generated command currently becomes:
${env:QUOTE}='hello'world';
For a PowerShell single-quoted string, the embedded quote should instead be doubled:
${env:QUOTE}='hello''world';
This is already how the existing PowerShell argument quoting logic handles embedded single quotes.
Relevant code
The behavior occurs in:
src/vs/workbench/contrib/debug/node/terminals.ts
inside the PowerShell handling in prepareCommand().
The relevant runInTerminal flow is:
DAP runInTerminal request
→ RawDebugSession
→ Debugger.runInTerminal
→ MainThreadDebugService
→ ExtHostDebugService.$runInTerminal
→ prepareCommand(...)
→ terminal shell integration / sendText()
Steps to reproduce
A focused unit test can reproduce the problem by calling prepareCommand() with PowerShell and an environment value containing a single quote.
For example:
prepareCommand(
'powershell',
[],
false,
undefined,
{ QUOTE: 'hello\'world' }
).trim()
Current output
${env:QUOTE}='hello'world';
Expected output
${env:QUOTE}='hello''world';
The same issue is visible with values containing multiple single quotes.
For example:
should be represented as:
${env:MULTI}='it''s ''ok''';
Expected behavior
PowerShell environment values should use the same PowerShell quoting rules as command arguments so that the original environment value is preserved exactly.
Values such as the following should all be represented correctly:
hello
hello world
hello'world
it's 'ok'
including an empty string.
Actual behavior
Environment values are manually wrapped in single quotes without escaping embedded single quotes.
As a result, values containing ' do not preserve the intended PowerShell string structure.
Proposed fix
Reuse the existing PowerShell quote() helper when constructing environment assignments instead of manually wrapping the value.
Conceptually:
- command += `\${env:${key}}='${value}'; `;
+ command += `\${env:${key}}=${quote(value)}; `;
This keeps PowerShell environment handling consistent with the existing argument quoting behavior and avoids introducing a second quoting implementation.
Regression coverage
A focused regression test covers:
- ordinary values
- values containing spaces
- empty values
- one embedded single quote
- multiple embedded single quotes
The regression test fails with the current implementation for quoted values and passes after reusing the existing PowerShell quoting helper.
Relevant test file:
src/vs/workbench/contrib/debug/test/node/terminals.test.ts
Validation
After applying the minimal change:
powershell - quotes environment values
1 passing
The existing nearby prepareCommand test suite also passes:
Debug - prepareCommand
7 passing
The client transpilation also completes successfully:
npm run transpile-client
✓ completed successfully
Security considerations
I have not identified a separate security boundary violation from this behavior.
Normal debugging requires Workspace Trust, and the runInTerminal request originates from the active debug adapter/debug extension context.
Based on the current analysis, this appears to be a correctness and shell-command construction issue, rather than a security vulnerability.
Environment
Observed in the current VS Code development source tree on Windows with PowerShell command construction.
When VS Code constructs a PowerShell command for a Debug Adapter Protocol
runInTerminalrequest, environment variable values are handled differently from normal command arguments.PowerShell command arguments already use the existing shell-specific quoting helper, which escapes embedded single quotes correctly. However, environment values are currently manually wrapped in single quotes.
For example, the environment assignment is constructed in a form equivalent to:
This works for ordinary values, but a value containing a single quote produces malformed or unintended PowerShell syntax.
Example
Given an environment value such as:
the generated command currently becomes:
For a PowerShell single-quoted string, the embedded quote should instead be doubled:
This is already how the existing PowerShell argument quoting logic handles embedded single quotes.
Relevant code
The behavior occurs in:
inside the PowerShell handling in
prepareCommand().The relevant
runInTerminalflow is:Steps to reproduce
A focused unit test can reproduce the problem by calling
prepareCommand()with PowerShell and an environment value containing a single quote.For example:
Current output
Expected output
The same issue is visible with values containing multiple single quotes.
For example:
should be represented as:
Expected behavior
PowerShell environment values should use the same PowerShell quoting rules as command arguments so that the original environment value is preserved exactly.
Values such as the following should all be represented correctly:
including an empty string.
Actual behavior
Environment values are manually wrapped in single quotes without escaping embedded single quotes.
As a result, values containing
'do not preserve the intended PowerShell string structure.Proposed fix
Reuse the existing PowerShell
quote()helper when constructing environment assignments instead of manually wrapping the value.Conceptually:
This keeps PowerShell environment handling consistent with the existing argument quoting behavior and avoids introducing a second quoting implementation.
Regression coverage
A focused regression test covers:
The regression test fails with the current implementation for quoted values and passes after reusing the existing PowerShell quoting helper.
Relevant test file:
Validation
After applying the minimal change:
The existing nearby
prepareCommandtest suite also passes:The client transpilation also completes successfully:
Security considerations
I have not identified a separate security boundary violation from this behavior.
Normal debugging requires Workspace Trust, and the
runInTerminalrequest originates from the active debug adapter/debug extension context.Based on the current analysis, this appears to be a correctness and shell-command construction issue, rather than a security vulnerability.
Environment
Observed in the current VS Code development source tree on Windows with PowerShell command construction.