mirror of
https://github.com/falcosecurity/falco.git
synced 2025-06-28 07:37:32 +00:00
feat: support parsing of system environment variables in yaml
In order to allow the user to supply environment variables in standard ways performed in other applications the get_scalar function has been extended to support defining an environment variable in the format `${FOO}`. Environment variables can be escaped via defining as `$${FOO}`. As this handles some additional complexity, a unit test has been added to cover this new functionality Signed-off-by: Daniel Wright <danielwright@bitgo.com>
This commit is contained in:
parent
5ffffeeada
commit
513f122aff
@ -88,16 +88,88 @@ TEST(Configuration, modify_yaml_fields)
|
|||||||
{
|
{
|
||||||
std::string key = "base_value.subvalue.subvalue2.boolean";
|
std::string key = "base_value.subvalue.subvalue2.boolean";
|
||||||
yaml_helper conf;
|
yaml_helper conf;
|
||||||
|
|
||||||
/* Get original value */
|
/* Get original value */
|
||||||
conf.load_from_string(sample_yaml);
|
conf.load_from_string(sample_yaml);
|
||||||
ASSERT_EQ(conf.get_scalar<bool>(key, false), true);
|
ASSERT_EQ(conf.get_scalar<bool>(key, false), true);
|
||||||
|
|
||||||
/* Modify the original value */
|
/* Modify the original value */
|
||||||
conf.set_scalar<bool>(key, false);
|
conf.set_scalar<bool>(key, false);
|
||||||
ASSERT_EQ(conf.get_scalar<bool>(key, true), false);
|
ASSERT_EQ(conf.get_scalar<bool>(key, true), false);
|
||||||
|
|
||||||
/* Modify it again */
|
/* Modify it again */
|
||||||
conf.set_scalar<bool>(key, true);
|
conf.set_scalar<bool>(key, true);
|
||||||
ASSERT_EQ(conf.get_scalar<bool>(key, false), true);
|
ASSERT_EQ(conf.get_scalar<bool>(key, false), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(Configuration, configuration_environment_variables)
|
||||||
|
{
|
||||||
|
// Set an environment variable for testing purposes
|
||||||
|
std::string env_var_value = "envVarValue";
|
||||||
|
std::string env_var_name = "ENV_VAR";
|
||||||
|
std::string default_value = "default";
|
||||||
|
setenv(env_var_name.c_str(), env_var_value.c_str(), 1);
|
||||||
|
yaml_helper conf;
|
||||||
|
|
||||||
|
std::string sample_yaml =
|
||||||
|
"base_value:\n"
|
||||||
|
" id: $ENV_VAR\n"
|
||||||
|
" name: '${ENV_VAR}'\n"
|
||||||
|
" string: my_string\n"
|
||||||
|
" invalid: $${ENV_VAR}\n"
|
||||||
|
" invalid_env: $$ENV_VAR\n"
|
||||||
|
" escaped: \"${ENV_VAR}\"\n"
|
||||||
|
" subvalue:\n"
|
||||||
|
" subvalue2:\n"
|
||||||
|
" boolean: ${UNSED_XX_X_X_VAR}\n"
|
||||||
|
"base_value_2:\n"
|
||||||
|
" sample_list:\n"
|
||||||
|
" - ${ENV_VAR}\n"
|
||||||
|
" - ' ${ENV_VAR}'\n"
|
||||||
|
" - $UNSED_XX_X_X_VAR\n";
|
||||||
|
conf.load_from_string(sample_yaml);
|
||||||
|
|
||||||
|
/* Check if the base values are defined */
|
||||||
|
ASSERT_TRUE(conf.is_defined("base_value"));
|
||||||
|
ASSERT_TRUE(conf.is_defined("base_value_2"));
|
||||||
|
ASSERT_FALSE(conf.is_defined("unknown_base_value"));
|
||||||
|
|
||||||
|
/* Test fetching of a regular string without any environment variable */
|
||||||
|
std::string base_value_string = conf.get_scalar<std::string>("base_value.string", default_value);
|
||||||
|
ASSERT_EQ(base_value_string, "my_string");
|
||||||
|
|
||||||
|
/* Test fetching of escaped environment variable format. Should return the string as-is after stripping the leading `$` */
|
||||||
|
std::string base_value_invalid = conf.get_scalar<std::string>("base_value.invalid", default_value);
|
||||||
|
ASSERT_EQ(base_value_invalid, "${ENV_VAR}");
|
||||||
|
|
||||||
|
/* Test fetching of invalid escaped environment variable format. Should return the string as-is */
|
||||||
|
std::string base_value_invalid_env = conf.get_scalar<std::string>("base_value.invalid_env", default_value);
|
||||||
|
ASSERT_EQ(base_value_invalid_env, "$$ENV_VAR");
|
||||||
|
|
||||||
|
/* Test fetching of strings that contain environment variables */
|
||||||
|
std::string base_value_id = conf.get_scalar<std::string>("base_value.id", default_value);
|
||||||
|
ASSERT_EQ(base_value_id, "$ENV_VAR"); // Does not follow the `${VAR}` format, so it should be treated as a regular string
|
||||||
|
|
||||||
|
std::string base_value_name = conf.get_scalar<std::string>("base_value.name", default_value);
|
||||||
|
ASSERT_EQ(base_value_name, env_var_value); // Proper environment variable format
|
||||||
|
|
||||||
|
std::string 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.*/
|
||||||
|
std::string unknown_boolean = conf.get_scalar<std::string>("base_value.subvalue.subvalue2.boolean", default_value);
|
||||||
|
ASSERT_EQ(unknown_boolean, default_value);
|
||||||
|
|
||||||
|
/* Test fetching of environment variables from a list */
|
||||||
|
std::string base_value_2_list_0 = conf.get_scalar<std::string>("base_value_2.sample_list[0]", default_value);
|
||||||
|
ASSERT_EQ(base_value_2_list_0, env_var_value); // Proper environment variable format
|
||||||
|
|
||||||
|
std::string base_value_2_list_1 = conf.get_scalar<std::string>("base_value_2.sample_list[1]", default_value);
|
||||||
|
ASSERT_EQ(base_value_2_list_1, " ${ENV_VAR}"); // Environment variable preceded by a space, hence treated as a regular string
|
||||||
|
|
||||||
|
std::string base_value_2_list_2 = conf.get_scalar<std::string>("base_value_2.sample_list[2]", default_value);
|
||||||
|
ASSERT_EQ(base_value_2_list_2, "$UNSED_XX_X_X_VAR"); // Does not follow the `${VAR}` format, so should be treated as a regular string
|
||||||
|
|
||||||
|
/* Clear the set environment variable after testing */
|
||||||
|
unsetenv(env_var_name.c_str());
|
||||||
|
}
|
||||||
|
@ -72,6 +72,41 @@ public:
|
|||||||
get_node(node, key);
|
get_node(node, key);
|
||||||
if(node.IsDefined())
|
if(node.IsDefined())
|
||||||
{
|
{
|
||||||
|
std::string 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 {
|
||||||
|
std::stringstream ss(str);
|
||||||
|
T result;
|
||||||
|
if (ss >> result) return result;
|
||||||
|
return default_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// If the value starts with `$$`, check for a subsequent `{...}`
|
||||||
|
if (value.size() >= 3 && value[0] == '$' && value[1] == '$')
|
||||||
|
{
|
||||||
|
// If after stripping the first `$`, the string format is like `${VAR}`, treat it as a plain string and don't resolve.
|
||||||
|
if (value[2] == '{' && value[value.size() - 1] == '}')
|
||||||
|
{
|
||||||
|
value = value.substr(1);
|
||||||
|
return convert_str_to_t(value);
|
||||||
|
}
|
||||||
|
else return convert_str_to_t(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the value is an environment variable reference
|
||||||
|
if(value.size() >= 2 && value[0] == '$' && value[1] == '{' && value[value.size() - 1] == '}')
|
||||||
|
{
|
||||||
|
// Format: ${ENV_VAR_NAME}
|
||||||
|
std::string env_var = value.substr(2, value.size() - 3);
|
||||||
|
|
||||||
|
const char* env_value = std::getenv(env_var.c_str()); // Get the environment variable value
|
||||||
|
if(env_value) return convert_str_to_t(env_value);
|
||||||
|
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If it's not an environment variable reference, return the value as is
|
||||||
return node.as<T>();
|
return node.as<T>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,10 +153,10 @@ private:
|
|||||||
* The provided key string can navigate the document in its
|
* The provided key string can navigate the document in its
|
||||||
* nested nodes, with arbitrary depth. The key string follows
|
* nested nodes, with arbitrary depth. The key string follows
|
||||||
* this regular language:
|
* this regular language:
|
||||||
*
|
*
|
||||||
* Key := NodeKey ('.' NodeKey)*
|
* Key := NodeKey ('.' NodeKey)*
|
||||||
* NodeKey := (any)+ ('[' (integer)+ ']')*
|
* NodeKey := (any)+ ('[' (integer)+ ']')*
|
||||||
*
|
*
|
||||||
* Some examples of accepted key strings:
|
* Some examples of accepted key strings:
|
||||||
* - NodeName
|
* - NodeName
|
||||||
* - ListValue[3].subvalue
|
* - ListValue[3].subvalue
|
||||||
@ -146,7 +181,7 @@ private:
|
|||||||
if (i > 0 && nodeKey.empty() && key[i - 1] != '.')
|
if (i > 0 && nodeKey.empty() && key[i - 1] != '.')
|
||||||
{
|
{
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"Parsing error: expected '.' character at pos "
|
"Parsing error: expected '.' character at pos "
|
||||||
+ std::to_string(i - 1));
|
+ std::to_string(i - 1));
|
||||||
}
|
}
|
||||||
nodeKey += c;
|
nodeKey += c;
|
||||||
@ -157,7 +192,7 @@ private:
|
|||||||
if (nodeKey.empty())
|
if (nodeKey.empty())
|
||||||
{
|
{
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
"Parsing error: unexpected character at pos "
|
"Parsing error: unexpected character at pos "
|
||||||
+ std::to_string(i));
|
+ std::to_string(i));
|
||||||
}
|
}
|
||||||
ret.reset(ret[nodeKey]);
|
ret.reset(ret[nodeKey]);
|
||||||
@ -181,7 +216,7 @@ private:
|
|||||||
throw std::runtime_error("Config error at key \"" + key + "\": " + std::string(e.what()));
|
throw std::runtime_error("Config error at key \"" + key + "\": " + std::string(e.what()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void get_sequence_from_node(T& ret, const YAML::Node& node) const
|
void get_sequence_from_node(T& ret, const YAML::Node& node) const
|
||||||
{
|
{
|
||||||
@ -250,7 +285,7 @@ namespace YAML {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user