mirror of
https://github.com/falcosecurity/falco.git
synced 2026-03-20 11:42:06 +00:00
Compare commits
13 Commits
0.36.2
...
add-contex
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff75db9477 | ||
|
|
3b49a0a85f | ||
|
|
fc5858a3d6 | ||
|
|
23e28e32c2 | ||
|
|
2fef3f3dd5 | ||
|
|
4a0bb56586 | ||
|
|
b710217181 | ||
|
|
01b3a0aa95 | ||
|
|
eb7433f838 | ||
|
|
ffc9ac56d4 | ||
|
|
74e2833cd7 | ||
|
|
d1a6666742 | ||
|
|
4830f6991c |
@@ -41,6 +41,9 @@ class FalcoTest(Test):
|
||||
build_dir = os.path.join('/build', build_type)
|
||||
self.falcodir = self.params.get('falcodir', '/', default=os.path.join(self.basedir, build_dir))
|
||||
|
||||
self.stdout_is = self.params.get('stdout_is', '*', default='')
|
||||
self.stderr_is = self.params.get('stderr_is', '*', default='')
|
||||
|
||||
self.stdout_contains = self.params.get('stdout_contains', '*', default='')
|
||||
|
||||
if not isinstance(self.stdout_contains, list):
|
||||
@@ -83,8 +86,21 @@ class FalcoTest(Test):
|
||||
if not isinstance(self.rules_file, list):
|
||||
self.rules_file = [self.rules_file]
|
||||
|
||||
self.validate_rules_file = self.params.get('validate_rules_file', '*', default=False)
|
||||
|
||||
if self.validate_rules_file == False:
|
||||
self.validate_rules_file = []
|
||||
else:
|
||||
if not isinstance(self.validate_rules_file, list):
|
||||
self.validate_rules_file = [self.validate_rules_file]
|
||||
|
||||
self.rules_args = ""
|
||||
|
||||
for file in self.validate_rules_file:
|
||||
if not os.path.isabs(file):
|
||||
file = os.path.join(self.basedir, file)
|
||||
self.rules_args = self.rules_args + "-V " + file + " "
|
||||
|
||||
for file in self.rules_file:
|
||||
if not os.path.isabs(file):
|
||||
file = os.path.join(self.basedir, file)
|
||||
@@ -433,6 +449,15 @@ class FalcoTest(Test):
|
||||
|
||||
res = self.falco_proc.run(timeout=180, sig=9)
|
||||
|
||||
if self.stdout_is != '':
|
||||
print(self.stdout_is)
|
||||
if self.stdout_is != res.stdout:
|
||||
self.fail("Stdout was not exactly {}".format(self.stdout_is))
|
||||
|
||||
if self.stderr_is != '':
|
||||
if self.stderr_is != res.stdout:
|
||||
self.fail("Stdout was not exactly {}".format(self.stderr_is))
|
||||
|
||||
for pattern in self.stderr_contains:
|
||||
match = re.search(pattern, res.stderr)
|
||||
if match is None:
|
||||
|
||||
@@ -238,6 +238,199 @@ trace_files: !mux
|
||||
- rules/endswith.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
invalid_not_yaml:
|
||||
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
|
||||
|
||||
invalid_not_array:
|
||||
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
|
||||
|
||||
invalid_array_item_not_object:
|
||||
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
|
||||
|
||||
invalid_unexpected object:
|
||||
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
|
||||
|
||||
invalid_engine_version_not_number:
|
||||
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
|
||||
|
||||
invalid_yaml_parse_error:
|
||||
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
|
||||
|
||||
invalid_list_without_items:
|
||||
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
|
||||
|
||||
invalid_macro_without_condition:
|
||||
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
|
||||
|
||||
invalid_rule_without_output:
|
||||
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
|
||||
|
||||
invalid_append_rule_without_condition:
|
||||
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
|
||||
|
||||
invalid_append_macro_dangling:
|
||||
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
|
||||
|
||||
invalid_list_append_dangling:
|
||||
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
|
||||
|
||||
invalid_rule_append_dangling:
|
||||
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
|
||||
|
||||
invalid_missing_rule_name:
|
||||
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
|
||||
|
||||
invalid_missing_list_name:
|
||||
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
|
||||
|
||||
invalid_missing_macro_name:
|
||||
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
|
||||
|
||||
invalid_rule_output:
|
||||
exit_status: 1
|
||||
stderr_contains: "Runtime error: Error loading rules:.* Invalid output format 'An open was seen %not_a_real_field': 'invalid formatting token not_a_real_field'. Exiting."
|
||||
@@ -601,7 +794,7 @@ trace_files: !mux
|
||||
|
||||
list_append_failure:
|
||||
exit_status: 1
|
||||
stderr_contains: "List my_list has 'append' key but no list by that name already exists. Exiting"
|
||||
stderr_contains: "List my_list has 'append' key but no list by that name already exists"
|
||||
rules_file:
|
||||
- rules/list_append_failure.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -621,7 +814,7 @@ trace_files: !mux
|
||||
|
||||
macro_append_failure:
|
||||
exit_status: 1
|
||||
stderr_contains: "Macro my_macro has 'append' key but no macro by that name already exists. Exiting"
|
||||
stderr_contains: "Macro my_macro has 'append' key but no macro by that name already exists"
|
||||
rules_file:
|
||||
- rules/macro_append_failure.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
@@ -641,7 +834,7 @@ trace_files: !mux
|
||||
|
||||
rule_append_failure:
|
||||
exit_status: 1
|
||||
stderr_contains: "Rule my_rule has 'append' key but no rule by that name already exists. Exiting"
|
||||
stderr_contains: "Rule my_rule has 'append' key but no rule by that name already exists"
|
||||
rules_file:
|
||||
- rules/rule_append_failure.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
3
test/rules/invalid_append_macro_dangling.yaml
Normal file
3
test/rules/invalid_append_macro_dangling.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
- macro: dangling append
|
||||
condition: and evt.type=execve
|
||||
append: true
|
||||
2
test/rules/invalid_append_rule_without_condition.yaml
Normal file
2
test/rules/invalid_append_rule_without_condition.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
- rule: no condition rule
|
||||
append: true
|
||||
1
test/rules/invalid_array_item_not_object.yaml
Normal file
1
test/rules/invalid_array_item_not_object.yaml
Normal file
@@ -0,0 +1 @@
|
||||
- foo
|
||||
34
test/rules/invalid_engine_version_not_number.yaml
Normal file
34
test/rules/invalid_engine_version_not_number.yaml
Normal file
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# Copyright (C) 2016-2018 Draios Inc dba Sysdig.
|
||||
#
|
||||
# This file is part of falco.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
- required_engine_version: not-a-number
|
||||
|
||||
- list: cat_binaries
|
||||
items: [cat]
|
||||
|
||||
- list: cat_capable_binaries
|
||||
items: [cat_binaries]
|
||||
|
||||
- macro: is_cat
|
||||
condition: proc.name in (cat_capable_binaries)
|
||||
|
||||
- rule: open_from_cat
|
||||
desc: A process named cat does an open
|
||||
condition: evt.type=open and is_cat
|
||||
output: "An open was seen (command=%proc.cmdline)"
|
||||
priority: WARNING
|
||||
5
test/rules/invalid_list_without_items.yaml
Normal file
5
test/rules/invalid_list_without_items.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
- list: good_list
|
||||
items: [foo]
|
||||
|
||||
- list: bad_list
|
||||
no_items: foo
|
||||
6
test/rules/invalid_macro_without_condition.yaml
Normal file
6
test/rules/invalid_macro_without_condition.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
- macro: bad_macro
|
||||
nope: 1
|
||||
|
||||
- macro: good_macro
|
||||
condition: evt.type=execve
|
||||
|
||||
2
test/rules/invalid_missing_list_name.yaml
Normal file
2
test/rules/invalid_missing_list_name.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
- list:
|
||||
items: [foo]
|
||||
2
test/rules/invalid_missing_macro_name.yaml
Normal file
2
test/rules/invalid_missing_macro_name.yaml
Normal file
@@ -0,0 +1,2 @@
|
||||
- macro:
|
||||
condition: evt.type=execve
|
||||
4
test/rules/invalid_missing_rule_name.yaml
Normal file
4
test/rules/invalid_missing_rule_name.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
- rule:
|
||||
desc: some desc
|
||||
condition: evt.type=execve
|
||||
output: some output
|
||||
1
test/rules/invalid_not_array.yaml
Normal file
1
test/rules/invalid_not_array.yaml
Normal file
@@ -0,0 +1 @@
|
||||
foo: bar
|
||||
1
test/rules/invalid_not_yaml.yaml
Normal file
1
test/rules/invalid_not_yaml.yaml
Normal file
@@ -0,0 +1 @@
|
||||
This is not yaml
|
||||
4
test/rules/invalid_rule_without_output.yaml
Normal file
4
test/rules/invalid_rule_without_output.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
- rule: no output rule
|
||||
desc: some desc
|
||||
condition: evt.type=fork
|
||||
priority: INFO
|
||||
1
test/rules/invalid_unexpected_object.yaml
Normal file
1
test/rules/invalid_unexpected_object.yaml
Normal file
@@ -0,0 +1 @@
|
||||
- foo: bar
|
||||
1
test/rules/invalid_yaml_parse_error.yaml
Normal file
1
test/rules/invalid_yaml_parse_error.yaml
Normal file
@@ -0,0 +1 @@
|
||||
this : is : not : yaml
|
||||
@@ -62,12 +62,12 @@ function expand_macros(ast, defs, changed)
|
||||
elseif ast.type == "Filter" then
|
||||
if (ast.value.type == "Macro") then
|
||||
if (defs[ast.value.value] == nil) then
|
||||
error("Undefined macro '".. ast.value.value .. "' used in filter.")
|
||||
return false, "Undefined macro '".. ast.value.value .. "' used in filter."
|
||||
end
|
||||
defs[ast.value.value].used = true
|
||||
ast.value = copy_ast_obj(defs[ast.value.value].ast)
|
||||
changed = true
|
||||
return changed
|
||||
return true, changed
|
||||
end
|
||||
return expand_macros(ast.value, defs, changed)
|
||||
|
||||
@@ -75,7 +75,7 @@ function expand_macros(ast, defs, changed)
|
||||
|
||||
if (ast.left.type == "Macro") then
|
||||
if (defs[ast.left.value] == nil) then
|
||||
error("Undefined macro '".. ast.left.value .. "' used in filter.")
|
||||
return false, "Undefined macro '".. ast.left.value .. "' used in filter."
|
||||
end
|
||||
defs[ast.left.value].used = true
|
||||
ast.left = copy_ast_obj(defs[ast.left.value].ast)
|
||||
@@ -84,21 +84,27 @@ function expand_macros(ast, defs, changed)
|
||||
|
||||
if (ast.right.type == "Macro") then
|
||||
if (defs[ast.right.value] == nil) then
|
||||
error("Undefined macro ".. ast.right.value .. " used in filter.")
|
||||
return false, "Undefined macro ".. ast.right.value .. " used in filter."
|
||||
end
|
||||
defs[ast.right.value].used = true
|
||||
ast.right = copy_ast_obj(defs[ast.right.value].ast)
|
||||
changed = true
|
||||
end
|
||||
|
||||
local changed_left = expand_macros(ast.left, defs, false)
|
||||
local changed_right = expand_macros(ast.right, defs, false)
|
||||
return changed or changed_left or changed_right
|
||||
local status, changed_left = expand_macros(ast.left, defs, false)
|
||||
if status == false then
|
||||
return false, changed_left
|
||||
end
|
||||
local status, changed_right = expand_macros(ast.right, defs, false)
|
||||
if status == false then
|
||||
return false, changed_right
|
||||
end
|
||||
return true, changed or changed_left or changed_right
|
||||
|
||||
elseif ast.type == "UnaryBoolOp" then
|
||||
if (ast.argument.type == "Macro") then
|
||||
if (defs[ast.argument.value] == nil) then
|
||||
error("Undefined macro ".. ast.argument.value .. " used in filter.")
|
||||
return false, "Undefined macro ".. ast.argument.value .. " used in filter."
|
||||
end
|
||||
defs[ast.argument.value].used = true
|
||||
ast.argument = copy_ast_obj(defs[ast.argument.value].ast)
|
||||
@@ -106,7 +112,7 @@ function expand_macros(ast, defs, changed)
|
||||
end
|
||||
return expand_macros(ast.argument, defs, changed)
|
||||
end
|
||||
return changed
|
||||
return true, changed
|
||||
end
|
||||
|
||||
function get_macros(ast, set)
|
||||
@@ -195,7 +201,7 @@ function compiler.compile_macro(line, macro_defs, list_defs)
|
||||
|
||||
if (error_msg) then
|
||||
msg = "Compilation error when compiling \""..line.."\": ".. error_msg
|
||||
error(msg)
|
||||
return false, msg
|
||||
end
|
||||
|
||||
-- Simply as a validation step, try to expand all macros in this
|
||||
@@ -206,14 +212,18 @@ function compiler.compile_macro(line, macro_defs, list_defs)
|
||||
if (ast.type == "Rule") then
|
||||
-- Line is a filter, so expand macro references
|
||||
repeat
|
||||
expanded = expand_macros(ast_copy, macro_defs, false)
|
||||
status, expanded = expand_macros(ast_copy, macro_defs, false)
|
||||
if status == false then
|
||||
msg = "Compilation error when compiling \""..line.."\": ".. expanded
|
||||
return false, msg
|
||||
end
|
||||
until expanded == false
|
||||
|
||||
else
|
||||
error("Unexpected top-level AST type: "..ast.type)
|
||||
return false, "Unexpected top-level AST type: "..ast.type
|
||||
end
|
||||
|
||||
return ast
|
||||
return true, ast
|
||||
end
|
||||
|
||||
--[[
|
||||
@@ -227,22 +237,25 @@ function compiler.compile_filter(name, source, macro_defs, list_defs)
|
||||
|
||||
if (error_msg) then
|
||||
msg = "Compilation error when compiling \""..source.."\": "..error_msg
|
||||
error(msg)
|
||||
return false, msg
|
||||
end
|
||||
|
||||
if (ast.type == "Rule") then
|
||||
-- Line is a filter, so expand macro references
|
||||
repeat
|
||||
expanded = expand_macros(ast, macro_defs, false)
|
||||
status, expanded = expand_macros(ast, macro_defs, false)
|
||||
if status == false then
|
||||
return false, expanded
|
||||
end
|
||||
until expanded == false
|
||||
|
||||
else
|
||||
error("Unexpected top-level AST type: "..ast.type)
|
||||
return false, "Unexpected top-level AST type: "..ast.type
|
||||
end
|
||||
|
||||
filters = get_filters(ast)
|
||||
|
||||
return ast, filters
|
||||
return true, ast, filters
|
||||
end
|
||||
|
||||
|
||||
|
||||
@@ -179,6 +179,71 @@ function table.tostring( tbl )
|
||||
return "{" .. table.concat( result, "," ) .. "}"
|
||||
end
|
||||
|
||||
-- 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 = {}
|
||||
|
||||
idx = 1
|
||||
last_pos = 1
|
||||
pos = string.find(rules_content, "\n", 1, true)
|
||||
|
||||
while pos ~= nil do
|
||||
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
|
||||
|
||||
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,
|
||||
json_lua_parser,
|
||||
@@ -190,16 +255,45 @@ function load_rules(sinsp_lua_parser,
|
||||
replace_container_info,
|
||||
min_priority)
|
||||
|
||||
local rules = yaml.load(rules_content)
|
||||
local required_engine_version = 0
|
||||
|
||||
local lines, indices = split_lines(rules_content)
|
||||
|
||||
local status, rules = pcall(yaml.load, rules_content)
|
||||
|
||||
if status == false then
|
||||
local pat = "^([%d]+):([%d]+): "
|
||||
-- rules is actually an error string
|
||||
|
||||
local row = 0
|
||||
local col = 0
|
||||
|
||||
row, col = string.match(rules, pat)
|
||||
if row ~= nil and col ~= nil then
|
||||
rules = string.gsub(rules, pat, "")
|
||||
end
|
||||
|
||||
row = tonumber(row)
|
||||
col = tonumber(col)
|
||||
|
||||
return false, build_error(lines, row, 3, rules)
|
||||
end
|
||||
|
||||
if rules == nil then
|
||||
-- An empty rules file is acceptable
|
||||
return required_engine_version
|
||||
return true, required_engine_version
|
||||
end
|
||||
|
||||
if type(rules) ~= "table" then
|
||||
error("Rules content \""..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, build_error(lines, 1, 1, "Rules content is not yaml array of objects")
|
||||
end
|
||||
end
|
||||
|
||||
-- Iterate over yaml list. In this pass, all we're doing is
|
||||
@@ -209,17 +303,25 @@ function load_rules(sinsp_lua_parser,
|
||||
for i,v in ipairs(rules) do
|
||||
|
||||
if (not (type(v) == "table")) then
|
||||
error ("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, 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
|
||||
error("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, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Macro name is empty")
|
||||
end
|
||||
|
||||
if v['source'] == nil then
|
||||
v['source'] = "syscall"
|
||||
end
|
||||
@@ -228,9 +330,9 @@ function load_rules(sinsp_lua_parser,
|
||||
state.ordered_macro_names[#state.ordered_macro_names+1] = v['macro']
|
||||
end
|
||||
|
||||
for i, field in ipairs({'condition'}) do
|
||||
for j, field in ipairs({'condition'}) do
|
||||
if (v[field] == nil) then
|
||||
error ("Missing "..field.." in macro with name "..v['macro'])
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Macro must have property "..field)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -243,7 +345,7 @@ function load_rules(sinsp_lua_parser,
|
||||
|
||||
if append then
|
||||
if state.macros_by_name[v['macro']] == nil then
|
||||
error ("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']
|
||||
@@ -254,13 +356,17 @@ function load_rules(sinsp_lua_parser,
|
||||
|
||||
elseif (v['list']) then
|
||||
|
||||
if (v['list'] == nil or type(v['list']) == "table") then
|
||||
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
|
||||
state.ordered_list_names[#state.ordered_list_names+1] = v['list']
|
||||
end
|
||||
|
||||
for i, field in ipairs({'items'}) do
|
||||
for j, field in ipairs({'items'}) do
|
||||
if (v[field] == nil) then
|
||||
error ("Missing "..field.." in list with name "..v['list'])
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "List must have property "..field)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -273,10 +379,10 @@ function load_rules(sinsp_lua_parser,
|
||||
|
||||
if append then
|
||||
if state.lists_by_name[v['list']] == nil then
|
||||
error ("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 i, elem in ipairs(v['items']) do
|
||||
for j, elem in ipairs(v['items']) do
|
||||
table.insert(state.lists_by_name[v['list']]['items'], elem)
|
||||
end
|
||||
else
|
||||
@@ -286,7 +392,7 @@ function load_rules(sinsp_lua_parser,
|
||||
elseif (v['rule']) then
|
||||
|
||||
if (v['rule'] == nil or type(v['rule']) == "table") then
|
||||
error ("Missing name in rule")
|
||||
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
|
||||
@@ -309,15 +415,15 @@ function load_rules(sinsp_lua_parser,
|
||||
if append then
|
||||
|
||||
-- For append rules, all you need is the condition
|
||||
for i, field in ipairs({'condition'}) do
|
||||
for j, field in ipairs({'condition'}) do
|
||||
if (v[field] == nil) then
|
||||
error ("Missing "..field.." in rule with name "..v['rule'])
|
||||
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
|
||||
error ("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']
|
||||
@@ -325,9 +431,9 @@ function load_rules(sinsp_lua_parser,
|
||||
|
||||
else
|
||||
|
||||
for i, field in ipairs({'condition', 'output', 'desc', 'priority'}) do
|
||||
for j, field in ipairs({'condition', 'output', 'desc', 'priority'}) do
|
||||
if (v[field] == nil) then
|
||||
error ("Missing "..field.." in rule with name "..v['rule'])
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), "Rule must have property "..field)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -356,7 +462,7 @@ function load_rules(sinsp_lua_parser,
|
||||
end
|
||||
end
|
||||
else
|
||||
error ("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
|
||||
|
||||
@@ -393,7 +499,11 @@ function load_rules(sinsp_lua_parser,
|
||||
|
||||
local v = state.macros_by_name[name]
|
||||
|
||||
local ast = compiler.compile_macro(v['condition'], state.macros, state.lists)
|
||||
local status, ast = compiler.compile_macro(v['condition'], state.macros, state.lists)
|
||||
|
||||
if status == false then
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), ast)
|
||||
end
|
||||
|
||||
if v['source'] == "syscall" then
|
||||
if not all_events then
|
||||
@@ -413,8 +523,12 @@ function load_rules(sinsp_lua_parser,
|
||||
warn_evttypes = v['warn_evttypes']
|
||||
end
|
||||
|
||||
local filter_ast, filters = compiler.compile_filter(v['rule'], v['condition'],
|
||||
state.macros, state.lists)
|
||||
local status, filter_ast, filters = compiler.compile_filter(v['rule'], v['condition'],
|
||||
state.macros, state.lists)
|
||||
|
||||
if status == false then
|
||||
return false, build_error(lines, indices[i], (indices[i+1]-indices[i]), filter_ast)
|
||||
end
|
||||
|
||||
local evtttypes = {}
|
||||
local syscallnums = {}
|
||||
@@ -551,7 +665,7 @@ function load_rules(sinsp_lua_parser,
|
||||
formatter = formats.formatter(v['source'], v['output'])
|
||||
formats.free_formatter(v['source'], formatter)
|
||||
else
|
||||
error ("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::
|
||||
@@ -574,7 +688,7 @@ function load_rules(sinsp_lua_parser,
|
||||
|
||||
io.flush()
|
||||
|
||||
return required_engine_version
|
||||
return true, required_engine_version
|
||||
end
|
||||
|
||||
local rule_fmt = "%-50s %s"
|
||||
|
||||
@@ -17,6 +17,8 @@ limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "rules.h"
|
||||
#include "logger.h"
|
||||
|
||||
@@ -425,15 +427,30 @@ 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, 1, 0) != 0)
|
||||
if(lua_pcall(m_ls, 9, 2, 0) != 0)
|
||||
{
|
||||
const char* lerr = lua_tostring(m_ls, -1);
|
||||
|
||||
string err = "Error loading rules: " + string(lerr);
|
||||
|
||||
throw falco_exception(err);
|
||||
}
|
||||
|
||||
required_engine_version = lua_tonumber(m_ls, -1);
|
||||
lua_pop(m_ls, 1);
|
||||
// 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, -1);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string err = lua_tostring(m_ls, -1);
|
||||
throw falco_exception(err);
|
||||
}
|
||||
|
||||
lua_pop(m_ls, 4);
|
||||
|
||||
} else {
|
||||
throw falco_exception("No function " + m_lua_load_rules + " found in lua rule module");
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ limitations under the License.
|
||||
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
|
||||
#include "sinsp.h"
|
||||
#include "filter.h"
|
||||
@@ -57,13 +58,13 @@ class falco_rules
|
||||
void add_filter(string &rule, std::set<uint32_t> &evttypes, std::set<uint32_t> &syscalls, std::set<string> &tags);
|
||||
void add_k8s_audit_filter(string &rule, std::set<string> &tags);
|
||||
void enable_rule(string &rule, bool enabled);
|
||||
std::string get_context(const std::string &content, uint64_t line, uint64_t column);
|
||||
|
||||
lua_parser* m_sinsp_lua_parser;
|
||||
lua_parser* m_json_lua_parser;
|
||||
sinsp* m_inspector;
|
||||
falco_engine *m_engine;
|
||||
lua_State* m_ls;
|
||||
|
||||
string m_lua_load_rules = "load_rules";
|
||||
string m_lua_ignored_syscalls = "ignored_syscalls";
|
||||
string m_lua_ignored_events = "ignored_events";
|
||||
|
||||
@@ -716,7 +716,17 @@ int falco_init(int argc, char **argv)
|
||||
}
|
||||
for(auto file : validate_rules_filenames)
|
||||
{
|
||||
engine->load_rules_file(file, verbose, all_events);
|
||||
// Only include the prefix if there is more than one file
|
||||
std::string prefix = (validate_rules_filenames.size() > 1 ? file + ": " : "");
|
||||
try {
|
||||
engine->load_rules_file(file, verbose, all_events);
|
||||
}
|
||||
catch(falco_exception &e)
|
||||
{
|
||||
printf("%s%s\n", prefix.c_str(), e.what());
|
||||
throw;
|
||||
}
|
||||
printf("%sOk\n", prefix.c_str());
|
||||
}
|
||||
falco_logger::log(LOG_INFO, "Ok\n");
|
||||
goto exit;
|
||||
|
||||
Reference in New Issue
Block a user