mirror of
https://github.com/falcosecurity/falco.git
synced 2025-08-09 10:07:57 +00:00
Add automated tests for plugins
Test infrastructure and sample confs/rules/traces for plugins automated tests: New test cases are in falco_tests_plugins.yaml and cover: - Listing plugins and fields when plugins are loaded. - Basic cloudtrail + json plugin on a fake cloudtrail json file and a sample rule that uses both plugins. - Conflicts between source/extractor plugins - Incompatible plugin api - Wrong plugin path - Checking for warnings when reading rules with unnown sources (e.g. when plugins are not loaded) Some test-only plugins written in C are in test/plugins and built on the fly. (They aren't included in packages of course). The test framework needed some small changes to handle these tests: - Add a mode to not check detection counts at all (for --list/--list-plugins) - addl_cmdline_opts to allow specifying --list/--list-plugins - Using DOTALL when matching stderr/stdout (allows multi-line matches more easily) Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
This commit is contained in:
parent
6a1f4f7374
commit
2a4e4d555d
@ -1,4 +1,7 @@
|
|||||||
add_subdirectory(trace_files)
|
add_subdirectory(trace_files)
|
||||||
|
|
||||||
add_custom_target(test-trace-files ALL)
|
add_custom_target(test-trace-files ALL)
|
||||||
add_dependencies(test-trace-files trace-files-base-scap trace-files-psp trace-files-k8s-audit)
|
add_dependencies(test-trace-files trace-files-base-scap trace-files-psp trace-files-k8s-audit trace-files-plugins)
|
||||||
|
|
||||||
|
add_subdirectory(plugins)
|
||||||
|
add_subdirectory(confs/plugins)
|
||||||
|
16
test/confs/plugins/CMakeLists.txt
Normal file
16
test/confs/plugins/CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# This list is populated at cmake time, not build time
|
||||||
|
file(GLOB test_conf_files
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/*.yaml")
|
||||||
|
|
||||||
|
foreach(conf_file_path ${test_conf_files})
|
||||||
|
get_filename_component(conf_file ${conf_file_path} NAME)
|
||||||
|
add_custom_target(test-conf-${conf_file} ALL
|
||||||
|
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${conf_file})
|
||||||
|
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${conf_file}
|
||||||
|
COMMAND sed -e s!BUILD_DIR!${CMAKE_BINARY_DIR}! < ${CMAKE_CURRENT_SOURCE_DIR}/${conf_file} > ${CMAKE_CURRENT_BINARY_DIR}/${conf_file}
|
||||||
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${conf_file})
|
||||||
|
list(APPEND PLUGINS_CONF_FILES_TARGETS test-conf-${conf_file})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
add_custom_target(conf-files-plugins ALL)
|
||||||
|
add_dependencies(conf-files-plugins ${PLUGINS_CONF_FILES_TARGETS})
|
14
test/confs/plugins/cloudtrail_json_create_instances.yaml
Normal file
14
test/confs/plugins/cloudtrail_json_create_instances.yaml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
stdout_output:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- name: cloudtrail
|
||||||
|
library_path: BUILD_DIR/cloudtrail-plugin-prefix/src/cloudtrail-plugin/libcloudtrail.so
|
||||||
|
init_config: ""
|
||||||
|
open_params: "BUILD_DIR/test/trace_files/plugins/alice_start_instances.json"
|
||||||
|
- name: json
|
||||||
|
library_path: BUILD_DIR/json-plugin-prefix/src/json-plugin/libjson.so
|
||||||
|
init_config: ""
|
||||||
|
|
||||||
|
# Optional
|
||||||
|
load_plugins: [cloudtrail, json]
|
@ -0,0 +1,14 @@
|
|||||||
|
stdout_output:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- name: cloudtrail
|
||||||
|
library_path: BUILD_DIR/cloudtrail-plugin-prefix/src/cloudtrail-plugin/libcloudtrail.so
|
||||||
|
init_config: ""
|
||||||
|
open_params: "BUILD_DIR/test/trace_files/plugins/alice_start_instances_bigevent.json"
|
||||||
|
- name: json
|
||||||
|
library_path: BUILD_DIR/json-plugin-prefix/src/json-plugin/libjson.so
|
||||||
|
init_config: ""
|
||||||
|
|
||||||
|
# Optional
|
||||||
|
load_plugins: [cloudtrail, json]
|
14
test/confs/plugins/incompatible_extract_sources.yaml
Normal file
14
test/confs/plugins/incompatible_extract_sources.yaml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
stdout_output:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- name: cloudtrail
|
||||||
|
library_path: BUILD_DIR/cloudtrail-plugin-prefix/src/cloudtrail-plugin/libcloudtrail.so
|
||||||
|
init_config: ""
|
||||||
|
open_params: ""
|
||||||
|
- name: test_extract_p1
|
||||||
|
library_path: BUILD_DIR/test/plugins/libtest_extract_p1.so
|
||||||
|
init_config: ""
|
||||||
|
|
||||||
|
# Optional
|
||||||
|
load_plugins: [cloudtrail, test_extract_p1]
|
10
test/confs/plugins/incompatible_plugin_api.yaml
Normal file
10
test/confs/plugins/incompatible_plugin_api.yaml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
stdout_output:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- name: incompatible_plugin_api
|
||||||
|
library_path: BUILD_DIR/test/plugins/libtest_incompat_api.so
|
||||||
|
init_config: ""
|
||||||
|
|
||||||
|
# Optional
|
||||||
|
load_plugins: [incompatible_plugin_api]
|
15
test/confs/plugins/multiple_source_plugins.yaml
Normal file
15
test/confs/plugins/multiple_source_plugins.yaml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
stdout_output:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- name: cloudtrail
|
||||||
|
library_path: BUILD_DIR/cloudtrail-plugin-prefix/src/cloudtrail-plugin/libcloudtrail.so
|
||||||
|
init_config: ""
|
||||||
|
open_params: "BUILD_DIR/test/trace_files/plugins/alice_start_instances.json"
|
||||||
|
- name: test_source
|
||||||
|
library_path: BUILD_DIR/test/plugins/libtest_source.so
|
||||||
|
init_config: ""
|
||||||
|
open_params: ""
|
||||||
|
|
||||||
|
# Optional
|
||||||
|
load_plugins: [cloudtrail, test_source]
|
17
test/confs/plugins/overlap_extract_sources.yaml
Normal file
17
test/confs/plugins/overlap_extract_sources.yaml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
stdout_output:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- name: test_source
|
||||||
|
library_path: BUILD_DIR/test/plugins/libtest_source.so
|
||||||
|
init_config: ""
|
||||||
|
open_params: ""
|
||||||
|
- name: test_extract_p1
|
||||||
|
library_path: BUILD_DIR/test/plugins/libtest_extract_p1.so
|
||||||
|
init_config: ""
|
||||||
|
- name: test_extract_p2
|
||||||
|
library_path: BUILD_DIR/test/plugins/libtest_extract_p2.so
|
||||||
|
init_config: ""
|
||||||
|
|
||||||
|
# Optional
|
||||||
|
load_plugins: [test_source, test_extract_p1, test_extract_p2]
|
10
test/confs/plugins/wrong_plugin_path.yaml
Normal file
10
test/confs/plugins/wrong_plugin_path.yaml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
stdout_output:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- name: wrong_plugin_path
|
||||||
|
library_path: BUILD_DIR/test/plugins/wrong_plugin_path.so
|
||||||
|
init_config: ""
|
||||||
|
|
||||||
|
# Optional
|
||||||
|
load_plugins: [wrong_plugin_path]
|
@ -82,6 +82,7 @@ class FalcoTest(Test):
|
|||||||
|
|
||||||
self.exit_status = self.params.get('exit_status', '*', default=0)
|
self.exit_status = self.params.get('exit_status', '*', default=0)
|
||||||
self.should_detect = self.params.get('detect', '*', default=False)
|
self.should_detect = self.params.get('detect', '*', default=False)
|
||||||
|
self.check_detection_counts = self.params.get('check_detection_counts', '*', default=True)
|
||||||
self.trace_file = self.params.get('trace_file', '*', default='')
|
self.trace_file = self.params.get('trace_file', '*', default='')
|
||||||
|
|
||||||
if self.trace_file and not os.path.isabs(self.trace_file):
|
if self.trace_file and not os.path.isabs(self.trace_file):
|
||||||
@ -94,6 +95,7 @@ class FalcoTest(Test):
|
|||||||
'json_include_tags_property', '*', default=True)
|
'json_include_tags_property', '*', default=True)
|
||||||
self.all_events = self.params.get('all_events', '*', default=False)
|
self.all_events = self.params.get('all_events', '*', default=False)
|
||||||
self.priority = self.params.get('priority', '*', default='debug')
|
self.priority = self.params.get('priority', '*', default='debug')
|
||||||
|
self.addl_cmdline_opts = self.params.get('addl_cmdline_opts', '*', default='')
|
||||||
self.rules_file = self.params.get(
|
self.rules_file = self.params.get(
|
||||||
'rules_file', '*', default=os.path.join(self.basedir, '../rules/falco_rules.yaml'))
|
'rules_file', '*', default=os.path.join(self.basedir, '../rules/falco_rules.yaml'))
|
||||||
|
|
||||||
@ -130,6 +132,7 @@ class FalcoTest(Test):
|
|||||||
|
|
||||||
self.conf_file = self.params.get(
|
self.conf_file = self.params.get(
|
||||||
'conf_file', '*', default=os.path.join(self.basedir, '../falco.yaml'))
|
'conf_file', '*', default=os.path.join(self.basedir, '../falco.yaml'))
|
||||||
|
self.conf_file = self.conf_file.replace("BUILD_DIR", build_dir)
|
||||||
if not os.path.isabs(self.conf_file):
|
if not os.path.isabs(self.conf_file):
|
||||||
self.conf_file = os.path.join(self.basedir, self.conf_file)
|
self.conf_file = os.path.join(self.basedir, self.conf_file)
|
||||||
|
|
||||||
@ -617,9 +620,9 @@ class FalcoTest(Test):
|
|||||||
self.log.debug("Converted Rules: {}".format(psp_rules))
|
self.log.debug("Converted Rules: {}".format(psp_rules))
|
||||||
|
|
||||||
# Run falco
|
# Run falco
|
||||||
cmd = '{} {} {} -c {} {} -o json_output={} -o json_include_output_property={} -o json_include_tags_property={} -o priority={} -v'.format(
|
cmd = '{} {} {} -c {} {} -o json_output={} -o json_include_output_property={} -o json_include_tags_property={} -o priority={} -v {}'.format(
|
||||||
self.falco_binary_path, self.rules_args, self.disabled_args, self.conf_file, trace_arg, self.json_output,
|
self.falco_binary_path, self.rules_args, self.disabled_args, self.conf_file, trace_arg, self.json_output,
|
||||||
self.json_include_output_property, self.json_include_tags_property, self.priority)
|
self.json_include_output_property, self.json_include_tags_property, self.priority, self.addl_cmdline_opts)
|
||||||
|
|
||||||
for tag in self.disable_tags:
|
for tag in self.disable_tags:
|
||||||
cmd += ' -T {}'.format(tag)
|
cmd += ' -T {}'.format(tag)
|
||||||
@ -650,13 +653,13 @@ class FalcoTest(Test):
|
|||||||
self.fail("Stdout was not exactly {}".format(self.stderr_is))
|
self.fail("Stdout was not exactly {}".format(self.stderr_is))
|
||||||
|
|
||||||
for pattern in self.stderr_contains:
|
for pattern in self.stderr_contains:
|
||||||
match = re.search(pattern, res.stderr.decode("utf-8"))
|
match = re.search(pattern, res.stderr.decode("utf-8"), re.DOTALL)
|
||||||
if match is None:
|
if match is None:
|
||||||
self.fail(
|
self.fail(
|
||||||
"Stderr of falco process did not contain content matching {}".format(pattern))
|
"Stderr of falco process did not contain content matching {}".format(pattern))
|
||||||
|
|
||||||
for pattern in self.stdout_contains:
|
for pattern in self.stdout_contains:
|
||||||
match = re.search(pattern, res.stdout.decode("utf-8"))
|
match = re.search(pattern, res.stdout.decode("utf-8"), re.DOTALL)
|
||||||
if match is None:
|
if match is None:
|
||||||
self.fail("Stdout of falco process '{}' did not contain content matching {}".format(
|
self.fail("Stdout of falco process '{}' did not contain content matching {}".format(
|
||||||
res.stdout.decode("utf-8"), pattern))
|
res.stdout.decode("utf-8"), pattern))
|
||||||
@ -684,7 +687,7 @@ class FalcoTest(Test):
|
|||||||
self.check_rules_warnings(res)
|
self.check_rules_warnings(res)
|
||||||
if len(self.rules_events) > 0:
|
if len(self.rules_events) > 0:
|
||||||
self.check_rules_events(res)
|
self.check_rules_events(res)
|
||||||
if len(self.validate_rules_file) == 0:
|
if len(self.validate_rules_file) == 0 and self.check_detection_counts:
|
||||||
self.check_detections(res)
|
self.check_detections(res)
|
||||||
if len(self.detect_counts) > 0:
|
if len(self.detect_counts) > 0:
|
||||||
self.check_detections_by_rule(res)
|
self.check_detections_by_rule(res)
|
||||||
|
106
test/falco_tests_plugins.yaml
Normal file
106
test/falco_tests_plugins.yaml
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
#
|
||||||
|
# Copyright (C) 2021 The Falco Authors.
|
||||||
|
#
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
|
||||||
|
trace_files: !mux
|
||||||
|
|
||||||
|
list_plugins:
|
||||||
|
check_detection_counts: False
|
||||||
|
rules_file:
|
||||||
|
- rules/plugins/cloudtrail_create_instances.yaml
|
||||||
|
conf_file: BUILD_DIR/test/confs/plugins/cloudtrail_json_create_instances.yaml
|
||||||
|
addl_cmdline_opts: --list-plugins
|
||||||
|
stdout_contains: "2 Plugins Loaded.*Name: cloudtrail.*Type: source plugin.*Name: json.*Type: extractor plugin"
|
||||||
|
|
||||||
|
list_plugin_fields:
|
||||||
|
check_detection_counts: False
|
||||||
|
rules_file:
|
||||||
|
- rules/plugins/cloudtrail_create_instances.yaml
|
||||||
|
conf_file: BUILD_DIR/test/confs/plugins/cloudtrail_json_create_instances.yaml
|
||||||
|
addl_cmdline_opts: --list
|
||||||
|
stdout_contains: "ct.id"
|
||||||
|
|
||||||
|
detect_create_instance:
|
||||||
|
detect: True
|
||||||
|
detect_level: INFO
|
||||||
|
rules_file:
|
||||||
|
- rules/plugins/cloudtrail_create_instances.yaml
|
||||||
|
detect_counts:
|
||||||
|
- 'Cloudtrail Create Instance': 1
|
||||||
|
conf_file: BUILD_DIR/test/confs/plugins/cloudtrail_json_create_instances.yaml
|
||||||
|
|
||||||
|
detect_create_instance_bigevent:
|
||||||
|
detect: True
|
||||||
|
detect_level: INFO
|
||||||
|
rules_file:
|
||||||
|
- rules/plugins/cloudtrail_create_instances.yaml
|
||||||
|
detect_counts:
|
||||||
|
- 'Cloudtrail Create Instance': 1
|
||||||
|
conf_file: BUILD_DIR/test/confs/plugins/cloudtrail_json_create_instances_bigevent.yaml
|
||||||
|
|
||||||
|
multiple_source_plugins:
|
||||||
|
exit_status: 1
|
||||||
|
stderr_contains: "Runtime error: Can not load multiple source plugins. cloudtrail already loaded. Exiting."
|
||||||
|
conf_file: BUILD_DIR/test/confs/plugins/multiple_source_plugins.yaml
|
||||||
|
rules_file:
|
||||||
|
- rules/plugins/cloudtrail_create_instances.yaml
|
||||||
|
|
||||||
|
incompatible_extract_sources:
|
||||||
|
exit_status: 1
|
||||||
|
stderr_contains: "Runtime error: Extractor plugin not compatible with event source aws_cloudtrail. Exiting."
|
||||||
|
conf_file: BUILD_DIR/test/confs/plugins/incompatible_extract_sources.yaml
|
||||||
|
rules_file:
|
||||||
|
- rules/plugins/cloudtrail_create_instances.yaml
|
||||||
|
|
||||||
|
overlap_extract_sources:
|
||||||
|
exit_status: 1
|
||||||
|
stderr_contains: "Runtime error: Extractor plugins have overlapping compatible event source test_source. Exiting."
|
||||||
|
conf_file: BUILD_DIR/test/confs/plugins/overlap_extract_sources.yaml
|
||||||
|
rules_file:
|
||||||
|
- rules/plugins/cloudtrail_create_instances.yaml
|
||||||
|
|
||||||
|
incompat_plugin_api:
|
||||||
|
exit_status: 1
|
||||||
|
stderr_contains: "Unsupported plugin required api version 10000000.0.0"
|
||||||
|
conf_file: BUILD_DIR/test/confs/plugins/incompatible_plugin_api.yaml
|
||||||
|
rules_file:
|
||||||
|
- rules/plugins/cloudtrail_create_instances.yaml
|
||||||
|
|
||||||
|
incompat_plugin_rules_version:
|
||||||
|
exit_status: 1
|
||||||
|
stderr_contains: "Runtime error: Plugin cloudtrail version 0.1.0 not compatible with required plugin version 100000.0.0. Exiting."
|
||||||
|
conf_file: BUILD_DIR/test/confs/plugins/cloudtrail_json_create_instances.yaml
|
||||||
|
rules_file:
|
||||||
|
- rules/plugins/cloudtrail_incompat_plugin_version.yaml
|
||||||
|
|
||||||
|
wrong_plugin_path:
|
||||||
|
exit_status: 1
|
||||||
|
stderr_contains: "error loading plugin.*No such file or directory. Exiting"
|
||||||
|
conf_file: BUILD_DIR/test/confs/plugins/wrong_plugin_path.yaml
|
||||||
|
rules_file:
|
||||||
|
- rules/plugins/cloudtrail_incompat_plugin_version.yaml
|
||||||
|
|
||||||
|
no_plugins_unknown_source:
|
||||||
|
detect: False
|
||||||
|
rules_file:
|
||||||
|
- rules/plugins/cloudtrail_create_instances.yaml
|
||||||
|
trace_file: trace_files/empty.scap
|
||||||
|
rules_warning:
|
||||||
|
- Cloudtrail Create Instance
|
||||||
|
stderr_contains: "Rule Cloudtrail Create Instance: warning .unknown-source.: unknown source aws_cloudtrail, skipping"
|
||||||
|
|
||||||
|
|
13
test/plugins/CMakeLists.txt
Normal file
13
test/plugins/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
add_library(test_extract_p1 SHARED test_extract.cpp)
|
||||||
|
add_library(test_extract_p2 SHARED test_extract.cpp)
|
||||||
|
add_library(test_source SHARED test_source.cpp)
|
||||||
|
add_library(test_incompat_api SHARED incompat_api.cpp)
|
||||||
|
|
||||||
|
target_include_directories(
|
||||||
|
test_extract_p1 PUBLIC "${LIBSCAP_INCLUDE_DIRS}")
|
||||||
|
|
||||||
|
target_include_directories(
|
||||||
|
test_extract_p2 PUBLIC "${LIBSCAP_INCLUDE_DIRS}")
|
||||||
|
|
||||||
|
target_include_directories(
|
||||||
|
test_source PUBLIC "${LIBSCAP_INCLUDE_DIRS}")
|
28
test/plugins/incompat_api.cpp
Normal file
28
test/plugins/incompat_api.cpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2021 The Falco Authors.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
// Don't need any function other than plugin_get_required_api_version,
|
||||||
|
// plugin load will fail after that.
|
||||||
|
static const char *pl_required_api_version = "10000000.0.0";
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
const char* plugin_get_required_api_version()
|
||||||
|
{
|
||||||
|
return pl_required_api_version;
|
||||||
|
}
|
116
test/plugins/test_extract.cpp
Normal file
116
test/plugins/test_extract.cpp
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2021 The Falco Authors.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <plugin_info.h>
|
||||||
|
|
||||||
|
static const char *pl_required_api_version = "0.1.0";
|
||||||
|
static uint32_t pl_type = TYPE_EXTRACTOR_PLUGIN;
|
||||||
|
static const char *pl_name_base = "test_extract";
|
||||||
|
static char pl_name[1024];
|
||||||
|
static const char *pl_desc = "Test Plugin For Regression Tests";
|
||||||
|
static const char *pl_contact = "github.com/falcosecurity/falco";
|
||||||
|
static const char *pl_version = "0.1.0";
|
||||||
|
static const char *pl_extract_sources = "[\"test_source\"]";
|
||||||
|
static const char *pl_fields = "[]";
|
||||||
|
|
||||||
|
// This struct represents the state of a plugin. Just has a placeholder string value.
|
||||||
|
typedef struct plugin_state
|
||||||
|
{
|
||||||
|
} plugin_state;
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
const char* plugin_get_required_api_version()
|
||||||
|
{
|
||||||
|
return pl_required_api_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
uint32_t plugin_get_type()
|
||||||
|
{
|
||||||
|
return pl_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
const char* plugin_get_name()
|
||||||
|
{
|
||||||
|
// Add a random-ish suffix to the end, as some tests load
|
||||||
|
// multiple copies of this plugin
|
||||||
|
snprintf(pl_name, sizeof(pl_name)-1, "%s%ld\n", pl_name_base, random());
|
||||||
|
return pl_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
const char* plugin_get_description()
|
||||||
|
{
|
||||||
|
return pl_desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
const char* plugin_get_contact()
|
||||||
|
{
|
||||||
|
return pl_contact;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
const char* plugin_get_version()
|
||||||
|
{
|
||||||
|
return pl_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
const char* plugin_get_extract_event_sources()
|
||||||
|
{
|
||||||
|
return pl_extract_sources;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
const char* plugin_get_fields()
|
||||||
|
{
|
||||||
|
return pl_fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
const char* plugin_get_last_error(ss_plugin_t* s)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
ss_plugin_t* plugin_init(const char* config, int32_t* rc)
|
||||||
|
{
|
||||||
|
// Note: Using new/delete is okay, as long as the plugin
|
||||||
|
// framework is not deleting the memory.
|
||||||
|
plugin_state *ret = new plugin_state();
|
||||||
|
*rc = SS_PLUGIN_SUCCESS;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
void plugin_destroy(ss_plugin_t* s)
|
||||||
|
{
|
||||||
|
plugin_state *ps = (plugin_state *) s;
|
||||||
|
|
||||||
|
delete(ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
int32_t plugin_extract_fields(ss_plugin_t *s, const ss_plugin_event *evt, uint32_t num_fields, ss_plugin_extract_field *fields)
|
||||||
|
{
|
||||||
|
return SS_PLUGIN_SUCCESS;
|
||||||
|
}
|
161
test/plugins/test_source.cpp
Normal file
161
test/plugins/test_source.cpp
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
/*
|
||||||
|
Copyright (C) 2021 The Falco Authors.
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <plugin_info.h>
|
||||||
|
|
||||||
|
static const char *pl_required_api_version = "0.1.0";
|
||||||
|
static uint32_t pl_type = TYPE_SOURCE_PLUGIN;
|
||||||
|
static uint32_t pl_id = 999;
|
||||||
|
static const char *pl_name = "test_source";
|
||||||
|
static const char *pl_desc = "Test Plugin For Regression Tests";
|
||||||
|
static const char *pl_contact = "github.com/falcosecurity/falco";
|
||||||
|
static const char *pl_version = "0.1.0";
|
||||||
|
static const char *pl_event_source = "test_source";
|
||||||
|
static const char *pl_fields = "[]";
|
||||||
|
|
||||||
|
// This struct represents the state of a plugin. Just has a placeholder string value.
|
||||||
|
typedef struct plugin_state
|
||||||
|
{
|
||||||
|
} plugin_state;
|
||||||
|
|
||||||
|
typedef struct instance_state
|
||||||
|
{
|
||||||
|
} instance_state;
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
const char* plugin_get_required_api_version()
|
||||||
|
{
|
||||||
|
return pl_required_api_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
uint32_t plugin_get_type()
|
||||||
|
{
|
||||||
|
return pl_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
uint32_t plugin_get_id()
|
||||||
|
{
|
||||||
|
return pl_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
const char* plugin_get_name()
|
||||||
|
{
|
||||||
|
return pl_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
const char* plugin_get_description()
|
||||||
|
{
|
||||||
|
return pl_desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
const char* plugin_get_contact()
|
||||||
|
{
|
||||||
|
return pl_contact;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
const char* plugin_get_version()
|
||||||
|
{
|
||||||
|
return pl_version;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
const char* plugin_get_event_source()
|
||||||
|
{
|
||||||
|
return pl_event_source;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
const char* plugin_get_fields()
|
||||||
|
{
|
||||||
|
return pl_fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
const char* plugin_get_last_error(ss_plugin_t* s)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
ss_plugin_t* plugin_init(const char* config, int32_t* rc)
|
||||||
|
{
|
||||||
|
// Note: Using new/delete is okay, as long as the plugin
|
||||||
|
// framework is not deleting the memory.
|
||||||
|
plugin_state *ret = new plugin_state();
|
||||||
|
|
||||||
|
*rc = SS_PLUGIN_SUCCESS;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
void plugin_destroy(ss_plugin_t* s)
|
||||||
|
{
|
||||||
|
plugin_state *ps = (plugin_state *) s;
|
||||||
|
|
||||||
|
delete(ps);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
ss_instance_t* plugin_open(ss_plugin_t* s, const char* params, int32_t* rc)
|
||||||
|
{
|
||||||
|
// Note: Using new/delete is okay, as long as the plugin
|
||||||
|
// framework is not deleting the memory.
|
||||||
|
instance_state *ret = new instance_state();
|
||||||
|
*rc = SS_PLUGIN_SUCCESS;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
void plugin_close(ss_plugin_t* s, ss_instance_t* i)
|
||||||
|
{
|
||||||
|
instance_state *istate = (instance_state *) i;
|
||||||
|
|
||||||
|
delete(istate);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
int32_t plugin_next_batch(ss_plugin_t* s, ss_instance_t* i, uint32_t *nevts, ss_plugin_event **evts)
|
||||||
|
{
|
||||||
|
return SS_PLUGIN_EOF;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This plugin does not implement plugin_next_batch, due to the lower
|
||||||
|
// overhead of calling C functions from the plugin framework compared
|
||||||
|
// to calling Go functions.
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
const char *plugin_event_to_string(ss_plugin_t *s, const uint8_t *data, uint32_t datalen)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C"
|
||||||
|
int32_t plugin_extract_fields(ss_plugin_t *s, const ss_plugin_event *evt, uint32_t num_fields, ss_plugin_extract_field *fields)
|
||||||
|
{
|
||||||
|
return SS_PLUGIN_SUCCESS;
|
||||||
|
}
|
6
test/rules/plugins/cloudtrail_create_instances.yaml
Normal file
6
test/rules/plugins/cloudtrail_create_instances.yaml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
- rule: Cloudtrail Create Instance
|
||||||
|
desc: Detect Creating an EC2 Instance
|
||||||
|
condition: evt.num > 0 and ct.name="StartInstances"
|
||||||
|
output: EC2 Instance Created (evtnum=%evt.num info=%evt.plugininfo id=%ct.id user name=%json.value[/userIdentity/userName])
|
||||||
|
priority: INFO
|
||||||
|
source: aws_cloudtrail
|
10
test/rules/plugins/cloudtrail_incompat_plugin_version.yaml
Normal file
10
test/rules/plugins/cloudtrail_incompat_plugin_version.yaml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
- required_plugin_versions:
|
||||||
|
- name: cloudtrail
|
||||||
|
version: 100000.0.0
|
||||||
|
|
||||||
|
- rule: Cloudtrail Create Instance
|
||||||
|
desc: Detect Creating an EC2 Instance
|
||||||
|
condition: evt.num > 0 and ct.name="StartInstances"
|
||||||
|
output: EC2 Instance Created (evtnum=%evt.num info=%evt.plugininfo id=%ct.id user name=%json.value[/userIdentity/userName])
|
||||||
|
priority: INFO
|
||||||
|
source: aws_cloudtrail
|
@ -99,7 +99,7 @@ function run_tests() {
|
|||||||
# as we're watching the return status when running avocado.
|
# as we're watching the return status when running avocado.
|
||||||
set +e
|
set +e
|
||||||
TEST_RC=0
|
TEST_RC=0
|
||||||
suites=($SCRIPTDIR/falco_traces.yaml $SCRIPTDIR/falco_tests.yaml $SCRIPTDIR/falco_k8s_audit_tests.yaml $SCRIPTDIR/falco_tests_psp.yaml $SCRIPTDIR/falco_tests_exceptions.yaml)
|
suites=($SCRIPTDIR/falco_traces.yaml $SCRIPTDIR/falco_tests.yaml $SCRIPTDIR/falco_k8s_audit_tests.yaml $SCRIPTDIR/falco_tests_psp.yaml $SCRIPTDIR/falco_tests_exceptions.yaml $SCRIPTDIR/falco_tests_plugins.yaml)
|
||||||
|
|
||||||
if [ "$SKIP_PACKAGES_TESTS" = false ] ; then
|
if [ "$SKIP_PACKAGES_TESTS" = false ] ; then
|
||||||
suites+=($SCRIPTDIR/falco_tests_package.yaml)
|
suites+=($SCRIPTDIR/falco_tests_package.yaml)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
add_subdirectory(k8s_audit)
|
add_subdirectory(k8s_audit)
|
||||||
add_subdirectory(psp)
|
add_subdirectory(psp)
|
||||||
|
add_subdirectory(plugins)
|
||||||
|
|
||||||
# Note: list of traces is created at cmake time, not build time
|
# Note: list of traces is created at cmake time, not build time
|
||||||
file(GLOB test_trace_files
|
file(GLOB test_trace_files
|
||||||
|
16
test/trace_files/plugins/CMakeLists.txt
Normal file
16
test/trace_files/plugins/CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# Note: list of traces is created at cmake time, not build time
|
||||||
|
file(GLOB test_trace_files
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/*.json")
|
||||||
|
|
||||||
|
foreach(trace_file_path ${test_trace_files})
|
||||||
|
get_filename_component(trace_file ${trace_file_path} NAME)
|
||||||
|
add_custom_target(test-trace-${trace_file} ALL
|
||||||
|
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${trace_file})
|
||||||
|
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${trace_file}
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy ${trace_file_path} ${CMAKE_CURRENT_BINARY_DIR}/${trace_file}
|
||||||
|
DEPENDS ${trace_file_path})
|
||||||
|
list(APPEND PLUGINS_TRACE_FILES_TARGETS test-trace-${trace_file})
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
add_custom_target(trace-files-plugins ALL)
|
||||||
|
add_dependencies(trace-files-plugins ${PLUGINS_TRACE_FILES_TARGETS})
|
31
test/trace_files/plugins/alice_start_instances.json
Normal file
31
test/trace_files/plugins/alice_start_instances.json
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
{"Records": [{
|
||||||
|
"eventVersion": "1.0",
|
||||||
|
"userIdentity": {
|
||||||
|
"type": "IAMUser",
|
||||||
|
"principalId": "EX_PRINCIPAL_ID",
|
||||||
|
"arn": "arn:aws:iam::123456789012:user/Alice",
|
||||||
|
"accessKeyId": "EXAMPLE_KEY_ID",
|
||||||
|
"accountId": "123456789012",
|
||||||
|
"userName": "Alice"
|
||||||
|
},
|
||||||
|
"eventTime": "2014-03-06T21:22:54Z",
|
||||||
|
"eventSource": "ec2.amazonaws.com",
|
||||||
|
"eventType": "AwsApiCall",
|
||||||
|
"eventName": "StartInstances",
|
||||||
|
"awsRegion": "us-east-2",
|
||||||
|
"sourceIPAddress": "205.251.233.176",
|
||||||
|
"userAgent": "ec2-api-tools 1.6.12.2",
|
||||||
|
"requestParameters": {"instancesSet": {"items": [{"instanceId": "i-ebeaf9e2"}]}},
|
||||||
|
"responseElements": {"instancesSet": {"items": [{
|
||||||
|
"instanceId": "i-ebeaf9e2",
|
||||||
|
"currentState": {
|
||||||
|
"code": 0,
|
||||||
|
"name": "pending"
|
||||||
|
},
|
||||||
|
"previousState": {
|
||||||
|
"code": 80,
|
||||||
|
"name": "stopped"
|
||||||
|
}
|
||||||
|
}]}}
|
||||||
|
}]}
|
||||||
|
|
32
test/trace_files/plugins/alice_start_instances_bigevent.json
Normal file
32
test/trace_files/plugins/alice_start_instances_bigevent.json
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user