diff options
| author | Liam Mitchell <[email protected]> | 2026-03-09 19:45:28 -0700 |
|---|---|---|
| committer | GitHub Enterprise <[email protected]> | 2026-03-09 19:45:28 -0700 |
| commit | 1cdea42adf7ade0e1950c69bdef2ebf6832fdc59 (patch) | |
| tree | f49a67466d076930541c9d9e0fffeb4bc73a463f /src | |
| parent | Merge pull request #710 from ue-foundation/lm/oidctoken-exe-path (diff) | |
| parent | Merge branch 'main' into lm/restrict-content-type (diff) | |
| download | zen-1cdea42adf7ade0e1950c69bdef2ebf6832fdc59.tar.xz zen-1cdea42adf7ade0e1950c69bdef2ebf6832fdc59.zip | |
Merge pull request #752 from ue-foundation/lm/restrict-content-type
Restrict content-type on POST requests to compact binary or JSON
Diffstat (limited to 'src')
| -rw-r--r-- | src/zen/cmds/projectstore_cmd.cpp | 4 | ||||
| -rw-r--r-- | src/zenserver-test/projectstore-tests.cpp | 8 | ||||
| -rw-r--r-- | src/zenserver/storage/projectstore/httpprojectstore.cpp | 28 | ||||
| -rw-r--r-- | src/zenserver/storage/projectstore/httpprojectstore.h | 2 | ||||
| -rw-r--r-- | src/zenserver/storage/storageconfig.cpp | 7 | ||||
| -rw-r--r-- | src/zenserver/storage/storageconfig.h | 1 | ||||
| -rw-r--r-- | src/zenserver/storage/zenstorageserver.cpp | 1 |
7 files changed, 45 insertions, 6 deletions
diff --git a/src/zen/cmds/projectstore_cmd.cpp b/src/zen/cmds/projectstore_cmd.cpp index 5ff591b54..db931e49a 100644 --- a/src/zen/cmds/projectstore_cmd.cpp +++ b/src/zen/cmds/projectstore_cmd.cpp @@ -811,6 +811,7 @@ CreateOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg } IoBuffer OplogPayload; + OplogPayload.SetContentType(ZenContentType::kCbObject); if (!m_GcPath.empty()) { OplogPayload = MakeCbObjectPayload([&](CbObjectWriter& Writer) { Writer.AddString("gcpath"sv, m_GcPath); }); @@ -1144,7 +1145,7 @@ ExportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg if (CreateOplog) { ZEN_CONSOLE_WARN("Creating zen remote oplog '{}/{}'", m_ZenProjectName, m_ZenOplogName); - if (HttpClient::Response Result = TargetHttp.Post(Url); !Result) + if (HttpClient::Response Result = TargetHttp.Post(Url, IoBuffer(), ZenContentType::kCbObject); !Result) { Result.ThrowError("failed creating zen remote oplog"sv); } @@ -1633,6 +1634,7 @@ ImportOplogCommand::Run(const ZenCliOptions& GlobalOptions, int argc, char** arg if (CreateOplog) { IoBuffer OplogPayload; + OplogPayload.SetContentType(ZenContentType::kCbObject); if (!m_GcPath.empty()) { OplogPayload = MakeCbObjectPayload([&](CbObjectWriter& Writer) { Writer.AddString("gcpath"sv, m_GcPath); }); diff --git a/src/zenserver-test/projectstore-tests.cpp b/src/zenserver-test/projectstore-tests.cpp index c73910aaa..eb2e187d7 100644 --- a/src/zenserver-test/projectstore-tests.cpp +++ b/src/zenserver-test/projectstore-tests.cpp @@ -88,7 +88,7 @@ TEST_CASE("project.basic") HttpClient Http{BaseUri}; { - auto Response = Http.Post(""sv); + auto Response = Http.Post(""sv, IoBuffer{}, ZenContentType::kCbObject); CHECK(Response.StatusCode == HttpResponseCode::Created); } @@ -443,7 +443,8 @@ TEST_CASE("project.remote") auto MakeOplog = [](std::string_view UrlBase, std::string_view ProjectName, std::string_view OplogName) { HttpClient Http{UrlBase}; - HttpClient::Response Response = Http.Post(fmt::format("/prj/{}/oplog/{}", ProjectName, OplogName), IoBuffer{}); + HttpClient::Response Response = + Http.Post(fmt::format("/prj/{}/oplog/{}", ProjectName, OplogName), IoBuffer{}, ZenContentType::kCbObject); REQUIRE(Response); }; @@ -893,7 +894,8 @@ TEST_CASE("project.rpcappendop") }; auto MakeOplog = [](HttpClient& Client, std::string_view ProjectName, std::string_view OplogName) { - HttpClient::Response Response = Client.Post(fmt::format("/prj/{}/oplog/{}", ProjectName, OplogName)); + HttpClient::Response Response = + Client.Post(fmt::format("/prj/{}/oplog/{}", ProjectName, OplogName), IoBuffer{}, ZenContentType::kCbObject); REQUIRE_MESSAGE(Response.IsSuccess(), Response.ErrorMessage("")); }; auto GetOplog = [](HttpClient& Client, std::string_view ProjectName, std::string_view OplogName) { diff --git a/src/zenserver/storage/projectstore/httpprojectstore.cpp b/src/zenserver/storage/projectstore/httpprojectstore.cpp index 661eeef5c..2fa10a292 100644 --- a/src/zenserver/storage/projectstore/httpprojectstore.cpp +++ b/src/zenserver/storage/projectstore/httpprojectstore.cpp @@ -666,6 +666,7 @@ HttpProjectService::HttpProjectService(CidStore& Store, AuthMgr& AuthMgr, OpenProcessCache& InOpenProcessCache, JobQueue& InJobQueue, + bool InRestrictContentTypes, const std::filesystem::path& InOidcTokenExePath, bool InAllowExternalOidcTokenExe) : m_Log(logging::Get("project")) @@ -676,6 +677,7 @@ HttpProjectService::HttpProjectService(CidStore& Store, , m_AuthMgr(AuthMgr) , m_OpenProcessCache(InOpenProcessCache) , m_JobQueue(InJobQueue) +, m_RestrictContentTypes(InRestrictContentTypes) , m_OidcTokenExePath(InOidcTokenExePath) , m_AllowExternalOidcTokenExe(InAllowExternalOidcTokenExe) { @@ -2006,6 +2008,14 @@ HttpProjectService::HandleOpLogRequest(HttpRouterRequest& Req) { return HttpReq.WriteResponse(HttpResponseCode::InsufficientStorage); } + + if (m_RestrictContentTypes && (HttpReq.RequestContentType() == HttpContentType::kText || + HttpReq.RequestContentType() == HttpContentType::kUnknownContentType)) + { + m_ProjectStats.BadRequestCount++; + return HttpReq.WriteResponse(HttpResponseCode::BadRequest, HttpContentType::kText, "Invalid request content type"); + } + std::filesystem::path OplogMarkerPath; if (CbObject Params = HttpReq.ReadPayloadObject()) { @@ -2296,6 +2306,13 @@ HttpProjectService::HandleProjectRequest(HttpRouterRequest& Req) return HttpReq.WriteResponse(HttpResponseCode::InsufficientStorage); } + if (m_RestrictContentTypes && (HttpReq.RequestContentType() == HttpContentType::kText || + HttpReq.RequestContentType() == HttpContentType::kUnknownContentType)) + { + m_ProjectStats.BadRequestCount++; + return HttpReq.WriteResponse(HttpResponseCode::BadRequest, HttpContentType::kText, "Invalid request content type"); + } + CbValidateError ValidateResult; if (CbObject Params = ValidateAndReadCompactBinaryObject(HttpReq.ReadPayload(), ValidateResult); ValidateResult == CbValidateError::None) @@ -2711,10 +2728,17 @@ HttpProjectService::HandleRpcRequest(HttpRouterRequest& Req) CbObject Cb; switch (PayloadContentType) { - case HttpContentType::kJSON: - case HttpContentType::kUnknownContentType: case HttpContentType::kText: + case HttpContentType::kUnknownContentType: + case HttpContentType::kJSON: { + if (m_RestrictContentTypes && + (PayloadContentType == HttpContentType::kText || PayloadContentType == HttpContentType::kUnknownContentType)) + { + m_ProjectStats.BadRequestCount++; + return HttpReq.WriteResponse(HttpResponseCode::BadRequest, HttpContentType::kText, "Invalid request content type"); + } + std::string JsonText(reinterpret_cast<const char*>(Payload.GetData()), Payload.GetSize()); Cb = LoadCompactBinaryFromJson(JsonText).AsObject(); if (!Cb) diff --git a/src/zenserver/storage/projectstore/httpprojectstore.h b/src/zenserver/storage/projectstore/httpprojectstore.h index 8bf2162e1..917337324 100644 --- a/src/zenserver/storage/projectstore/httpprojectstore.h +++ b/src/zenserver/storage/projectstore/httpprojectstore.h @@ -45,6 +45,7 @@ public: AuthMgr& AuthMgr, OpenProcessCache& InOpenProcessCache, JobQueue& InJobQueue, + bool InRestrictContentTypes, const std::filesystem::path& InOidcTokenExePath, bool AllowExternalOidcTokenExe); ~HttpProjectService(); @@ -112,6 +113,7 @@ private: metrics::OperationTiming m_HttpRequests; RwLock m_ThreadWorkersLock; Ref<TransferThreadWorkers> m_ThreadWorkers; + bool m_RestrictContentTypes; std::filesystem::path m_OidcTokenExePath; bool m_AllowExternalOidcTokenExe; diff --git a/src/zenserver/storage/storageconfig.cpp b/src/zenserver/storage/storageconfig.cpp index 1554c9e51..e8ccb9097 100644 --- a/src/zenserver/storage/storageconfig.cpp +++ b/src/zenserver/storage/storageconfig.cpp @@ -496,6 +496,7 @@ ZenStorageServerConfigurator::AddConfigOptions(LuaConfig::Options& LuaOptions) LuaOptions.AddOption("security.encryptionaeskey"sv, ServerOptions.EncryptionKey, "encryption-aes-key"sv); LuaOptions.AddOption("security.encryptionaesiv"sv, ServerOptions.EncryptionIV, "encryption-aes-iv"sv); LuaOptions.AddOption("security.openidproviders"sv, ServerOptions.AuthConfig); + LuaOptions.AddOption("security.restrictcontenttypes"sv, ServerOptions.RestrictContentTypes, "restrict-content-types"sv); LuaOptions.AddOption("security.oidctokenexecutable"sv, ServerOptions.OidcTokenExecutable, "oidctoken-exe-path"sv); LuaOptions.AddOption("security.allowexternaloidctokenexecutable"sv, ServerOptions.AllowExternalOidcTokenExe, @@ -655,6 +656,12 @@ ZenStorageServerCmdLineOptions::AddSecurityOptions(cxxopts::Options& options, Ze options.add_option("security", "", "openid-client-id", "Open ID client ID", cxxopts::value<std::string>(OpenIdClientId), ""); options.add_option("security", "", + "restrict-content-types", + "Restrict content-type in requests to content-types that are not allowed in CORS simple requests", + cxxopts::value<bool>(ServerOptions.RestrictContentTypes), + ""); + options.add_option("security", + "", "oidctoken-exe-path", "Path to OidcToken executable", cxxopts::value<std::string>(OidcTokenExecutable), diff --git a/src/zenserver/storage/storageconfig.h b/src/zenserver/storage/storageconfig.h index dd8c41041..128804d92 100644 --- a/src/zenserver/storage/storageconfig.h +++ b/src/zenserver/storage/storageconfig.h @@ -159,6 +159,7 @@ struct ZenStorageServerConfig : public ZenServerConfig bool ObjectStoreEnabled = false; bool ComputeEnabled = true; std::string ScrubOptions; + bool RestrictContentTypes = false; std::filesystem::path OidcTokenExecutable; bool AllowExternalOidcTokenExe = true; }; diff --git a/src/zenserver/storage/zenstorageserver.cpp b/src/zenserver/storage/zenstorageserver.cpp index c5df78abc..d4b8e37ef 100644 --- a/src/zenserver/storage/zenstorageserver.cpp +++ b/src/zenserver/storage/zenstorageserver.cpp @@ -229,6 +229,7 @@ ZenStorageServer::InitializeServices(const ZenStorageServerConfig& ServerOptions *m_AuthMgr, *m_OpenProcessCache, *m_JobQueue, + ServerOptions.RestrictContentTypes, ServerOptions.OidcTokenExecutable, ServerOptions.AllowExternalOidcTokenExe}); |