Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion c/include/dd/policies/evaluator_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@
X(PREFIX, 1) \
X(SUFFIX, 2) \
X(CONTAINS, 3) \
X(EXACT, 4)
X(EXACT, 4) \
X(WILDCARD, 5)

#define ENUM_COMPARATOR_VAL(VAL, IX) PLCS_STR_CMP_##VAL = IX,
typedef enum plcs_string_comparator {
Expand Down
51 changes: 51 additions & 0 deletions c/src/evaluator_default.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,53 @@ plcs_evaluation_result string_evaluator_contains(const char *eval, const char *p
return (strstr(param, eval) != NULL) ? PLCS_EVAL_RESULT_TRUE : PLCS_EVAL_RESULT_FALSE;
}

plcs_evaluation_result string_evaluator_wildcard(const char *pattern, const char *str) {
if (!pattern || !str) {
return PLCS_EVAL_RESULT_ABSTAIN; // 'dont-care' state
}

// no need to check lengths :)
const char *star_pattern = NULL;
const char *star_str = NULL;

while (*str) {
// Exact match, or '?' matches any single character
if (*pattern == *str || *pattern == '?') {
pattern++;
str++;
continue;
}

if (*pattern == '*') {
// skip multiple '***'
while (*pattern == '*') {
pattern++;
}
// break soon :)
if (*pattern == '\0') {
return PLCS_EVAL_RESULT_TRUE;
}
star_pattern = pattern;
star_str = str;
continue;
}

if (star_pattern) {
pattern = star_pattern;
str = ++star_str;
continue;
}

return PLCS_EVAL_RESULT_FALSE;
}

while (*pattern == '*') {
pattern++;
}

return *pattern == '\0' ? PLCS_EVAL_RESULT_TRUE : PLCS_EVAL_RESULT_FALSE;
}

plcs_evaluation_result plcs_default_string_evaluator(
const char *policy,
const plcs_string_comparator cmp,
Expand Down Expand Up @@ -137,6 +184,10 @@ plcs_evaluation_result plcs_default_string_evaluator(
return string_evaluator_contains(policy, ctx);
break;

case PLCS_STR_CMP_WILDCARD:
return string_evaluator_wildcard(policy, ctx);
break;

case PLCS_STR_CMP_STR_UNKNOWN:
case PLCS_STR_CMP__COUNT:
// error we should not get here!
Expand Down
8 changes: 6 additions & 2 deletions c/src/schema/evaluators_reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@ static inline int dd_wls_CmpTypeNUM_is_known_value(dd_wls_CmpTypeNUM_enum_t valu
* CMP_PREFIX : Compare if the string starts with the value
* CMP_SUFFIX : Compare if the string ends with the value
* CMP_CONTAINS : Compare if the string contains the value
* CMP_EXACT : Compare if the string is exactly equal to the value */
* CMP_EXACT : Compare if the string is exactly equal to the value
* CMP_WILDCARD : Compare using wildcard matching ('?' single char, '*' any chars) */
typedef int8_t dd_wls_CmpTypeSTR_enum_t;
__flatbuffers_define_integer_type(dd_wls_CmpTypeSTR, dd_wls_CmpTypeSTR_enum_t, 8)
/** An enum representing the type of action to take. */
Expand All @@ -129,7 +130,8 @@ __flatbuffers_define_integer_type(dd_wls_CmpTypeSTR, dd_wls_CmpTypeSTR_enum_t, 8
#define dd_wls_CmpTypeSTR_CMP_SUFFIX ((dd_wls_CmpTypeSTR_enum_t)INT8_C(2))
#define dd_wls_CmpTypeSTR_CMP_CONTAINS ((dd_wls_CmpTypeSTR_enum_t)INT8_C(3))
#define dd_wls_CmpTypeSTR_CMP_EXACT ((dd_wls_CmpTypeSTR_enum_t)INT8_C(4))
#define dd_wls_CmpTypeSTR_CMP_COUNT ((dd_wls_CmpTypeSTR_enum_t)INT8_C(5))
#define dd_wls_CmpTypeSTR_CMP_WILDCARD ((dd_wls_CmpTypeSTR_enum_t)INT8_C(5))
#define dd_wls_CmpTypeSTR_CMP_COUNT ((dd_wls_CmpTypeSTR_enum_t)INT8_C(6))

static inline const char *dd_wls_CmpTypeSTR_name(dd_wls_CmpTypeSTR_enum_t value)
{
Expand All @@ -139,6 +141,7 @@ static inline const char *dd_wls_CmpTypeSTR_name(dd_wls_CmpTypeSTR_enum_t value)
case dd_wls_CmpTypeSTR_CMP_SUFFIX: return "CMP_SUFFIX";
case dd_wls_CmpTypeSTR_CMP_CONTAINS: return "CMP_CONTAINS";
case dd_wls_CmpTypeSTR_CMP_EXACT: return "CMP_EXACT";
case dd_wls_CmpTypeSTR_CMP_WILDCARD: return "CMP_WILDCARD";
case dd_wls_CmpTypeSTR_CMP_COUNT: return "CMP_COUNT";
default: return "";
}
Expand All @@ -152,6 +155,7 @@ static inline int dd_wls_CmpTypeSTR_is_known_value(dd_wls_CmpTypeSTR_enum_t valu
case dd_wls_CmpTypeSTR_CMP_SUFFIX: return 1;
case dd_wls_CmpTypeSTR_CMP_CONTAINS: return 1;
case dd_wls_CmpTypeSTR_CMP_EXACT: return 1;
case dd_wls_CmpTypeSTR_CMP_WILDCARD: return 1;
case dd_wls_CmpTypeSTR_CMP_COUNT: return 1;
default: return 0;
}
Expand Down
167 changes: 167 additions & 0 deletions c/src/test/test_evaluator.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ extern plcs_evaluation_result string_evaluator_exact(const char *eval, const cha
extern plcs_evaluation_result string_evaluator_prefix(const char *eval, const char *param);
extern plcs_evaluation_result string_evaluator_suffix(const char *eval, const char *param);
extern plcs_evaluation_result string_evaluator_contains(const char *eval, const char *param);
extern plcs_evaluation_result string_evaluator_wildcard(const char *pattern, const char *str);

extern void plcs_eval_ctx_set_str_eval_error(plcs_string_evaluators ix, plcs_errors error);
extern void plcs_eval_ctx_set_num_eval_error(plcs_numeric_evaluators ix, plcs_errors error);
Expand Down Expand Up @@ -176,6 +177,168 @@ UTEST(evaluator, test_string_evaluator) {
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
}

UTEST(evaluator, test_string_evaluator_wildcard) {
/* NULL parameters */
int res = string_evaluator_wildcard(NULL, "test");
ASSERT_EQ(res, PLCS_EVAL_RESULT_ABSTAIN);
res = string_evaluator_wildcard("test", NULL);
ASSERT_EQ(res, PLCS_EVAL_RESULT_ABSTAIN);
res = string_evaluator_wildcard(NULL, NULL);
ASSERT_EQ(res, PLCS_EVAL_RESULT_ABSTAIN);

/* Exact match (no wildcards) */
res = string_evaluator_wildcard("hello", "hello");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("hello", "world");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);
res = string_evaluator_wildcard("", "");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("", "a");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);
res = string_evaluator_wildcard("a", "");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);

/* ? matches exactly one character */
res = string_evaluator_wildcard("?", "a");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("?", "");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);
res = string_evaluator_wildcard("?", "ab");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);
res = string_evaluator_wildcard("a?c", "abc");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("a?c", "aXc");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("a?c", "ac");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);
res = string_evaluator_wildcard("a?c", "abbc");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);
res = string_evaluator_wildcard("???", "abc");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("???", "ab");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);
res = string_evaluator_wildcard("???", "abcd");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);

/* * matches zero or more characters */
res = string_evaluator_wildcard("*", "");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("*", "anything");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("a*", "a");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("a*", "abc");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("a*", "b");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);
res = string_evaluator_wildcard("*c", "c");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("*c", "abc");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("*c", "abd");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);
res = string_evaluator_wildcard("a*c", "ac");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("a*c", "abc");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("a*c", "aXYZc");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("a*c", "aXYZd");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);

/* Multiple * wildcards */
res = string_evaluator_wildcard("*foo*", "foo");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("*foo*", "XXfooYY");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("*foo*bar*", "foobar");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("*foo*bar*", "XXfooYYbarZZ");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("*foo*bar*", "barfoo");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);
res = string_evaluator_wildcard("*foo*bar*", "fooXXbaz");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);

/* Consecutive * treated as single * */
res = string_evaluator_wildcard("a***b", "ab");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("a***b", "aXXXb");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);

/* Mixed ? and * */
res = string_evaluator_wildcard("a?*", "ab");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("a?*", "abc");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("a?*", "a");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);
res = string_evaluator_wildcard("*?", "a");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("*?", "");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);

/* RFC-style patterns: executable path matching */
res = string_evaluator_wildcard("**/java", "/usr/bin/java");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("**/java-1.5*/**/java", "/usr/lib/java-1.5.0/bin/java");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("**/java-1.5*/**/java", "/usr/lib/java-1.8.0/bin/java");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);

/* RFC-style patterns: exe? matching single char suffix */
res = string_evaluator_wildcard("**/exe?", "/some/exe2");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("**/exe?", "/some/other/exeA");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("**/exe?", "/some/exe");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);
res = string_evaluator_wildcard("**/exe?", "/some/exe22");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);

/* RFC-style patterns: argument matching */
res = string_evaluator_wildcard("1.*", "1.2.3");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("1.*", "2.0.0");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);
res = string_evaluator_wildcard("*csc.dll", "csc.dll");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("*csc.dll", "/path/to/csc.dll");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
res = string_evaluator_wildcard("*csc.dll", "other.dll");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);

/* Pathological patterns: ensure no excessive backtracking */
res = string_evaluator_wildcard("*a*a*a*a*b", "aaaaaaaaaaac");
ASSERT_EQ(res, PLCS_EVAL_RESULT_FALSE);
res = string_evaluator_wildcard("*a*a*a*a*b", "aaaaaaaaaaab");
ASSERT_EQ(res, PLCS_EVAL_RESULT_TRUE);
}

UTEST(evaluator, default_string_eval_wildcard) {
/* Test wildcard through the plcs_default_string_evaluator dispatch */
ASSERT_EQ(
plcs_default_string_evaluator("*.dll", PLCS_STR_CMP_WILDCARD, "test.dll", "d", PLCS_STR_EVAL_COMPONENT),
(plcs_evaluation_result)PLCS_EVAL_RESULT_TRUE
);
ASSERT_EQ(
plcs_default_string_evaluator("*.dll", PLCS_STR_CMP_WILDCARD, "test.exe", "d", PLCS_STR_EVAL_COMPONENT),
(plcs_evaluation_result)PLCS_EVAL_RESULT_FALSE
);
ASSERT_EQ(
plcs_default_string_evaluator("he??o", PLCS_STR_CMP_WILDCARD, "hello", "d", PLCS_STR_EVAL_COMPONENT),
(plcs_evaluation_result)PLCS_EVAL_RESULT_TRUE
);
ASSERT_EQ(
plcs_default_string_evaluator(NULL, PLCS_STR_CMP_WILDCARD, "test", "d", PLCS_STR_EVAL_COMPONENT),
(plcs_evaluation_result)PLCS_EVAL_RESULT_ABSTAIN
);
ASSERT_EQ(
plcs_default_string_evaluator("test", PLCS_STR_CMP_WILDCARD, NULL, "d", PLCS_STR_EVAL_COMPONENT),
(plcs_evaluation_result)PLCS_EVAL_RESULT_ABSTAIN
);
}

UTEST(evaluator, out_of_bounds_eval_error_setters) {
plcs_eval_ctx_set_str_eval_error(PLCS_STR_EVAL__COUNT, PLCS_EUNKNOWN_EVAL_IX);
int err = plcs_eval_ctx_get_str_eval_error(PLCS_STR_EVAL__COUNT);
Expand Down Expand Up @@ -208,6 +371,10 @@ UTEST(evaluator, default_string_eval_sanity) {
plcs_default_string_evaluator("b", PLCS_STR_CMP_CONTAINS, "abc", "d", PLCS_STR_EVAL_COMPONENT),
(plcs_evaluation_result)PLCS_EVAL_RESULT_TRUE
);
ASSERT_EQ(
plcs_default_string_evaluator("a?c", PLCS_STR_CMP_WILDCARD, "abc", "d", PLCS_STR_EVAL_COMPONENT),
(plcs_evaluation_result)PLCS_EVAL_RESULT_TRUE
);

/* Abstain on missing data */
ASSERT_EQ(
Expand Down
2 changes: 1 addition & 1 deletion fbs-schema/evaluator_ids.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ enum StringEvaluators : byte {
PROCESS_ARGV_5,
PROCESS_ARGV_N,

PROCESS_ENVAR, // Check "KEY=VALUE" pattern
PROCESS_ENVAR, // Check "KEY=VALUE" pattern, with CMP_WILDCARD can check wildcards too

/// Represents the count of String evaluators.
/// This is used to ensure that the enum is always in sync with the number of evaluators.
Expand Down
2 changes: 2 additions & 0 deletions fbs-schema/evaluators.fbs
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@ enum CmpTypeNUM : byte {
/// CMP_SUFFIX : Compare if the string ends with the value
/// CMP_CONTAINS : Compare if the string contains the value
/// CMP_EXACT : Compare if the string is exactly equal to the value
/// CMP_WILDCARD : Compare using wildcard matching ('?' single char, '*' any chars)
enum CmpTypeSTR : byte {
/// An enum representing the type of action to take.
CMP_STR_UNKNOWN = 0,
CMP_PREFIX,
CMP_SUFFIX,
CMP_CONTAINS,
CMP_EXACT,
CMP_WILDCARD,
CMP_COUNT
}

Expand Down
2 changes: 1 addition & 1 deletion fbs-schema/json/policy.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
},
"dd_wls_CmpTypeSTR" : {
"type" : "string",
"enum": ["CMP_PREFIX", "CMP_SUFFIX", "CMP_CONTAINS", "CMP_EXACT", "CMP_COUNT"]
"enum": ["CMP_PREFIX", "CMP_SUFFIX", "CMP_CONTAINS", "CMP_EXACT", "CMP_WILDCARD", "CMP_COUNT"]
},
"dd_wls_EvaluatorType" : {
"type" : "string",
Expand Down
Loading
Loading