diff --git a/news/2 Fixes/5861.md b/news/2 Fixes/5861.md new file mode 100644 index 000000000000..5bff0305615a --- /dev/null +++ b/news/2 Fixes/5861.md @@ -0,0 +1 @@ +Disable quoting of paths sent to the debugger as arguments. \ No newline at end of file diff --git a/src/client/api.ts b/src/client/api.ts index 71c857e6e903..177dc997397d 100644 --- a/src/client/api.ts +++ b/src/client/api.ts @@ -4,7 +4,7 @@ 'use strict'; import { traceError } from './common/logger'; -import { RemoteDebuggerLauncherScriptProvider } from './debugger/debugAdapter/DebugClients/launcherProvider'; +import { RemoteDebuggerExternalLauncherScriptProvider } from './debugger/debugAdapter/DebugClients/launcherProvider'; /* * Do not introduce any breaking changes to this API. @@ -42,7 +42,7 @@ export function buildApi(ready: Promise) { }), debug: { async getRemoteLauncherCommand(host: string, port: number, waitUntilDebuggerAttaches: boolean = true): Promise { - return new RemoteDebuggerLauncherScriptProvider().getLauncherArgs({ host, port, waitUntilDebuggerAttaches }); + return new RemoteDebuggerExternalLauncherScriptProvider().getLauncherArgs({ host, port, waitUntilDebuggerAttaches }); } } }; diff --git a/src/client/debugger/debugAdapter/DebugClients/launcherProvider.ts b/src/client/debugger/debugAdapter/DebugClients/launcherProvider.ts index 455db85cde63..466c1f1707fd 100644 --- a/src/client/debugger/debugAdapter/DebugClients/launcherProvider.ts +++ b/src/client/debugger/debugAdapter/DebugClients/launcherProvider.ts @@ -15,7 +15,7 @@ export class NoDebugLauncherScriptProvider implements IDebugLauncherScriptProvid constructor(@optional() private script: string = pathToScript) { } public getLauncherArgs(options: LocalDebugOptions): string[] { const customDebugger = options.customDebugger ? '--custom' : '--default'; - return [this.script.fileToCommandArgument(), customDebugger, '--nodebug', '--client', '--host', options.host, '--port', options.port.toString()]; + return [this.script, customDebugger, '--nodebug', '--client', '--host', options.host, '--port', options.port.toString()]; } } @@ -23,11 +23,18 @@ export class DebuggerLauncherScriptProvider implements IDebugLauncherScriptProvi constructor(@optional() private script: string = pathToScript) { } public getLauncherArgs(options: LocalDebugOptions): string[] { const customDebugger = options.customDebugger ? '--custom' : '--default'; - return [this.script.fileToCommandArgument(), customDebugger, '--client', '--host', options.host, '--port', options.port.toString()]; + return [this.script, customDebugger, '--client', '--host', options.host, '--port', options.port.toString()]; } } -export class RemoteDebuggerLauncherScriptProvider implements IRemoteDebugLauncherScriptProvider { +/** + * This class is used to provide the launch scripts so external code can launch the debugger. + * As we're passing command arguments, we need to ensure the file paths are quoted. + * @export + * @class RemoteDebuggerExternalLauncherScriptProvider + * @implements {IRemoteDebugLauncherScriptProvider} + */ +export class RemoteDebuggerExternalLauncherScriptProvider implements IRemoteDebugLauncherScriptProvider { constructor(@optional() private script: string = pathToScript) { } public getLauncherArgs(options: RemoteDebugOptions): string[] { const waitArgs = options.waitUntilDebuggerAttaches ? ['--wait'] : []; diff --git a/src/test/debugger/debugAdapter/debugClients/launcherProvider.unit.test.ts b/src/test/debugger/debugAdapter/debugClients/launcherProvider.unit.test.ts index a3f67e3c22dd..8aeeed217b0a 100644 --- a/src/test/debugger/debugAdapter/debugClients/launcherProvider.unit.test.ts +++ b/src/test/debugger/debugAdapter/debugClients/launcherProvider.unit.test.ts @@ -7,7 +7,7 @@ import { expect } from 'chai'; import * as fs from 'fs-extra'; import * as path from 'path'; import { EXTENSION_ROOT_DIR } from '../../../../client/common/constants'; -import { DebuggerLauncherScriptProvider, NoDebugLauncherScriptProvider, RemoteDebuggerLauncherScriptProvider } from '../../../../client/debugger/debugAdapter/DebugClients/launcherProvider'; +import { DebuggerLauncherScriptProvider, NoDebugLauncherScriptProvider, RemoteDebuggerExternalLauncherScriptProvider } from '../../../../client/debugger/debugAdapter/DebugClients/launcherProvider'; const expectedPath = path.join(EXTENSION_ROOT_DIR, 'pythonFiles', 'ptvsd_launcher.py'); @@ -21,12 +21,12 @@ suite('Debugger - Launcher Script Provider', () => { { testName: 'When path to ptvsd launcher does not contains spaces', path: path.join('path', 'to', 'ptvsd_launcher'), - expectedPath: 'path/to/ptvsd_launcher' + expectedPath: path.join('path', 'to', 'ptvsd_launcher') }, { testName: 'When path to ptvsd launcher contains spaces', path: path.join('path', 'to', 'ptvsd_launcher', 'with spaces'), - expectedPath: '"path/to/ptvsd_launcher/with spaces"' + expectedPath: path.join('path', 'to', 'ptvsd_launcher', 'with spaces') } ]; @@ -52,15 +52,33 @@ suite('Debugger - Launcher Script Provider', () => { const expectedArgs = [testParams.expectedPath, '--custom', '--nodebug', '--client', '--host', 'something', '--port', '1234']; expect(args).to.be.deep.equal(expectedArgs); }); - test('Test remote debug launcher args (and do not wait for debugger to attach)', async () => { - const args = new RemoteDebuggerLauncherScriptProvider(testParams.path).getLauncherArgs({ host: 'something', port: 1234, waitUntilDebuggerAttaches: false }); - const expectedArgs = [testParams.expectedPath, '--default', '--host', 'something', '--port', '1234']; - expect(args).to.be.deep.equal(expectedArgs); - }); - test('Test remote debug launcher args (and wait for debugger to attach)', async () => { - const args = new RemoteDebuggerLauncherScriptProvider(testParams.path).getLauncherArgs({ host: 'something', port: 1234, waitUntilDebuggerAttaches: true }); - const expectedArgs = [testParams.expectedPath, '--default', '--host', 'something', '--port', '1234', '--wait']; - expect(args).to.be.deep.equal(expectedArgs); + }); + }); + + suite('External Debug Launcher', () => { + [ + { + testName: 'When path to ptvsd launcher does not contains spaces', + path: path.join('path', 'to', 'ptvsd_launcher'), + expectedPath: 'path/to/ptvsd_launcher' + }, + { + testName: 'When path to ptvsd launcher contains spaces', + path: path.join('path', 'to', 'ptvsd_launcher', 'with spaces'), + expectedPath: '"path/to/ptvsd_launcher/with spaces"' + } + ].forEach(testParams => { + suite(testParams.testName, async () => { + test('Test remote debug launcher args (and do not wait for debugger to attach)', async () => { + const args = new RemoteDebuggerExternalLauncherScriptProvider(testParams.path).getLauncherArgs({ host: 'something', port: 1234, waitUntilDebuggerAttaches: false }); + const expectedArgs = [testParams.expectedPath, '--default', '--host', 'something', '--port', '1234']; + expect(args).to.be.deep.equal(expectedArgs); + }); + test('Test remote debug launcher args (and wait for debugger to attach)', async () => { + const args = new RemoteDebuggerExternalLauncherScriptProvider(testParams.path).getLauncherArgs({ host: 'something', port: 1234, waitUntilDebuggerAttaches: true }); + const expectedArgs = [testParams.expectedPath, '--default', '--host', 'something', '--port', '1234', '--wait']; + expect(args).to.be.deep.equal(expectedArgs); + }); }); }); });