mirror of
https://github.com/falcosecurity/falco.git
synced 2025-07-12 05:58:26 +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"
|
||||
"is_test: ${ENV_VAR_BOOL}\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;
|
||||
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);
|
||||
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);
|
||||
ASSERT_EQ(unknown_boolean, default_value);
|
||||
ASSERT_EQ(unknown_boolean, "");
|
||||
|
||||
/* 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);
|
||||
@ -237,9 +241,17 @@ TEST(Configuration, configuration_environment_variables)
|
||||
auto integer = conf.get_scalar<int32_t>("num_test", -1);
|
||||
ASSERT_EQ(integer, 12);
|
||||
|
||||
// An env var that resolves to an empty string returns default value
|
||||
auto empty_default_str = conf.get_scalar<std::string>("empty_test", "test");
|
||||
ASSERT_EQ(empty_default_str, "test");
|
||||
// An env var that resolves to an empty string returns ""
|
||||
auto empty_default_str = conf.get_scalar<std::string>("empty_test", default_value);
|
||||
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 */
|
||||
SET_ENV_VAR(env_var_name.c_str(), "");
|
||||
|
@ -31,13 +31,46 @@ limitations under the License.
|
||||
#include <set>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <type_traits>
|
||||
|
||||
#include "config_falco.h"
|
||||
|
||||
#include "event_drops.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
|
||||
*/
|
||||
@ -50,6 +83,7 @@ public:
|
||||
void load_from_string(const std::string& input)
|
||||
{
|
||||
m_root = YAML::Load(input);
|
||||
pre_process_env_vars();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -58,6 +92,7 @@ public:
|
||||
void load_from_file(const std::string& path)
|
||||
{
|
||||
m_root = YAML::LoadFile(path);
|
||||
pre_process_env_vars();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -78,79 +113,8 @@ public:
|
||||
get_node(node, key);
|
||||
if(node.IsDefined())
|
||||
{
|
||||
auto value = node.as<std::string>();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
if constexpr (std::is_same_v<T, std::string>)
|
||||
{
|
||||
return str;
|
||||
}
|
||||
std::stringstream ss(str);
|
||||
T result;
|
||||
if (ss >> std::boolalpha >> result) return result;
|
||||
return default_value;
|
||||
};
|
||||
|
||||
auto start_pos = value.find('$');
|
||||
while (start_pos != std::string::npos)
|
||||
{
|
||||
auto substr = value.substr(start_pos);
|
||||
// Case 1 -> ${}
|
||||
if (substr.rfind("${", 0) == 0)
|
||||
{
|
||||
auto end_pos = substr.find('}');
|
||||
if (end_pos != std::string::npos)
|
||||
{
|
||||
// Eat "${" and "}" when getting the env var name
|
||||
auto env_str = substr.substr(2, end_pos - 2);
|
||||
const char* env_value = std::getenv(env_str.c_str()); // Get the environment variable value
|
||||
if(env_value)
|
||||
{
|
||||
// env variable name + "${}"
|
||||
value.replace(start_pos, env_str.length() + 3, env_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
value.erase(start_pos, env_str.length() + 3);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// There are no "}" chars anymore; just break leaving rest of value untouched.
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Case 2 -> $${}
|
||||
else if (substr.rfind("$${", 0) == 0)
|
||||
{
|
||||
auto end_pos = substr.find('}');
|
||||
if (end_pos != std::string::npos)
|
||||
{
|
||||
// Consume first "$" token
|
||||
value.erase(start_pos, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// There are no "}" chars anymore; just break leaving rest of value untouched.
|
||||
break;
|
||||
}
|
||||
start_pos++; // consume the second '$' token
|
||||
}
|
||||
else
|
||||
{
|
||||
start_pos += substr.length();
|
||||
}
|
||||
start_pos = value.find("$", start_pos);
|
||||
}
|
||||
return convert_str_to_t(value);
|
||||
return node.as<T>(default_value);
|
||||
}
|
||||
|
||||
return default_value;
|
||||
}
|
||||
|
||||
@ -189,6 +153,71 @@ public:
|
||||
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('$');
|
||||
while (start_pos != std::string::npos)
|
||||
{
|
||||
auto substr = value.substr(start_pos);
|
||||
// Case 1 -> ${}
|
||||
if (substr.rfind("${", 0) == 0)
|
||||
{
|
||||
auto end_pos = substr.find('}');
|
||||
if (end_pos != std::string::npos)
|
||||
{
|
||||
// Eat "${" and "}" when getting the env var name
|
||||
auto env_str = substr.substr(2, end_pos - 2);
|
||||
const char* env_value = std::getenv(env_str.c_str()); // Get the environment variable value
|
||||
if(env_value)
|
||||
{
|
||||
// env variable name + "${}"
|
||||
value.replace(start_pos, env_str.length() + 3, env_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
value.erase(start_pos, env_str.length() + 3);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// There are no "}" chars anymore; just break leaving rest of value untouched.
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Case 2 -> $${}
|
||||
else if (substr.rfind("$${", 0) == 0)
|
||||
{
|
||||
auto end_pos = substr.find('}');
|
||||
if (end_pos != std::string::npos)
|
||||
{
|
||||
// Consume first "$" token
|
||||
value.erase(start_pos, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
// There are no "}" chars anymore; just break leaving rest of value untouched.
|
||||
break;
|
||||
}
|
||||
start_pos++; // consume the second '$' token
|
||||
}
|
||||
else
|
||||
{
|
||||
start_pos += substr.length();
|
||||
}
|
||||
start_pos = value.find("$", start_pos);
|
||||
}
|
||||
scalar = value;
|
||||
})(m_root);
|
||||
}
|
||||
|
||||
/**
|
||||
* Key is a string representing a node in the YAML document.
|
||||
* The provided key string can navigate the document in its
|
||||
|
Loading…
Reference in New Issue
Block a user