diff --git a/build/jsdoc-typeof-plugin.js b/build/jsdoc-typeof-plugin.js index 70564825a1..105802d450 100644 --- a/build/jsdoc-typeof-plugin.js +++ b/build/jsdoc-typeof-plugin.js @@ -3,7 +3,7 @@ */ exports.handlers = { jsdocCommentFound: event => { - event.comment = (event.comment || '').replace(/\{.*typeof\s+([^\s\|]+).*\}/g, typeExpression => { + event.comment = (event.comment || '').replace(/\{(?:(?!\{).)*typeof\s+([^\s\|]+)(?:(?![^\s\|]).)*\}/g, typeExpression => { return typeExpression.replace(/typeof\s+([^\s\|\}]+)/g, (expression, className) => { return 'Class<' + className + '>'; }); diff --git a/test/build/jsdoc-typeof-plugin.test.js b/test/build/jsdoc-typeof-plugin.test.js new file mode 100644 index 0000000000..60b05d4a06 --- /dev/null +++ b/test/build/jsdoc-typeof-plugin.test.js @@ -0,0 +1,17 @@ +const QUnit = require('qunit'); +const plugin = require('../../build/jsdoc-typeof-plugin.js'); +const { handlers } = plugin; + +QUnit.module('build/jsdoc-typeof-plugin', () => { + + QUnit.test('ReDos attack', assert => { + const evt = { comment: '{'.repeat(100000) + 't' }; + const t0 = performance.now(); + + handlers.jsdocCommentFound(evt); + const dt = performance.now() - t0; + + assert.ok(dt < 5000, `expected < 5000ms, got ${dt.toFixed(1)}ms`); + }); + +});