From e8f3a8478a61a5fb681d098c9b1c556bb39b705a Mon Sep 17 00:00:00 2001 From: Dino Chiesa Date: Tue, 27 Feb 2024 15:03:26 -0800 Subject: [PATCH] fix: handle unusual spacing in conditions --- build/ConditionParser.js | 252 ++++++++++--------- lib/package/plugins/CC007-ConditionSyntax.js | 4 +- lib/peggy/Apigee-Condition.pegjs | 8 +- test/specs/CC007-Condition-Syntax-Test.js | 56 +++++ test/specs/ConditionParserTest.js | 4 +- 5 files changed, 199 insertions(+), 125 deletions(-) diff --git a/build/ConditionParser.js b/build/ConditionParser.js index c0070da..1d1d29f 100644 --- a/build/ConditionParser.js +++ b/build/ConditionParser.js @@ -529,40 +529,22 @@ function peg$parse(input, options) { if (s1 !== peg$FAILED) { s2 = []; s3 = peg$parsews(); - if (s3 !== peg$FAILED) { - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parsews(); - } - } else { - s2 = peg$FAILED; + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsews(); } - if (s2 !== peg$FAILED) { - s3 = peg$parseop_boolean(); - if (s3 !== peg$FAILED) { - s4 = []; + s3 = peg$parseop_boolean(); + if (s3 !== peg$FAILED) { + s4 = []; + s5 = peg$parsews(); + while (s5 !== peg$FAILED) { + s4.push(s5); s5 = peg$parsews(); - if (s5 !== peg$FAILED) { - while (s5 !== peg$FAILED) { - s4.push(s5); - s5 = peg$parsews(); - } - } else { - s4 = peg$FAILED; - } - if (s4 !== peg$FAILED) { - s5 = peg$parseboolean_stmt1(); - if (s5 !== peg$FAILED) { - peg$savedPos = s0; - s0 = peg$f0(s1, s3, s5); - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } - } else { - peg$currPos = s0; - s0 = peg$FAILED; - } + } + s5 = peg$parseboolean_stmt1(); + if (s5 !== peg$FAILED) { + peg$savedPos = s0; + s0 = peg$f0(s1, s3, s5); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -675,67 +657,79 @@ function peg$parse(input, options) { } function peg$parseprimary() { - var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9; + var s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12; s0 = peg$currPos; + s1 = []; + s2 = peg$parsews(); + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$parsews(); + } if (input.charCodeAt(peg$currPos) === 40) { - s1 = peg$c0; + s2 = peg$c0; peg$currPos++; } else { - s1 = peg$FAILED; + s2 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e0); } } - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$parsews(); - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parsews(); + if (s2 !== peg$FAILED) { + s3 = []; + s4 = peg$parsews(); + while (s4 !== peg$FAILED) { + s3.push(s4); + s4 = peg$parsews(); } - s3 = peg$parsetoken(); - if (s3 !== peg$FAILED) { - s4 = []; - s5 = peg$parsews(); - if (s5 !== peg$FAILED) { - while (s5 !== peg$FAILED) { - s4.push(s5); - s5 = peg$parsews(); + s4 = peg$parsetoken(); + if (s4 !== peg$FAILED) { + s5 = []; + s6 = peg$parsews(); + if (s6 !== peg$FAILED) { + while (s6 !== peg$FAILED) { + s5.push(s6); + s6 = peg$parsews(); } } else { - s4 = peg$FAILED; + s5 = peg$FAILED; } - if (s4 !== peg$FAILED) { - s5 = peg$parseop_accepts_literal(); - if (s5 !== peg$FAILED) { - s6 = []; - s7 = peg$parsews(); - if (s7 !== peg$FAILED) { - while (s7 !== peg$FAILED) { - s6.push(s7); - s7 = peg$parsews(); + if (s5 !== peg$FAILED) { + s6 = peg$parseop_accepts_literal(); + if (s6 !== peg$FAILED) { + s7 = []; + s8 = peg$parsews(); + if (s8 !== peg$FAILED) { + while (s8 !== peg$FAILED) { + s7.push(s8); + s8 = peg$parsews(); } } else { - s6 = peg$FAILED; + s7 = peg$FAILED; } - if (s6 !== peg$FAILED) { - s7 = peg$parsevalue(); - if (s7 !== peg$FAILED) { - s8 = []; - s9 = peg$parsews(); - while (s9 !== peg$FAILED) { - s8.push(s9); - s9 = peg$parsews(); + if (s7 !== peg$FAILED) { + s8 = peg$parsevalue(); + if (s8 !== peg$FAILED) { + s9 = []; + s10 = peg$parsews(); + while (s10 !== peg$FAILED) { + s9.push(s10); + s10 = peg$parsews(); } if (input.charCodeAt(peg$currPos) === 41) { - s9 = peg$c1; + s10 = peg$c1; peg$currPos++; } else { - s9 = peg$FAILED; + s10 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e1); } } - if (s9 !== peg$FAILED) { + if (s10 !== peg$FAILED) { + s11 = []; + s12 = peg$parsews(); + while (s12 !== peg$FAILED) { + s11.push(s12); + s12 = peg$parsews(); + } peg$savedPos = s0; - s0 = peg$f3(s3, s5, s7); + s0 = peg$f3(s4, s6, s8); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -818,64 +812,76 @@ function peg$parse(input, options) { } if (s0 === peg$FAILED) { s0 = peg$currPos; + s1 = []; + s2 = peg$parsews(); + while (s2 !== peg$FAILED) { + s1.push(s2); + s2 = peg$parsews(); + } if (input.charCodeAt(peg$currPos) === 40) { - s1 = peg$c0; + s2 = peg$c0; peg$currPos++; } else { - s1 = peg$FAILED; + s2 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e0); } } - if (s1 !== peg$FAILED) { - s2 = []; - s3 = peg$parsews(); - while (s3 !== peg$FAILED) { - s2.push(s3); - s3 = peg$parsews(); + if (s2 !== peg$FAILED) { + s3 = []; + s4 = peg$parsews(); + while (s4 !== peg$FAILED) { + s3.push(s4); + s4 = peg$parsews(); } - s3 = peg$parsetoken(); - if (s3 !== peg$FAILED) { - s4 = []; - s5 = peg$parsews(); - if (s5 !== peg$FAILED) { - while (s5 !== peg$FAILED) { - s4.push(s5); - s5 = peg$parsews(); + s4 = peg$parsetoken(); + if (s4 !== peg$FAILED) { + s5 = []; + s6 = peg$parsews(); + if (s6 !== peg$FAILED) { + while (s6 !== peg$FAILED) { + s5.push(s6); + s6 = peg$parsews(); } } else { - s4 = peg$FAILED; + s5 = peg$FAILED; } - if (s4 !== peg$FAILED) { - s5 = peg$parseop_accepts_variable(); - if (s5 !== peg$FAILED) { - s6 = []; - s7 = peg$parsews(); - if (s7 !== peg$FAILED) { - while (s7 !== peg$FAILED) { - s6.push(s7); - s7 = peg$parsews(); + if (s5 !== peg$FAILED) { + s6 = peg$parseop_accepts_variable(); + if (s6 !== peg$FAILED) { + s7 = []; + s8 = peg$parsews(); + if (s8 !== peg$FAILED) { + while (s8 !== peg$FAILED) { + s7.push(s8); + s8 = peg$parsews(); } } else { - s6 = peg$FAILED; + s7 = peg$FAILED; } - if (s6 !== peg$FAILED) { - s7 = peg$parsetoken(); - if (s7 !== peg$FAILED) { - s8 = []; - s9 = peg$parsews(); - while (s9 !== peg$FAILED) { - s8.push(s9); - s9 = peg$parsews(); + if (s7 !== peg$FAILED) { + s8 = peg$parsetoken(); + if (s8 !== peg$FAILED) { + s9 = []; + s10 = peg$parsews(); + while (s10 !== peg$FAILED) { + s9.push(s10); + s10 = peg$parsews(); } if (input.charCodeAt(peg$currPos) === 41) { - s9 = peg$c1; + s10 = peg$c1; peg$currPos++; } else { - s9 = peg$FAILED; + s10 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e1); } } - if (s9 !== peg$FAILED) { + if (s10 !== peg$FAILED) { + s11 = []; + s12 = peg$parsews(); + while (s12 !== peg$FAILED) { + s11.push(s12); + s12 = peg$parsews(); + } peg$savedPos = s0; - s0 = peg$f5(s3, s5, s7); + s0 = peg$f5(s4, s6, s8); } else { peg$currPos = s0; s0 = peg$FAILED; @@ -974,18 +980,30 @@ function peg$parse(input, options) { if (peg$silentFails === 0) { peg$fail(peg$e0); } } if (s1 !== peg$FAILED) { - s2 = peg$parsetoken(); - if (s2 !== peg$FAILED) { + s2 = []; + s3 = peg$parsews(); + while (s3 !== peg$FAILED) { + s2.push(s3); + s3 = peg$parsews(); + } + s3 = peg$parsetoken(); + if (s3 !== peg$FAILED) { + s4 = []; + s5 = peg$parsews(); + while (s5 !== peg$FAILED) { + s4.push(s5); + s5 = peg$parsews(); + } if (input.charCodeAt(peg$currPos) === 41) { - s3 = peg$c1; + s5 = peg$c1; peg$currPos++; } else { - s3 = peg$FAILED; + s5 = peg$FAILED; if (peg$silentFails === 0) { peg$fail(peg$e1); } } - if (s3 !== peg$FAILED) { + if (s5 !== peg$FAILED) { peg$savedPos = s0; - s0 = peg$f8(s2); + s0 = peg$f8(s3); } else { peg$currPos = s0; s0 = peg$FAILED; diff --git a/lib/package/plugins/CC007-ConditionSyntax.js b/lib/package/plugins/CC007-ConditionSyntax.js index 0ccbbce..c96b8a3 100644 --- a/lib/package/plugins/CC007-ConditionSyntax.js +++ b/lib/package/plugins/CC007-ConditionSyntax.js @@ -1,5 +1,5 @@ /* - Copyright 2019-2023 Google LLC + Copyright 2019-2024 Google LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -31,7 +31,7 @@ const plugin = { const onCondition = function (condition, cb) { debug(`onCondition (${condition.getExpression()})`); let flagged = false; - const expr = condition.getExpression(); + const expr = condition.getExpression().trim(); try { parser.parse(expr); } catch (e) { diff --git a/lib/peggy/Apigee-Condition.pegjs b/lib/peggy/Apigee-Condition.pegjs index 47fec58..72d1fef 100644 --- a/lib/peggy/Apigee-Condition.pegjs +++ b/lib/peggy/Apigee-Condition.pegjs @@ -66,7 +66,7 @@ start = boolean_stmt1 boolean_stmt1 - = left:boolean_stmt2 ws+ op1:op_boolean ws+ right:boolean_stmt1 { return {operator:op1, operands:[left, right] } } + = left:boolean_stmt2 ws* op1:op_boolean ws* right:boolean_stmt1 { return {operator:op1, operands:[left, right] } } / boolean_stmt2 boolean_stmt2 @@ -78,20 +78,20 @@ factor / primary primary - = "(" ws* t1:token ws+ op1:op_accepts_literal ws+ v1:value ws* ")" { + = ws* "(" ws* t1:token ws+ op1:op_accepts_literal ws+ v1:value ws* ")" ws* { return parseBinaryOp(t1,op1,v1,error); } / t1:token ws+ op1:op_accepts_literal ws+ v1:value { return parseBinaryOp(t1,op1,v1,error); } - / "(" ws* t1:token ws+ op1:op_accepts_variable ws+ t2:token ws* ")" { + / ws* "(" ws* t1:token ws+ op1:op_accepts_variable ws+ t2:token ws* ")" ws* { return { operator:op1, operands:[t1, t2]}; } / t1:token ws+ op1:op_accepts_variable ws+ t2:token { return { operator:op1, operands:[t1, t2]}; } / token1:token { return token1; } - / "(" token1:token ")" { return token1; } + / "(" ws* token1:token ws* ")" { return token1; } / "(" stmt1:boolean_stmt1 ")" { return stmt1; } op_boolean diff --git a/test/specs/CC007-Condition-Syntax-Test.js b/test/specs/CC007-Condition-Syntax-Test.js index 3ec54b5..df6e730 100644 --- a/test/specs/CC007-Condition-Syntax-Test.js +++ b/test/specs/CC007-Condition-Syntax-Test.js @@ -96,6 +96,62 @@ describe(`${testID} - ${plugin.plugin.name}`, function () { expression: 'request.header.content-type = "application/json" AND request.verb = "GET"', expectError: false + }, + + { + expression: + 'not(request.header.content-type = "application/json")AND(request.verb = "GET")', + expectError: false + }, + { + expression: '(true)AND(request.verb = "GET")', + expectError: false + }, + { + expression: '(true) AND(request.verb = "GET")', + expectError: false + }, + { + expression: '( true ) AND( request.verb = "GET" )', + expectError: false + }, + { + expression: '( true )AND( request.verb = "GET" )', + expectError: false + }, + { + expression: '(true )AND(request.verb = "GET")', + expectError: false + }, + { + expression: '(true ) AND(request.verb = "GET")', + expectError: false + }, + { + expression: '(true ) AND( request.verb = "GET" )', + expectError: false + }, + + /* The following four cases differ only in spacing */ + { + expression: + '((a MatchesPath "/b/d") or (b MatchesPath "/c/d/e")) and (request.verb = "GET")', + expectError: false + }, + { + expression: + '((a MatchesPath "/b/d") or (b MatchesPath "/c/d/e"))and (request.verb = "GET")', + expectError: false + }, + { + expression: + '((a MatchesPath "/b/d") or (b MatchesPath "/c/d/e") )and (request.verb = "GET") ', + expectError: false + }, + { + expression: + '( ( a MatchesPath "/b/d") or (b MatchesPath "/c/d/e") )and (request.verb = "GET")', + expectError: false } ]; cases.forEach((testcase, i) => { diff --git a/test/specs/ConditionParserTest.js b/test/specs/ConditionParserTest.js index 8146ec6..d90a2aa 100644 --- a/test/specs/ConditionParserTest.js +++ b/test/specs/ConditionParserTest.js @@ -336,7 +336,7 @@ describe("ConditionParser", function () { expect.fail(); } catch (e) { expect(e.toString()).to.include("SyntaxError"); - expect(e.toString()).to.include("Expected [ \\t\\n] or end"); + expect(e.toString()).to.include("Expected "); } try { parser.parse(c1.slice(0, -1)); @@ -428,7 +428,7 @@ describe("ConditionParser", function () { expect.fail(); } catch (e) { expect(e.toString()).to.include("SyntaxError"); - expect(e.toString()).to.include('Expected ")"'); + expect(e.toString()).to.include("Expected "); } try { const result = parser.parse(c1.replace('code"', 'code")'));