Skip to content

Commit

Permalink
Escape characters in scriptlet arguments
Browse files Browse the repository at this point in the history
  • Loading branch information
chrmod committed Nov 9, 2023
1 parent 0fdf598 commit 0d7eee6
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 11 deletions.
2 changes: 1 addition & 1 deletion packages/adblocker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"bundle": "tsc --build ./tsconfig.bundle.json && rollup --config ./rollup.config.ts --configPlugin typescript",
"prepack": "yarn run bundle",
"test": "nyc mocha --config ../../.mocharc.js",
"dev": "mocha --config ../../.mocharc.js --watch -f 'scriptlets arguments parsing'",
"dev": "mocha --config ../../.mocharc.js --watch",
"bench-metadata": "ts-node --project ./tools/tsconfig.json ./tools/bench-metadata.ts",
"bump-internal-engine-version": "ts-node --project ./tools/tsconfig.json ./tools/auto-bump-engine-version.ts",
"generate-codebooks": "concurrently -n build: \"yarn:codebook-*\" && yarn bump-internal-engine-version",
Expand Down
4 changes: 3 additions & 1 deletion packages/adblocker/src/filters/cosmetic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -791,7 +791,9 @@ export default class CosmeticFilter implements IFilter {
let script = js.get(name);
if (script !== undefined) {
for (let i = 0; i < args.length; i += 1) {
script = script.replace(`{{${i + 1}}}`, args[i]);
// escape some characters so they wont get evaluated with escape during script injection
const arg = args[i].replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
script = script.replace(`{{${i + 1}}}`, arg);
}

return script;
Expand Down
60 changes: 51 additions & 9 deletions packages/adblocker/test/parsing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1855,16 +1855,58 @@ describe('Cosmetic filters', () => {
// expect(CosmeticFilter.parse('###.selector /invalid/')).to.be.null;
// });

it('#getScript', () => {
const parsed = CosmeticFilter.parse('foo.com##+js(script.js, arg1, arg2, arg3)');
expect(parsed).not.to.be.null;
if (parsed !== null) {
expect(parsed.getScript(new Map([['script.js', '{{1}},{{2}},{{3}}']]))).to.equal(
'arg1,arg2,arg3',
);
describe('#getScript', () => {
const simpleScriptlet = CosmeticFilter.parse('foo.com##+js(script.js, arg1, arg2, arg3)');

expect(parsed.getScript(new Map())).to.be.undefined;
}
it('returns undefined if script does not exist', () => {
expect(simpleScriptlet?.getScript(new Map())).to.be.undefined;
});

it('returns a script if one exists', () => {
expect(simpleScriptlet?.getScript(new Map([['script.js', 'test']]))).to.equal('test');
});

context('with arguments', () => {
it('inject values', () => {
expect(simpleScriptlet?.getScript(new Map([['script.js', '{{1}},{{2}},{{3}}']]))).to.equal(
'arg1,arg2,arg3',
);
});

it('escapes special characters', () => {
for (const character of [
'.',
'*',
'+',
'?',
'^',
'$',
'{',
'}',
'(',
')',
'|',
'[',
']',
'\\',
]) {
const scriptlet = CosmeticFilter.parse(`foo.com##+js(script.js, ${character})`);
expect(scriptlet?.getScript(new Map([['script.js', '{{1}}']]))).to.equal(
`\\${character}`,
);
}
});

it('handles complex cases', () => {
for (const example of [
[String.raw`'\(a\)'`, String.raw`\\\(a\\\)`],
[String.raw`foo\*`, String.raw`foo\\\*`],
]) {
const scriptlet = CosmeticFilter.parse(`foo.com##+js(script.js, ${example[0]})`);
expect(scriptlet?.getScript(new Map([['script.js', '{{1}}']]))).to.equal(example[1]);
}
});
});
});

describe('#getTokens', () => {
Expand Down

0 comments on commit 0d7eee6

Please sign in to comment.