fix(config): prevent plugin library path traversal via relative paths

Signed-off-by: Leonardo Grasso <me@leonardograsso.com>
This commit is contained in:
Leonardo Grasso
2026-04-08 15:40:14 +02:00
parent 07d627a8e6
commit f57b0e74f6
2 changed files with 52 additions and 0 deletions

View File

@@ -146,6 +146,41 @@ plugins:
EXPECT_EQ(falco_config.m_plugins[0].m_init_config, "");
}
TEST(Configuration, plugin_library_path_traversal) {
falco_configuration falco_config;
config_loaded_res res;
// A relative path that stays within the plugins dir should succeed.
std::string config = R"(
plugins:
- name: myplugin
library_path: libmyplugin.so
)";
EXPECT_NO_THROW(res = falco_config.init_from_content(config, {}));
EXPECT_VALIDATION_STATUS(res, yaml_helper::validation_ok);
// The resolved path must start with the plugins dir.
EXPECT_TRUE(sinsp_utils::startswith(falco_config.m_plugins[0].m_library_path,
FALCO_ENGINE_PLUGINS_DIR));
// A relative path with ".." that escapes the plugins dir must be rejected.
config = R"(
plugins:
- name: evil
library_path: ../../tmp/evil.so
)";
EXPECT_THROW(falco_config.init_from_content(config, {}), std::exception);
// Absolute paths bypass the prefix logic and are allowed as-is.
config = R"(
plugins:
- name: myplugin
library_path: /opt/falco/plugins/libmyplugin.so
)";
EXPECT_NO_THROW(res = falco_config.init_from_content(config, {}));
EXPECT_VALIDATION_STATUS(res, yaml_helper::validation_ok);
EXPECT_EQ(falco_config.m_plugins[0].m_library_path, "/opt/falco/plugins/libmyplugin.so");
}
TEST(Configuration, schema_yaml_helper_validator) {
yaml_helper conf;
falco_configuration falco_config;

View File

@@ -31,6 +31,7 @@ limitations under the License.
#include <set>
#include <iostream>
#include <fstream>
#include <filesystem>
#include "config_falco.h"
#include "yaml_helper.h"
@@ -403,6 +404,22 @@ struct convert<falco_configuration::plugin_config> {
if(!rhs.m_library_path.empty() && rhs.m_library_path.at(0) != '/') {
// prepend share dir if path is not absolute
rhs.m_library_path = std::string(FALCO_ENGINE_PLUGINS_DIR) + rhs.m_library_path;
// Canonicalize to resolve ".." components and verify
// the resulting path stays within the plugins directory.
auto canonical_str = std::filesystem::weakly_canonical(rhs.m_library_path).string();
auto plugins_dir_str =
std::filesystem::weakly_canonical(FALCO_ENGINE_PLUGINS_DIR).string();
if(!plugins_dir_str.empty() && plugins_dir_str.back() != '/') {
plugins_dir_str += '/';
}
if(canonical_str.compare(0, plugins_dir_str.size(), plugins_dir_str) != 0) {
throw YAML::Exception(node["library_path"].Mark(),
"plugin library_path '" +
node["library_path"].as<std::string>() +
"' resolves outside the plugins directory (" +
std::string(FALCO_ENGINE_PLUGINS_DIR) + ")");
}
rhs.m_library_path = canonical_str;
}
if(node["init_config"] && !node["init_config"].IsNull()) {