mirror of
https://github.com/falcosecurity/falco.git
synced 2025-07-12 22:18:30 +00:00
new(unit_tests,userspace): properly support env var expansions in all scalar values of yaml file.
Signed-off-by: Federico Di Pierro <nierro92@gmail.com>
This commit is contained in:
parent
3b095a5eda
commit
cbbcb61153
@ -158,7 +158,11 @@ TEST(Configuration, configuration_environment_variables)
|
|||||||
" - ${ENV_VAR_EMBEDDED}/foo\n"
|
" - ${ENV_VAR_EMBEDDED}/foo\n"
|
||||||
"is_test: ${ENV_VAR_BOOL}\n"
|
"is_test: ${ENV_VAR_BOOL}\n"
|
||||||
"num_test: ${ENV_VAR_INT}\n"
|
"num_test: ${ENV_VAR_INT}\n"
|
||||||
"empty_test: ${ENV_VAR_EMPTY}\n";
|
"empty_test: ${ENV_VAR_EMPTY}\n"
|
||||||
|
"plugins:\n"
|
||||||
|
" - name: k8saudit\n"
|
||||||
|
" library_path: /foo/${ENV_VAR}/libk8saudit.so\n"
|
||||||
|
" open_params: ${ENV_VAR_INT}\n";
|
||||||
|
|
||||||
yaml_helper conf;
|
yaml_helper conf;
|
||||||
conf.load_from_string(env_var_sample_yaml);
|
conf.load_from_string(env_var_sample_yaml);
|
||||||
@ -191,9 +195,9 @@ TEST(Configuration, configuration_environment_variables)
|
|||||||
auto base_value_escaped = conf.get_scalar<std::string>("base_value.escaped", default_value);
|
auto base_value_escaped = conf.get_scalar<std::string>("base_value.escaped", default_value);
|
||||||
ASSERT_EQ(base_value_escaped, env_var_value); // Environment variable within quotes
|
ASSERT_EQ(base_value_escaped, env_var_value); // Environment variable within quotes
|
||||||
|
|
||||||
/* Test fetching of an undefined environment variable. Expected to return the default value.*/
|
/* Test fetching of an undefined environment variable. Resolves to empty string. */
|
||||||
auto unknown_boolean = conf.get_scalar<std::string>("base_value.subvalue.subvalue2.boolean", default_value);
|
auto unknown_boolean = conf.get_scalar<std::string>("base_value.subvalue.subvalue2.boolean", default_value);
|
||||||
ASSERT_EQ(unknown_boolean, default_value);
|
ASSERT_EQ(unknown_boolean, "");
|
||||||
|
|
||||||
/* Test fetching of environment variables from a list */
|
/* Test fetching of environment variables from a list */
|
||||||
auto base_value_2_list_0 = conf.get_scalar<std::string>("base_value_2.sample_list[0]", default_value);
|
auto base_value_2_list_0 = conf.get_scalar<std::string>("base_value_2.sample_list[0]", default_value);
|
||||||
@ -237,9 +241,17 @@ TEST(Configuration, configuration_environment_variables)
|
|||||||
auto integer = conf.get_scalar<int32_t>("num_test", -1);
|
auto integer = conf.get_scalar<int32_t>("num_test", -1);
|
||||||
ASSERT_EQ(integer, 12);
|
ASSERT_EQ(integer, 12);
|
||||||
|
|
||||||
// An env var that resolves to an empty string returns default value
|
// An env var that resolves to an empty string returns ""
|
||||||
auto empty_default_str = conf.get_scalar<std::string>("empty_test", "test");
|
auto empty_default_str = conf.get_scalar<std::string>("empty_test", default_value);
|
||||||
ASSERT_EQ(empty_default_str, "test");
|
ASSERT_EQ(empty_default_str, "");
|
||||||
|
|
||||||
|
std::list<falco_configuration::plugin_config> plugins;
|
||||||
|
conf.get_sequence<std::list<falco_configuration::plugin_config>>(plugins, std::string("plugins"));
|
||||||
|
std::vector<falco_configuration::plugin_config> m_plugins{ std::make_move_iterator(std::begin(plugins)),
|
||||||
|
std::make_move_iterator(std::end(plugins)) };
|
||||||
|
ASSERT_EQ(m_plugins[0].m_name, "k8saudit");
|
||||||
|
ASSERT_EQ(m_plugins[0].m_library_path, "/foo/" + env_var_value + "/libk8saudit.so");
|
||||||
|
ASSERT_EQ(m_plugins[0].m_open_params, "12");
|
||||||
|
|
||||||
/* Clear the set environment variables after testing */
|
/* Clear the set environment variables after testing */
|
||||||
SET_ENV_VAR(env_var_name.c_str(), "");
|
SET_ENV_VAR(env_var_name.c_str(), "");
|
||||||
|
@ -31,13 +31,46 @@ limitations under the License.
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
#include "config_falco.h"
|
#include "config_falco.h"
|
||||||
|
|
||||||
#include "event_drops.h"
|
#include "event_drops.h"
|
||||||
#include "falco_outputs.h"
|
#include "falco_outputs.h"
|
||||||
|
|
||||||
|
class yaml_helper;
|
||||||
|
|
||||||
|
class yaml_visitor {
|
||||||
|
private:
|
||||||
|
using Callback = std::function<void(YAML::Node&)>;
|
||||||
|
yaml_visitor(Callback cb): seen(), cb(std::move(cb)) {}
|
||||||
|
|
||||||
|
void operator()(YAML::Node &cur) {
|
||||||
|
seen.push_back(cur);
|
||||||
|
if (cur.IsMap()) {
|
||||||
|
for (YAML::detail::iterator_value pair : cur) {
|
||||||
|
descend(pair.second);
|
||||||
|
}
|
||||||
|
} else if (cur.IsSequence()) {
|
||||||
|
for (YAML::detail::iterator_value child : cur) {
|
||||||
|
descend(child);
|
||||||
|
}
|
||||||
|
} else if (cur.IsScalar()) {
|
||||||
|
cb(cur);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void descend(YAML::Node &target) {
|
||||||
|
if (std::find(seen.begin(), seen.end(), target) == seen.end()) {
|
||||||
|
(*this)(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<YAML::Node> seen;
|
||||||
|
Callback cb;
|
||||||
|
|
||||||
|
friend class yaml_helper;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief An helper class for reading and editing YAML documents
|
* @brief An helper class for reading and editing YAML documents
|
||||||
*/
|
*/
|
||||||
@ -50,6 +83,7 @@ public:
|
|||||||
void load_from_string(const std::string& input)
|
void load_from_string(const std::string& input)
|
||||||
{
|
{
|
||||||
m_root = YAML::Load(input);
|
m_root = YAML::Load(input);
|
||||||
|
pre_process_env_vars();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -58,6 +92,7 @@ public:
|
|||||||
void load_from_file(const std::string& path)
|
void load_from_file(const std::string& path)
|
||||||
{
|
{
|
||||||
m_root = YAML::LoadFile(path);
|
m_root = YAML::LoadFile(path);
|
||||||
|
pre_process_env_vars();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,25 +113,56 @@ public:
|
|||||||
get_node(node, key);
|
get_node(node, key);
|
||||||
if(node.IsDefined())
|
if(node.IsDefined())
|
||||||
{
|
{
|
||||||
auto value = node.as<std::string>();
|
return node.as<T>(default_value);
|
||||||
|
}
|
||||||
// Helper function to convert string to the desired type T
|
|
||||||
auto convert_str_to_t = [&default_value](const std::string& str) -> T {
|
|
||||||
if (str.empty())
|
|
||||||
{
|
|
||||||
return default_value;
|
return default_value;
|
||||||
}
|
}
|
||||||
|
|
||||||
if constexpr (std::is_same_v<T, std::string>)
|
/**
|
||||||
|
* Set the node identified by key to value.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
void set_scalar(const std::string& key, const T& value)
|
||||||
{
|
{
|
||||||
return str;
|
YAML::Node node;
|
||||||
|
get_node(node, key);
|
||||||
|
node = value;
|
||||||
}
|
}
|
||||||
std::stringstream ss(str);
|
|
||||||
T result;
|
|
||||||
if (ss >> std::boolalpha >> result) return result;
|
|
||||||
return default_value;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the sequence value from the node identified by key.
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
void get_sequence(T& ret, const std::string& key) const
|
||||||
|
{
|
||||||
|
YAML::Node node;
|
||||||
|
get_node(node, key);
|
||||||
|
return get_sequence_from_node<T>(ret, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the node identified by key is defined.
|
||||||
|
*/
|
||||||
|
bool is_defined(const std::string& key) const
|
||||||
|
{
|
||||||
|
YAML::Node node;
|
||||||
|
get_node(node, key);
|
||||||
|
return node.IsDefined();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
YAML::Node m_root;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When loading a yaml file,
|
||||||
|
* we immediately pre process all scalar values through a visitor private API,
|
||||||
|
* and resolve any "${env_var}" to its value;
|
||||||
|
* moreover, any "$${str}" is resolved to simply "${str}".
|
||||||
|
*/
|
||||||
|
void pre_process_env_vars()
|
||||||
|
{
|
||||||
|
yaml_visitor([](YAML::Node &scalar) {
|
||||||
|
auto value = scalar.as<std::string>();
|
||||||
auto start_pos = value.find('$');
|
auto start_pos = value.find('$');
|
||||||
while (start_pos != std::string::npos)
|
while (start_pos != std::string::npos)
|
||||||
{
|
{
|
||||||
@ -148,47 +214,10 @@ public:
|
|||||||
}
|
}
|
||||||
start_pos = value.find("$", start_pos);
|
start_pos = value.find("$", start_pos);
|
||||||
}
|
}
|
||||||
return convert_str_to_t(value);
|
scalar = value;
|
||||||
|
})(m_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
return default_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the node identified by key to value.
|
|
||||||
*/
|
|
||||||
template<typename T>
|
|
||||||
void set_scalar(const std::string& key, const T& value)
|
|
||||||
{
|
|
||||||
YAML::Node node;
|
|
||||||
get_node(node, key);
|
|
||||||
node = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the sequence value from the node identified by key.
|
|
||||||
*/
|
|
||||||
template<typename T>
|
|
||||||
void get_sequence(T& ret, const std::string& key) const
|
|
||||||
{
|
|
||||||
YAML::Node node;
|
|
||||||
get_node(node, key);
|
|
||||||
return get_sequence_from_node<T>(ret, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if the node identified by key is defined.
|
|
||||||
*/
|
|
||||||
bool is_defined(const std::string& key) const
|
|
||||||
{
|
|
||||||
YAML::Node node;
|
|
||||||
get_node(node, key);
|
|
||||||
return node.IsDefined();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
YAML::Node m_root;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Key is a string representing a node in the YAML document.
|
* Key is a string representing a node in the YAML document.
|
||||||
* The provided key string can navigate the document in its
|
* The provided key string can navigate the document in its
|
||||||
|
Loading…
Reference in New Issue
Block a user