Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: escape backslash input #834

Merged
merged 1 commit into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 12 additions & 8 deletions src/shellEscape.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,31 @@ export function shellEscape(args: Array<string>): string {
var output: Array<string> = [];

if (getOSType() === OSType.windows) {
args.forEach(function(arg) {
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, '\\"') + '"';
arg = arg.replace(/^(?:"")+/g, '') // unduplicate double-quote at the beginning
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) {
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
arg = arg.replace(/\\/g, '\\\\');
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(' ');
};
};
}
}
29 changes: 19 additions & 10 deletions test/suite/shellEscape.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
/* 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';

import {shellEscape} from '../../src/shellEscape';

suite('shellEscape', () => {
let sandbox: sinon.SinonSandbox;
Expand All @@ -19,43 +18,53 @@ suite('shellEscape', () => {
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
const output = 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'
const output = 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"
const output = 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"'
const output = shellEscape(['--project-name', `'test's "name"'`]); // 'test's "name"'
assert.strictEqual(output, `--project-name \\''test'\\''s "name"'\\'`); // --project-name \''test'\''s "name"'\'
});
test.only('non windows case: flag with backspace character', () => {
sandbox.stub(utils, 'getOSType').returns(utils.OSType.macOSarm);
const output = shellEscape(['--project-name', `\\bte\\bst | whoami`]);
assert.strictEqual(output, `--project-name '\\\\bte\\\\bst | whoami'`);
});
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
const output = 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'
const output = 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"
const output = 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"'
const output = 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 backspace character', () => {
sandbox.stub(utils, 'getOSType').returns(utils.OSType.windows);
const output = shellEscape(['--project-name', `\\bte\\bst | whoami`]);
assert.strictEqual(output, `--project-name "\\\\bte\\\\bst | whoami"`);
});
});
});