sync code

This commit is contained in:
Ned Wright
2026-01-03 18:59:01 +00:00
parent c1058db57d
commit 2105628f05
188 changed files with 8272 additions and 2723 deletions

View File

@@ -15,286 +15,10 @@
#ifndef __NGINX_ATTACHMENT_COMMON_H__
#define __NGINX_ATTACHMENT_COMMON_H__
#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>
#include <assert.h>
// This file has been deprecated. Do not add anything here.
// Any future additions should be added to nano_attachment_common.h
// For any inquiries please contact Daniel Yashin.
#define MAX_NGINX_UID_LEN 32
#define NUM_OF_NGINX_IPC_ELEMENTS 200
#define DEFAULT_KEEP_ALIVE_INTERVAL_MSEC 300000
#define SHARED_MEM_PATH "/dev/shm/"
#define SHARED_REGISTRATION_SIGNAL_PATH SHARED_MEM_PATH "check-point/cp-nano-attachment-registration"
#define SHARED_KEEP_ALIVE_PATH SHARED_MEM_PATH "check-point/cp-nano-attachment-registration-expiration-socket"
#define SHARED_VERDICT_SIGNAL_PATH SHARED_MEM_PATH "check-point/cp-nano-http-transaction-handler"
#define SHARED_ATTACHMENT_CONF_PATH SHARED_MEM_PATH "cp_nano_http_attachment_conf"
#define DEFAULT_STATIC_RESOURCES_PATH SHARED_MEM_PATH "static_resources"
#define INJECT_POS_IRRELEVANT -1
#define CORRUPTED_SESSION_ID 0
#define METRIC_PERIODIC_TIMEOUT 600
extern char shared_verdict_signal_path[];
extern int workers_amount_to_send;
typedef int64_t ngx_http_cp_inject_pos_t;
#ifdef __cplusplus
typedef enum class ngx_http_modification_type
#else
typedef enum ngx_http_modification_type
#endif
{
APPEND,
INJECT,
REPLACE
} ngx_http_modification_type_e;
#ifdef __cplusplus
typedef enum class ngx_http_chunk_type
#else
typedef enum ngx_http_chunk_type
#endif
{
REQUEST_START,
REQUEST_HEADER,
REQUEST_BODY,
REQUEST_END,
RESPONSE_CODE,
RESPONSE_HEADER,
RESPONSE_BODY,
RESPONSE_END,
CONTENT_LENGTH,
METRIC_DATA_FROM_PLUGIN,
HOLD_DATA,
COUNT
} ngx_http_chunk_type_e;
#ifdef __cplusplus
typedef enum class ngx_http_plugin_metric_type
#else
typedef enum ngx_http_plugin_metric_type
#endif
{
TRANSPARENTS_COUNT,
TOTAL_TRANSPARENTS_TIME,
INSPECTION_OPEN_FAILURES_COUNT,
INSPECTION_CLOSE_FAILURES_COUNT,
INSPECTION_SUCCESSES_COUNT,
INJECT_VERDICTS_COUNT,
DROP_VERDICTS_COUNT,
ACCEPT_VERDICTS_COUNT,
IRRELEVANT_VERDICTS_COUNT,
RECONF_VERDICTS_COUNT,
INSPECT_VERDICTS_COUNT,
HOLD_VERDICTS_COUNT,
AVERAGE_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT,
MAX_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT,
MIN_OVERALL_PPROCESSING_TIME_UNTIL_VERDICT,
AVERAGE_REQ_PPROCESSING_TIME_UNTIL_VERDICT,
MAX_REQ_PPROCESSING_TIME_UNTIL_VERDICT,
MIN_REQ_PPROCESSING_TIME_UNTIL_VERDICT,
AVERAGE_RES_PPROCESSING_TIME_UNTIL_VERDICT,
MAX_RES_PPROCESSING_TIME_UNTIL_VERDICT,
MIN_RES_PPROCESSING_TIME_UNTIL_VERDICT,
THREAD_TIMEOUT,
REG_THREAD_TIMEOUT,
REQ_HEADER_THREAD_TIMEOUT,
REQ_BODY_THREAD_TIMEOUT,
AVERAGE_REQ_BODY_SIZE_UPON_TIMEOUT,
MAX_REQ_BODY_SIZE_UPON_TIMEOUT,
MIN_REQ_BODY_SIZE_UPON_TIMEOUT,
RES_HEADER_THREAD_TIMEOUT,
RES_BODY_THREAD_TIMEOUT,
HOLD_THREAD_TIMEOUT,
AVERAGE_RES_BODY_SIZE_UPON_TIMEOUT,
MAX_RES_BODY_SIZE_UPON_TIMEOUT,
MIN_RES_BODY_SIZE_UPON_TIMEOUT,
THREAD_FAILURE,
REQ_PROCCESSING_TIMEOUT,
RES_PROCCESSING_TIMEOUT,
REQ_FAILED_TO_REACH_UPSTREAM,
REQ_FAILED_COMPRESSION_COUNT,
RES_FAILED_COMPRESSION_COUNT,
REQ_FAILED_DECOMPRESSION_COUNT,
RES_FAILED_DECOMPRESSION_COUNT,
REQ_SUCCESSFUL_COMPRESSION_COUNT,
RES_SUCCESSFUL_COMPRESSION_COUNT,
REQ_SUCCESSFUL_DECOMPRESSION_COUNT,
RES_SUCCESSFUL_DECOMPRESSION_COUNT,
CORRUPTED_ZIP_SKIPPED_SESSION_COUNT,
CPU_USAGE,
AVERAGE_VM_MEMORY_USAGE,
AVERAGE_RSS_MEMORY_USAGE,
MAX_VM_MEMORY_USAGE,
MAX_RSS_MEMORY_USAGE,
REQUEST_OVERALL_SIZE_COUNT,
RESPONSE_OVERALL_SIZE_COUNT,
METRIC_TYPES_COUNT
} ngx_http_plugin_metric_type_e;
#ifdef __cplusplus
typedef enum class ngx_http_cp_verdict
#else
typedef enum ngx_http_cp_verdict
#endif
{
TRAFFIC_VERDICT_INSPECT,
TRAFFIC_VERDICT_ACCEPT,
TRAFFIC_VERDICT_DROP,
TRAFFIC_VERDICT_INJECT,
TRAFFIC_VERDICT_IRRELEVANT,
TRAFFIC_VERDICT_RECONF,
TRAFFIC_VERDICT_WAIT,
LIMIT_RESPONSE_HEADERS
} ngx_http_cp_verdict_e;
#ifdef __cplusplus
typedef enum class ngx_http_cp_debug_level
#else
typedef enum ngx_http_cp_debug_level
#endif
{
DBG_LEVEL_TRACE,
DBG_LEVEL_DEBUG,
DBG_LEVEL_INFO,
DBG_LEVEL_WARNING,
DBG_LEVEL_ERROR,
#ifndef __cplusplus
DBG_LEVEL_ASSERT,
#endif
DBG_LEVEL_COUNT
} ngx_http_cp_debug_level_e;
#ifdef __cplusplus
typedef enum class ngx_http_meta_data
#else
typedef enum ngx_http_meta_data
#endif
{
HTTP_PROTOCOL_SIZE,
HTTP_PROTOCOL_DATA,
HTTP_METHOD_SIZE,
HTTP_METHOD_DATA,
HOST_NAME_SIZE,
HOST_NAME_DATA,
LISTENING_ADDR_SIZE,
LISTENING_ADDR_DATA,
LISTENING_PORT,
URI_SIZE,
URI_DATA,
CLIENT_ADDR_SIZE,
CLIENT_ADDR_DATA,
CLIENT_PORT,
PARSED_HOST_SIZE,
PARSED_HOST_DATA,
PARSED_URI_SIZE,
PARSED_URI_DATA,
WAF_TAG_SIZE,
WAF_TAG_DATA,
META_DATA_COUNT
} ngx_http_meta_data_e;
#ifdef __cplusplus
typedef enum class ngx_http_header_data
#else
typedef enum ngx_http_header_data
#endif
{
HEADER_KEY_SIZE,
HEADER_KEY_DATA,
HEADER_VAL_SIZE,
HEADER_VAL_DATA,
HEADER_DATA_COUNT
} ngx_http_header_data_e;
typedef enum ngx_http_inspection_mode
{
NON_BLOCKING_THREAD,
BLOCKING_THREAD,
NO_THREAD,
INSPECTION_MODE_COUNT
} ngx_http_inspection_mode_e;
#ifdef __cplusplus
typedef enum class ngx_web_response_type
#else
typedef enum ngx_web_response_type
#endif
{
CUSTOM_WEB_RESPONSE,
CUSTOM_WEB_BLOCK_PAGE_RESPONSE,
RESPONSE_CODE_ONLY,
REDIRECT_WEB_RESPONSE,
NO_WEB_RESPONSE
} ngx_web_response_type_e;
typedef struct __attribute__((__packed__)) ngx_http_cp_inject_data {
ngx_http_cp_inject_pos_t injection_pos;
ngx_http_modification_type_e mod_type;
uint16_t injection_size;
uint8_t is_header;
uint8_t orig_buff_index;
char data[0];
} ngx_http_cp_inject_data_t;
typedef struct __attribute__((__packed__)) ngx_http_cp_web_response_data {
uint8_t web_repsonse_type;
uint8_t uuid_size;
union {
struct __attribute__((__packed__)) ngx_http_cp_custom_web_response_data {
uint16_t response_code;
uint8_t title_size;
uint8_t body_size;
char data[0];
} custom_response_data;
struct __attribute__((__packed__)) ngx_http_cp_redirect_data {
uint8_t unused_dummy;
uint8_t add_event_id;
uint16_t redirect_location_size;
char redirect_location[0];
} redirect_data;
} response_data;
} ngx_http_cp_web_response_data_t;
static_assert(
sizeof(((ngx_http_cp_web_response_data_t*)0)->response_data.custom_response_data) ==
sizeof(((ngx_http_cp_web_response_data_t*)0)->response_data.redirect_data),
"custom_response_data must be equal to redirect_data in size"
);
typedef union __attribute__((__packed__)) ngx_http_cp_modify_data {
ngx_http_cp_inject_data_t inject_data[0];
ngx_http_cp_web_response_data_t web_response_data[0];
} ngx_http_cp_modify_data_t;
typedef struct __attribute__((__packed__)) ngx_http_cp_reply_from_service {
uint16_t verdict;
uint32_t session_id;
uint8_t modification_count;
ngx_http_cp_modify_data_t modify_data[0];
} ngx_http_cp_reply_from_service_t;
typedef struct __attribute__((__packed__)) ngx_http_cp_request_data {
uint16_t data_type;
uint32_t session_id;
unsigned char data[0];
} ngx_http_cp_request_data_t;
typedef struct __attribute__((__packed__)) ngx_http_cp_metric_data {
uint16_t data_type;
#ifdef __cplusplus
uint64_t data[static_cast<int>(ngx_http_plugin_metric_type::METRIC_TYPES_COUNT)];
#else
uint64_t data[METRIC_TYPES_COUNT];
#endif
} ngx_http_cp_metric_data_t;
#endif // __NGINX_ATTACHMENT_COMMON_H__

View File

@@ -17,7 +17,7 @@
#include <stdio.h>
#include "nginx_attachment_common.h"
#include "nano_attachment_common.h"
#ifdef __cplusplus
extern "C" {
@@ -29,7 +29,7 @@ typedef const char * c_str;
int initAttachmentConfig(c_str conf_file);
ngx_http_inspection_mode_e getInspectionMode();
NanoHttpInspectionMode getInspectionMode();
unsigned int getNumOfNginxIpcElements();
unsigned int getKeepAliveIntervalMsec();
unsigned int getDbgLevel();
@@ -61,11 +61,16 @@ unsigned int getMinRetriesForVerdict();
unsigned int getMaxRetriesForVerdict();
unsigned int getReqBodySizeTrigger();
unsigned int getRemoveResServerHeader();
unsigned int getDecompressionPoolSize();
unsigned int getRecompressionPoolSize();
unsigned int getIsBrotliInspectionEnabled();
unsigned int getWaitingForVerdictThreadTimeout();
int isIPAddress(c_str ip_str);
int isSkipSource(c_str ip_str);
unsigned int isPairedAffinityEnabled();
unsigned int isAsyncModeEnabled();
#ifdef __cplusplus
}

View File

@@ -36,6 +36,13 @@ class I_SignalHandler;
namespace Config { enum class Errors; }
std::ostream & operator<<(std::ostream &, const Config::Errors &);
template <typename Rep, typename Period>
std::ostream& operator<<(std::ostream& os, const std::chrono::duration<Rep, Period>& d)
{
os << d.count();
return os;
}
enum class AlertTeam { CORE, WAAP, SDWAN, IOT };
class AlertInfo
@@ -233,9 +240,11 @@ public:
static void setNewDefaultStdout(std::ostream *new_stream);
static void setUnitTestFlag(DebugFlags flag, DebugLevel level);
static void setDebugFlag(DebugFlags flag, DebugLevel level);
static std::string findDebugFilePrefix(const std::string &file_name);
static std::string getExecutableName();
static bool getDebugFlagFromString(const std::string &flag_name, DebugFlags &flag);
private:
template <typename T, typename... Args>

View File

@@ -32,7 +32,8 @@ class IntelligenceComponentV2
Singleton::Consume<I_MainLoop>,
Singleton::Consume<I_AgentDetails>,
Singleton::Consume<I_RestApi>,
Singleton::Consume<I_TimeGet>
Singleton::Consume<I_TimeGet>,
Singleton::Consume<I_Environment>
{
public:
IntelligenceComponentV2();

View File

@@ -8,6 +8,16 @@
#include "messaging/http_response.h"
#include "i_http_client.h"
struct CurlHttpClientConfig {
int timeout_seconds = 30;
int connect_timeout_seconds = 10;
bool verbose_enabled = false;
bool ssl_verify_peer = true;
bool ssl_verify_host = true;
long http_version = CURL_HTTP_VERSION_NONE;
std::string user_agent = "";
};
class CurlHttpClient : public I_HttpClient
{
public:
@@ -17,6 +27,7 @@ public:
void setProxy(const std::string& hosts) override;
void setBasicAuth(const std::string& username, const std::string& password) override;
void authEnabled(bool enabled) override;
void setConfigs(const CurlHttpClientConfig& config);
HTTPResponse
get(
@@ -70,6 +81,8 @@ private:
bool auth_enabled;
std::string username;
std::string password;
CurlHttpClientConfig config;
};
#endif // __CURL_HTTP_CLIENT_H__

View File

@@ -17,6 +17,8 @@
#include "maybe_res.h"
#include <string>
#include <memory>
static const std::string data1_file_name = "data1.a";
static const std::string data4_file_name = "data4.a";
@@ -29,6 +31,7 @@ static const std::string session_token_file_name = "data3.a";
class I_Encryptor
{
public:
// Base64
virtual std::string base64Encode(const std::string &input) = 0;
virtual std::string base64Decode(const std::string &input) = 0;

View File

@@ -47,6 +47,16 @@ public:
const std::string &routine_name,
bool is_primary = false
) = 0;
virtual RoutineID
addBalancedIntervalRoutine(
RoutineType priority,
std::chrono::microseconds interval,
Routine func,
const std::string &routine_name,
std::chrono::microseconds offset = std::chrono::microseconds(0),
bool is_primary = false
) = 0;
virtual RoutineID
addFileRoutine(

View File

@@ -85,8 +85,8 @@ public:
) = 0;
virtual Maybe<void, HTTPResponse> uploadFile(
const std::string & uri,
const std::string & upload_file_path,
const std::string &uri,
const std::string &upload_file_path,
const MessageCategory category = MessageCategory::GENERIC,
MessageMetadata message_metadata = MessageMetadata()
) = 0;
@@ -100,6 +100,8 @@ public:
virtual bool setFogConnection(MessageCategory category = MessageCategory::GENERIC) = 0;
virtual void clearConnections() = 0;
protected:
virtual ~I_Messaging() {}
};

View File

@@ -53,8 +53,13 @@ public:
const std::string &uri,
const std::function<std::string(const std::string &)> &callback
) = 0;
virtual bool addPostCall(
const std::string &uri,
const std::function<Maybe<std::string>(const std::string &)> &callback
) = 0;
virtual uint16_t getListeningPort() const = 0;
virtual uint16_t getStartingPortRange() const = 0;
protected:
~I_RestApi() {}

View File

@@ -36,6 +36,7 @@ public:
virtual void closeSocket(socketFd &socket) = 0;
virtual bool writeData(socketFd socket, const std::vector<char> &data) = 0;
virtual bool writeDataAsync(socketFd socket, const std::vector<char> &data) = 0;
virtual Maybe<std::vector<char>> receiveData(socketFd socket, uint data_size, bool is_blocking = true) = 0;
virtual bool isDataAvailable(socketFd socket) = 0;

View File

@@ -80,6 +80,15 @@ I_Messaging::sendSyncMessage(
);
if (!response_data.ok()) return response_data.passErr();
if (response_data.unpack().getHTTPStatusCode() != HTTPStatusCode::HTTP_OK) {
return genError(
HTTPResponse(
response_data.unpack().getHTTPStatusCode(),
response_data.unpack().getBody()
)
);
}
auto res_obj = req_obj.loadJson(response_data.unpack().getBody());
if (!res_obj) {
return genError(
@@ -114,6 +123,7 @@ I_Messaging::sendSyncMessageWithoutResponse(
category,
message_metadata
);
if (!response_data.ok()) {
dbgWarning(D_MESSAGING)
<< "Received error from server. Status code: "
@@ -122,6 +132,16 @@ I_Messaging::sendSyncMessageWithoutResponse(
<< response_data.getErr().getBody();
return false;
}
if (response_data.unpack().getHTTPStatusCode() != HTTPStatusCode::HTTP_OK) {
dbgWarning(D_MESSAGING)
<< "Unexpected status code from server. Status code: "
<< int(response_data.unpack().getHTTPStatusCode())
<< ", response body: "
<< response_data.unpack().getBody();
return false;
}
return true;
}

View File

@@ -15,6 +15,11 @@ public:
uint (RoutineType, std::chrono::microseconds, Routine, const std::string &, bool)
);
MOCK_METHOD6(
addBalancedIntervalRoutine,
uint (RoutineType, std::chrono::microseconds, Routine, const std::string &, std::chrono::microseconds, bool)
);
MOCK_METHOD5(
addFileRoutine,
uint (RoutineType, int, Routine, const std::string &, bool)

View File

@@ -54,6 +54,7 @@ public:
MOCK_METHOD4(setFogConnection, bool(const string &, uint16_t, bool, MessageCategory));
MOCK_METHOD0(setFogConnection, bool());
MOCK_METHOD1(setFogConnection, bool(MessageCategory));
MOCK_METHOD0(clearConnections, void());
};
static std::ostream &

View File

@@ -9,7 +9,12 @@ class MockRestApi : public Singleton::Provide<I_RestApi>::From<MockProvider<I_Re
{
public:
MOCK_CONST_METHOD0(getListeningPort, uint16_t());
MOCK_CONST_METHOD0(getStartingPortRange, uint16_t());
MOCK_METHOD2(addGetCall, bool(const std::string &, const std::function<std::string()> &));
MOCK_METHOD2(
addPostCall,
bool(const std::string &, const std::function<Maybe<std::string>(const std::string &)> &)
);
MOCK_METHOD2(
addWildcardGetCall,
bool(const std::string &, const std::function<std::string(const std::string &)> &)

View File

@@ -15,6 +15,7 @@ public:
MOCK_METHOD1(closeSocket, void (socketFd &));
MOCK_METHOD1(isDataAvailable, bool (socketFd));
MOCK_METHOD2(writeData, bool (socketFd, const std::vector<char> &));
MOCK_METHOD2(writeDataAsync, bool (socketFd, const std::vector<char> &));
MOCK_METHOD3(receiveData, Maybe<std::vector<char>> (socketFd, uint, bool is_blocking));
};

View File

@@ -55,7 +55,8 @@ class AgentDetails
Singleton::Consume<I_Encryptor>,
Singleton::Consume<I_ShellCmd>,
Singleton::Consume<I_Environment>,
Singleton::Consume<I_MainLoop>
Singleton::Consume<I_MainLoop>,
Singleton::Consume<I_Messaging>
{
public:
AgentDetails() : Component("AgentDetails") {}
@@ -80,7 +81,10 @@ public:
void setFogDomain(const std::string &_fog_domain) { fog_domain = _fog_domain; }
void setFogPort(const uint16_t _fog_port) { fog_port = _fog_port; }
void setProxy(const std::string &_proxy) { proxy = _proxy; }
void setProxy(const std::string &_proxy) {
previous_proxy = proxy;
proxy = _proxy;
}
void setAgentId(const std::string &_agent_id) { agent_id = _agent_id; }
void setProfileId(const std::string &_profile_id) { profile_id = _profile_id; }
void setTenantId(const std::string &_tenant_id) { tenant_id = _tenant_id; }
@@ -121,6 +125,7 @@ private:
OrchestrationMode orchestration_mode = OrchestrationMode::ONLINE;
std::string server = "Unknown";
bool is_proxy_configured_via_settings = false;
std::string previous_proxy = "";
std::map<ProxyProtocol, ProxyData> proxies;
static const std::map<std::string, I_AgentDetails::MachineType> machineTypes;

View File

@@ -18,10 +18,20 @@
#error "config_impl.h should not be included directly"
#endif // __CONFIG_H__
#include <fstream>
#include <unistd.h>
#include <atomic>
#include <cstdlib>
#include <algorithm>
#include <cctype>
namespace Config
{
class MockConfigProvider : Singleton::Provide<I_Config> {};
class MockConfigProvider
:
public Singleton::Provide<I_Config>,
public Singleton::Consume<I_Environment>
{};
template<typename String>
std::size_t
@@ -62,22 +72,290 @@ getVector(const Strings & ... strs)
return res;
}
// Utility function to create a separated string from a vector
inline std::string
makeSeparatedStr(const std::vector<std::string> &vec, const std::string &separator = ", ")
{
if (vec.empty()) return "";
if (vec.size() == 1) return vec[0];
std::string result = vec[0];
for (size_t i = 1; i < vec.size(); ++i) {
result += separator + vec[i];
}
return result;
}
} // namespace Config
// Efficient service type checking for caching
inline bool isHttpTransactionHandler() {
static bool is_http_transaction_handler = false;
static bool service_checked = false;
if (!service_checked) {
auto i_environment = Singleton::Consume<I_Environment>::by<Config::MockConfigProvider>();
if (i_environment != nullptr) {
auto maybe_service_name = i_environment->get<std::string>("Service Name");
if (maybe_service_name.ok()) {
is_http_transaction_handler = (maybe_service_name.unpack() == "HTTP Transaction Handler");
service_checked = true;
}
}
}
return is_http_transaction_handler;
}
// Context registration for cache-enabled configurations
template <typename ConfigurationType>
struct ContextRegistration {
static std::map<std::vector<std::string>, std::string> path_to_context_map;
static void registerContext(const std::vector<std::string>& paths, const std::string& context_type) {
path_to_context_map[paths] = context_type;
}
static std::string getContext(const std::vector<std::string>& paths) {
auto it = path_to_context_map.find(paths);
return (it != path_to_context_map.end()) ? it->second : "";
}
};
// Static member definition
template <typename ConfigurationType>
std::map<std::vector<std::string>, std::string> ContextRegistration<ConfigurationType>::path_to_context_map;
template <typename ConfigurationType>
struct ConfigCacheKey {
std::vector<std::string> paths;
std::string context_value;
std::string policy_load_id;
bool operator==(const ConfigCacheKey &other) const
{
return paths == other.paths &&
context_value == other.context_value &&
policy_load_id == other.policy_load_id;
}
bool match(
const std::vector<std::string>& other_paths,
const std::string& other_context_value,
const std::string& other_policy_load_id
) const
{
return paths == other_paths &&
context_value == other_context_value &&
policy_load_id == other_policy_load_id;
}
};
template <typename ConfigurationType>
struct ConfigCacheEntry {
ConfigCacheKey<ConfigurationType> key;
Maybe<ConfigurationType, Config::Errors> value;
ConfigCacheEntry()
: key(), value(genError(Config::Errors::MISSING_TAG)) {}
bool isValid() const { return !key.context_value.empty(); }
void invalidate()
{
key.context_value.clear();
value = genError(Config::Errors::MISSING_TAG);
}
};
template <typename ConfigurationType, typename ... Strings>
const Maybe<ConfigurationType, Config::Errors> &
getConfiguration(const Strings & ... strs)
{
auto i_config = Singleton::Consume<Config::I_Config>::from<Config::MockConfigProvider>();
return i_config->getConfiguration(Config::getVector(strs ...)).template getValue<ConfigurationType>();
const auto &paths = Config::getVector(strs ...);
return i_config->getConfiguration(paths).template getValue<ConfigurationType>();
};
// LCOV_EXCL_START - Helper function to isolate static variables from lcov function data mismatch
// Helper function to get cache array - isolates static variables
template <typename ConfigurationType>
ConfigCacheEntry<ConfigurationType>* getCacheArray() {
static ConfigCacheEntry<ConfigurationType> config_cache[3];
return config_cache;
}
// Cache statistics tracking
struct CacheStats {
static std::atomic<uint64_t> hits;
static std::atomic<uint64_t> misses;
static bool tracking_enabled;
static void recordHit() {
if (tracking_enabled) hits.fetch_add(1, std::memory_order_relaxed);
}
static void recordMiss() {
if (tracking_enabled) misses.fetch_add(1, std::memory_order_relaxed);
}
static uint64_t getHits() { return hits.load(std::memory_order_relaxed); }
static uint64_t getMisses() { return misses.load(std::memory_order_relaxed); }
static void reset() {
hits.store(0, std::memory_order_relaxed);
misses.store(0, std::memory_order_relaxed);
}
static void enableTracking() { tracking_enabled = true; }
static void disableTracking() { tracking_enabled = false; }
static bool isTrackingEnabled() { return tracking_enabled; }
};
// Initialize cache tracking from environment variable
inline void initializeCacheTracking() {
const char* enable_tracking = std::getenv("ENABLE_CONFIG_CACHE_TRACKING");
if (enable_tracking != nullptr) {
// Check for various "true" values
std::string tracking_value(enable_tracking);
std::transform(tracking_value.begin(), tracking_value.end(), tracking_value.begin(), ::tolower);
if (tracking_value == "true") {
CacheStats::enableTracking();
CacheStats::reset(); // Start with clean counters when enabling tracking
}
}
}
// LCOV_EXCL_STOP
template <typename ConfigurationType, typename ... Strings>
const Maybe<ConfigurationType, Config::Errors> &
getConfigurationWithCache(const Strings & ... strs)
{
// Step 1: Check if current service is HTTP Transaction Handler
if (!isHttpTransactionHandler()) {
return getConfiguration<ConfigurationType>(strs...);
}
// Step 2: Fast checks - get basic info
auto i_config = Singleton::Consume<Config::I_Config>::from<Config::MockConfigProvider>();
const auto &paths = Config::getVector(strs ...);
size_t idx = paths.size();
// Step 3: Quick validation checks (fastest)
bool idx_valid = (idx >= 1 && idx <= 3); // max_cache_key_size = 3
if (!idx_valid || !i_config->isConfigCacheEnabled()) {
return getConfiguration<ConfigurationType>(strs...);
}
// Step 4: Single map lookup - get context if registered, empty string if not
std::string context_type = ContextRegistration<ConfigurationType>::getContext(paths);
if (context_type.empty()) {
return getConfiguration<ConfigurationType>(strs...);
}
// Step 5: Now we know it's registered - get environment value using the context
std::string context_value;
auto i_environment = Singleton::Consume<I_Environment>::by<Config::MockConfigProvider>();
if (i_environment != nullptr) {
auto maybe_context_value = i_environment->get<std::string>(
(context_type == "triggerId") ? "triggers" : "asset_id");
if (maybe_context_value.ok()) {
context_value = maybe_context_value.unpack();
}
}
// Step 6: Final cache enablement check
if (context_value.empty()) {
return getConfiguration<ConfigurationType>(strs...);
}
// Step 7: Cache operations
auto* config_cache = getCacheArray<ConfigurationType>();
std::string policy_load_id = i_config->getPolicyLoadId();
// Check cache first
ConfigCacheEntry<ConfigurationType> &entry = config_cache[idx - 1];
if (entry.key.match(paths, context_value, policy_load_id)) {
// Cache hit
CacheStats::recordHit();
return entry.value;
}
// Cache miss - get configuration and update cache
CacheStats::recordMiss();
const auto &maybe_val = i_config->getConfiguration(paths).template getValue<ConfigurationType>();
// Update cache
config_cache[idx - 1].key = ConfigCacheKey<ConfigurationType>{paths, context_value, policy_load_id};
config_cache[idx - 1].value = maybe_val;
return maybe_val;
}
template <typename ConfigurationType, typename ... Strings>
const Maybe<ConfigurationType, Config::Errors> &
setConfigurationInCache(const Strings & ... strs)
{
// Step 1: Check if current service is HTTP Transaction Handler
if (!isHttpTransactionHandler()) {
return getConfiguration<ConfigurationType>(strs...);
}
// Step 2: Fast checks - get basic info
auto i_config = Singleton::Consume<Config::I_Config>::from<Config::MockConfigProvider>();
const auto &paths = Config::getVector(strs ...);
size_t idx = paths.size();
// Step 3: Quick validation checks (fastest)
bool idx_valid = (idx >= 1 && idx <= 3); // max_cache_key_size = 3
if (!idx_valid || !i_config->isConfigCacheEnabled()) {
// Early exit - no caching possible, just fetch and return
return getConfiguration<ConfigurationType>(strs...);
}
// Step 4: Single map lookup - get context if registered, empty string if not
std::string context_type = ContextRegistration<ConfigurationType>::getContext(paths);
if (context_type.empty()) {
// Not registered for caching - just fetch and return
return getConfiguration<ConfigurationType>(strs...);
}
// Step 5: Now we know it's registered - get environment value using the context
std::string context_value;
auto i_environment = Singleton::Consume<I_Environment>::by<Config::MockConfigProvider>();
if (i_environment != nullptr) {
auto maybe_context_value = i_environment->get<std::string>(
(context_type == "triggerId") ? "triggers" : "asset_id");
if (maybe_context_value.ok()) {
context_value = maybe_context_value.unpack();
}
}
// Step 6: Final cache enablement check
if (context_value.empty()) {
// No valid context value - just fetch and return
return getConfiguration<ConfigurationType>(strs...);
}
// Step 7: Always fetch configuration and update cache (no cache check first)
auto* config_cache = getCacheArray<ConfigurationType>();
std::string policy_load_id = i_config->getPolicyLoadId();
// Fetch configuration directly - no cache hit check
const auto &maybe_val = i_config->getConfiguration(paths).template getValue<ConfigurationType>();
// Update cache with fresh value
config_cache[idx - 1].key = ConfigCacheKey<ConfigurationType>{paths, context_value, policy_load_id};
config_cache[idx - 1].value = maybe_val;
return maybe_val;
}
template <typename ConfigurationType, typename ... Strings>
const ConfigurationType &
getConfigurationWithDefault(const ConfigurationType &deafult_val, const Strings & ... tags)
{
if (!Singleton::exists<Config::I_Config>()) return deafult_val;
auto &res = getConfiguration<ConfigurationType>(tags ...);
auto &res = getConfigurationWithCache<ConfigurationType>(tags ...);
return res.ok() ? res.unpack() : deafult_val;
}
@@ -235,6 +513,18 @@ registerExpectedConfiguration(const Strings & ... tags)
i_config->registerExpectedConfiguration(std::move(conf));
}
template <typename ConfigurationType, typename ... Strings>
void
registerExpectedConfigurationWithCache(const std::string& context_type, const Strings & ... tags)
{
// Register with the original system using existing function
registerExpectedConfiguration<ConfigurationType>(tags...);
// Register the context mapping
const auto &paths = Config::getVector(tags ...);
ContextRegistration<ConfigurationType>::registerContext(paths, context_type);
}
template <typename ResourceType, typename ... Strings>
void
registerExpectedResource(const Strings & ... tags)
@@ -254,3 +544,4 @@ registerExpectedSetting(const Strings & ... tags)
}
#endif // __CONFIG_IMPL_H__

View File

@@ -106,6 +106,18 @@ public:
virtual void clearOldTenants() = 0;
virtual bool isConfigCacheEnabled() const = 0;
virtual void resetConfigCache() = 0;
virtual const std::string & getPolicyLoadId() const = 0;
// Cache statistics access functions
virtual uint64_t getCacheHits() const = 0;
virtual uint64_t getCacheMisses() const = 0;
virtual void resetCacheStats() = 0;
virtual void enableCacheTracking() = 0;
virtual void disableCacheTracking() = 0;
virtual bool isCacheTrackingEnabled() const = 0;
protected:
virtual ~I_Config() {}
};

View File

@@ -76,15 +76,27 @@ public:
template <typename T, typename ... Attr>
void registerValue(const std::string &name, const T &value, Attr ... attr);
template <typename T, typename ... Attr>
void registerQuickAccessValue(const std::string &name, const T &value, Attr ... attr);
template <typename ... Params>
void registerValue(MetaDataType name, Params ... params);
template <typename ... Params>
void registerQuickAccessValue(MetaDataType name, Params ... params);
template <typename T, typename ... Attr>
void registerFunc(const std::string &name, std::function<T()> &&func, Attr ... attr);
template <typename T, typename ... Attr>
void registerQuickAccessFunc(const std::string &name, std::function<T()> &&func, Attr ... attr);
template <typename T, typename ... Attr>
void registerFunc(const std::string &name, std::function<Return<T>()> &&func, Attr ... attr);
template <typename T, typename ... Attr>
void registerQuickAccessFunc(const std::string &name, std::function<Return<T>()> &&func, Attr ... attr);
template <typename T>
void unregisterKey(const std::string &name);
@@ -105,6 +117,7 @@ public:
private:
std::map<Key, std::unique_ptr<AbstractValue>> values;
std::map<Key, std::unique_ptr<AbstractValue>> quick_access_values; // Common values for all contexts
};
class ScopedContext;

View File

@@ -22,6 +22,7 @@ DEFINE_FLAG(D_INFRA, D_ALL)
DEFINE_FLAG(D_COMPRESSION, D_INFRA)
DEFINE_FLAG(D_SHMEM, D_INFRA)
DEFINE_FLAG(D_CONFIG, D_INFRA)
DEFINE_FLAG(D_CONFIG_CACHE, D_INFRA)
DEFINE_FLAG(D_ENVIRONMENT, D_INFRA)
DEFINE_FLAG(D_INTELLIGENCE, D_INFRA)
DEFINE_FLAG(D_RULEBASE_CONFIG, D_INFRA)
@@ -74,6 +75,7 @@ DEFINE_FLAG(D_COMPONENT, D_ALL)
DEFINE_FLAG(D_WAAP_AUTOMATION, D_WAAP)
DEFINE_FLAG(D_WAAP_REGEX, D_WAAP)
DEFINE_FLAG(D_WAAP_SAMPLE_SCAN, D_WAAP)
DEFINE_FLAG(D_WAAP_HYPERSCAN, D_WAAP)
DEFINE_FLAG(D_WAAP_ASSET_STATE, D_WAAP)
DEFINE_FLAG(D_WAAP_CONFIDENCE_CALCULATOR, D_WAAP)
DEFINE_FLAG(D_WAAP_SERIALIZE, D_WAAP)
@@ -89,6 +91,7 @@ DEFINE_FLAG(D_COMPONENT, D_ALL)
DEFINE_FLAG(D_WAAP_STREAMING_PARSING, D_WAAP)
DEFINE_FLAG(D_WAAP_HEADERS, D_WAAP)
DEFINE_FLAG(D_WAAP_OVERRIDE, D_WAAP)
DEFINE_FLAG(D_WAAP_LEARN, D_WAAP)
DEFINE_FLAG(D_WAAP_SAMPLE_HANDLING, D_WAAP_GLOBAL)
DEFINE_FLAG(D_WAAP_SAMPLE_PREPROCESS, D_WAAP_SAMPLE_HANDLING)
@@ -119,8 +122,11 @@ DEFINE_FLAG(D_COMPONENT, D_ALL)
DEFINE_FLAG(D_IPS, D_COMPONENT)
DEFINE_FLAG(D_FILE_UPLOAD, D_COMPONENT)
DEFINE_FLAG(D_RATE_LIMIT, D_COMPONENT)
DEFINE_FLAG(D_AUTH_ENFORCE, D_COMPONENT)
DEFINE_FLAG(D_ANOMALY_DETECTION, D_COMPONENT)
DEFINE_FLAG(D_ROLLBACK_TESTING, D_COMPONENT)
DEFINE_FLAG(D_NGINX_MANAGER, D_COMPONENT)
DEFINE_FLAG(D_BROWSER_AGENT, D_COMPONENT)
DEFINE_FLAG(D_PARSER, D_COMPONENT)
DEFINE_FLAG(D_WS, D_COMPONENT)
@@ -168,6 +174,7 @@ DEFINE_FLAG(D_COMPONENT, D_ALL)
DEFINE_FLAG(D_NGINX_MESSAGE_READER, D_REVERSE_PROXY)
DEFINE_FLAG(D_ERROR_REPORTER, D_REVERSE_PROXY)
DEFINE_FLAG(D_UPSTREAM_KEEPALIVE, D_REVERSE_PROXY)
DEFINE_FLAG(D_UPSTREAM_HEALTH_CHECKER, D_REVERSE_PROXY)
DEFINE_FLAG(D_FORWARD_PROXY, D_REVERSE_PROXY)
DEFINE_FLAG(D_IDA, D_COMPONENT)
@@ -204,6 +211,7 @@ DEFINE_FLAG(D_COMPONENT, D_ALL)
DEFINE_FLAG(D_HORIZON_TELEMETRY, D_COMPONENT)
DEFINE_FLAG(D_PROMETHEUS, D_COMPONENT)
DEFINE_FLAG(D_AIGUARD, D_COMPONENT)
DEFINE_FLAG(D_ERM, D_COMPONENT)
DEFINE_FLAG(D_FLOW, D_ALL)
DEFINE_FLAG(D_DROP, D_FLOW)

View File

@@ -109,6 +109,14 @@ Context::registerValue(const std::string &name, const T &value, Attr ... attr)
registerFunc(name, std::move(new_func), attr ...);
}
template <typename T, typename ... Attr>
void
Context::registerQuickAccessValue(const std::string &name, const T &value, Attr ... attr)
{
std::function<Return<T>()> new_func = [value] () { return Return<T>(value); };
registerQuickAccessFunc(name, std::move(new_func), attr ...);
}
template <typename ... Params>
void
Context::registerValue(MetaDataType name, Params ... params)
@@ -116,6 +124,13 @@ Context::registerValue(MetaDataType name, Params ... params)
return registerValue(convertToString(name), params ...);
}
template <typename ... Params>
void
Context::registerQuickAccessValue(MetaDataType name, Params ... params)
{
return registerQuickAccessValue(convertToString(name), params ...);
}
template <typename T, typename ... Attr>
void
Context::registerFunc(const std::string &name, std::function<T()> &&func, Attr ... attr)
@@ -124,6 +139,14 @@ Context::registerFunc(const std::string &name, std::function<T()> &&func, Attr .
registerFunc(name, std::move(new_func), attr ...);
}
template <typename T, typename ... Attr>
void
Context::registerQuickAccessFunc(const std::string &name, std::function<T()> &&func, Attr ... attr)
{
std::function<Return<T>()> new_func = [func] () { return Return<T>(func()); };
registerQuickAccessFunc(name, std::move(new_func), attr ...);
}
template <typename T, typename ... Attr>
void
Context::registerFunc(const std::string &name, std::function<Return<T>()> &&func, Attr ... attr)
@@ -133,6 +156,15 @@ Context::registerFunc(const std::string &name, std::function<Return<T>()> &&func
values[key] = std::make_unique<Value<T>>(std::move(func));
}
template <typename T, typename ... Attr>
void
Context::registerQuickAccessFunc(const std::string &name, std::function<Return<T>()> &&func, Attr ... attr)
{
dbgTrace(D_ENVIRONMENT) << "Registering key : " << name;
Key key(name, typeid(T), EnvKeyAttr::ParamAttr(attr ...));
quick_access_values[key] = std::make_unique<Value<T>>(std::move(func));
}
template <typename T>
void
Context::unregisterKey(const std::string &name)
@@ -140,6 +172,7 @@ Context::unregisterKey(const std::string &name)
dbgTrace(D_ENVIRONMENT) << "Unregistering key : " << name;
Key key(name, typeid(T));
values.erase(key);
quick_access_values.erase(key);
}
template <typename T>
@@ -154,8 +187,12 @@ Context::Return<T>
Context::get(const std::string &name) const
{
Key key(name, typeid(T));
auto iter = values.find(key);
if (iter == values.end()) return genError(Error::NO_VALUE);
auto iter = quick_access_values.find(key);
if (iter == quick_access_values.end()) {
// If not found in quick access, search in the main values map
iter = values.find(key);
if (iter == values.end()) return genError(Error::NO_VALUE);
}
Value<T> *val = dynamic_cast<Value<T> *>(iter->second.get());
return val->get();
}

View File

@@ -188,6 +188,11 @@ public:
bool matches(const Invalidation &other) const;
void serialize(cereal::JSONInputArchive &ar);
Invalidation & addHeader(const std::string &key, const std::string &value);
Maybe<std::string> getHeader(const std::string &key) const;
const std::map<std::string, std::string> & getHeaders() const;
bool hasHeader(const std::string &key) const;
private:
bool attr_matches(const std::vector<StrAttributes> &current, const std::vector<StrAttributes> &other) const;
bool attr_matches(const std::vector<IpAttributes> &current, const std::vector<IpAttributes> &other) const;
@@ -200,6 +205,7 @@ private:
Maybe<InvalidationType> invalidation_type;
Maybe<uint> listening_id;
Maybe<std::string> registration_id;
std::map<std::string, std::string> headers;
};
} // namespace Intelligence

View File

@@ -41,6 +41,7 @@ struct PrometheusData
{
try {
ar(cereal::make_nvp("metric_name", name));
ar(cereal::make_nvp("unique_name", unique_name));
ar(cereal::make_nvp("metric_type", type));
ar(cereal::make_nvp("metric_description", description));
ar(cereal::make_nvp("labels", label));
@@ -51,6 +52,7 @@ struct PrometheusData
}
std::string name;
std::string unique_name;
std::string type;
std::string description;
std::string label;

View File

@@ -66,13 +66,15 @@ enum class Tags {
CROWDSEC,
PLAYGROUND,
API_DISCOVERY,
LB_HEALTH_STATUS,
NGINX_PROXY_MANAGER,
WEB_SERVER_APISIX,
DEPLOYMENT_DOCKER,
WEB_SERVER_SWAG,
WEB_SERVER_NGINX_UNIFIED,
AIGUARD,
CENTRAL_NGINX_MANAGER,
BROWSER_AGENT,
COUNT
};
@@ -162,7 +164,9 @@ enum class IssuingEngine {
IDA_SAML_IDN_BLADE_REGISTRATION,
IDA_SAML_IDN_CLIENT_IP_NOTIFY,
HORIZON_TELEMETRY_METRICS,
API_DISCOVERY
API_DISCOVERY,
LB_HEALTH_STATUS,
BROWSER_AGENT
};
} // namespace ReportIS

View File

@@ -180,10 +180,31 @@ public:
/// @brief Performs the REST call using the input stream.
/// @param in The input stream containing the JSON data for the REST call.
/// @param headers The HTTP headers from the request.
/// @return A Maybe object containing the result of the REST call (either the JSON data or an error message).
Maybe<std::string> performRestCall(std::istream &in);
Maybe<std::string> performRestCall(std::istream &in, const std::map<std::string, std::string> &headers);
/// @brief Performs the REST call using the input stream (backwards compatibility overload).
/// @param in The input stream containing the JSON data for the REST call.
/// @return A Maybe object containing the result of the REST call (either the JSON data or an error message).
Maybe<std::string> performRestCall(std::istream &in) {
return performRestCall(in, std::map<std::string, std::string>());
}
/// @brief Indicates whether this handler wants to receive HTTP headers.
/// @return True if the handler wants headers, false otherwise. Default is false.
virtual bool wantsHeaders() const { return false; }
/// @brief Sets the HTTP headers for this handler (used by bulk handlers to propagate headers).
/// @param headers The HTTP headers to set.
void setRequestHeaders(const std::map<std::string, std::string> &headers) {
request_headers = headers;
}
protected:
/// @brief HTTP headers from the current request (only populated if wantsHeaders() returns true).
std::map<std::string, std::string> request_headers;
/// @brief Determines if the direction is for input.
/// @param dir The direction of the communication.
/// @return True if the direction is for input, false otherwise.

View File

@@ -43,6 +43,14 @@ copyFile(
mode_t permission = (S_IWUSR | S_IRUSR)
);
bool
createFileWithContent(
const std::string &dest,
const std::string &content,
bool overide_if_exists,
mode_t permission = (S_IWUSR | S_IRUSR)
);
bool deleteFile(const std::string &path);
std::string convertToHumanReadable(uint64_t size_in_bytes);
std::string getFileName(const std::string &path);
@@ -86,6 +94,7 @@ std::string removeTrailingWhitespaces(std::string str);
std::string removeLeadingWhitespaces(std::string str);
std::string trim(std::string str);
std::string toLower(std::string str);
bool startsWith(const std::string &str, const std::string &prefix);
} // namespace Strings