Skip to content

Commit

Permalink
feat(CC007): implement Condition parser and validator
Browse files Browse the repository at this point in the history
  • Loading branch information
DinoChiesa committed Dec 6, 2023
1 parent 332272d commit 9a08c53
Show file tree
Hide file tree
Showing 8 changed files with 1,356 additions and 358 deletions.
720 changes: 520 additions & 200 deletions build/ConditionsParser.js → build/ConditionParser.js

Large diffs are not rendered by default.

60 changes: 60 additions & 0 deletions lib/package/plugins/CC007-ConditionSyntax.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
Copyright 2019-2023 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

const ruleId = require("../myUtil.js").getRuleId(),
util = require("util"),
debug = require("debug")("apigeelint:" + ruleId);
const parser = require("../../../build/ConditionParser.js");

const plugin = {
ruleId,
name: "Condition Syntax",
fatal: false,
severity: 2, //error
nodeType: "Condition",
enabled: true
};

const onCondition = function (condition, cb) {
debug(`onCondition (${condition.getExpression()})`);
let flagged = false;
const expr = condition.getExpression();
try {
parser.parse(expr);
} catch (e) {
debug(util.format(e));
flagged = true;
const estr = e.toString(),
loc = e.location.start.column;
condition.addMessage({
source: condition.getExpression(),
line: condition.getElement().lineNumber,
column: condition.getElement().columnNumber,
plugin,
message: `Condition expression is invalid. Position ${loc}, ${estr}`
});
}

if (typeof cb == "function") {
cb(null, flagged);
}
return flagged;
};

module.exports = {
plugin,
onCondition
};
102 changes: 79 additions & 23 deletions src/peggy/Apigee-Condition.pegjs → lib/peggy/Apigee-Condition.pegjs
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,39 @@
//
// Examples of conditions that can be parsed by this grammar:
//
// proxy.pathsuffix MatchesPath "/authorize" and request.verb = "POST"
// (proxy.pathsuffix MatchesPath "/authorize") and (request.verb = "POST")
// (proxy.pathsuffix = "/token") and (request.verb = "POST")
// (request.verb = "POST") && (proxy.pathsuffix = "/token")
// !(request.verb = "POST")
// !valid
//
// Monday, 6 April 2015, 16:46
//

{{

function stringOp(op) {
return ["EqualsCaseInsensitive","StartsWith","Equals", "NotEquals", "JavaRegex","MatchesPath","Matches"].includes(op);
}
function numericOp(op) {
return ["Equals", "NotEquals", "GreaterThanOrEquals","GreaterThan","LesserThanOrEquals","LesserThan"].includes(op);
}

function parseBinaryOperation(t1,op1,v1,error) {
let isStringOp = stringOp(op1),
isNumericOp = numericOp(op1),
vType = Object.prototype.toString.call(v1),
v = (vType=== '[object Array]' ) ? v1.join("") : v1;
if (vType==='[object Number]' && !isNumericOp) {
error("not expecting number on RHS");
}
if (vType!=='[object Number]' && !isStringOp) {
error("expecting number on RHS");
}
return { operator:op1, operands:[t1, v]};
}

}}


start
= boolean_stmt1
Expand All @@ -44,15 +69,12 @@ factor
= "!" ws* operand:factor { return {operator:"NOT", operands:[operand] } }
/ primary


primary
= "(" ws* t1:token ws+ op1:operator ws+ v1:value ws* ")" {
var v = ( Object.prototype.toString.call( v1 ) === '[object Array]' ) ? v1.join("") : v1;
return { operator:op1, operands:[t1, v]};
return parseBinaryOperation(t1,op1,v1,error);
}
/ ws* t1:token ws+ op1:operator ws+ v1:value {
var v = ( Object.prototype.toString.call( v1 ) === '[object Array]' ) ? v1.join("") : v1;
return { operator:op1, operands:[t1, v]};
/ t1:token ws+ op1:operator ws+ v1:value {
return parseBinaryOperation(t1,op1,v1,error);
}
/ token1:token { return token1; }
/ "(" token1:token ")" { return token1; }
Expand All @@ -66,47 +88,81 @@ op_and = "and" / "AND" / "&&"

op_or = "or" / "OR" / "||"

// Ordering is important. For operators that share a common prefix,
// list the longer, more specific operator, first.

operator
= op_startswith {return "StartsWith";}
/ op_notequals { return "NotEquals"; }
= op_equalsnocase { return "EqualsCaseInsensitive"; }
/ op_startswith {return "StartsWith";}
/ op_equals { return "Equals"; }
/ op_regexmatch {return "RegexMatch";}
/ op_matchend {return "MatchEnd?";}
/ op_matchespath {return "MatchesPath";}
/ op_notequals { return "NotEquals"; }
/ op_greatereq {return "GreaterThanOrEquals";}
/ op_lessereq {return "LesserThanOrEquals";}
/ op_greater {return "GreaterThan";}
/ op_lessereq {return "LesserThanOrEquals";}
/ op_lesser {return "LesserThan";}
/ op_regexmatch {return "JavaRegex";}
/ op_matchespath {return "MatchesPath";}
/ op_matches {return "Matches";}

op_equals = "=" / "Equals" / "Is" / "is"

op_notequals = "!=" / "NotEquals" / "notequals" / "IsNot" / "isnot"

op_matchend = "~/" / "MatchEnd"
op_equalsnocase = ":=" / "EqualsCaseInsensitive"

op_regexmatch = "~~" / "JavaRegex"

op_matchespath = "~" / "MatchesPath" / "LikePath"
op_greater = ">" / "GreaterThan"

op_greatereq = ">=" / "GreaterThanOrEquals"

op_lesser = "<" / "LesserThan"

op_lessereq = "<=" / "LesserThanOrEquals"

op_greater = ">" / "GreaterThan"
op_regexmatch = "~~" / "JavaRegex"

op_lesser = "<" / "LesserThan"
op_matches = "~" / "Matches" / "Like"

op_matchespath = "~/" / "MatchesPath" / "LikePath"

op_startswith = "=|" / "StartsWith"


frac_string
= "." int1:DecimalIntegerLiteral { return "." + chars.join(''); }

DecimalLiteral
= "-"? DecimalIntegerLiteral "." DecimalDigit|1..10| {
return { type: "Literal", value: parseFloat(text()) };
}
/ "." DecimalDigit|1..10| {
return { type: "Literal", value: parseFloat(text()) };
}
/ "-"? DecimalIntegerLiteral {
return { type: "Literal", value: parseInt(text()) };
}

DecimalIntegerLiteral
= "0"
/ NonZeroDigit DecimalDigit|0..9|

DecimalDigit
= [0-9]

NonZeroDigit
= [1-9]

alpha
= [a-zA-Z]

token
= token:[a-zA-Z0-9_\.]+ { return token.join(""); }
= alpha [-a-zA-Z0-9_\.]* { return text(); }

value
= '"' value:[a-zA-Z0-9_\./]* '"' { value.unshift("'"); value.push("'"); return value; }
= '"' value:[^"]* '"' { value.unshift("'"); value.push("'"); return value; }
/ "null" { return null; }
/ "false" { return false; }
/ "true" { return true; }
/ value:DecimalLiteral { return value.value; }

ws
= [ \t]
= [ \t\n]
101 changes: 0 additions & 101 deletions out/apigeeLint.json

This file was deleted.

Loading

0 comments on commit 9a08c53

Please sign in to comment.