Add more validations (#329)

* Add the ability to validate multiple rules files

Allow multiple -V arguments just as we do with multiple -r arguments.

* With verbose output, print dangling macros/lists

Start tracking whether or not a given macro/list is actually used when
compiling the set of rules. Every macro/list has an attribute used,
which defaults to false and is set to true whenever it is referred to in
a macro/rule/list.

When run with -v, any macro/list that still has used=false results in a
warning message.

Also, it turns out the fix for
https://github.com/draios/falco/issues/197 wasn't being applied to
macros. Fix that.
This commit is contained in:
Mark Stemm
2018-02-26 16:59:18 -05:00
committed by GitHub
parent 947faca334
commit 38eb5b8741
3 changed files with 61 additions and 24 deletions

View File

@@ -76,7 +76,8 @@ function expand_macros(ast, defs, changed)
if (defs[ast.value.value] == nil) then
error("Undefined macro '".. ast.value.value .. "' used in filter.")
end
ast.value = copy_ast_obj(defs[ast.value.value])
defs[ast.value.value].used = true
ast.value = copy_ast_obj(defs[ast.value.value].ast)
changed = true
return changed
end
@@ -88,7 +89,8 @@ function expand_macros(ast, defs, changed)
if (defs[ast.left.value] == nil) then
error("Undefined macro '".. ast.left.value .. "' used in filter.")
end
ast.left = copy_ast_obj(defs[ast.left.value])
defs[ast.left.value].used = true
ast.left = copy_ast_obj(defs[ast.left.value].ast)
changed = true
end
@@ -96,7 +98,8 @@ function expand_macros(ast, defs, changed)
if (defs[ast.right.value] == nil) then
error("Undefined macro ".. ast.right.value .. " used in filter.")
end
ast.right = copy_ast_obj(defs[ast.right.value])
defs[ast.right.value].used = true
ast.right = copy_ast_obj(defs[ast.right.value].ast)
changed = true
end
@@ -109,7 +112,8 @@ function expand_macros(ast, defs, changed)
if (defs[ast.argument.value] == nil) then
error("Undefined macro ".. ast.argument.value .. " used in filter.")
end
ast.argument = copy_ast_obj(defs[ast.argument.value])
defs[ast.argument.value].used = true
ast.argument = copy_ast_obj(defs[ast.argument.value].ast)
changed = true
end
return expand_macros(ast.argument, defs, changed)
@@ -283,11 +287,28 @@ function get_evttypes(name, ast, source)
return evttypes
end
function compiler.expand_lists_in(source, list_defs)
for name, def in pairs(list_defs) do
local begin_name_pat = "^("..name..")([%s(),=])"
local mid_name_pat = "([%s(),=])("..name..")([%s(),=])"
local end_name_pat = "([%s(),=])("..name..")$"
source, subcount1 = string.gsub(source, begin_name_pat, table.concat(def.items, ", ").."%2")
source, subcount2 = string.gsub(source, mid_name_pat, "%1"..table.concat(def.items, ", ").."%3")
source, subcount3 = string.gsub(source, end_name_pat, "%1"..table.concat(def.items, ", "))
if (subcount1 + subcount2 + subcount3) > 0 then
def.used = true
end
end
return source
end
function compiler.compile_macro(line, macro_defs, list_defs)
for name, items in pairs(list_defs) do
line = string.gsub(line, name, table.concat(items, ", "))
end
line = compiler.expand_lists_in(line, list_defs)
local ast, error_msg = parser.parse_filter(line)
@@ -325,14 +346,7 @@ end
--]]
function compiler.compile_filter(name, source, macro_defs, list_defs)
for name, items in pairs(list_defs) do
local begin_name_pat = "^("..name..")([%s(),=])"
local mid_name_pat = "([%s(),=])("..name..")([%s(),=])"
local end_name_pat = "([%s(),=])("..name..")$"
source = string.gsub(source, begin_name_pat, table.concat(items, ", ").."%2")
source = string.gsub(source, mid_name_pat, "%1"..table.concat(items, ", ").."%3")
source = string.gsub(source, end_name_pat, "%1"..table.concat(items, ", "))
end
source = compiler.expand_lists_in(source, list_defs)
local ast, error_msg = parser.parse_filter(source)

View File

@@ -347,13 +347,13 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
if (state.lists[item] == nil) then
items[#items+1] = item
else
for i, exp_item in ipairs(state.lists[item]) do
for i, exp_item in ipairs(state.lists[item].items) do
items[#items+1] = exp_item
end
end
end
state.lists[v['list']] = items
state.lists[v['list']] = {["items"] = items, ["used"] = false}
end
for i, name in ipairs(state.ordered_macro_names) do
@@ -361,7 +361,7 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
local v = state.macros_by_name[name]
local ast = compiler.compile_macro(v['condition'], state.macros, state.lists)
state.macros[v['macro']] = ast.filter.value
state.macros[v['macro']] = {["ast"] = ast.filter.value, ["used"] = false}
end
for i, name in ipairs(state.ordered_rule_names) do
@@ -443,6 +443,21 @@ function load_rules(rules_content, rules_mgr, verbose, all_events, extra, replac
end
end
if verbose then
-- Print info on any dangling lists or macros that were not used anywhere
for name, macro in pairs(state.macros) do
if macro.used == false then
print("Warning: macro "..name.." not refered to by any rule/macro")
end
end
for name, list in pairs(state.lists) do
if list.used == false then
print("Warning: list "..name.." not refered to by any rule/macro/list")
end
end
end
io.flush()
end

View File

@@ -111,7 +111,8 @@ static void usage()
" single line emitted by falco to be flushed, which generates higher CPU\n"
" usage but is useful when piping those outputs into another process\n"
" or into a script.\n"
" -V,--validate <rules_file> Read the contents of the specified rules file and exit\n"
" -V,--validate <rules_file> Read the contents of the specified rules(s) file and exit\n"
" Can be specified multiple times to validate multiple files.\n"
" -v Verbose output.\n"
" --version Print version number.\n"
"\n"
@@ -245,7 +246,7 @@ int falco_init(int argc, char **argv)
string pidfilename = "/var/run/falco.pid";
bool describe_all_rules = false;
string describe_rule = "";
string validate_rules_file = "";
list<string> validate_rules_filenames;
string stats_filename = "";
bool verbose = false;
bool all_events = false;
@@ -396,7 +397,7 @@ int falco_init(int argc, char **argv)
verbose = true;
break;
case 'V':
validate_rules_file = optarg;
validate_rules_filenames.push_back(optarg);
break;
case 'w':
outfile = optarg;
@@ -460,10 +461,17 @@ int falco_init(int argc, char **argv)
}
}
if(validate_rules_file != "")
if(validate_rules_filenames.size() > 0)
{
falco_logger::log(LOG_INFO, "Validating rules file: " + validate_rules_file + "...\n");
engine->load_rules_file(validate_rules_file, verbose, all_events);
falco_logger::log(LOG_INFO, "Validating rules file(s):\n");
for(auto file : validate_rules_filenames)
{
falco_logger::log(LOG_INFO, " " + file + "\n");
}
for(auto file : validate_rules_filenames)
{
engine->load_rules_file(file, verbose, all_events);
}
falco_logger::log(LOG_INFO, "Ok\n");
goto exit;
}