Skip to content

Commit 5551413

Browse files
ecclesia-robotcopybara-github
authored andcommitted
Changed the $filter string conversion to use Regex and be more fault tolerant.
PiperOrigin-RevId: 615581952
1 parent 67a6dbe commit 5551413

3 files changed

Lines changed: 73 additions & 11 deletions

File tree

ecclesia/lib/redfish/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ cc_library(
3131
"@com_google_absl//absl/time",
3232
"@com_google_absl//absl/types:optional",
3333
"@com_google_absl//absl/types:span",
34+
"@com_googlesource_code_re2//:re2",
3435
"@com_json//:json",
3536
],
3637
)

ecclesia/lib/redfish/interface.cc

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@
3535
#include "absl/strings/str_split.h"
3636
#include "absl/strings/string_view.h"
3737
#include "absl/types/optional.h"
38+
#include "re2/re2.h"
39+
40+
// Pattern for expression [lhs][operator][rhs]
41+
constexpr LazyRE2 kRelationalExpressionRegex = {
42+
"^(?P<left>[^\\s<=>!]+)(?:(<=|>=|!=|>|<|=)(?P<right>[^<=>!]+))$"};
3843

3944
constexpr std::array<const char *, 6> kPredicateOperators = {
4045
"<=", ">=", "!=", ">", "<", "=",
@@ -64,14 +69,19 @@ struct EncodedPredicate {
6469
absl::StatusOr<RelationalExpression> EncodeRelationalExpression(
6570
std::string_view expression) {
6671
RelationalExpression relational_expression;
67-
for (const std::string predicate_operator : kPredicateOperators) {
68-
auto pos = expression.find(predicate_operator);
69-
if (pos == std::string::npos) continue;
70-
RelationalExpression rel_expr;
71-
rel_expr.lhs = expression.substr(0, pos);
72-
rel_expr.rel_operator = predicate_operator;
73-
rel_expr.rhs = expression.substr(pos + predicate_operator.size());
74-
return rel_expr;
72+
73+
// Regex match the expression. The operator must be a valid relational
74+
// operator and the right hand side must not have spaces or characters
75+
// included in relation operators.
76+
std::string lhs;
77+
std::string op;
78+
std::string rhs;
79+
if (RE2::FullMatch(expression, *kRelationalExpressionRegex, &lhs, &op,
80+
&rhs)) {
81+
relational_expression.lhs = lhs;
82+
relational_expression.rel_operator = op;
83+
relational_expression.rhs = rhs;
84+
return relational_expression;
7585
}
7686
return absl::InvalidArgumentError("Invalid expression");
7787
}
@@ -83,7 +93,7 @@ absl::StatusOr<RelationalExpression> EncodeRelationalExpression(
8393
// expressions = [[lhs: "Prop1", rel_operator: "<=", rhs: "42"],
8494
// [lhs: "Prop1", rel_operator: ">", rhs: "84"]
8595
// ]
86-
EncodedPredicate EncodePredicate(absl::string_view predicate) {
96+
absl::StatusOr<EncodedPredicate> EncodePredicate(absl::string_view predicate) {
8797
// The high level logic of this function is to break down the predicate into
8898
// logical operators (or/and) and the relational expressions (prop>value).
8999
// After breaking them up go through the relational expressions and break them
@@ -120,6 +130,9 @@ EncodedPredicate EncodePredicate(absl::string_view predicate) {
120130
auto encoded_expression = EncodeRelationalExpression(expression);
121131
if (encoded_expression.ok()) {
122132
encoded_predicate.expressions.push_back(encoded_expression.value());
133+
} else {
134+
// Invalid expression, return an error
135+
return absl::InvalidArgumentError(encoded_expression.status().message());
123136
}
124137
}
125138
return encoded_predicate;
@@ -206,15 +219,29 @@ std::string GenerateFilterString(const EncodedPredicate &predicate_object) {
206219

207220
void RedfishQueryParamFilter::BuildFromRedpathPredicate(
208221
absl::string_view predicate) {
209-
filter_string_ = GenerateFilterString(EncodePredicate(predicate));
222+
auto encoded_predicate = EncodePredicate(predicate);
223+
if (!encoded_predicate.ok()) {
224+
// Invalid predicate, set filter_string_ to empty, indicating an invalid
225+
// expression, and return.
226+
filter_string_ = "";
227+
return;
228+
}
229+
filter_string_ = GenerateFilterString(*encoded_predicate);
210230
}
211231

212232
void RedfishQueryParamFilter::BuildFromRedpathPredicateList(
213233
const std::vector<std::string> &predicates) {
214234
std::vector<std::string> filter_strings;
215235
filter_strings.reserve(predicates.size());
216236
for (absl::string_view predicate : predicates) {
217-
filter_strings.push_back(GenerateFilterString(EncodePredicate(predicate)));
237+
auto encoded_predicate = EncodePredicate(predicate);
238+
if (!encoded_predicate.ok()) {
239+
// Invalid predicate, set filter_string_ to empty, indicating an invalid
240+
// expression, and return.
241+
filter_string_ = "";
242+
return;
243+
}
244+
filter_strings.push_back(GenerateFilterString(*encoded_predicate));
218245
}
219246
filter_string_ = absl::StrJoin(filter_strings, "%20or%20");
220247
}

ecclesia/lib/redfish/interface_test.cc

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,40 @@ TEST(RedfishVariant, RedfishQueryParamFilter) {
201201
EXPECT_EQ(filter.ToString(), absl::StrCat("$filter=", filter_string));
202202
}
203203

204+
TEST(RedfishVariant, RedfishQueryParamFilterInvalid) {
205+
auto filter = RedfishQueryParamFilter();
206+
// Add a valid expression to $filter to make sure the string is overwritten by
207+
// the builder.
208+
filter.BuildFromRedpathPredicate("Prop1=42");
209+
// Invalid operator (wrong equality)
210+
std::string predicate1 = "Prop1==42";
211+
filter.BuildFromRedpathPredicate(predicate1);
212+
EXPECT_EQ(filter.ToString(), "");
213+
// Invalid operator
214+
std::string predicate2 = "Prop1>>42";
215+
filter.BuildFromRedpathPredicate(predicate2);
216+
EXPECT_EQ(filter.ToString(), "");
217+
// Spaces on left
218+
std::string predicate3 = "Bad Property>42";
219+
filter.BuildFromRedpathPredicate(predicate3);
220+
EXPECT_EQ(filter.ToString(), "");
221+
// Add a valid expression to $filter to make sure the string is overwritten by
222+
// the list builder.
223+
filter.BuildFromRedpathPredicate("Prop1=42");
224+
// Special characters in operands
225+
std::string predicate4 = "Prop<erty1=42";
226+
std::string predicate5 = "Property2=4>2";
227+
filter.BuildFromRedpathPredicateList({predicate4, predicate5});
228+
EXPECT_EQ(filter.ToString(), "");
229+
// One side of a logical exp is bad. Try both sides.
230+
std::string predicate6 = "Property2=42";
231+
std::string predicate7 = "Prop<erty1=42";
232+
filter.BuildFromRedpathPredicateList({predicate6, predicate7});
233+
EXPECT_EQ(filter.ToString(), "");
234+
filter.BuildFromRedpathPredicateList({predicate7, predicate6});
235+
EXPECT_EQ(filter.ToString(), "");
236+
}
237+
204238
TEST(RedfishVariant, RedfishQueryParamFilterEmpty) {
205239
auto filter = RedfishQueryParamFilter();
206240
EXPECT_EQ(filter.ToString(), "");

0 commit comments

Comments
 (0)