Skip to content

Commit

Permalink
Improve abort-on-stack-trace
Browse files Browse the repository at this point in the history
Add ability to abort inline and injected script
Improve log-on-stack-trace - fix issue with logging in Firefox
Fix maximum call stack size exceeded in getDescriptorAddon when Math.random is used
Fix test for matchStackTrace
  • Loading branch information
AdamWr committed Nov 13, 2022
1 parent 0fb636b commit fc2c355
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/helpers/get-descriptor-addon.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export function getDescriptorAddon() {
this.isAbortingSuspended = false;
return result;
} catch {
this.isAbortingSuspended = false;
const rid = randomId();
this.isAbortingSuspended = false;
// It's necessary to throw error
// otherwise script will be not aborted
throw new ReferenceError(rid);
Expand Down
50 changes: 49 additions & 1 deletion src/helpers/match-stack.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { toRegExp } from './string-utils';
import { toRegExp, startsWith } from './string-utils';
import { getNativeRegexpTest } from './regexp-utils';

/**
Expand All @@ -13,6 +13,54 @@ export const matchStackTrace = (stackMatch, stackTrace) => {
return true;
}

const isInlineScript = stackMatch.indexOf('inlineScript') > -1;
const isInjectedScript = stackMatch.indexOf('injectedScript') > -1;
if (isInlineScript || isInjectedScript) {
const injectedScript = 'injectedScript';
const injectedScriptMatch = '<anonymous>';
let documentURL = window.location.href;
const pos = documentURL.indexOf('#');
if (pos !== -1) {
documentURL = documentURL.slice(0, pos);
}
const stackSteps = stackTrace.split('\n').slice(2).map((line) => line.trim());
const stackLines = stackSteps.map((line) => {
let stack;
const reg = /(.*?@)?(\S+)(:\d+):\d+\)?$/.exec(line);
if (reg) {
let stackURL = reg[2];
if (startsWith(stackURL, '(')) {
stackURL = stackURL.slice(1);
}
if (startsWith(stackURL, injectedScriptMatch)) {
stackURL = injectedScript;
let stackFunction = reg[1] !== undefined
? reg[1].slice(0, -1)
: line.slice(0, reg.index).trim();
if (startsWith(stackFunction, 'at')) {
stackFunction = stackFunction.slice(2).trim();
}
stack = `${stackFunction} ${stackURL}`.trim();
} else {
stack = stackURL;
}
} else {
stack = line;
}
return stack;
});
if (stackLines) {
for (let index = 0; index < stackLines.length; index += 1) {
if (isInlineScript && documentURL === stackLines[index]) {
return true;
}
if (isInjectedScript && startsWith(stackLines[index], injectedScript)) {
return true;
}
}
}
}

const stackRegexp = toRegExp(stackMatch);
const refinedStackTrace = stackTrace
.split('\n')
Expand Down
4 changes: 3 additions & 1 deletion src/scriptlets/abort-on-stack-trace.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
toRegExp,
isEmptyObject,
getNativeRegexpTest,
startsWith,
} from '../helpers/index';

/* eslint-disable max-len */
Expand Down Expand Up @@ -75,7 +76,7 @@ export function abortOnStackTrace(source, property, stack) {
return;
}

if (!isValidStrPattern(stack)) {
if (!stack.match(/^(inlineScript|injectedScript)$/) && !isValidStrPattern(stack)) {
// eslint-disable-next-line no-console
console.log(`Invalid parameter: ${stack}`);
return;
Expand Down Expand Up @@ -141,4 +142,5 @@ abortOnStackTrace.injections = [
toRegExp,
isEmptyObject,
getNativeRegexpTest,
startsWith,
];
5 changes: 5 additions & 0 deletions src/scriptlets/log-on-stack-trace.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,15 @@ export function logOnStacktrace(source, property) {
let funcFullPath;
/* eslint-disable-next-line no-useless-escape */
const reg = /\(([^\)]+)\)/;
const regFirefox = /(.*?@)(\S+)(:\d+):\d+\)?$/;
if (line.match(reg)) {
funcName = line.split(' ').slice(0, -1).join(' ');
/* eslint-disable-next-line prefer-destructuring, no-useless-escape */
funcFullPath = line.match(reg)[1];
} else if (line.match(regFirefox)) {
funcName = line.split('@').slice(0, -1).join(' ');
/* eslint-disable-next-line prefer-destructuring, no-useless-escape */
funcFullPath = line.match(regFirefox)[2];
} else {
// For when func name is not available
funcName = 'function name is not available';
Expand Down
2 changes: 1 addition & 1 deletion tests/helpers/match-stack.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ module(name);
test('Test matchStackTrace for working with getNativeRegexpTest helper', async (assert) => {
const match = matchStackTrace('stack', new Error().stack);

assert.ok(!match);
assert.ok(match);
});
71 changes: 71 additions & 0 deletions tests/scriptlets/abort-on-stack-trace.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,77 @@ test('Protected from infinite loop when prop is used in a helper', (assert) => {
assert.strictEqual(window.hit, undefined, 'hit should NOT fire');
});

test('abort Math.random, injected script', (assert) => {
const property = 'Math.random';
const stackMatch = 'injectedScript';
const scriptletArgs = [property, stackMatch];
runScriptlet(name, scriptletArgs);

window.testPassed = false;
const scriptElement = document.createElement('script');
scriptElement.type = 'text/javascript';
// set window.testPassed to true if script is aborted
scriptElement.innerText = 'try { Math.random(); } catch(error) { window.testPassed = true; console.log("Script aborted:", error); }';
document.body.appendChild(scriptElement);
scriptElement.parentNode.removeChild(scriptElement);

assert.strictEqual(window.testPassed, true, 'testPassed set to true, script has been aborted');
assert.strictEqual(window.hit, 'FIRED', 'hit fired');
});

test('abort String.fromCharCode, inline script', (assert) => {
const property = 'String.fromCharCode';
const stackMatch = 'inlineScript';
const scriptletArgs = [property, stackMatch];
runScriptlet(name, scriptletArgs);
assert.throws(
() => String.fromCharCode(65),
/ReferenceError/,
'Reference error thrown when trying to access property String.fromCharCode',
);
assert.strictEqual(window.hit, 'FIRED', 'hit fired');
});

test('do NOT abort Math.round, test for injected script', (assert) => {
const property = 'Math.round';
const stackMatch = 'injectedScript';
const scriptletArgs = [property, stackMatch];
runScriptlet(name, scriptletArgs);

let testPassed = false;
try {
const testNumber = Math.round(1.5);
// eslint-disable-next-line no-console
console.log('Number:', testNumber);
testPassed = true;
} catch (error) {
// eslint-disable-next-line no-console
console.log('Something went wrong', error);
}
assert.strictEqual(testPassed, true, 'testPassed set to true, script has been aborted');
assert.strictEqual(window.hit, undefined, 'hit should NOT fire');
});

test('abort Math.max in injected script, but not abort inline script', (assert) => {
const property = 'Math.max';
const stackMatch = 'injectedScript';
const scriptletArgs = [property, stackMatch];
runScriptlet(name, scriptletArgs);

window.testPassed = false;
const number = Math.max(20, 10);
if (number) {
const scriptElement = document.createElement('script');
scriptElement.type = 'text/javascript';
// set window.testPassed to true if script is aborted
scriptElement.innerText = 'try { debugger; Math.max(10, 20); } catch(error) { window.testPassed = true; console.log("Script aborted:", error); }';
document.body.appendChild(scriptElement);
scriptElement.parentNode.removeChild(scriptElement);
}
assert.strictEqual(window.testPassed, true, 'testPassed set to true, script has been aborted');
assert.strictEqual(window.hit, 'FIRED', 'hit fired');
});

test('abort RegExp, matches stack', (assert) => {
const property = 'RegExp';
const stackMatch = 'abort-on-stack';
Expand Down
19 changes: 19 additions & 0 deletions tests/scriptlets/log-on-stack-trace.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const { test, module } = QUnit;
const name = 'log-on-stack-trace';
const PROPERTY = 'testMe';
const nativeConsole = console.log;
const tableConsole = console.table;

const changingProps = [PROPERTY, 'hit', '__debug'];

Expand All @@ -17,6 +18,7 @@ const beforeEach = () => {
const afterEach = () => {
clearGlobalProps(...changingProps);
console.log = nativeConsole;
console.table = tableConsole;
};

module(name, { beforeEach, afterEach });
Expand Down Expand Up @@ -68,3 +70,20 @@ test('logs specific message', (assert) => {

assert.strictEqual(window.hit, 'FIRED', 'hit fired');
});

test('check if message is not empty', (assert) => {
const scriptletArgs = [PROPERTY];
const setProp = (prop) => {
window[prop] = 'init';
};
runScriptlet(name, scriptletArgs);

console.table = function log(input) {
const inputString = JSON.stringify(input);
const checkString = 'log-on-stack-trace';
assert.ok(inputString.indexOf(checkString) > -1, 'Log message is not empty');
};
setProp(PROPERTY);

assert.strictEqual(window.hit, 'FIRED', 'hit fired');
});

0 comments on commit fc2c355

Please sign in to comment.