14#ifndef NAH_OVERRIDES_H
15#define NAH_OVERRIDES_H
20#include <nlohmann/json.hpp>
26using json = nlohmann::json;
27using namespace nah::core;
31inline std::string safe_getenv(
const char* name) {
35 if (_dupenv_s(&buf, &sz, name) == 0 && buf !=
nullptr) {
36 std::string result(buf);
42 const char* val = std::getenv(name);
43 return val ? val :
"";
55struct EnvOverrideParseResult {
59 std::unordered_map<std::string, std::string> values;
67inline EnvOverrideParseResult parse_env_override() {
68 EnvOverrideParseResult result;
70 std::string env_val = detail::safe_getenv(
"NAH_OVERRIDE_ENVIRONMENT");
71 if (env_val.empty()) {
75 result.present =
true;
78 if (!json::accept(env_val)) {
79 result.error =
"invalid JSON";
84 auto j = json::parse(env_val,
nullptr,
false);
86 if (j.is_discarded()) {
87 result.error =
"JSON parse failed";
92 result.error =
"expected object";
96 for (
auto& [key, val] : j.items()) {
97 if (!val.is_string()) {
98 result.error =
"value for '" + key +
"' must be string";
101 result.values[key] = val.get<std::string>();
114inline EnvOverrideParseResult parse_env_override(
115 const std::unordered_map<std::string, std::string>& process_env)
117 EnvOverrideParseResult result;
119 auto it = process_env.find(
"NAH_OVERRIDE_ENVIRONMENT");
120 if (it == process_env.end()) {
124 result.present =
true;
126 const std::string& env_val = it->second;
129 if (!json::accept(env_val)) {
130 result.error =
"invalid JSON";
135 auto j = json::parse(env_val,
nullptr,
false);
137 if (j.is_discarded()) {
138 result.error =
"JSON parse failed";
142 if (!j.is_object()) {
143 result.error =
"expected object";
147 for (
auto& [key, val] : j.items()) {
148 if (!val.is_string()) {
149 result.error =
"value for '" + key +
"' must be string";
152 result.values[key] = val.get<std::string>();
166inline bool is_key_allowed(
const std::string& key,
const HostEnvironment& host_env) {
167 if (!host_env.overrides.allow_env_overrides) {
170 if (host_env.overrides.allowed_env_keys.empty()) {
173 for (
const auto& allowed : host_env.overrides.allowed_env_keys) {
174 if (allowed == key) {
200inline void apply_overrides(
201 CompositionResult& result,
202 const HostEnvironment& host_env,
203 const std::unordered_map<std::string, std::string>& process_env)
209 auto parsed = parse_env_override(process_env);
211 if (!parsed.present) {
217 result.warnings.push_back({
218 warning_to_string(Warning::override_invalid),
221 {
"target",
"NAH_OVERRIDE_ENVIRONMENT"},
222 {
"reason",
"parse_failure"},
223 {
"source_kind", trace_source::PROCESS_ENV},
224 {
"source_ref",
"NAH_OVERRIDE_ENVIRONMENT"}
231 if (!host_env.overrides.allow_env_overrides) {
232 result.warnings.push_back({
233 warning_to_string(Warning::override_denied),
236 {
"target",
"NAH_OVERRIDE_ENVIRONMENT"},
237 {
"reason",
"overrides_disabled"},
238 {
"source_kind", trace_source::PROCESS_ENV},
239 {
"source_ref",
"NAH_OVERRIDE_ENVIRONMENT"}
246 for (
const auto& [key, value] : parsed.values) {
247 if (is_key_allowed(key, host_env)) {
248 result.contract.environment[key] = value;
250 result.warnings.push_back({
251 warning_to_string(Warning::override_denied),
255 {
"reason",
"key_not_allowed"},
256 {
"source_kind", trace_source::PROCESS_ENV},
257 {
"source_ref",
"NAH_OVERRIDE_ENVIRONMENT"}
272inline void apply_overrides(CompositionResult& result,
const HostEnvironment& host_env)
278 auto parsed = parse_env_override();
280 if (!parsed.present) {
285 result.warnings.push_back({
286 warning_to_string(Warning::override_invalid),
289 {
"target",
"NAH_OVERRIDE_ENVIRONMENT"},
290 {
"reason",
"parse_failure"},
291 {
"source_kind", trace_source::PROCESS_ENV},
292 {
"source_ref",
"NAH_OVERRIDE_ENVIRONMENT"}
298 if (!host_env.overrides.allow_env_overrides) {
299 result.warnings.push_back({
300 warning_to_string(Warning::override_denied),
303 {
"target",
"NAH_OVERRIDE_ENVIRONMENT"},
304 {
"reason",
"overrides_disabled"},
305 {
"source_kind", trace_source::PROCESS_ENV},
306 {
"source_ref",
"NAH_OVERRIDE_ENVIRONMENT"}
312 for (
const auto& [key, value] : parsed.values) {
313 if (is_key_allowed(key, host_env)) {
314 result.contract.environment[key] = value;
316 result.warnings.push_back({
317 warning_to_string(Warning::override_denied),
321 {
"reason",
"key_not_allowed"},
322 {
"source_kind", trace_source::PROCESS_ENV},
323 {
"source_ref",
"NAH_OVERRIDE_ENVIRONMENT"}