From 220046e992737121e7d605838880bbfcf9edb0a5 Mon Sep 17 00:00:00 2001 From: petruki <31597636+petruki@users.noreply.github.com> Date: Sun, 29 Jan 2023 17:52:07 -0800 Subject: [PATCH 1/2] Fixes #371 - Preventing reDoS --- src/client/resolvers.js | 10 +- src/helpers/timed-match/match-proc.js | 15 ++ src/helpers/timed-match/try-match.js | 34 ++++ src/models/config-strategy.js | 17 +- tests/client-api.test.js | 16 +- tests/config-strategy.test.js | 52 +++--- tests/unit-test/config-strategy.test.js | 239 ++++++++++++------------ tests/unit-test/try-match.test.js | 29 +++ 8 files changed, 247 insertions(+), 165 deletions(-) create mode 100644 src/helpers/timed-match/match-proc.js create mode 100644 src/helpers/timed-match/try-match.js create mode 100644 tests/unit-test/try-match.test.js diff --git a/src/client/resolvers.js b/src/client/resolvers.js index 8bd0029..600a4a0 100644 --- a/src/client/resolvers.js +++ b/src/client/resolvers.js @@ -213,21 +213,21 @@ function checkFlags(config, group, domain, environment) { } } -function checkStrategy(entry, strategies, environment) { +async function checkStrategy(entry, strategies, environment) { if (strategies) { for (const strategy of strategies) { if (!strategy.activated[environment]) continue; - checkStrategyInput(entry, strategy); + await checkStrategyInput(entry, strategy); } } } -function checkStrategyInput(entry, { strategy, operation, values }) { +async function checkStrategyInput(entry, { strategy, operation, values }) { if (entry && entry.length) { const strategyEntry = entry.filter(e => e.strategy === strategy); - if (strategyEntry.length == 0 || !processOperation(strategy, operation, strategyEntry[0].input, values)) { + if (strategyEntry.length == 0 || !(await processOperation(strategy, operation, strategyEntry[0].input, values))) { throw new Error(`Strategy '${strategy}' does not agree`); } } else { @@ -260,7 +260,7 @@ export async function resolveCriteria(config, context, strategyFilter) { try { checkFlags(config, group, domain, environment); - checkStrategy(context.entry, strategies, environment); + await checkStrategy(context.entry, strategies, environment); await resolveRelay(config, environment, context.entry, response); } catch (e) { response.result = false; diff --git a/src/helpers/timed-match/match-proc.js b/src/helpers/timed-match/match-proc.js new file mode 100644 index 0000000..dfc641e --- /dev/null +++ b/src/helpers/timed-match/match-proc.js @@ -0,0 +1,15 @@ +function tryMatch(values, input) { + let result = false; + for (const value of values) { + if (input.match(value)) { + result = true; + break; + } + } + + return result; +} + +process.on('message', ({ values, input }) => { + process.send(tryMatch(values, input)); +}); \ No newline at end of file diff --git a/src/helpers/timed-match/try-match.js b/src/helpers/timed-match/try-match.js new file mode 100644 index 0000000..2223c65 --- /dev/null +++ b/src/helpers/timed-match/try-match.js @@ -0,0 +1,34 @@ +import cp from 'child_process'; + +export default async function tryMatch(values, input, timeout = 3000) { + let result = false; + let cp, timer; + + const matchPromise = new Promise((resolve) => { + cp = createChildProcess(); + cp.on('message', resolve); + cp.send({ values, input }); + }); + + const matchTimer = new Promise((resolve) => { + timer = setTimeout(resolve, timeout, false); + }); + + await Promise.any([matchPromise, matchTimer]).then((value) => { + cp.kill(); + clearTimeout(timer); + result = value; + }); + + return result; +} + +function createChildProcess() { + const match_proc = cp.fork(`${__dirname}/match-proc.js`, { + stdio: 'ignore' + }); + + match_proc.unref(); + match_proc.channel.unref(); + return match_proc; +} \ No newline at end of file diff --git a/src/models/config-strategy.js b/src/models/config-strategy.js index fb09244..e74b160 100644 --- a/src/models/config-strategy.js +++ b/src/models/config-strategy.js @@ -5,6 +5,7 @@ import moment from 'moment'; import { NotFoundError } from '../exceptions'; import { parseJSON, payloadReader } from '../helpers'; import IPCIDR from '../helpers/ipcidr'; +import tryMatch from '../helpers/timed-match/try-match'; export const StrategiesType = Object.freeze({ NETWORK: 'NETWORK_VALIDATION', @@ -147,7 +148,7 @@ export function strategyRequirements(strategy) { }; } -export function processOperation(strategy, operation, input, values) { +export async function processOperation(strategy, operation, input, values) { switch(strategy) { case StrategiesType.NETWORK: return processNETWORK(operation, input, values); @@ -262,18 +263,12 @@ function processDATE(operation, input, values) { } } -function processREGEX(operation, input, values) { +async function processREGEX(operation, input, values) { switch(operation) { - case OperationsType.EXIST: { - for (const value of values) { - if (input.match(value)) { - return true; - } - } - return false; - } + case OperationsType.EXIST: + return await tryMatch(values, input); case OperationsType.NOT_EXIST: - return !processREGEX(OperationsType.EXIST, input, values); + return !(await processREGEX(OperationsType.EXIST, input, values)); case OperationsType.EQUAL: return input.match(`\\b${values[0]}\\b`) != null; case OperationsType.NOT_EQUAL: diff --git a/tests/client-api.test.js b/tests/client-api.test.js index 31e947c..eaaf0b1 100644 --- a/tests/client-api.test.js +++ b/tests/client-api.test.js @@ -31,7 +31,7 @@ import { import { RouterTypes } from '../src/models/permission'; const changeStrategy = async (strategyId, newOperation, status, environment) => { - const strategy = await ConfigStrategy.findById(strategyId); + const strategy = await ConfigStrategy.findById(strategyId).exec(); strategy.operation = newOperation ? newOperation : strategy.operation; strategy.activated.set(environment, status !== undefined ? status : strategy.activated.get(environment)); strategy.updatedBy = adminMasterAccountId; @@ -39,14 +39,14 @@ const changeStrategy = async (strategyId, newOperation, status, environment) => }; const changeConfigStatus = async (configid, status, environment) => { - const config = await Config.findById(configid); + const config = await Config.findById(configid).exec(); config.activated.set(environment, status !== undefined ? status : config.activated.get(environment)); config.updatedBy = adminMasterAccountId; await config.save(); }; const changeConfigDisableMetricFlag = async (configid, status, environment) => { - const config = await Config.findById(configid); + const config = await Config.findById(configid).exec(); if (!config.disable_metrics) config.disable_metrics = new Map; @@ -56,14 +56,14 @@ const changeConfigDisableMetricFlag = async (configid, status, environment) => { }; const changeGroupConfigStatus = async (groupconfigid, status, environment) => { - const groupConfig = await GroupConfig.findById(groupconfigid); + const groupConfig = await GroupConfig.findById(groupconfigid).exec(); groupConfig.activated.set(environment, status !== undefined ? status : groupConfig.activated.get(environment)); groupConfig.updatedBy = adminMasterAccountId; await groupConfig.save(); }; const changeDomainStatus = async (domainid, status, environment) => { - const domain = await Domain.findById(domainid); + const domain = await Domain.findById(domainid).exec(); domain.activated.set(environment, status !== undefined ? status : domain.activated.get(environment)); domain.updatedBy = adminMasterAccountId; await domain.save(); @@ -363,7 +363,7 @@ describe('Testing criteria [GraphQL] ', () => { .expect(200); //get total of metric data - const numMetricData = await Metric.find({ config: configId }).countDocuments(); + const numMetricData = await Metric.find({ config: configId }).countDocuments().exec(); //disable metrics await changeConfigDisableMetricFlag(configId, true, EnvType.DEFAULT); @@ -379,7 +379,7 @@ describe('Testing criteria [GraphQL] ', () => { .expect(200); //test - const afterNumMetricData = await Metric.find({ config: configId }).countDocuments(); + const afterNumMetricData = await Metric.find({ config: configId }).countDocuments().exec(); expect(numMetricData === afterNumMetricData).toBe(true); }); }); @@ -902,7 +902,7 @@ describe('Testing domain [Adm-GraphQL] ', () => { test('CLIENT_SUITE - Should NOT return domain structure for an excluded team member', async () => { //given - const admin = await Admin.findById(adminAccountId); + const admin = await Admin.findById(adminAccountId).exec(); admin.teams = []; await admin.save(); diff --git a/tests/config-strategy.test.js b/tests/config-strategy.test.js index 3e5e439..9f82c02 100644 --- a/tests/config-strategy.test.js +++ b/tests/config-strategy.test.js @@ -42,7 +42,7 @@ describe('Testing strategy creation #1', () => { }).expect(201); // DB validation - document created - const configStrategy = await ConfigStrategy.findById(response.body._id).lean(); + const configStrategy = await ConfigStrategy.findById(response.body._id).lean().exec(); expect(configStrategy).not.toBeNull(); // Response validation @@ -339,7 +339,7 @@ describe('Testing reading strategies #1', () => { }).expect(201); // DB validation - document created - const configStrategy = await ConfigStrategy.findById(response.body._id).lean(); + const configStrategy = await ConfigStrategy.findById(response.body._id).lean().exec(); expect(configStrategy).not.toBeNull(); response = await request(app) @@ -418,19 +418,19 @@ describe('Testing reading strategies #2', () => { test('STRATEGY_SUITE - Should delete Config Strategy', async () => { // DB validation Before deleting - let domain = await Domain.findById(domainId).lean(); + let domain = await Domain.findById(domainId).lean().exec(); expect(domain).not.toBeNull(); - let group = await GroupConfig.findById(groupConfigId).lean(); + let group = await GroupConfig.findById(groupConfigId).lean().exec(); expect(group).not.toBeNull(); - let config1 = await Config.findById(configId1).lean(); + let config1 = await Config.findById(configId1).lean().exec(); expect(config1).not.toBeNull(); - let config2 = await Config.findById(configId2).lean(); + let config2 = await Config.findById(configId2).lean().exec(); expect(config2).not.toBeNull(); - let configStrategy = await ConfigStrategy.findById(configStrategyId).lean(); + let configStrategy = await ConfigStrategy.findById(configStrategyId).lean().exec(); expect(configStrategy).not.toBeNull(); await request(app) @@ -438,23 +438,23 @@ describe('Testing reading strategies #2', () => { .set('Authorization', `Bearer ${adminMasterAccountToken}`) .send().expect(200); - const admin = await Admin.findById(adminMasterAccountId).lean(); + const admin = await Admin.findById(adminMasterAccountId).lean().exec(); expect(admin).not.toBeNull(); // DB validation After - Verify deleted dependencies - domain = await Domain.findById(domainId).lean(); + domain = await Domain.findById(domainId).lean().exec(); expect(domain).not.toBeNull(); - group = await GroupConfig.findById(groupConfigId).lean(); + group = await GroupConfig.findById(groupConfigId).lean().exec(); expect(group).not.toBeNull(); - config1 = await Config.findById(configId1).lean(); + config1 = await Config.findById(configId1).lean().exec(); expect(config1).not.toBeNull(); - config2 = await Config.findById(configId2).lean(); + config2 = await Config.findById(configId2).lean().exec(); expect(config2).not.toBeNull(); - configStrategy = await ConfigStrategy.findById(configStrategyId).lean(); + configStrategy = await ConfigStrategy.findById(configStrategyId).lean().exec(); expect(configStrategy).toBeNull(); }); @@ -476,7 +476,7 @@ describe('Testing update strategies #1', () => { test('STRATEGY_SUITE - Should update Config Strategy info', async () => { - let configStrategy = await ConfigStrategy.findById(configStrategyId).lean(); + let configStrategy = await ConfigStrategy.findById(configStrategyId).lean().exec(); expect(configStrategy).not.toBeNull(); await request(app) @@ -487,7 +487,7 @@ describe('Testing update strategies #1', () => { }).expect(200); // DB validation - verify flag updated - configStrategy = await ConfigStrategy.findById(configStrategyId).lean(); + configStrategy = await ConfigStrategy.findById(configStrategyId).lean().exec(); expect(configStrategy).not.toBeNull(); expect(configStrategy.description).toEqual('New description'); }); @@ -556,7 +556,7 @@ describe('Testing update strategies #1', () => { expect(response.body).not.toEqual([]); // DB validation - let history = await History.find({ elementId: strategyId }).lean(); + let history = await History.find({ elementId: strategyId }).lean().exec(); expect(history[0].oldValue['description']).toEqual('Description of my new Config Strategy'); expect(history[0].newValue['description']).toEqual('New description'); @@ -568,7 +568,7 @@ describe('Testing update strategies #1', () => { }).expect(200); // DB validation - history = await History.find({ elementId: strategyId }).lean(); + history = await History.find({ elementId: strategyId }).lean().exec(); expect(history.length).toEqual(2); }); @@ -624,7 +624,7 @@ describe('Testing update strategies #1', () => { description: 'New description' }).expect(200); - let history = await History.find({ elementId: strategyId }).lean(); + let history = await History.find({ elementId: strategyId }).lean().exec(); expect(history.length > 0).toEqual(true); await request(app) @@ -632,7 +632,7 @@ describe('Testing update strategies #1', () => { .set('Authorization', `Bearer ${adminMasterAccountToken}`) .send().expect(200); - history = await History.find({ elementId: strategyId }).lean(); + history = await History.find({ elementId: strategyId }).lean().exec(); expect(history.length > 0).toEqual(false); }); @@ -675,7 +675,7 @@ describe('Testing update strategies #1', () => { expect(response.body.values[response.body.values.length - 1]).toEqual('USER_4'); // DB validation - const configStrategy = await ConfigStrategy.findOne({ _id: configStrategyId }).lean(); + const configStrategy = await ConfigStrategy.findOne({ _id: configStrategyId }).lean().exec(); const foundExistingOne = configStrategy.values.find((element) => element === 'USER_4'); expect('USER_4').toEqual(foundExistingOne); }); @@ -762,7 +762,7 @@ describe('Testing update strategies #1', () => { expect(response.body.values[response.body.values.length - 1]).toEqual('USER_THREE'); // DB validation - const configStrategy = await ConfigStrategy.findOne({ _id: configStrategyId }).lean(); + const configStrategy = await ConfigStrategy.findOne({ _id: configStrategyId }).lean().exec(); const foundExistingOne = configStrategy.values.find((element) => element === 'USER_THREE'); expect('USER_THREE').toEqual(foundExistingOne); @@ -822,7 +822,7 @@ describe('Testing update strategies #2', () => { }); test('STRATEGY_SUITE - Should remove a value from Strategy values', async () => { - let configStrategy = await ConfigStrategy.findOne({ _id: configStrategyId }).lean(); + let configStrategy = await ConfigStrategy.findOne({ _id: configStrategyId }).lean().exec(); const numberOfValues = configStrategy.values.length; let response = await request(app) @@ -835,7 +835,7 @@ describe('Testing update strategies #2', () => { expect(response.body.values.length + 1).toEqual(numberOfValues); // DB validation - configStrategy = await ConfigStrategy.findOne({ _id: configStrategyId }).lean(); + configStrategy = await ConfigStrategy.findOne({ _id: configStrategyId }).lean().exec(); const notFoundOldOne = configStrategy.values.find((element) => element === 'USER_3'); expect(notFoundOldOne).toEqual(undefined); }); @@ -935,7 +935,7 @@ describe('Testing fetch strategies', () => { expect(response.body.activated[EnvType.DEFAULT]).toEqual(false); // DB validation - verify status updated - const strategy = await ConfigStrategy.findById(configStrategyId).lean(); + const strategy = await ConfigStrategy.findById(configStrategyId).lean().exec(); expect(strategy.activated[EnvType.DEFAULT]).toEqual(false); }); @@ -976,7 +976,7 @@ describe('Scenario: creating QA environment and modifying its status', () => { expect(response.body.activated['QA']).toEqual(true); // DB validation - verify status updated - let strategy = await ConfigStrategy.findById(newStrategy.body._id).lean(); + let strategy = await ConfigStrategy.findById(newStrategy.body._id).lean().exec(); expect(strategy.activated['QA']).toEqual(true); // Inactivating QA. Default environment should stay activated @@ -987,7 +987,7 @@ describe('Scenario: creating QA environment and modifying its status', () => { QA: false }).expect(200); - strategy = await ConfigStrategy.findById(newStrategy.body._id).lean(); + strategy = await ConfigStrategy.findById(newStrategy.body._id).lean().exec(); expect(strategy.activated['QA']).toEqual(false); }); diff --git a/tests/unit-test/config-strategy.test.js b/tests/unit-test/config-strategy.test.js index d8888ad..e43e4a1 100644 --- a/tests/unit-test/config-strategy.test.js +++ b/tests/unit-test/config-strategy.test.js @@ -101,44 +101,44 @@ describe('Processing strategy: VALUE', () => { 'USER_1', 'USER_2' ]; - test('UNIT_STRATEGY_SUITE - Should return TRUE when input EXIST', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input EXIST', async () => { + const result = await processOperation( StrategiesType.VALUE, OperationsType.EXIST, 'USER_1', fixture_values1); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return FALSE when input DOES NOT EXIST', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return FALSE when input DOES NOT EXIST', async () => { + const result = await processOperation( StrategiesType.VALUE, OperationsType.EXIST, 'USER_123', fixture_values1); expect(result).toBe(false); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when input DOES NOT EXIST', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input DOES NOT EXIST', async () => { + const result = await processOperation( StrategiesType.VALUE, OperationsType.NOT_EXIST, 'USER_123', fixture_values1); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when input is EQUAL', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input is EQUAL', async () => { + const result = await processOperation( StrategiesType.VALUE, OperationsType.EQUAL, 'USER_1', fixture_values1); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return FALSE when input is NOT EQUAL', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return FALSE when input is NOT EQUAL', async () => { + const result = await processOperation( StrategiesType.VALUE, OperationsType.EQUAL, 'USER_2', fixture_values1); expect(result).toBe(false); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when input is NOT EQUAL', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input is NOT EQUAL', async () => { + const result = await processOperation( StrategiesType.VALUE, OperationsType.NOT_EQUAL, 'USER_123', fixture_values2); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return FALSE when input is NOT EQUAL', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return FALSE when input is NOT EQUAL', async () => { + const result = await processOperation( StrategiesType.VALUE, OperationsType.NOT_EQUAL, 'USER_2', fixture_values2); expect(result).toBe(false); }); @@ -157,104 +157,104 @@ describe('Processing strategy: NUMERIC', () => { '1.5' ]; - test('UNIT_STRATEGY_SUITE - Should return TRUE when input EXIST in values - String type', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input EXIST in values - String type', async () => { + const result = await processOperation( StrategiesType.NUMERIC, OperationsType.EXIST, '3', fixture_values2); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when input EXIST in values - Number type', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input EXIST in values - Number type', async () => { + const result = await processOperation( StrategiesType.NUMERIC, OperationsType.EXIST, 3, fixture_values2); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return FALSE when input exist but test as DOES NOT EXIST ', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return FALSE when input exist but test as DOES NOT EXIST ', async () => { + const result = await processOperation( StrategiesType.NUMERIC, OperationsType.NOT_EXIST, '1', fixture_values2); expect(result).toBe(false); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when input DOES NOT EXIST in values', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input DOES NOT EXIST in values', async () => { + const result = await processOperation( StrategiesType.NUMERIC, OperationsType.NOT_EXIST, '2', fixture_values2); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when input is EQUAL to value', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input is EQUAL to value', async () => { + const result = await processOperation( StrategiesType.NUMERIC, OperationsType.EQUAL, '1', fixture_values1); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return FALSE when input is not equal but test as EQUAL', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return FALSE when input is not equal but test as EQUAL', async () => { + const result = await processOperation( StrategiesType.NUMERIC, OperationsType.EQUAL, '2', fixture_values1); expect(result).toBe(false); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when input is NOT EQUAL to value', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input is NOT EQUAL to value', async () => { + const result = await processOperation( StrategiesType.NUMERIC, OperationsType.NOT_EQUAL, '2', fixture_values1); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when input is GREATER than value', () => { - let result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input is GREATER than value', async () => { + let result = await processOperation( StrategiesType.NUMERIC, OperationsType.GREATER, '2', fixture_values1); expect(result).toBe(true); // test decimal - result = processOperation( + result = await processOperation( StrategiesType.NUMERIC, OperationsType.GREATER, '1.01', fixture_values1); expect(result).toBe(true); - result = processOperation( + result = await processOperation( StrategiesType.NUMERIC, OperationsType.GREATER, '1.55', fixture_values3); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return FALSE when input is lower but tested as GREATER than value', () => { - let result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return FALSE when input is lower but tested as GREATER than value', async () => { + let result = await processOperation( StrategiesType.NUMERIC, OperationsType.GREATER, '0', fixture_values1); expect(result).toBe(false); // test decimal - result = processOperation( + result = await processOperation( StrategiesType.NUMERIC, OperationsType.GREATER, '0.99', fixture_values1); expect(result).toBe(false); - result = processOperation( + result = await processOperation( StrategiesType.NUMERIC, OperationsType.GREATER, '1.49', fixture_values3); expect(result).toBe(false); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when input is LOWER than value', () => { - let result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input is LOWER than value', async () => { + let result = await processOperation( StrategiesType.NUMERIC, OperationsType.LOWER, '0', fixture_values1); expect(result).toBe(true); // test decimal - result = processOperation( + result = await processOperation( StrategiesType.NUMERIC, OperationsType.LOWER, '0.99', fixture_values1); expect(result).toBe(true); - result = processOperation( + result = await processOperation( StrategiesType.NUMERIC, OperationsType.LOWER, '1.49', fixture_values3); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when input is BETWEEN values', () => { - let result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input is BETWEEN values', async () => { + let result = await processOperation( StrategiesType.NUMERIC, OperationsType.BETWEEN, '1', fixture_values2); expect(result).toBe(true); // test decimal - result = processOperation( + result = await processOperation( StrategiesType.NUMERIC, OperationsType.BETWEEN, '2.99', fixture_values2); expect(result).toBe(true); - result = processOperation( + result = await processOperation( StrategiesType.NUMERIC, OperationsType.BETWEEN, '1.001', fixture_values2); expect(result).toBe(true); }); @@ -270,50 +270,50 @@ describe('Processing strategy: TIME', () => { '08:00', '10:00' ]; - test('UNIT_STRATEGY_SUITE - Should return TRUE when input is LOWER', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input is LOWER', async () => { + const result = await processOperation( StrategiesType.TIME, OperationsType.LOWER, '06:00', fixture_values1); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when input is LOWER or SAME', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input is LOWER or SAME', async () => { + const result = await processOperation( StrategiesType.TIME, OperationsType.LOWER, '08:00', fixture_values1); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return FALSE when input is NOT LOWER', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return FALSE when input is NOT LOWER', async () => { + const result = await processOperation( StrategiesType.TIME, OperationsType.LOWER, '10:00', fixture_values1); expect(result).toBe(false); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when input is GREATER', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input is GREATER', async () => { + const result = await processOperation( StrategiesType.TIME, OperationsType.GREATER, '10:00', fixture_values1); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when input is GREATER or SAME', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input is GREATER or SAME', async () => { + const result = await processOperation( StrategiesType.TIME, OperationsType.GREATER, '08:00', fixture_values1); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return FALSE when input is NOT GREATER', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return FALSE when input is NOT GREATER', async () => { + const result = await processOperation( StrategiesType.TIME, OperationsType.GREATER, '06:00', fixture_values1); expect(result).toBe(false); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when input is in BETWEEN', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input is in BETWEEN', async () => { + const result = await processOperation( StrategiesType.TIME, OperationsType.BETWEEN, '09:00', fixture_values2); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return FALSE when input is NOT in BETWEEN', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return FALSE when input is NOT in BETWEEN', async () => { + const result = await processOperation( StrategiesType.TIME, OperationsType.BETWEEN, '07:00', fixture_values2); expect(result).toBe(false); }); @@ -349,68 +349,68 @@ describe('Processing strategy: DATE', () => { '2019-12-01T08:30' ]; - test('UNIT_STRATEGY_SUITE - Should return TRUE when input is LOWER', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input is LOWER', async () => { + const result = await processOperation( StrategiesType.DATE, OperationsType.LOWER, '2019-11-26', fixture_values1); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when input is LOWER or SAME', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input is LOWER or SAME', async () => { + const result = await processOperation( StrategiesType.DATE, OperationsType.LOWER, '2019-12-01', fixture_values1); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return FALSE when input is NOT LOWER', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return FALSE when input is NOT LOWER', async () => { + const result = await processOperation( StrategiesType.DATE, OperationsType.LOWER, '2019-12-02', fixture_values1); expect(result).toBe(false); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when input is GREATER', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input is GREATER', async () => { + const result = await processOperation( StrategiesType.DATE, OperationsType.GREATER, '2019-12-02', fixture_values1); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when input is GREATER or SAME', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input is GREATER or SAME', async () => { + const result = await processOperation( StrategiesType.DATE, OperationsType.GREATER, '2019-12-01', fixture_values1); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return FALSE when input is NOT GREATER', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return FALSE when input is NOT GREATER', async () => { + const result = await processOperation( StrategiesType.DATE, OperationsType.GREATER, '2019-11-10', fixture_values1); expect(result).toBe(false); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when input is in BETWEEN', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input is in BETWEEN', async () => { + const result = await processOperation( StrategiesType.DATE, OperationsType.BETWEEN, '2019-12-03', fixture_values2); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return FALSE when input is NOT in BETWEEN', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return FALSE when input is NOT in BETWEEN', async () => { + const result = await processOperation( StrategiesType.DATE, OperationsType.BETWEEN, '2019-12-12', fixture_values2); expect(result).toBe(false); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when input is LOWER including time', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input is LOWER including time', async () => { + const result = await processOperation( StrategiesType.DATE, OperationsType.LOWER, '2019-12-01T07:00', fixture_values3); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return FALSE when input is NOT LOWER including time', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return FALSE when input is NOT LOWER including time', async () => { + const result = await processOperation( StrategiesType.DATE, OperationsType.LOWER, '2019-12-01T07:00', fixture_values1); expect(result).toBe(false); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when input is GREATER including time', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when input is GREATER including time', async () => { + const result = await processOperation( StrategiesType.DATE, OperationsType.GREATER, '2019-12-01T08:40', fixture_values3); expect(result).toBe(true); }); @@ -455,66 +455,75 @@ describe('Processing strategy: REGEX', () => { 'USER_[0-9]{1,2}' ]; - test('UNIT_STRATEGY_SUITE - Should return TRUE when expect to exist using EXIST operation', () => { - let result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when expect to exist using EXIST operation', async () => { + let result = await processOperation( StrategiesType.REGEX, OperationsType.EXIST, 'USER_1', fixture_values1); expect(result).toBe(true); - result = processOperation( + result = await processOperation( StrategiesType.REGEX, OperationsType.EXIST, 'user-01', fixture_values2); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return FALSE when expect to exist using EXIST operation', () => { - let result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return FALSE when expect to exist using EXIST operation', async () => { + let result = await processOperation( StrategiesType.REGEX, OperationsType.EXIST, 'USER_123', fixture_values1); expect(result).toBe(false); //fixture_values3 does not require exact match - result = processOperation( + result = await processOperation( StrategiesType.REGEX, OperationsType.EXIST, 'USER_123', fixture_values3); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when expect to not exist using NOT_EXIST operation', () => { - let result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when expect to not exist using NOT_EXIST operation', async () => { + let result = await processOperation( StrategiesType.REGEX, OperationsType.NOT_EXIST, 'USER_123', fixture_values1); expect(result).toBe(true); - result = processOperation( + result = await processOperation( StrategiesType.REGEX, OperationsType.NOT_EXIST, 'user-123', fixture_values2); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return FALSE when expect to not exist using NOT_EXIST operation', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return FALSE when expect to not exist using NOT_EXIST operation', async () => { + const result = await processOperation( StrategiesType.REGEX, OperationsType.NOT_EXIST, 'USER_12', fixture_values1); expect(result).toBe(false); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when expect to be equal using EQUAL operation', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when expect to be equal using EQUAL operation', async () => { + const result = await processOperation( StrategiesType.REGEX, OperationsType.EQUAL, 'USER_11', fixture_values3); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return FALSE when expect to be equal using EQUAL operation', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return FALSE when expect to be equal using EQUAL operation', async () => { + const result = await processOperation( StrategiesType.REGEX, OperationsType.EQUAL, 'user-11', fixture_values3); expect(result).toBe(false); }); - test('UNIT_STRATEGY_SUITE - Should return TRUE when expect to not be equal using NOT_EQUAL operation', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return TRUE when expect to not be equal using NOT_EQUAL operation', async () => { + const result = await processOperation( StrategiesType.REGEX, OperationsType.NOT_EQUAL, 'USER_123', fixture_values3); expect(result).toBe(true); }); - test('UNIT_STRATEGY_SUITE - Should return FALSE when expect to not be equal using NOT_EQUAL operation', () => { - const result = processOperation( + test('UNIT_STRATEGY_SUITE - Should return FALSE when expect to not be equal using NOT_EQUAL operation', async () => { + const result = await processOperation( StrategiesType.REGEX, OperationsType.NOT_EQUAL, 'USER_1', fixture_values3); expect(result).toBe(false); }); + + test('UNIT_STRATEGY_SUITE - Should fail for reDoS attempt', async () => { + const result = await processOperation( + StrategiesType.REGEX, OperationsType.EXIST, + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!', + ['^(([a-z])+.)+[A-Z]([a-z])+$']); + + expect(result).toBe(false); + }); }); describe('Processing strategy: PAYLOAD', () => { @@ -591,36 +600,36 @@ describe('Processing strategy: PAYLOAD', () => { ]); }); - test('UNIT_PAYLOAD_SUITE - Should return TRUE when payload has field', () => { - expect(processOperation( + test('UNIT_PAYLOAD_SUITE - Should return TRUE when payload has field', async () => { + expect(await processOperation( StrategiesType.PAYLOAD, OperationsType.HAS_ONE, fixture_1, ['login']) ).toBe(true); }); - test('UNIT_PAYLOAD_SUITE - Should return FALSE when payload does not have field', () => { - expect(processOperation( + test('UNIT_PAYLOAD_SUITE - Should return FALSE when payload does not have field', async () => { + expect(await processOperation( StrategiesType.PAYLOAD, OperationsType.HAS_ONE, fixture_1, ['user']) ).toBe(false); }); - test('UNIT_PAYLOAD_SUITE - Should return TRUE when payload has nested field', () => { - expect(processOperation( + test('UNIT_PAYLOAD_SUITE - Should return TRUE when payload has nested field', async () => { + expect(await processOperation( StrategiesType.PAYLOAD, OperationsType.HAS_ONE, fixture_values2, [ 'order.qty', 'order.total' ]) ).toBe(true); }); - test('UNIT_PAYLOAD_SUITE - Should return TRUE when payload has nested field with arrays', () => { - expect(processOperation( + test('UNIT_PAYLOAD_SUITE - Should return TRUE when payload has nested field with arrays', async () => { + expect(await processOperation( StrategiesType.PAYLOAD, OperationsType.HAS_ONE, fixture_values2, [ 'order.deliver.tracking.status' ]) ).toBe(true); }); - test('UNIT_PAYLOAD_SUITE - Should return TRUE when payload has all', () => { - expect(processOperation( + test('UNIT_PAYLOAD_SUITE - Should return TRUE when payload has all', async () => { + expect(await processOperation( StrategiesType.PAYLOAD, OperationsType.HAS_ALL, fixture_values2, [ 'product', 'order', @@ -634,8 +643,8 @@ describe('Processing strategy: PAYLOAD', () => { ).toBe(true); }); - test('UNIT_PAYLOAD_SUITE - Should return FALSE when payload does not have all', () => { - expect(processOperation( + test('UNIT_PAYLOAD_SUITE - Should return FALSE when payload does not have all', async () => { + expect(await processOperation( StrategiesType.PAYLOAD, OperationsType.HAS_ALL, fixture_values2, [ 'product', 'order', @@ -644,8 +653,8 @@ describe('Processing strategy: PAYLOAD', () => { ).toBe(false); }); - test('UNIT_PAYLOAD_SUITE - Should return FALSE when payload is not a JSON string', () => { - expect(processOperation( + test('UNIT_PAYLOAD_SUITE - Should return FALSE when payload is not a JSON string', async () => { + expect(await processOperation( StrategiesType.PAYLOAD, OperationsType.HAS_ALL, 'NOT_JSON', []) ).toBe(false); }); diff --git a/tests/unit-test/try-match.test.js b/tests/unit-test/try-match.test.js new file mode 100644 index 0000000..2b4b099 --- /dev/null +++ b/tests/unit-test/try-match.test.js @@ -0,0 +1,29 @@ +import tryMatch from '../../src/helpers/timed-match/try-match'; + +describe('Test tryMatch', () => { + + test('UNIT_TRY_MATCH - Should match value', async () => { + const result = await tryMatch(['USER_[0-9]{1,2}'], 'USER_1'); + expect(result).toBe(true); + }); + + test('UNIT_TRY_MATCH - Should fail for reDoS attempt - default 3000 ms', async () => { + let timer = Date.now(); + const result = await tryMatch(['^(([a-z])+.)+[A-Z]([a-z])+$'], 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'); + timer = Date.now() - timer; + + expect(timer).not.toBeLessThan(3000); + expect(result).toBe(false); + }); + + test('UNIT_TRY_MATCH - Should fail for reDoS attempt - 2000 ms', async () => { + let timer = Date.now(); + const result = await tryMatch(['^(([a-z])+.)+[A-Z]([a-z])+$'], 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 2000); + timer = Date.now() - timer; + + expect(timer).not.toBeLessThan(2000); + expect(timer).not.toBeGreaterThan(2500); + expect(result).toBe(false); + }); + +}); \ No newline at end of file From 7d17da4f2041ac3fe5a54ff723533b8b668011c9 Mon Sep 17 00:00:00 2001 From: petruki <31597636+petruki@users.noreply.github.com> Date: Sun, 29 Jan 2023 18:05:02 -0800 Subject: [PATCH 2/2] Removed worker proc from coverage --- sonar-project.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sonar-project.properties b/sonar-project.properties index 0c78925..f3bb8ac 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -9,7 +9,7 @@ sonar.javascript.lcov.reportPaths=coverage/lcov.info # Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows. sonar.sources=src -sonar.exclusions=src/api-docs/** +sonar.exclusions=src/api-docs/**, src/helpers/timed-match/match-proc.js sonar.tests=tests sonar.language=js