mirror of
https://github.com/falcosecurity/falco.git
synced 2025-06-28 15:47:25 +00:00
Add tests for multiple files, disabled rules.
Add test that cover reading from multiple sets of rule files and disabling rules. Specific changes: - Modify falco to allow multiple -r arguments to read from multiple files. - In the test multiplex file, add a disabled_rules attribute, containing a sequence of rules to disable. Result in -D arguments when running falco. - In the test multiplex file, 'rules_file' can be a sequence. It results in multiple -r arguments when running falco. - In the test multiplex file, 'detect_level' can be a squence of multiple severity levels. All levels will be checked for in the output. - Move all test rules files to a rules subdirectory and all trace files to a traces subdirectory. - Add a small trace file for a simple cat of /dev/null. Used by the new tests. - Add the following new tests: - Reading from multiple files, with the first file being empty. Ensure that the rules from the second file are properly loaded. - Reading from multiple files with the last being empty. Ensures that the empty file doesn't overwrite anything from the first file. - Reading from multiple files with varying severity levels for each rule. Ensures that both files are properly read. - Disabling rules from a rules file, both with full rule names and regexes. Will result in not detecting anything.
This commit is contained in:
parent
09405e4fad
commit
f1748060c5
@ -26,8 +26,28 @@ class FalcoTest(Test):
|
||||
self.json_output = self.params.get('json_output', '*', default=False)
|
||||
self.rules_file = self.params.get('rules_file', '*', default=os.path.join(self.basedir, '../rules/falco_rules.yaml'))
|
||||
|
||||
if not os.path.isabs(self.rules_file):
|
||||
self.rules_file = os.path.join(self.basedir, self.rules_file)
|
||||
if not isinstance(self.rules_file, list):
|
||||
self.rules_file = [self.rules_file]
|
||||
|
||||
self.rules_args = ""
|
||||
|
||||
for file in self.rules_file:
|
||||
if not os.path.isabs(file):
|
||||
file = os.path.join(self.basedir, file)
|
||||
self.rules_args = self.rules_args + "-r " + file + " "
|
||||
|
||||
self.disabled_rules = self.params.get('disabled_rules', '*', default='')
|
||||
|
||||
if self.disabled_rules == '':
|
||||
self.disabled_rules = []
|
||||
|
||||
if not isinstance(self.disabled_rules, list):
|
||||
self.disabled_rules = [self.disabled_rules]
|
||||
|
||||
self.disabled_args = ""
|
||||
|
||||
for rule in self.disabled_rules:
|
||||
self.disabled_args = self.disabled_args + "-D " + rule + " "
|
||||
|
||||
self.rules_warning = self.params.get('rules_warning', '*', default=False)
|
||||
if self.rules_warning == False:
|
||||
@ -49,6 +69,9 @@ class FalcoTest(Test):
|
||||
if self.should_detect:
|
||||
self.detect_level = self.params.get('detect_level', '*')
|
||||
|
||||
if not isinstance(self.detect_level, list):
|
||||
self.detect_level = [self.detect_level]
|
||||
|
||||
# Doing this in 2 steps instead of simply using
|
||||
# module_is_loaded to avoid logging lsmod output to the log.
|
||||
lsmod_output = process.system_output("lsmod", verbose=False)
|
||||
@ -105,16 +128,17 @@ class FalcoTest(Test):
|
||||
if events_detected == 0:
|
||||
self.fail("Detected {} events when should have detected > 0".format(events_detected))
|
||||
|
||||
level_line = '(?i){}: (\d+)'.format(self.detect_level)
|
||||
match = re.search(level_line, res.stdout)
|
||||
for level in self.detect_level:
|
||||
level_line = '(?i){}: (\d+)'.format(level)
|
||||
match = re.search(level_line, res.stdout)
|
||||
|
||||
if match is None:
|
||||
self.fail("Could not find a line '{}: <count>' in falco output".format(self.detect_level))
|
||||
if match is None:
|
||||
self.fail("Could not find a line '{}: <count>' in falco output".format(level))
|
||||
|
||||
events_detected = int(match.group(1))
|
||||
events_detected = int(match.group(1))
|
||||
|
||||
if not events_detected > 0:
|
||||
self.fail("Detected {} events at level {} when should have detected > 0".format(events_detected, self.detect_level))
|
||||
if not events_detected > 0:
|
||||
self.fail("Detected {} events at level {} when should have detected > 0".format(events_detected, level))
|
||||
|
||||
def check_json_output(self, res):
|
||||
if self.json_output:
|
||||
@ -131,8 +155,8 @@ class FalcoTest(Test):
|
||||
self.log.info("Trace file %s", self.trace_file)
|
||||
|
||||
# Run the provided trace file though falco
|
||||
cmd = '{}/userspace/falco/falco -r {} -c {}/../falco.yaml -e {} -o json_output={} -v'.format(
|
||||
self.falcodir, self.rules_file, self.falcodir, self.trace_file, self.json_output)
|
||||
cmd = '{}/userspace/falco/falco {} {} -c {}/../falco.yaml -e {} -o json_output={} -v'.format(
|
||||
self.falcodir, self.rules_args, self.disabled_args, self.falcodir, self.trace_file, self.json_output)
|
||||
|
||||
self.falco_proc = process.SubProcess(cmd)
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
trace_files: !mux
|
||||
builtin_rules_no_warnings:
|
||||
detect: False
|
||||
trace_file: empty.scap
|
||||
trace_file: trace_files/empty.scap
|
||||
rules_warning: False
|
||||
|
||||
test_warnings:
|
||||
detect: False
|
||||
trace_file: empty.scap
|
||||
rules_file: falco_rules_warnings.yaml
|
||||
trace_file: trace_files/empty.scap
|
||||
rules_file: rules/falco_rules_warnings.yaml
|
||||
rules_warning:
|
||||
- no_evttype
|
||||
- evttype_not_equals
|
||||
@ -60,3 +60,48 @@ trace_files: !mux
|
||||
- repeated_evttypes_with_in: [open]
|
||||
- repeated_evttypes_with_separate_in: [open]
|
||||
- repeated_evttypes_with_mix: [open]
|
||||
|
||||
multiple_rules_first_empty:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/empty_rules.yaml
|
||||
- rules/single_rule.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
multiple_rules_last_empty:
|
||||
detect: True
|
||||
detect_level: WARNING
|
||||
rules_file:
|
||||
- rules/single_rule.yaml
|
||||
- rules/empty_rules.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
multiple_rules:
|
||||
detect: True
|
||||
detect_level:
|
||||
- WARNING
|
||||
- INFO
|
||||
- ERROR
|
||||
rules_file:
|
||||
- rules/single_rule.yaml
|
||||
- rules/double_rule.yaml
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
disabled_rules:
|
||||
detect: False
|
||||
rules_file:
|
||||
- rules/empty_rules.yaml
|
||||
- rules/single_rule.yaml
|
||||
disabled_rules:
|
||||
- open_from_cat
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
||||
disabled_rules_using_regex:
|
||||
detect: False
|
||||
rules_file:
|
||||
- rules/empty_rules.yaml
|
||||
- rules/single_rule.yaml
|
||||
disabled_rules:
|
||||
- "open.*"
|
||||
trace_file: trace_files/cat_write.scap
|
||||
|
13
test/rules/double_rule.yaml
Normal file
13
test/rules/double_rule.yaml
Normal file
@ -0,0 +1,13 @@
|
||||
# This ruleset depends on the is_cat macro defined in single_rule.yaml
|
||||
|
||||
- rule: exec_from_cat
|
||||
desc: A process named cat does execve
|
||||
condition: evt.type=execve and is_cat
|
||||
output: "An exec was seen (command=%proc.cmdline)"
|
||||
priority: ERROR
|
||||
|
||||
- rule: access_from_cat
|
||||
desc: A process named cat does an access
|
||||
condition: evt.type=access and is_cat
|
||||
output: "An access was seen (command=%proc.cmdline)"
|
||||
priority: INFO
|
0
test/rules/empty_rules.yaml
Normal file
0
test/rules/empty_rules.yaml
Normal file
8
test/rules/single_rule.yaml
Normal file
8
test/rules/single_rule.yaml
Normal file
@ -0,0 +1,8 @@
|
||||
- macro: is_cat
|
||||
condition: proc.name=cat
|
||||
|
||||
- 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
|
BIN
test/trace_files/cat_write.scap
Normal file
BIN
test/trace_files/cat_write.scap
Normal file
Binary file not shown.
@ -33,7 +33,7 @@ void falco_configuration::init(string conf_filename, list<string> &cmdline_optio
|
||||
|
||||
init_cmdline_options(cmdline_options);
|
||||
|
||||
m_rules_filename = m_config->get_scalar<string>("rules_file", "/etc/falco_rules.yaml");
|
||||
m_rules_filenames.push_back(m_config->get_scalar<string>("rules_file", "/etc/falco_rules.yaml"));
|
||||
m_json_output = m_config->get_scalar<bool>("json_output", false);
|
||||
|
||||
falco_outputs::output_config file_output;
|
||||
|
@ -123,7 +123,7 @@ class falco_configuration
|
||||
void init(std::string conf_filename, std::list<std::string> &cmdline_options);
|
||||
void init(std::list<std::string> &cmdline_options);
|
||||
|
||||
std::string m_rules_filename;
|
||||
std::list<std::string> m_rules_filenames;
|
||||
bool m_json_output;
|
||||
std::vector<falco_outputs::output_config> m_outputs;
|
||||
private:
|
||||
|
@ -2,6 +2,9 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fstream>
|
||||
#include <set>
|
||||
#include <list>
|
||||
#include <string>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
@ -41,6 +44,7 @@ static void usage()
|
||||
" -p, --pidfile <pid_file> When run as a daemon, write pid to specified file\n"
|
||||
" -e <events_file> Read the events from <events_file> (in .scap format) instead of tapping into live.\n"
|
||||
" -r <rules_file> Rules file (defaults to value set in configuration file, or /etc/falco_rules.yaml).\n"
|
||||
" Can be specified multiple times to read from multiple files.\n"
|
||||
" -D <pattern> Disable any rules matching the regex <pattern>. Can be specified multiple times.\n"
|
||||
" -L Show the name and description of all rules and exit.\n"
|
||||
" -l <rule> Show the name and description of the rule with name <rule> and exit.\n"
|
||||
@ -140,7 +144,7 @@ int falco_init(int argc, char **argv)
|
||||
int long_index = 0;
|
||||
string scap_filename;
|
||||
string conf_filename;
|
||||
string rules_filename;
|
||||
list<string> rules_filenames;
|
||||
bool daemon = false;
|
||||
string pidfilename = "/var/run/falco.pid";
|
||||
bool describe_all_rules = false;
|
||||
@ -192,7 +196,7 @@ int falco_init(int argc, char **argv)
|
||||
scap_filename = optarg;
|
||||
break;
|
||||
case 'r':
|
||||
rules_filename = optarg;
|
||||
rules_filenames.push_back(optarg);
|
||||
break;
|
||||
case 'D':
|
||||
pattern = optarg;
|
||||
@ -273,14 +277,16 @@ int falco_init(int argc, char **argv)
|
||||
falco_logger::log(LOG_INFO, "Falco initialized. No configuration file found, proceeding with defaults\n");
|
||||
}
|
||||
|
||||
if (rules_filename.size())
|
||||
if (rules_filenames.size())
|
||||
{
|
||||
config.m_rules_filename = rules_filename;
|
||||
config.m_rules_filenames = rules_filenames;
|
||||
}
|
||||
|
||||
engine->load_rules_file(rules_filename, verbose, all_events);
|
||||
|
||||
falco_logger::log(LOG_INFO, "Parsed rules from file " + rules_filename + "\n");
|
||||
for (auto filename : config.m_rules_filenames)
|
||||
{
|
||||
engine->load_rules_file(filename, verbose, all_events);
|
||||
falco_logger::log(LOG_INFO, "Parsed rules from file " + filename + "\n");
|
||||
}
|
||||
|
||||
for (auto pattern : disabled_rule_patterns)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user