Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 38 additions & 2 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "runtime_settings.h"
#include "json.h"
#include <algorithm>
#include <cstdio>
#include <fstream>
#include <sstream>
#include <limits>
Expand Down Expand Up @@ -1399,6 +1400,37 @@ void ClearProviders(Config& config) {
config.model.decoder.session_options.providers.clear();
}

// Escape a string for safe embedding inside a JSON string literal. Prevents JSON
// injection when caller-supplied values are concatenated into a JSON document that
// will subsequently be parsed (e.g. in SetProviderOption below). Handles the
// mandatory JSON escapes: quote, backslash, and the C0 control-character shortcuts.
// Any other control characters (< 0x20) are emitted as \u00XX.
static std::string EscapeJsonString(std::string_view s) {
std::string result;
result.reserve(s.size());
for (char c : s) {
switch (c) {
case '"': result += "\\\""; break;
case '\\': result += "\\\\"; break;
case '\b': result += "\\b"; break;
case '\f': result += "\\f"; break;
case '\n': result += "\\n"; break;
case '\r': result += "\\r"; break;
case '\t': result += "\\t"; break;
default:
if (static_cast<unsigned char>(c) < 0x20) {
char buf[7];
std::snprintf(buf, sizeof(buf), "\\u%04x", static_cast<unsigned char>(c));
result += buf;
Comment thread
apsonawane marked this conversation as resolved.
Outdated
} else {
result += c;
}
break;
}
}
return result;
}

void SetProviderOption(Config& config, std::string_view provider_name, std::string_view option_name, std::string_view option_value) {
// Normalize the provider name once
auto normalized_provider = NormalizeProviderName(provider_name);
Expand All @@ -1421,10 +1453,14 @@ void SetProviderOption(Config& config, std::string_view provider_name, std::stri
}
}

// JSON-escape all caller-supplied string fragments before concatenating them into the
// JSON document. Without escaping, quote/backslash characters in provider_name,
// option_name, or option_value would let a caller inject arbitrary JSON structure
// (sibling keys, new provider entries, etc.) into the parsed configuration.
std::ostringstream json;
json << R"({")" << provider_name << R"(":{)";
json << R"({")" << EscapeJsonString(provider_name) << R"(":{)";
if (!option_name.empty()) {
json << R"(")" << option_name << R"(":")" << option_value << R"(")";
json << R"(")" << EscapeJsonString(option_name) << R"(":")" << EscapeJsonString(option_value) << R"(")";
}
json << R"(}})";

Expand Down
Loading