Skip to content

Commit b872035

Browse files
committed
Escape single quotes when launching a script by path
Since we surround it with single quotes.
1 parent 015fb46 commit b872035

File tree

4 files changed

+57
-48
lines changed

4 files changed

+57
-48
lines changed

src/PowerShellEditorServices/Services/DebugAdapter/Handlers/ConfigurationDoneHandler.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright (c) Microsoft Corporation.
1+
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

44
using System.Management.Automation;
@@ -112,7 +112,7 @@ internal async Task LaunchScriptAsync(string scriptToLaunch)
112112
{
113113
// For a saved file we just execute its path (after escaping it).
114114
command = PSCommandHelpers.BuildDotSourceCommandWithArguments(
115-
string.Concat("'", scriptToLaunch, "'"), _debugStateService?.Arguments);
115+
PSCommandHelpers.EscapeScriptFilePath(scriptToLaunch), _debugStateService?.Arguments);
116116
}
117117
else // It's a URI to an untitled script, or a raw script.
118118
{

src/PowerShellEditorServices/Utility/PSCommandExtensions.cs

+2
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ private static StringBuilder AddCommandText(this StringBuilder sb, Command comma
126126
return sb;
127127
}
128128

129+
public static string EscapeScriptFilePath(string f) => string.Concat("'", f.Replace("'", "''"), "'");
130+
129131
public static PSCommand BuildDotSourceCommandWithArguments(string command, IEnumerable<string> arguments)
130132
{
131133
string args = string.Join(" ", arguments ?? Array.Empty<string>());

test/PowerShellEditorServices.Test/Debugging/DebugServiceTests.cs

+53-46
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ public class DebugServiceTests : IDisposable
4141
private readonly BlockingCollection<DebuggerStoppedEventArgs> debuggerStoppedQueue = new();
4242
private readonly WorkspaceService workspace;
4343
private readonly ScriptFile debugScriptFile;
44+
private readonly ScriptFile oddPathScriptFile;
4445
private readonly ScriptFile variableScriptFile;
4546
private readonly TestReadLine testReadLine = new();
4647

@@ -68,9 +69,10 @@ public DebugServiceTests()
6869

6970
debugService.DebuggerStopped += OnDebuggerStopped;
7071

71-
// Load the test debug files
72+
// Load the test debug files.
7273
workspace = new WorkspaceService(NullLoggerFactory.Instance);
7374
debugScriptFile = GetDebugScript("DebugTest.ps1");
75+
oddPathScriptFile = GetDebugScript("Debug' W&ith $Params [Test].ps1");
7476
variableScriptFile = GetDebugScript("VariableTest.ps1");
7577
}
7678

@@ -105,16 +107,16 @@ private VariableDetailsBase[] GetVariables(string scopeName)
105107
return debugService.GetVariables(scope.Id);
106108
}
107109

108-
private Task ExecutePowerShellCommand(string command, params string[] args)
110+
private Task ExecuteScriptFileAsync(string scriptFilePath, params string[] args)
109111
{
110112
return psesHost.ExecutePSCommandAsync(
111-
PSCommandHelpers.BuildDotSourceCommandWithArguments(string.Concat('"', command, '"'), args),
113+
PSCommandHelpers.BuildDotSourceCommandWithArguments(PSCommandHelpers.EscapeScriptFilePath(scriptFilePath), args),
112114
CancellationToken.None);
113115
}
114116

115-
private Task ExecuteDebugFile() => ExecutePowerShellCommand(debugScriptFile.FilePath);
117+
private Task ExecuteDebugFileAsync() => ExecuteScriptFileAsync(debugScriptFile.FilePath);
116118

117-
private Task ExecuteVariableScriptFile() => ExecutePowerShellCommand(variableScriptFile.FilePath);
119+
private Task ExecuteVariableScriptFileAsync() => ExecuteScriptFileAsync(variableScriptFile.FilePath);
118120

119121
private void AssertDebuggerPaused()
120122
{
@@ -201,27 +203,22 @@ await debugService.SetCommandBreakpointsAsync(
201203
[MemberData(nameof(DebuggerAcceptsScriptArgsTestData))]
202204
public async Task DebuggerAcceptsScriptArgs(string[] args)
203205
{
204-
// The path is intentionally odd (some escaped chars but not all) because we are testing
205-
// the internal path escaping mechanism - it should escape certain chars ([, ] and space) but
206-
// it should not escape already escaped chars.
207-
ScriptFile debugWithParamsFile = GetDebugScript("Debug W&ith Params [Test].ps1");
208-
209206
BreakpointDetails[] breakpoints = await debugService.SetLineBreakpointsAsync(
210-
debugWithParamsFile,
211-
new[] { BreakpointDetails.Create(debugWithParamsFile.FilePath, 3) }).ConfigureAwait(true);
207+
oddPathScriptFile,
208+
new[] { BreakpointDetails.Create(oddPathScriptFile.FilePath, 3) }).ConfigureAwait(true);
212209

213210
Assert.Single(breakpoints);
214211
Assert.Collection(breakpoints, (breakpoint) =>
215212
{
216213
// TODO: The drive letter becomes lower cased on Windows for some reason.
217-
Assert.Equal(debugWithParamsFile.FilePath, breakpoint.Source, ignoreCase: true);
214+
Assert.Equal(oddPathScriptFile.FilePath, breakpoint.Source, ignoreCase: true);
218215
Assert.Equal(3, breakpoint.LineNumber);
219216
Assert.True(breakpoint.Verified);
220217
});
221218

222-
Task _ = ExecutePowerShellCommand(debugWithParamsFile.FilePath, args);
219+
Task _ = ExecuteScriptFileAsync(oddPathScriptFile.FilePath, args);
223220

224-
AssertDebuggerStopped(debugWithParamsFile.FilePath, 3);
221+
AssertDebuggerStopped(oddPathScriptFile.FilePath, 3);
225222

226223
VariableDetailsBase[] variables = GetVariables(VariableContainerDetails.LocalScopeName);
227224

@@ -273,8 +270,7 @@ public async Task DebuggerSetsAndClearsFunctionBreakpoints()
273270
breakpoints = await debugService.SetCommandBreakpointsAsync(
274271
new[] { CommandBreakpointDetails.Create("Get-Host") }).ConfigureAwait(true);
275272

276-
Assert.Single(breakpoints);
277-
Assert.Equal("Get-Host", breakpoints[0].Name);
273+
Assert.Equal("Get-Host", Assert.Single(breakpoints).Name);
278274

279275
breakpoints = await debugService.SetCommandBreakpointsAsync(
280276
Array.Empty<CommandBreakpointDetails>()).ConfigureAwait(true);
@@ -288,7 +284,7 @@ public async Task DebuggerStopsOnFunctionBreakpoints()
288284
CommandBreakpointDetails[] breakpoints = await debugService.SetCommandBreakpointsAsync(
289285
new[] { CommandBreakpointDetails.Create("Write-Host") }).ConfigureAwait(true);
290286

291-
Task _ = ExecuteDebugFile();
287+
Task _ = ExecuteDebugFileAsync();
292288
AssertDebuggerStopped(debugScriptFile.FilePath, 6);
293289

294290
VariableDetailsBase[] variables = GetVariables(VariableContainerDetails.LocalScopeName);
@@ -355,7 +351,7 @@ await debugService.SetLineBreakpointsAsync(
355351
BreakpointDetails.Create(debugScriptFile.FilePath, 7)
356352
}).ConfigureAwait(true);
357353

358-
Task _ = ExecuteDebugFile();
354+
Task _ = ExecuteDebugFileAsync();
359355
AssertDebuggerStopped(debugScriptFile.FilePath, 5);
360356
debugService.Continue();
361357
AssertDebuggerStopped(debugScriptFile.FilePath, 7);
@@ -373,7 +369,7 @@ await debugService.SetLineBreakpointsAsync(
373369
BreakpointDetails.Create(debugScriptFile.FilePath, 7, null, $"$i -eq {breakpointValue1} -or $i -eq {breakpointValue2}"),
374370
}).ConfigureAwait(true);
375371

376-
Task _ = ExecuteDebugFile();
372+
Task _ = ExecuteDebugFileAsync();
377373
AssertDebuggerStopped(debugScriptFile.FilePath, 7);
378374

379375
VariableDetailsBase[] variables = GetVariables(VariableContainerDetails.LocalScopeName);
@@ -409,7 +405,7 @@ await debugService.SetLineBreakpointsAsync(
409405
BreakpointDetails.Create(debugScriptFile.FilePath, 6, null, null, $"{hitCount}"),
410406
}).ConfigureAwait(true);
411407

412-
Task _ = ExecuteDebugFile();
408+
Task _ = ExecuteDebugFileAsync();
413409
AssertDebuggerStopped(debugScriptFile.FilePath, 6);
414410

415411
VariableDetailsBase[] variables = GetVariables(VariableContainerDetails.LocalScopeName);
@@ -430,7 +426,7 @@ await debugService.SetLineBreakpointsAsync(
430426
debugScriptFile,
431427
new[] { BreakpointDetails.Create(debugScriptFile.FilePath, 6, null, "$i % 2 -eq 0", $"{hitCount}") }).ConfigureAwait(true);
432428

433-
Task _ = ExecuteDebugFile();
429+
Task _ = ExecuteDebugFileAsync();
434430
AssertDebuggerStopped(debugScriptFile.FilePath, 6);
435431

436432
VariableDetailsBase[] variables = GetVariables(VariableContainerDetails.LocalScopeName);
@@ -498,7 +494,7 @@ public async Task DebuggerBreaksWhenRequested()
498494
{
499495
IReadOnlyList<LineBreakpoint> confirmedBreakpoints = await GetConfirmedBreakpoints(debugScriptFile).ConfigureAwait(true);
500496
Assert.Equal(0, confirmedBreakpoints.Count);
501-
Task _ = ExecuteDebugFile();
497+
Task _ = ExecuteDebugFileAsync();
502498
// NOTE: This must be run on a separate thread so the async event handlers can fire.
503499
await Task.Run(() => debugService.Break()).ConfigureAwait(true);
504500
AssertDebuggerPaused();
@@ -507,7 +503,7 @@ public async Task DebuggerBreaksWhenRequested()
507503
[Fact]
508504
public async Task DebuggerRunsCommandsWhileStopped()
509505
{
510-
Task _ = ExecuteDebugFile();
506+
Task _ = ExecuteDebugFileAsync();
511507
// NOTE: This must be run on a separate thread so the async event handlers can fire.
512508
await Task.Run(() => debugService.Break()).ConfigureAwait(true);
513509
AssertDebuggerPaused();
@@ -529,7 +525,7 @@ await debugService.SetCommandBreakpointsAsync(
529525
new[] { CommandBreakpointDetails.Create("Write-Host") }).ConfigureAwait(true);
530526

531527
ScriptFile testScript = GetDebugScript("PSDebugContextTest.ps1");
532-
Task _ = ExecutePowerShellCommand(testScript.FilePath);
528+
Task _ = ExecuteScriptFileAsync(testScript.FilePath);
533529
AssertDebuggerStopped(testScript.FilePath, 11);
534530

535531
VariableDetails prompt = await debugService.EvaluateExpressionAsync("prompt", false).ConfigureAwait(true);
@@ -582,12 +578,10 @@ public async Task RecordsF5CommandInPowerShellHistory()
582578
CancellationToken.None).ConfigureAwait(true);
583579

584580
// Check the PowerShell history
585-
Assert.Single(historyResult);
586-
Assert.Equal(". '" + debugScriptFile.FilePath + "'", historyResult[0]);
581+
Assert.Equal(". '" + debugScriptFile.FilePath + "'", Assert.Single(historyResult));
587582

588583
// Check the stubbed PSReadLine history
589-
Assert.Single(testReadLine.history);
590-
Assert.Equal(". '" + debugScriptFile.FilePath + "'", testReadLine.history[0]);
584+
Assert.Equal(". '" + debugScriptFile.FilePath + "'", Assert.Single(testReadLine.history));
591585
}
592586

593587
[Fact]
@@ -606,12 +600,25 @@ public async Task RecordsF8CommandInHistory()
606600
CancellationToken.None).ConfigureAwait(true);
607601

608602
// Check the PowerShell history
609-
Assert.Single(historyResult);
610-
Assert.Equal(script, historyResult[0]);
603+
Assert.Equal(script, Assert.Single(historyResult));
611604

612605
// Check the stubbed PSReadLine history
613-
Assert.Single(testReadLine.history);
614-
Assert.Equal(script, testReadLine.history[0]);
606+
Assert.Equal(script, Assert.Single(testReadLine.history));
607+
}
608+
609+
[Fact]
610+
public async Task OddFilePathsLaunchCorrectly()
611+
{
612+
ConfigurationDoneHandler configurationDoneHandler = new(
613+
NullLoggerFactory.Instance, null, debugService, null, null, psesHost, workspace, null, psesHost);
614+
await configurationDoneHandler.LaunchScriptAsync(oddPathScriptFile.FilePath).ConfigureAwait(true);
615+
616+
IReadOnlyList<string> historyResult = await psesHost.ExecutePSCommandAsync<string>(
617+
new PSCommand().AddScript("(Get-History).CommandLine"),
618+
CancellationToken.None).ConfigureAwait(true);
619+
620+
// Check the PowerShell history
621+
Assert.Equal(". " + PSCommandHelpers.EscapeScriptFilePath(oddPathScriptFile.FilePath), Assert.Single(historyResult));
615622
}
616623

617624
[Fact]
@@ -621,7 +628,7 @@ await debugService.SetLineBreakpointsAsync(
621628
variableScriptFile,
622629
new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 8) }).ConfigureAwait(true);
623630

624-
Task _ = ExecuteVariableScriptFile();
631+
Task _ = ExecuteVariableScriptFileAsync();
625632
AssertDebuggerStopped(variableScriptFile.FilePath);
626633

627634
VariableDetailsBase[] variables = GetVariables(VariableContainerDetails.LocalScopeName);
@@ -639,7 +646,7 @@ await debugService.SetLineBreakpointsAsync(
639646
variableScriptFile,
640647
new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 21) }).ConfigureAwait(true);
641648

642-
Task _ = ExecuteVariableScriptFile();
649+
Task _ = ExecuteVariableScriptFileAsync();
643650
AssertDebuggerStopped(variableScriptFile.FilePath);
644651

645652
VariableDetailsBase[] variables = GetVariables(VariableContainerDetails.LocalScopeName);
@@ -689,7 +696,7 @@ await debugService.SetLineBreakpointsAsync(
689696
variableScriptFile,
690697
new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 14) }).ConfigureAwait(true);
691698

692-
Task _ = ExecuteVariableScriptFile();
699+
Task _ = ExecuteVariableScriptFileAsync();
693700
AssertDebuggerStopped(variableScriptFile.FilePath);
694701

695702
VariableScope[] scopes = debugService.GetVariableScopes(0);
@@ -743,7 +750,7 @@ await debugService.SetLineBreakpointsAsync(
743750
new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 14) }).ConfigureAwait(true);
744751

745752
// Execute the script and wait for the breakpoint to be hit
746-
Task _ = ExecuteVariableScriptFile();
753+
Task _ = ExecuteVariableScriptFileAsync();
747754
AssertDebuggerStopped(variableScriptFile.FilePath);
748755

749756
VariableScope[] scopes = debugService.GetVariableScopes(0);
@@ -799,7 +806,7 @@ await debugService.SetLineBreakpointsAsync(
799806
new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 15) }).ConfigureAwait(true);
800807

801808
// Execute the script and wait for the breakpoint to be hit
802-
Task _ = ExecuteVariableScriptFile();
809+
Task _ = ExecuteVariableScriptFileAsync();
803810
AssertDebuggerStopped(variableScriptFile.FilePath);
804811

805812
StackFrameDetails[] stackFrames = await debugService.GetStackFramesAsync().ConfigureAwait(true);
@@ -819,7 +826,7 @@ await debugService.SetLineBreakpointsAsync(
819826
new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 11) }).ConfigureAwait(true);
820827

821828
// Execute the script and wait for the breakpoint to be hit
822-
Task _ = ExecuteVariableScriptFile();
829+
Task _ = ExecuteVariableScriptFileAsync();
823830
AssertDebuggerStopped(variableScriptFile.FilePath);
824831

825832
StackFrameDetails[] stackFrames = await debugService.GetStackFramesAsync().ConfigureAwait(true);
@@ -852,7 +859,7 @@ await debugService.SetLineBreakpointsAsync(
852859
new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 16) }).ConfigureAwait(true);
853860

854861
// Execute the script and wait for the breakpoint to be hit
855-
Task _ = ExecuteVariableScriptFile();
862+
Task _ = ExecuteVariableScriptFileAsync();
856863
AssertDebuggerStopped(variableScriptFile.FilePath);
857864

858865
StackFrameDetails[] stackFrames = await debugService.GetStackFramesAsync().ConfigureAwait(true);
@@ -872,7 +879,7 @@ await debugService.SetLineBreakpointsAsync(
872879
new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 17) }).ConfigureAwait(true);
873880

874881
// Execute the script and wait for the breakpoint to be hit
875-
Task _ = ExecuteVariableScriptFile();
882+
Task _ = ExecuteVariableScriptFileAsync();
876883
AssertDebuggerStopped(variableScriptFile.FilePath);
877884

878885
StackFrameDetails[] stackFrames = await debugService.GetStackFramesAsync().ConfigureAwait(true);
@@ -898,7 +905,7 @@ public async Task DebuggerEnumerableShowsRawView()
898905
await debugService.SetCommandBreakpointsAsync(new[] { breakpoint }).ConfigureAwait(true);
899906

900907
// Execute the script and wait for the breakpoint to be hit
901-
Task _ = ExecuteVariableScriptFile();
908+
Task _ = ExecuteVariableScriptFileAsync();
902909
AssertDebuggerStopped(commandBreakpointDetails: breakpoint);
903910

904911
VariableDetailsBase simpleArrayVar = Array.Find(
@@ -935,7 +942,7 @@ public async Task DebuggerDictionaryShowsRawView()
935942
await debugService.SetCommandBreakpointsAsync(new[] { breakpoint }).ConfigureAwait(true);
936943

937944
// Execute the script and wait for the breakpoint to be hit
938-
Task _ = ExecuteVariableScriptFile();
945+
Task _ = ExecuteVariableScriptFileAsync();
939946
AssertDebuggerStopped(commandBreakpointDetails: breakpoint);
940947

941948
VariableDetailsBase simpleDictionaryVar = Array.Find(
@@ -971,7 +978,7 @@ public async Task DebuggerDerivedDictionaryPropertyInRawView()
971978
await debugService.SetCommandBreakpointsAsync(new[] { breakpoint }).ConfigureAwait(true);
972979

973980
// Execute the script and wait for the breakpoint to be hit
974-
Task _ = ExecuteVariableScriptFile();
981+
Task _ = ExecuteVariableScriptFileAsync();
975982
AssertDebuggerStopped(commandBreakpointDetails: breakpoint);
976983

977984
VariableDetailsBase sortedDictionaryVar = Array.Find(
@@ -1000,7 +1007,7 @@ await debugService.SetLineBreakpointsAsync(
10001007
new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 18) }).ConfigureAwait(true);
10011008

10021009
// Execute the script and wait for the breakpoint to be hit
1003-
Task _ = ExecuteVariableScriptFile();
1010+
Task _ = ExecuteVariableScriptFileAsync();
10041011
AssertDebuggerStopped(variableScriptFile.FilePath);
10051012

10061013
StackFrameDetails[] stackFrames = await debugService.GetStackFramesAsync().ConfigureAwait(true);
@@ -1029,7 +1036,7 @@ await debugService.SetLineBreakpointsAsync(
10291036
new[] { BreakpointDetails.Create(variableScriptFile.FilePath, 19) }).ConfigureAwait(true);
10301037

10311038
// Execute the script and wait for the breakpoint to be hit
1032-
Task _ = ExecuteVariableScriptFile();
1039+
Task _ = ExecuteVariableScriptFileAsync();
10331040
AssertDebuggerStopped(variableScriptFile.FilePath);
10341041

10351042
StackFrameDetails[] stackFrames = await debugService.GetStackFramesAsync().ConfigureAwait(true);

0 commit comments

Comments
 (0)