From e4c64c2fec00269a1bb9c9a5e5aeb04f77dfe3c6 Mon Sep 17 00:00:00 2001 From: komaruworld Date: Tue, 18 Aug 2026 22:35:27 +0300 Subject: [PATCH] Add ca_bundle: to config #18 --- config/mocktail.example.yaml | 3 +++ include/runtime/runtime_config.h | 6 ++++++ src/runtime/runtime_config.cc | 7 +++++++ src/runtime/runtime_config_bootstrap.cc | 3 +++ src/runtime/runtime_config_file.cc | 26 +++++++++++++++++++++++++ 5 files changed, 45 insertions(+) diff --git a/config/mocktail.example.yaml b/config/mocktail.example.yaml index 5620136..ccfb127 100644 --- a/config/mocktail.example.yaml +++ b/config/mocktail.example.yaml @@ -108,6 +108,9 @@ network: # proxy_host: 127.0.0.1 # Integer (optional, default: disabled): proxy TCP port from 1 to 65535. # proxy_port: 8080 + # Absolute path (optional): custom PEM CA bundle. Mocktail reads this file + # directly and does not replace it during Roblox payload updates. + # ca_bundle: /home/user/.config/mocktail/cacert.pem updates: # Boolean (default: true): check the latest Roblox version, derive an exact diff --git a/include/runtime/runtime_config.h b/include/runtime/runtime_config.h index 8647849..79c87d5 100644 --- a/include/runtime/runtime_config.h +++ b/include/runtime/runtime_config.h @@ -102,6 +102,10 @@ class RuntimeConfig { const std::optional& network_proxy() const { return network_proxy_; } + const std::optional& ca_bundle() const { + return ca_bundle_; + } + bool ca_bundle_valid() const { return ca_bundle_valid_; } bool use_system_proxy() const { return use_system_proxy_; } const DiscordRpcConfig& discord_rpc() const { return discord_rpc_; } bool discord_rpc_valid() const { return discord_rpc_valid_; } @@ -134,6 +138,8 @@ class RuntimeConfig { bool audio_output_device_valid_ = true; bool use_system_proxy_ = false; std::optional network_proxy_; + std::optional ca_bundle_; + bool ca_bundle_valid_ = true; DiscordRpcConfig discord_rpc_; bool discord_rpc_valid_ = true; std::vector unsafe_detached_thread_overrides_; diff --git a/src/runtime/runtime_config.cc b/src/runtime/runtime_config.cc index 185c9ca..b251d16 100644 --- a/src/runtime/runtime_config.cc +++ b/src/runtime/runtime_config.cc @@ -278,6 +278,13 @@ RuntimeConfig RuntimeConfig::FromEnvironment(const Environment& environment) { config.use_system_proxy_ = LegacyEnabled(environment, "MOCKTAIL_USE_SYSTEM_PROXY"); config.network_proxy_ = ReadNetworkProxy(environment); + if (const std::optional ca_bundle = + environment.Get("MOCKTAIL_CA_BUNDLE"); + ca_bundle.has_value()) { + config.ca_bundle_ = *ca_bundle; + config.ca_bundle_valid_ = !ca_bundle->empty() && + config.ca_bundle_->is_absolute(); + } bool discord_booleans_valid = true; config.discord_rpc_.enabled = ReadBoolean( environment, "MOCKTAIL_DISCORD_RPC_ENABLED", false, diff --git a/src/runtime/runtime_config_bootstrap.cc b/src/runtime/runtime_config_bootstrap.cc index acdcb3f..187e0ae 100644 --- a/src/runtime/runtime_config_bootstrap.cc +++ b/src/runtime/runtime_config_bootstrap.cc @@ -126,6 +126,9 @@ network: # proxy_host: 127.0.0.1 # Integer (optional, default: disabled): proxy TCP port from 1 to 65535. # proxy_port: 8080 + # Absolute path (optional): custom PEM CA bundle. Mocktail reads this file + # directly and does not replace it during Roblox payload updates. + # ca_bundle: /home/user/.config/mocktail/cacert.pem updates: # Boolean (default: true): check the latest Roblox version, derive an exact diff --git a/src/runtime/runtime_config_file.cc b/src/runtime/runtime_config_file.cc index 05c0f32..02a9a21 100644 --- a/src/runtime/runtime_config_file.cc +++ b/src/runtime/runtime_config_file.cc @@ -184,6 +184,7 @@ bool ValidateAndMap(const ValueMap& yaml, ValueMap* environment, "network.use_system_proxy", "network.proxy_host", "network.proxy_port", + "network.ca_bundle", "integrations.discord_rpc.enabled", "integrations.discord_rpc.show_place_name", "integrations.discord_rpc.show_elapsed_time", @@ -440,6 +441,15 @@ bool ValidateAndMap(const ValueMap& yaml, ValueMap* environment, (*environment)["MOCKTAIL_HTTP_PROXY_HOST"] = *proxy_host; (*environment)["MOCKTAIL_HTTP_PROXY_PORT"] = *proxy_port; } + if (const auto ca_bundle = value("network.ca_bundle"); + ca_bundle.has_value()) { + const std::filesystem::path path(*ca_bundle); + if (ca_bundle->empty() || !path.is_absolute()) { + *error = "network.ca_bundle must be an absolute file path"; + return false; + } + (*environment)["MOCKTAIL_CA_BUNDLE"] = *ca_bundle; + } for (const auto& [key, variable] : { std::pair( "integrations.discord_rpc.enabled", @@ -702,6 +712,8 @@ RuntimeConfigLoadResult LoadRuntimeConfig( result.error = "GameMode policy is invalid"; } else if (!result.config.performance().physics_worker_mode_valid) { result.error = "physics worker policy is invalid"; + } else if (!result.config.ca_bundle_valid()) { + result.error = "CA bundle path is invalid"; } else if (!result.config.discord_rpc_valid()) { result.error = "Discord Rich Presence configuration is invalid"; } @@ -740,6 +752,12 @@ bool ExportRuntimeConfigEnvironment(const RuntimeConfig& config, } return false; } + if (!config.ca_bundle_valid()) { + if (error != nullptr) { + *error = "cannot export an invalid CA bundle path"; + } + return false; + } if (!config.discord_rpc_valid()) { if (error != nullptr) { *error = "cannot export an invalid Discord Rich Presence policy"; @@ -842,6 +860,14 @@ bool ExportRuntimeConfigEnvironment(const RuntimeConfig& config, if (!base_exported) { return false; } + if (config.ca_bundle().has_value()) { + if (!SetEnvironmentValue("MOCKTAIL_CA_BUNDLE", + config.ca_bundle()->string(), error)) { + return false; + } + } else if (!UnsetEnvironmentValue("MOCKTAIL_CA_BUNDLE", error)) { + return false; + } if (config.roblox_http_user_agent().has_value() && !SetEnvironmentValue("MOCKTAIL_USER_AGENT", *config.roblox_http_user_agent(), error)) {