mirror of
https://github.com/falcosecurity/falco.git
synced 2026-03-18 10:44:27 +00:00
Get all tests working
Now the context doesn't have a column pointer but it either a line for yaml parse errors or an individual object (rule, macro, etc) from the parsed yaml.
This commit is contained in:
@@ -242,8 +242,9 @@ trace_files: !mux
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
Rules content is not yaml
|
||||
---
|
||||
This is not yaml
|
||||
^
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/invalid_not_yaml.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -252,8 +253,9 @@ trace_files: !mux
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
Rules content is not yaml array of objects
|
||||
---
|
||||
foo: bar
|
||||
^
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/invalid_not_array.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -262,8 +264,9 @@ trace_files: !mux
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
Unexpected element of type string. Each element should be a yaml associative array.
|
||||
---
|
||||
- foo
|
||||
^
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/invalid_array_item_not_object.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -272,8 +275,9 @@ trace_files: !mux
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
Unknown rule object: {foo="bar"}
|
||||
---
|
||||
- foo: bar
|
||||
^
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/invalid_unexpected_object.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -282,8 +286,9 @@ trace_files: !mux
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
Value of required_engine_version must be a number
|
||||
---
|
||||
- required_engine_version: not-a-number
|
||||
^
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/invalid_engine_version_not_number.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -292,8 +297,9 @@ trace_files: !mux
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
mapping values are not allowed in this context
|
||||
---
|
||||
this : is : not : yaml
|
||||
^
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/invalid_yaml_parse_error.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -302,8 +308,10 @@ trace_files: !mux
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
List must have property items
|
||||
---
|
||||
- list: bad_list
|
||||
^
|
||||
no_items: foo
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/invalid_list_without_items.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -312,8 +320,10 @@ trace_files: !mux
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
Macro must have property condition
|
||||
---
|
||||
- macro: bad_macro
|
||||
^
|
||||
nope: 1
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/invalid_macro_without_condition.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -322,8 +332,12 @@ trace_files: !mux
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
Rule must have property output
|
||||
---
|
||||
- rule: no output rule
|
||||
^
|
||||
desc: some desc
|
||||
condition: evt.type=fork
|
||||
priority: INFO
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/invalid_rule_without_output.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -332,8 +346,10 @@ trace_files: !mux
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
Rule must have property condition
|
||||
---
|
||||
- rule: no condition rule
|
||||
^
|
||||
append: true
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/invalid_append_rule_without_condition.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -342,8 +358,11 @@ trace_files: !mux
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
Macro dangling append has 'append' key but no macro by that name already exists
|
||||
---
|
||||
- macro: dangling append
|
||||
^
|
||||
condition: and evt.type=execve
|
||||
append: true
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/invalid_append_macro_dangling.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -352,8 +371,11 @@ trace_files: !mux
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
List my_list has 'append' key but no list by that name already exists
|
||||
---
|
||||
- list: my_list
|
||||
^
|
||||
items: [not-cat]
|
||||
append: true
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/list_append_failure.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -362,8 +384,11 @@ trace_files: !mux
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
Rule my_rule has 'append' key but no rule by that name already exists
|
||||
---
|
||||
- rule: my_rule
|
||||
^
|
||||
condition: evt.type=open
|
||||
append: true
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/rule_append_failure.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -372,8 +397,12 @@ trace_files: !mux
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
Rule name is empty
|
||||
---
|
||||
- rule:
|
||||
^
|
||||
desc: some desc
|
||||
condition: evt.type=execve
|
||||
output: some output
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/invalid_missing_rule_name.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -382,8 +411,10 @@ trace_files: !mux
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
List name is empty
|
||||
---
|
||||
- list:
|
||||
^
|
||||
items: [foo]
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/invalid_missing_list_name.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -392,8 +423,10 @@ trace_files: !mux
|
||||
exit_status: 1
|
||||
stdout_is: |+
|
||||
Macro name is empty
|
||||
---
|
||||
- macro:
|
||||
^
|
||||
condition: evt.type=execve
|
||||
---
|
||||
validate_rules_file:
|
||||
- rules/invalid_missing_macro_name.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
@@ -179,29 +179,70 @@ function table.tostring( tbl )
|
||||
return "{" .. table.concat( result, "," ) .. "}"
|
||||
end
|
||||
|
||||
function find_indices(rules_content)
|
||||
-- To provide context when iterating over the objects in the list,
|
||||
-- record the line numbers of all top-level objects in the
|
||||
-- file. This doesn't actually use a yaml parser but simply looks
|
||||
-- for lines starting with '-'.
|
||||
-- Split rules_content by lines and also remember the line numbers for
|
||||
-- each top -level object. Returns a table of lines and a table of
|
||||
-- line numbers for objects.
|
||||
|
||||
function split_lines(rules_content)
|
||||
lines = {}
|
||||
indices = {}
|
||||
|
||||
line = 1
|
||||
if string.sub(rules_content, 1, 1) == '-' then
|
||||
indices[#indices+1] = line
|
||||
end
|
||||
|
||||
idx = 1
|
||||
last_pos = 1
|
||||
pos = string.find(rules_content, "\n", 1, true)
|
||||
|
||||
while pos ~= nil do
|
||||
line = line + 1
|
||||
if string.sub(rules_content, pos+1, pos+1) == '-' then
|
||||
indices[#indices+1] = line
|
||||
line = string.sub(rules_content, last_pos, pos-1)
|
||||
if line ~= "" then
|
||||
lines[#lines+1] = line
|
||||
if string.sub(line, 1, 1) == '-' then
|
||||
indices[#indices+1] = idx
|
||||
end
|
||||
|
||||
idx = idx + 1
|
||||
end
|
||||
|
||||
last_pos = pos+1
|
||||
pos = string.find(rules_content, "\n", pos+1, true)
|
||||
end
|
||||
|
||||
return indices
|
||||
if last_pos < string.len(rules_content) then
|
||||
line = string.sub(rules_content, last_pos)
|
||||
lines[#lines+1] = line
|
||||
if string.sub(line, 1, 1) == '-' then
|
||||
indices[#indices+1] = idx
|
||||
end
|
||||
|
||||
idx = idx + 1
|
||||
end
|
||||
|
||||
-- Add a final index for last line in document
|
||||
indices[#indices+1] = idx
|
||||
|
||||
return lines, indices
|
||||
end
|
||||
|
||||
function get_context(rules_lines, row, num_lines)
|
||||
|
||||
local ret = "---\n"
|
||||
|
||||
idx = row
|
||||
while (idx < (row + num_lines) and idx <= #rules_lines) do
|
||||
ret = ret..rules_lines[idx].."\n"
|
||||
idx = idx + 1
|
||||
end
|
||||
|
||||
ret = ret.."---"
|
||||
|
||||
return ret
|
||||
|
||||
end
|
||||
|
||||
function build_error(rules_lines, row, num_lines, err)
|
||||
|
||||
local ret = err.."\n"..get_context(rules_lines, row, num_lines)
|
||||
|
||||
return ret
|
||||
end
|
||||
|
||||
function load_rules(sinsp_lua_parser,
|
||||
@@ -216,6 +257,8 @@ function load_rules(sinsp_lua_parser,
|
||||
|
||||
local required_engine_version = 0
|
||||
|
||||
local lines, indices = split_lines(rules_content)
|
||||
|
||||
local status, rules = pcall(yaml.load, rules_content)
|
||||
|
||||
if status == false then
|
||||
@@ -230,7 +273,10 @@ function load_rules(sinsp_lua_parser,
|
||||
rules = string.gsub(rules, pat, "")
|
||||
end
|
||||
|
||||
return false, row, col, rules
|
||||
row = tonumber(row)
|
||||
col = tonumber(col)
|
||||
|
||||
return false, build_error(lines, row, 3, rules)
|
||||
end
|
||||
|
||||
if rules == nil then
|
||||
@@ -239,19 +285,17 @@ function load_rules(sinsp_lua_parser,
|
||||
end
|
||||
|
||||
if type(rules) ~= "table" then
|
||||
return false, 1, 1, "Rules content is not yaml"
|
||||
return false, build_error(lines, 1, 1, "Rules content is not yaml")
|
||||
end
|
||||
|
||||
-- Look for non-numeric indices--implies that document is not array
|
||||
-- of objects.
|
||||
for key, val in pairs(rules) do
|
||||
if type(key) ~= "number" then
|
||||
return false, 1, 1, "Rules content is not yaml array of objects"
|
||||
return false, build_error(lines, 1, 1, "Rules content is not yaml array of objects")
|
||||
end
|
||||
end
|
||||
|
||||
indices = find_indices(rules_content)
|
||||
|
||||
-- Iterate over yaml list. In this pass, all we're doing is
|
||||
-- populating the set of rules, macros, and lists. We're not
|
||||
-- expanding/compiling anything yet. All that will happen in a
|
||||
@@ -259,23 +303,23 @@ function load_rules(sinsp_lua_parser,
|
||||
for i,v in ipairs(rules) do
|
||||
|
||||
if (not (type(v) == "table")) then
|
||||
return false, indices[i], 1, "Unexpected element of type " ..type(v)..". Each element should be a yaml associative array."
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Unexpected element of type " ..type(v)..". Each element should be a yaml associative array.")
|
||||
end
|
||||
|
||||
if (v['required_engine_version']) then
|
||||
required_engine_version = v['required_engine_version']
|
||||
if type(required_engine_version) ~= "number" then
|
||||
return false, indices[i], 1, "Value of required_engine_version must be a number"
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Value of required_engine_version must be a number")
|
||||
end
|
||||
|
||||
if falco_rules.engine_version(rules_mgr) < v['required_engine_version'] then
|
||||
return false, indices[i], 1, "Rules require engine version "..v['required_engine_version']..", but engine version is "..falco_rules.engine_version(rules_mgr)
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Rules require engine version "..v['required_engine_version']..", but engine version is "..falco_rules.engine_version(rules_mgr))
|
||||
end
|
||||
|
||||
elseif (v['macro']) then
|
||||
|
||||
if (v['macro'] == nil or type(v['macro']) == "table") then
|
||||
return false, indices[i], 3, "Macro name is empty"
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Macro name is empty")
|
||||
end
|
||||
|
||||
if v['source'] == nil then
|
||||
@@ -288,7 +332,7 @@ function load_rules(sinsp_lua_parser,
|
||||
|
||||
for j, field in ipairs({'condition'}) do
|
||||
if (v[field] == nil) then
|
||||
return false, indices[i], 3, "Macro must have property "..field
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Macro must have property "..field)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -301,7 +345,7 @@ function load_rules(sinsp_lua_parser,
|
||||
|
||||
if append then
|
||||
if state.macros_by_name[v['macro']] == nil then
|
||||
return false, indices[i], 1, "Macro " ..v['macro'].. " has 'append' key but no macro by that name already exists"
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Macro " ..v['macro'].. " has 'append' key but no macro by that name already exists")
|
||||
end
|
||||
|
||||
state.macros_by_name[v['macro']]['condition'] = state.macros_by_name[v['macro']]['condition'] .. " " .. v['condition']
|
||||
@@ -313,7 +357,7 @@ function load_rules(sinsp_lua_parser,
|
||||
elseif (v['list']) then
|
||||
|
||||
if (v['list'] == nil or type(v['list']) == "table") then
|
||||
return false, indices[i], 3, "List name is empty"
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "List name is empty")
|
||||
end
|
||||
|
||||
if state.lists_by_name[v['list']] == nil then
|
||||
@@ -322,7 +366,7 @@ function load_rules(sinsp_lua_parser,
|
||||
|
||||
for j, field in ipairs({'items'}) do
|
||||
if (v[field] == nil) then
|
||||
return false, indices[i], 3, "List must have property "..field
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "List must have property "..field)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -335,7 +379,7 @@ function load_rules(sinsp_lua_parser,
|
||||
|
||||
if append then
|
||||
if state.lists_by_name[v['list']] == nil then
|
||||
return false, indices[i], 1, "List " ..v['list'].. " has 'append' key but no list by that name already exists"
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "List " ..v['list'].. " has 'append' key but no list by that name already exists")
|
||||
end
|
||||
|
||||
for j, elem in ipairs(v['items']) do
|
||||
@@ -348,7 +392,7 @@ function load_rules(sinsp_lua_parser,
|
||||
elseif (v['rule']) then
|
||||
|
||||
if (v['rule'] == nil or type(v['rule']) == "table") then
|
||||
return false, indices[i], 3, "Rule name is empty"
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Rule name is empty")
|
||||
end
|
||||
|
||||
-- By default, if a rule's condition refers to an unknown
|
||||
@@ -373,13 +417,13 @@ function load_rules(sinsp_lua_parser,
|
||||
-- For append rules, all you need is the condition
|
||||
for j, field in ipairs({'condition'}) do
|
||||
if (v[field] == nil) then
|
||||
return false, indices[i], 3, "Rule must have property "..field
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Rule must have property "..field)
|
||||
end
|
||||
end
|
||||
|
||||
if state.rules_by_name[v['rule']] == nil then
|
||||
if state.skipped_rules_by_name[v['rule']] == nil then
|
||||
return false, indices[i], 1, "Rule " ..v['rule'].. " has 'append' key but no rule by that name already exists"
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Rule " ..v['rule'].. " has 'append' key but no rule by that name already exists")
|
||||
end
|
||||
else
|
||||
state.rules_by_name[v['rule']]['condition'] = state.rules_by_name[v['rule']]['condition'] .. " " .. v['condition']
|
||||
@@ -389,7 +433,7 @@ function load_rules(sinsp_lua_parser,
|
||||
|
||||
for j, field in ipairs({'condition', 'output', 'desc', 'priority'}) do
|
||||
if (v[field] == nil) then
|
||||
return false, indices[i], 3, "Rule must have property "..field
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Rule must have property "..field)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -418,7 +462,7 @@ function load_rules(sinsp_lua_parser,
|
||||
end
|
||||
end
|
||||
else
|
||||
return false, indices[i], 1, "Unknown rule object: "..table.tostring(v)
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Unknown rule object: "..table.tostring(v))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -458,7 +502,7 @@ function load_rules(sinsp_lua_parser,
|
||||
local status, ast = compiler.compile_macro(v['condition'], state.macros, state.lists)
|
||||
|
||||
if status == false then
|
||||
return false, indices[i], 1, ast
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), ast)
|
||||
end
|
||||
|
||||
if v['source'] == "syscall" then
|
||||
@@ -483,7 +527,7 @@ function load_rules(sinsp_lua_parser,
|
||||
state.macros, state.lists)
|
||||
|
||||
if status == false then
|
||||
return false, indices[i], 1, filter_ast
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), filter_ast)
|
||||
end
|
||||
|
||||
local evtttypes = {}
|
||||
@@ -621,7 +665,7 @@ function load_rules(sinsp_lua_parser,
|
||||
formatter = formats.formatter(v['source'], v['output'])
|
||||
formats.free_formatter(v['source'], formatter)
|
||||
else
|
||||
return false, indices[i], 1, "Unexpected type in load_rule: "..filter_ast.type
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Unexpected type in load_rule: "..filter_ast.type)
|
||||
end
|
||||
|
||||
::next_rule::
|
||||
|
||||
@@ -207,31 +207,6 @@ void falco_rules::enable_rule(string &rule, bool enabled)
|
||||
m_engine->enable_rule(rule, enabled);
|
||||
}
|
||||
|
||||
std::string falco_rules::get_context(const std::string &content, uint64_t lineno, uint64_t colno)
|
||||
{
|
||||
istringstream ctx(content);
|
||||
std::string line;
|
||||
std::string ret;
|
||||
|
||||
for(uint32_t i=1; ctx && i<=lineno; i++)
|
||||
{
|
||||
getline(ctx, line);
|
||||
if(i == lineno)
|
||||
{
|
||||
ret += line + "\n";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for(uint32_t j=1; j<colno; j++)
|
||||
{
|
||||
ret += " ";
|
||||
}
|
||||
ret += "^";
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int falco_rules::engine_version(lua_State *ls)
|
||||
{
|
||||
if (! lua_islightuserdata(ls, -1))
|
||||
@@ -452,7 +427,7 @@ void falco_rules::load_rules(const string &rules_content,
|
||||
lua_pushstring(m_ls, extra.c_str());
|
||||
lua_pushboolean(m_ls, (replace_container_info ? 1 : 0));
|
||||
lua_pushnumber(m_ls, min_priority);
|
||||
if(lua_pcall(m_ls, 9, 4, 0) != 0)
|
||||
if(lua_pcall(m_ls, 9, 2, 0) != 0)
|
||||
{
|
||||
const char* lerr = lua_tostring(m_ls, -1);
|
||||
|
||||
@@ -461,27 +436,16 @@ void falco_rules::load_rules(const string &rules_content,
|
||||
throw falco_exception(err);
|
||||
}
|
||||
|
||||
// Either returns (true, required_engine_version), or (false, row, col, error string)
|
||||
bool successful = lua_toboolean(m_ls, -4);
|
||||
// Either returns (true, required_engine_version), or (false, error string)
|
||||
bool successful = lua_toboolean(m_ls, -2);
|
||||
|
||||
if(successful)
|
||||
{
|
||||
required_engine_version = lua_tonumber(m_ls, -3);
|
||||
required_engine_version = lua_tonumber(m_ls, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
int line = lua_tonumber(m_ls, -3);
|
||||
int col = lua_tonumber(m_ls, -2);
|
||||
std::string err = lua_tostring(m_ls, -1);
|
||||
|
||||
// If the line/columns are >= 0, use them to
|
||||
// extract meaninful context from the result.
|
||||
if(line >= 1 && col >= 1)
|
||||
{
|
||||
err += "\n";
|
||||
err += get_context(rules_content, line, col);
|
||||
}
|
||||
|
||||
throw falco_exception(err);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user