Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conditional rules #364

Merged
merged 2 commits into from
May 3, 2018
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
27 changes: 27 additions & 0 deletions test/falco_tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -713,3 +713,30 @@ trace_files: !mux
- open_dev_null: 1
dev_null: 0
trace_file: trace_files/cat_write.scap

skip_unknown_noevt:
detect: False
stdout_contains: Skipping rule "Contains Unknown Event And Skipping" that contains unknown filter proc.nobody
rules_file:
- rules/skip_unknown_evt.yaml
trace_file: trace_files/cat_write.scap

skip_unknown_prefix:
detect: False
rules_file:
- rules/skip_unknown_prefix.yaml
trace_file: trace_files/cat_write.scap

skip_unknown_error:
exit_status: 1
stderr_contains: Rule "Contains Unknown Event And Not Skipping" contains unknown filter proc.nobody. Exiting.
rules_file:
- rules/skip_unknown_error.yaml
trace_file: trace_files/cat_write.scap

skip_unknown_unspec_error:
exit_status: 1
stderr_contains: Rule "Contains Unknown Event And Unspecified" contains unknown filter proc.nobody. Exiting.
rules_file:
- rules/skip_unknown_unspec.yaml
trace_file: trace_files/cat_write.scap
6 changes: 6 additions & 0 deletions test/rules/skip_unknown_error.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- rule: Contains Unknown Event And Not Skipping
desc: Contains an unknown event
condition: proc.nobody=cat
output: Never
skip-if-unknown-filter: false
priority: INFO
6 changes: 6 additions & 0 deletions test/rules/skip_unknown_evt.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
- rule: Contains Unknown Event And Skipping
desc: Contains an unknown event
condition: evt.type=open and proc.nobody=cat
output: Never
skip-if-unknown-filter: true
priority: INFO
8 changes: 8 additions & 0 deletions test/rules/skip_unknown_prefix.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
- rule: Contains Prefix of Filter
desc: Testing matching filter prefixes
condition: >
evt.type=open and evt.arg.path="foo" and evt.arg[0]="foo"
and proc.aname="ls" and proc.aname[1]="ls"
and proc.apid=10 and proc.apid[1]=10
output: Never
priority: INFO
5 changes: 5 additions & 0 deletions test/rules/skip_unknown_unspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
- rule: Contains Unknown Event And Unspecified
desc: Contains an unknown event
condition: proc.nobody=cat
output: Never
priority: INFO
19 changes: 18 additions & 1 deletion userspace/engine/lua/compiler.lua
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,21 @@ function get_evttypes_syscalls(name, ast, source, warn_evttypes)
return evttypes, syscallnums
end

function get_filters(ast)

local filters = {}

function cb(node)
if node.type == "FieldName" then
filters[node.value] = 1
end
end

parser.traverse_ast(ast.filter.value, {FieldName=1} , cb)

return filters
end

function compiler.expand_lists_in(source, list_defs)

for name, def in pairs(list_defs) do
Expand Down Expand Up @@ -408,7 +423,9 @@ function compiler.compile_filter(name, source, macro_defs, list_defs, warn_evtty

evttypes, syscallnums = get_evttypes_syscalls(name, ast, source, warn_evttypes)

return ast, evttypes, syscallnums
filters = get_filters(ast)

return ast, evttypes, syscallnums, filters
end


Expand Down
39 changes: 36 additions & 3 deletions userspace/engine/lua/rule_loader.lua
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,12 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
error ("Missing name in rule")
end

-- By default, if a rule's condition refers to an unknown
-- filter like evt.type, etc the loader throws an error.
if v['skip-if-unknown-filter'] == nil then
v['skip-if-unknown-filter'] = false
end

-- Possibly append to the condition field of an existing rule
append = false

Expand Down Expand Up @@ -378,9 +384,34 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
warn_evttypes = v['warn_evttypes']
end

local filter_ast, evttypes, syscallnums = compiler.compile_filter(v['rule'], v['condition'],
state.macros, state.lists,
warn_evttypes)
local filter_ast, evttypes, syscallnums, filters = compiler.compile_filter(v['rule'], v['condition'],
state.macros, state.lists,
warn_evttypes)

-- If a filter in the rule doesn't exist, either skip the rule
-- or raise an error, depending on the value of
-- skip-if-unknown-filter.
for filter, _ in pairs(filters) do
found = false

for pat, _ in pairs(defined_filters) do
if string.match(filter, pat) ~= nil then
found = true
break
end
end

if not found then
if v['skip-if-unknown-filter'] then
if verbose then
print("Skipping rule \""..v['rule'].."\" that contains unknown filter "..filter)
end
goto next_rule
else
error("Rule \""..v['rule'].."\" contains unknown filter "..filter)
end
end
end

if (filter_ast.type == "Rule") then
state.n_rules = state.n_rules + 1
Expand Down Expand Up @@ -452,6 +483,8 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
else
error ("Unexpected type in load_rule: "..filter_ast.type)
end

::next_rule::
end

if verbose then
Expand Down
57 changes: 57 additions & 0 deletions userspace/engine/rules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,63 @@ void falco_rules::load_rules(const string &rules_content,

lua_setglobal(m_ls, m_lua_ignored_syscalls.c_str());

// Create a table containing all filtercheck names.
lua_newtable(m_ls);

vector<const filter_check_info*> fc_plugins;
sinsp::get_filtercheck_fields_info(&fc_plugins);

for(uint32_t j = 0; j < fc_plugins.size(); j++)
{
const filter_check_info* fci = fc_plugins[j];

if(fci->m_flags & filter_check_info::FL_HIDDEN)
{
continue;
}

for(int32_t k = 0; k < fci->m_nfields; k++)
{
const filtercheck_field_info* fld = &fci->m_fields[k];

if(fld->m_flags & EPF_TABLE_ONLY ||
fld->m_flags & EPF_PRINT_ONLY)
{
continue;
}

// Some filters can work with or without an argument
std::set<string> flexible_filters = {
"^proc.aname",
"^proc.apid"
};

std::list<string> fields;
std::string field_base = string("^") + fld->m_name;

if(fld->m_flags & EPF_REQUIRES_ARGUMENT ||
flexible_filters.find(field_base) != flexible_filters.end())
{
fields.push_back(field_base + "[%[%.]");
}

if(!(fld->m_flags & EPF_REQUIRES_ARGUMENT) ||
flexible_filters.find(field_base) != flexible_filters.end())
{
fields.push_back(field_base + "$");
}

for(auto &field : fields)
{
lua_pushstring(m_ls, field.c_str());
lua_pushnumber(m_ls, 1);
lua_settable(m_ls, -3);
}
}
}

lua_setglobal(m_ls, m_lua_defined_filters.c_str());

lua_pushstring(m_ls, rules_content.c_str());
lua_pushlightuserdata(m_ls, this);
lua_pushboolean(m_ls, (verbose ? 1 : 0));
Expand Down
1 change: 1 addition & 0 deletions userspace/engine/rules.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class falco_rules
string m_lua_load_rules = "load_rules";
string m_lua_ignored_syscalls = "ignored_syscalls";
string m_lua_ignored_events = "ignored_events";
string m_lua_defined_filters = "defined_filters";
string m_lua_events = "events";
string m_lua_syscalls = "syscalls";
string m_lua_describe_rule = "describe_rule";
Expand Down