Make field index information public

Json-related filtercheck fields supported indexing with brackets, but
when looking at the field descriptions you couldn't tell if a field
allowed an index, required an index, or did not allow an index.

This information was available, but it was a part of the protected
aliases map within the class.

Move this to the public field information so it can be used outside the
class.

Also add m_ prefixes for member names, now that the struct isn't
trivial.

Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
This commit is contained in:
Mark Stemm 2019-06-27 16:18:51 -07:00 committed by Mark Stemm
parent 19c12042f4
commit c1035ce4de
3 changed files with 110 additions and 83 deletions

View File

@ -93,21 +93,21 @@ void falco_engine::list_fields(bool names_only)
if(!names_only)
{
printf("\n----------------------\n");
printf("Field Class: %s (%s)\n\n", chk_field.name.c_str(), chk_field.desc.c_str());
printf("Field Class: %s (%s)\n\n", chk_field.m_name.c_str(), chk_field.m_desc.c_str());
}
for(auto &field : chk_field.fields)
for(auto &field : chk_field.m_fields)
{
uint32_t l, m;
printf("%s", field.name.c_str());
printf("%s", field.m_name.c_str());
if(names_only)
{
printf("\n");
continue;
}
uint32_t namelen = field.name.size();
uint32_t namelen = field.m_name.size();
if(namelen >= DESCRIPTION_TEXT_START)
{
@ -120,7 +120,7 @@ void falco_engine::list_fields(bool names_only)
printf(" ");
}
size_t desclen = field.desc.size();
size_t desclen = field.m_desc.size();
for(l = 0; l < desclen; l++)
{
@ -134,7 +134,7 @@ void falco_engine::list_fields(bool names_only)
}
}
printf("%c", field.desc.at(l));
printf("%c", field.m_desc.at(l));
}
printf("\n");

View File

@ -70,30 +70,51 @@ std::string json_event_filter_check::json_as_string(const json &j)
}
}
json_event_filter_check::alias::alias()
json_event_filter_check::field_info::field_info()
: m_idx_mode(IDX_NONE), m_idx_type(IDX_NUMERIC)
{
}
json_event_filter_check::alias::alias(nlohmann::json::json_pointer ptr)
: m_jptr(ptr), m_format(def_format),
json_event_filter_check::field_info::field_info(std::string name,
std::string desc)
: m_name(name), m_desc(desc),
m_idx_mode(IDX_NONE), m_idx_type(IDX_NUMERIC)
{
}
json_event_filter_check::field_info::field_info(std::string name,
std::string desc,
index_mode mode)
: m_name(name), m_desc(desc),
m_idx_mode(mode), m_idx_type(IDX_NUMERIC)
{
}
json_event_filter_check::field_info::field_info(std::string name,
std::string desc,
index_mode mode,
index_type itype)
: m_name(name), m_desc(desc),
m_idx_mode(mode), m_idx_type(itype)
{
}
json_event_filter_check::field_info::~field_info()
{
}
json_event_filter_check::alias::alias()
{
}
json_event_filter_check::alias::alias(nlohmann::json::json_pointer ptr)
: m_jptr(ptr), m_format(def_format)
{
}
json_event_filter_check::alias::alias(nlohmann::json::json_pointer ptr,
format_t format)
: m_jptr(ptr), m_format(format),
m_idx_mode(IDX_NONE), m_idx_type(IDX_NUMERIC)
{
}
json_event_filter_check::alias::alias(nlohmann::json::json_pointer ptr,
format_t format,
index_mode mode,
index_type itype)
: m_jptr(ptr), m_format(format),
m_idx_mode(mode), m_idx_type(itype)
: m_jptr(ptr), m_format(format)
{
}
@ -118,18 +139,25 @@ int32_t json_event_filter_check::parse_field_name(const char *str, bool alloc_st
size_t idx_len = 0;
for(auto &pair : m_aliases)
for(auto &info : m_info.m_fields)
{
// What follows the match must not be alphanumeric or a dot
if(strncmp(pair.first.c_str(), str, pair.first.size()) == 0 &&
!isalnum((int) str[pair.first.size()]) &&
str[pair.first.size()] != '.' &&
pair.first.size() > match_len)
if(m_aliases.find(info.m_name) == m_aliases.end())
{
m_jptr = pair.second.m_jptr;
m_field = pair.first;
m_format = pair.second.m_format;
match_len = pair.first.size();
throw falco_exception("Could not find alias for field name " + info.m_name);
}
auto &al = m_aliases[info.m_name];
// What follows the match must not be alphanumeric or a dot
if(strncmp(info.m_name.c_str(), str, info.m_name.size()) == 0 &&
!isalnum((int) str[info.m_name.size()]) &&
str[info.m_name.size()] != '.' &&
info.m_name.size() > match_len)
{
m_jptr = al.m_jptr;
m_field = info.m_name;
m_format = al.m_format;
match_len = info.m_name.size();
const char *start = str + m_field.size();
@ -147,18 +175,18 @@ int32_t json_event_filter_check::parse_field_name(const char *str, bool alloc_st
idx_len = (end - start + 2);
}
if(m_idx.empty() && pair.second.m_idx_mode == alias::IDX_REQUIRED)
if(m_idx.empty() && info.m_idx_mode == IDX_REQUIRED)
{
throw falco_exception(string("When parsing filtercheck ") + string(str) + string(": ") + m_field + string(" requires an index but none provided"));
}
if(!m_idx.empty() && pair.second.m_idx_mode == alias::IDX_NONE)
if(!m_idx.empty() && info.m_idx_mode == IDX_NONE)
{
throw falco_exception(string("When parsing filtercheck ") + string(str) + string(": ") + m_field + string(" forbids an index but one provided"));
}
if(!m_idx.empty() &&
pair.second.m_idx_type == alias::IDX_NUMERIC &&
info.m_idx_type == IDX_NUMERIC &&
m_idx.find_first_not_of("0123456789") != string::npos)
{
throw falco_exception(string("When parsing filtercheck ") + string(str) + string(": ") + m_field + string(" requires a numeric index"));
@ -291,7 +319,7 @@ jevt_filter_check::jevt_filter_check()
{s_jevt_time_field, "json event timestamp as a string that includes the nanosecond part"},
{s_jevt_time_iso_8601_field, "json event timestamp in ISO 8601 format, including nanoseconds and time zone offset (in UTC)"},
{s_jevt_rawtime_field, "absolute event timestamp, i.e. nanoseconds from epoch."},
{s_jevt_value_field, "General way to access single property from json object. The syntax is [<json pointer expression>]. The property is returned as a string"},
{s_jevt_value_field, "General way to access single property from json object. The syntax is [<json pointer expression>]. The property is returned as a string", IDX_REQUIRED, IDX_KEY},
{s_jevt_obj_field, "The entire json object, stringified"}
}};
}
@ -603,36 +631,34 @@ k8s_audit_filter_check::k8s_audit_filter_check()
{"ka.impuser.name", "The impersonated user name"},
{"ka.verb", "The action being performed"},
{"ka.uri", "The request URI as sent from client to server"},
{"ka.uri.param", "The value of a given query parameter in the uri (e.g. when uri=/foo?key=val, ka.uri.param[key] is val)."},
{"ka.uri.param", "The value of a given query parameter in the uri (e.g. when uri=/foo?key=val, ka.uri.param[key] is val).", IDX_REQUIRED, IDX_KEY},
{"ka.target.name", "The target object name"},
{"ka.target.namespace", "The target object namespace"},
{"ka.target.resource", "The target object resource"},
{"ka.target.subresource", "The target object subresource"},
{"ka.req.binding.subjects", "When the request object refers to a cluster role binding, the subject (e.g. account/users) being linked by the binding"},
{"ka.req.binding.subject.has_name", "When the request object refers to a cluster role binding, return true if a subject with the provided name exists"},
{"ka.req.binding.subject.has_name", "When the request object refers to a cluster role binding, return true if a subject with the provided name exists", IDX_REQUIRED, IDX_KEY},
{"ka.req.binding.role", "When the request object refers to a cluster role binding, the role being linked by the binding"},
{"ka.req.configmap.name", "If the request object refers to a configmap, the configmap name"},
{"ka.req.configmap.obj", "If the request object refers to a configmap, the entire configmap object"},
{"ka.req.container.image", "When the request object refers to a container, the container's images. Can be indexed (e.g. ka.req.container.image[0]). Without any index, returns the first image"},
{"ka.req.container.image.repository", "The same as req.container.image, but only the repository part (e.g. sysdig/falco)"},
{"ka.req.container.image", "When the request object refers to a container, the container's images. Can be indexed (e.g. ka.req.container.image[0]). Without any index, returns the first image", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.container.image.repository", "The same as req.container.image, but only the repository part (e.g. sysdig/falco)", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.container.host_network", "When the request object refers to a container, the value of the hostNetwork flag."},
{"ka.req.container.privileged", "When the request object refers to a container, whether or not any container is run privileged. With an index, return whether or not the ith container is run privileged."},
{"ka.req.container.privileged", "When the request object refers to a container, whether or not any container is run privileged. With an index, return whether or not the ith container is run privileged.", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules", "When the request object refers to a role/cluster role, the rules associated with the role"},
{"ka.req.role.rules.apiGroups", "When the request object refers to a role/cluster role, the api groups associated with the role's rules. With an index, return only the api groups from the ith rule. Without an index, return all api groups concatenated"},
{"ka.req.role.rules.nonResourceURLs", "When the request object refers to a role/cluster role, the non resource urls associated with the role's rules. With an index, return only the non resource urls from the ith rule. Without an index, return all non resource urls concatenated"},
{"ka.req.role.rules.verbs", "When the request object refers to a role/cluster role, the verbs associated with the role's rules. With an index, return only the verbs from the ith rule. Without an index, return all verbs concatenated"},
{"ka.req.role.rules.resources", "When the request object refers to a role/cluster role, the resources associated with the role's rules. With an index, return only the resources from the ith rule. Without an index, return all resources concatenated"},
{"ka.req.role.rules.apiGroups", "When the request object refers to a role/cluster role, the api groups associated with the role's rules. With an index, return only the api groups from the ith rule. Without an index, return all api groups concatenated", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules.nonResourceURLs", "When the request object refers to a role/cluster role, the non resource urls associated with the role's rules. With an index, return only the non resource urls from the ith rule. Without an index, return all non resource urls concatenated", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules.verbs", "When the request object refers to a role/cluster role, the verbs associated with the role's rules. With an index, return only the verbs from the ith rule. Without an index, return all verbs concatenated", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.role.rules.resources", "When the request object refers to a role/cluster role, the resources associated with the role's rules. With an index, return only the resources from the ith rule. Without an index, return all resources concatenated", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.service.type", "When the request object refers to a service, the service type"},
{"ka.req.service.ports", "When the request object refers to a service, the service's ports. Can be indexed (e.g. ka.req.service.ports[0]). Without any index, returns all ports"},
{"ka.req.volume.hostpath", "If the request object contains volume definitions, whether or not a hostPath volume exists that mounts the specified path from the host (...hostpath[/etc]=true if a volume mounts /etc from the host). The index can be a glob, in which case all volumes are considered to find any path matching the specified glob (...hostpath[/usr/*] would match either /usr/local or /usr/bin)"},
{"ka.req.service.ports", "When the request object refers to a service, the service's ports. Can be indexed (e.g. ka.req.service.ports[0]). Without any index, returns all ports", IDX_ALLOWED, IDX_NUMERIC},
{"ka.req.volume.hostpath", "If the request object contains volume definitions, whether or not a hostPath volume exists that mounts the specified path from the host (...hostpath[/etc]=true if a volume mounts /etc from the host). The index can be a glob, in which case all volumes are considered to find any path matching the specified glob (...hostpath[/usr/*] would match either /usr/local or /usr/bin)", IDX_REQUIRED, IDX_KEY},
{"ka.resp.name", "The response object name"},
{"ka.response.code", "The response code"},
{"ka.response.reason", "The response reason (usually present only for failures)"}
}};
{
using a = alias;
m_aliases = {
{"ka.auditid", {"/auditID"_json_pointer}},
{"ka.stage", {"/stage"_json_pointer}},
@ -643,28 +669,28 @@ k8s_audit_filter_check::k8s_audit_filter_check()
{"ka.impuser.name", {"/impersonatedUser/username"_json_pointer}},
{"ka.verb", {"/verb"_json_pointer}},
{"ka.uri", {"/requestURI"_json_pointer}},
{"ka.uri.param", {"/requestURI"_json_pointer, index_query_param, a::IDX_REQUIRED, a::IDX_KEY}},
{"ka.uri.param", {"/requestURI"_json_pointer, index_query_param}},
{"ka.target.name", {"/objectRef/name"_json_pointer}},
{"ka.target.namespace", {"/objectRef/namespace"_json_pointer}},
{"ka.target.resource", {"/objectRef/resource"_json_pointer}},
{"ka.target.subresource", {"/objectRef/subresource"_json_pointer}},
{"ka.req.binding.subjects", {"/requestObject/subjects"_json_pointer}},
{"ka.req.binding.subject.has_name", {"/requestObject/subjects"_json_pointer, index_has_name, a::IDX_REQUIRED, a::IDX_KEY}},
{"ka.req.binding.subject.has_name", {"/requestObject/subjects"_json_pointer, index_has_name}},
{"ka.req.binding.role", {"/requestObject/roleRef/name"_json_pointer}},
{"ka.req.configmap.name", {"/objectRef/name"_json_pointer}},
{"ka.req.configmap.obj", {"/requestObject/data"_json_pointer}},
{"ka.req.container.image", {"/requestObject/spec/containers"_json_pointer, index_image, a::IDX_ALLOWED, a::IDX_NUMERIC}},
{"ka.req.container.image.repository", {"/requestObject/spec/containers"_json_pointer, index_image, a::IDX_ALLOWED, a::IDX_NUMERIC}},
{"ka.req.container.image", {"/requestObject/spec/containers"_json_pointer, index_image}},
{"ka.req.container.image.repository", {"/requestObject/spec/containers"_json_pointer, index_image}},
{"ka.req.container.host_network", {"/requestObject/spec/hostNetwork"_json_pointer}},
{"ka.req.container.privileged", {"/requestObject/spec/containers"_json_pointer, index_privileged, a::IDX_ALLOWED, a::IDX_NUMERIC}},
{"ka.req.container.privileged", {"/requestObject/spec/containers"_json_pointer, index_privileged}},
{"ka.req.role.rules", {"/requestObject/rules"_json_pointer}},
{"ka.req.role.rules.apiGroups", {"/requestObject/rules"_json_pointer, index_select, a::IDX_ALLOWED, a::IDX_NUMERIC}},
{"ka.req.role.rules.nonResourceURLs", {"/requestObject/rules"_json_pointer, index_select, a::IDX_ALLOWED, a::IDX_NUMERIC}},
{"ka.req.role.rules.resources", {"/requestObject/rules"_json_pointer, index_select, a::IDX_ALLOWED, a::IDX_NUMERIC}},
{"ka.req.role.rules.verbs", {"/requestObject/rules"_json_pointer, index_select, a::IDX_ALLOWED, a::IDX_NUMERIC}},
{"ka.req.role.rules.apiGroups", {"/requestObject/rules"_json_pointer, index_select}},
{"ka.req.role.rules.nonResourceURLs", {"/requestObject/rules"_json_pointer, index_select}},
{"ka.req.role.rules.resources", {"/requestObject/rules"_json_pointer, index_select}},
{"ka.req.role.rules.verbs", {"/requestObject/rules"_json_pointer, index_select}},
{"ka.req.service.type", {"/requestObject/spec/type"_json_pointer}},
{"ka.req.service.ports", {"/requestObject/spec/ports"_json_pointer, index_generic, a::IDX_ALLOWED, a::IDX_NUMERIC}},
{"ka.req.volume.hostpath", {"/requestObject/spec/volumes"_json_pointer, check_hostpath_vols, a::IDX_REQUIRED, a::IDX_KEY}},
{"ka.req.service.ports", {"/requestObject/spec/ports"_json_pointer, index_generic}},
{"ka.req.volume.hostpath", {"/requestObject/spec/volumes"_json_pointer, check_hostpath_vols}},
{"ka.resp.name", {"/responseObject/metadata/name"_json_pointer}},
{"ka.response.code", {"/responseStatus/code"_json_pointer}},
{"ka.response.reason", {"/responseStatus/reason"_json_pointer}}

View File

@ -62,19 +62,40 @@ protected:
class json_event_filter_check : public gen_event_filter_check
{
public:
enum index_mode {
IDX_REQUIRED,
IDX_ALLOWED,
IDX_NONE
};
enum index_type {
IDX_KEY,
IDX_NUMERIC
};
// A struct describing a single filtercheck field ("ka.user")
struct field_info {
std::string name;
std::string desc;
std::string m_name;
std::string m_desc;
index_mode m_idx_mode;
index_type m_idx_type;
// The variants allow for brace-initialization either
// with just the name/desc or additionally with index
// information
field_info();
field_info(std::string name, std::string desc);
field_info(std::string name, std::string desc, index_mode mode);
field_info(std::string name, std::string desc, index_mode mode, index_type itype);
virtual ~field_info();
};
// A struct describing a group of filtercheck fields ("ka")
struct check_info {
std::string name;
std::string desc;
std::string m_name;
std::string m_desc;
std::list<field_info> fields;
std::list<field_info> m_fields;
};
json_event_filter_check();
@ -115,28 +136,12 @@ protected:
typedef std::function<std::string (const nlohmann::json &, std::string &field, std::string &idx)> format_t;
struct alias {
// Whether this alias requires an index, allows an
// index, or should not have an index.
enum index_mode {
IDX_REQUIRED,
IDX_ALLOWED,
IDX_NONE
};
enum index_type {
IDX_KEY,
IDX_NUMERIC
};
// The variants allow for brace-initialization either
// with just the pointer or with both the pointer and
// a format function.
alias();
alias(nlohmann::json::json_pointer ptr);
alias(nlohmann::json::json_pointer ptr, format_t format);
alias(nlohmann::json::json_pointer ptr, format_t format, index_mode mode);
alias(nlohmann::json::json_pointer ptr, format_t format, index_mode mode, index_type itype);
virtual ~alias();
// A json pointer used to extract a referenced value
@ -149,10 +154,6 @@ protected:
// indexing, searches, etc.) or string reformatting to
// trim unnecessary parts of the value.
format_t m_format;
index_mode m_idx_mode;
index_type m_idx_type;
};
// This map defines the aliases defined by this filter check