Skip to content

Commit

Permalink
Escape strings to be passed in as arguments. (#833)
Browse files Browse the repository at this point in the history
* add check

* missed semicolon

* empty line

* add update for file path

* add more test cases
  • Loading branch information
kwu-stripe authored Oct 11, 2024
1 parent a6d71b2 commit ab74d3c
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 2 deletions.
30 changes: 30 additions & 0 deletions src/shellEscape.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {OSType, getOSType} from './utils';

export function shellEscape(args: Array<string>): string {
var output: Array<string> = [];

if (getOSType() === OSType.windows) {
args.forEach(function(arg) {
// Check if the argument is a file path
const isFilePath = /^([a-zA-Z]:)?(\\[^<>:"/\\|?*]+)+\.exe$/.test(arg);

if (!isFilePath && /[^A-Za-z0-9_\/:=-]/.test(arg)) {
arg = '"' + arg.replace(/"/g, '\\"') + '"';
arg = arg.replace(/^(?:"")+/g, '') // unduplicate double-quote at the beginning
.replace(/\\"""/g, '\\"'); // remove non-escaped double-quote if there are enclosed between 2 escaped
}
output.push(arg);
});
return output.join(' ');
} else {
args.forEach(function(arg) {
if (/[^A-Za-z0-9_\/:=-]/.test(arg)) {
arg = "'" + arg.replace(/'/g,"'\\''") + "'";
arg = arg.replace(/^(?:'')+/g, '') // unduplicate single-quote at the beginning
.replace(/\\'''/g, "\\'"); // remove non-escaped single-quote if there are enclosed between 2 escaped
};
output.push(arg);
});
return output.join(' ');
};
};
5 changes: 3 additions & 2 deletions src/stripeTerminal.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as vscode from 'vscode';
import {StripeClient} from './stripeClient';
import {shellEscape} from './shellEscape';

type SupportedStripeCommand = 'events' | 'listen' | 'logs' | 'login' | 'trigger';

Expand Down Expand Up @@ -29,9 +30,9 @@ export class StripeTerminal {
}
}

const globalCLIFLags = this.getGlobalCLIFlags();
const globalCLIFlags = this.getGlobalCLIFlags();

const commandString = [cliPath, command, ...args, ...globalCLIFLags].join(' ');
const commandString = shellEscape([cliPath, command, ...args, ...globalCLIFlags]);

try {
const terminal = await this.createTerminal();
Expand Down
61 changes: 61 additions & 0 deletions test/suite/shellEscape.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/* eslint-disable quotes */
import * as assert from 'assert';
import * as shellEscape from '../../src/shellEscape';
import * as sinon from 'sinon';
import * as utils from '../../src/utils';


suite('shellEscape', () => {
let sandbox: sinon.SinonSandbox;

setup(() => {
sandbox = sinon.createSandbox();
});

teardown(() => {
sandbox.restore();
});

suite('shellEscape', () => {
test('non windows case: flag with spaces', () => {
sandbox.stub(utils, 'getOSType').returns(utils.OSType.macOSarm);
const output = shellEscape.shellEscape(['--project-name', 'test | whoami']); // test | whoami
assert.strictEqual(output, `--project-name 'test | whoami'`); // --project-name 'test | whoami'
});
test('non windows case: flag with single quote around entire arg', () => {
sandbox.stub(utils, 'getOSType').returns(utils.OSType.macOSarm);
const output = shellEscape.shellEscape(['--project-name', `'test name'`]); // 'test name'
assert.strictEqual(output, `--project-name \\''test name'\\'`); // --project-name \''test name'\'
});
test('non windows case: flag with double quote', () => {
sandbox.stub(utils, 'getOSType').returns(utils.OSType.macOSarm);
const output = shellEscape.shellEscape(['--project-name', `test "name"`]); // test "name"
assert.strictEqual(output, `--project-name 'test "name"'`); // --project-name 'test "name"'
});
test('non windows case: flag with lots of quotes', () => {
sandbox.stub(utils, 'getOSType').returns(utils.OSType.macOSarm);
const output = shellEscape.shellEscape(['--project-name', `'test's "name"'`]); // 'test's "name"'
assert.strictEqual(output, `--project-name \\''test'\\''s "name"'\\'`); // --project-name \''test'\''s "name"'\'
});
test.only('windows case: flag with space', () => {
sandbox.stub(utils, 'getOSType').returns(utils.OSType.windows);
const output = shellEscape.shellEscape(['--project-name', 'test | whoami']); // test | whoami
assert.strictEqual(output, `--project-name "test | whoami"`); // --project-name "test | whoami"
});
test.only('windows case: flag with single quote around entire arg', () => {
sandbox.stub(utils, 'getOSType').returns(utils.OSType.windows);
const output = shellEscape.shellEscape(['--project-name', `'test name'`]); // 'test name'
assert.strictEqual(output, `--project-name "'test name'"`); // --project-name "'test name'"
});
test.only('windows case: flag with double quote', () => {
sandbox.stub(utils, 'getOSType').returns(utils.OSType.windows);
const output = shellEscape.shellEscape(['--project-name', `test "name"`]); // test "name"
assert.strictEqual(output, `--project-name "test \\"name\\""`); // --project-name "test \"name\""
});
test.only('windows case: flag with lots of quotes', () => {
sandbox.stub(utils, 'getOSType').returns(utils.OSType.windows);
const output = shellEscape.shellEscape(['--project-name', `'test's "name"'`]); // 'test's "name"'
assert.strictEqual(output, `--project-name "'test's \\"name\\"'"`); // --project-name "'test's \"name\"'"
});
});
});

0 comments on commit ab74d3c

Please sign in to comment.