escape printed characters to prevent command injection #45

This commit is contained in:
undergroundwires
2021-02-21 12:34:33 +01:00
parent 45a3669443
commit 1260eea690
4 changed files with 72 additions and 22 deletions

View File

@@ -5,6 +5,12 @@ export class BatchBuilder extends CodeBuilder {
return '::'; return '::';
} }
protected writeStandardOut(text: string): string { protected writeStandardOut(text: string): string {
return `echo ${text}`; return `echo ${escapeForEcho(text)}`;
} }
} }
function escapeForEcho(text: string) {
return text
.replace(/&/g, '^&')
.replace(/%/g, '%%');
}

View File

@@ -5,6 +5,11 @@ export class ShellBuilder extends CodeBuilder {
return '#'; return '#';
} }
protected writeStandardOut(text: string): string { protected writeStandardOut(text: string): string {
return `echo '${text}'`; return `echo '${escapeForEcho(text)}'`;
} }
} }
function escapeForEcho(text: string) {
return text
.replace(/'/g, '\'\\\'\'');
}

View File

@@ -23,15 +23,37 @@ describe('BatchBuilder', () => {
}); });
}); });
describe('writeStandardOut', () => { describe('writeStandardOut', () => {
it('prepends expected', () => { const testData = [
// arrange {
const text = 'test'; name: 'plain text',
const expected = `echo ${text}`; text: 'test',
const sut = new BatchBuilderRevealer(); expected: 'echo test',
// act },
const actual = sut.writeStandardOut(text); {
// assert name: 'text with ampersand',
expect(expected).to.equal(actual); text: 'a & b',
}); expected: 'echo a ^& b',
},
{
name: 'text with percent sign',
text: '90%',
expected: 'echo 90%%',
},
{
name: 'text with multiple ampersands and percent signs',
text: 'Me&you in % ? You & me = 0%',
expected: 'echo Me^&you in %% ? You ^& me = 0%%',
},
];
for (const test of testData) {
it(test.name, () => {
// arrange
const sut = new BatchBuilderRevealer();
// act
const actual = sut.writeStandardOut(test.text);
// assert
expect(test.expected).to.equal(actual);
});
}
}); });
}); });

View File

@@ -23,15 +23,32 @@ describe('ShellBuilder', () => {
}); });
}); });
describe('writeStandardOut', () => { describe('writeStandardOut', () => {
it('prepends expected', () => { const testData = [
// arrange {
const text = 'test'; name: 'plain text',
const expected = `echo '${text}'`; text: 'test',
const sut = new ShellBuilderRevealer(); expected: 'echo \'test\'',
// act },
const actual = sut.writeStandardOut(text); {
// assert name: 'text with single quote',
expect(expected).to.equal(actual); text: 'I\'m not who you think I am',
}); expected: 'echo \'I\'\\\'\'m not who you think I am\'',
},
{
name: 'text with multiple single quotes',
text: 'I\'m what you\'re',
expected: 'echo \'I\'\\\'\'m what you\'\\\'\'re\'',
},
];
for (const test of testData) {
it(test.name, () => {
// arrange
const sut = new ShellBuilderRevealer();
// act
const actual = sut.writeStandardOut(test.text);
// assert
expect(test.expected).to.equal(actual);
});
}
}); });
}); });