diff --git a/CMakeLists.txt b/CMakeLists.txt index e9b981d9..8a064ecd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,6 +111,7 @@ if(BUILD_TESTS) unittests/Logger.cpp unittests/Config.cpp unittests/ProfilerTest.cpp + unittests/JsonUtilTest.cpp unittests/DOSGuard.cpp unittests/SubscriptionTest.cpp unittests/SubscriptionManagerTest.cpp diff --git a/src/rpc/RPCHelpers.h b/src/rpc/RPCHelpers.h index e4a00869..f237e5c2 100644 --- a/src/rpc/RPCHelpers.h +++ b/src/rpc/RPCHelpers.h @@ -33,6 +33,7 @@ #include #include +#include namespace RPC { @@ -231,8 +232,10 @@ logDuration(Web::Context const& ctx, T const& dur) static clio::Logger log{"RPC"}; auto const millis = std::chrono::duration_cast(dur).count(); auto const seconds = std::chrono::duration_cast(dur).count(); - auto const msg = - fmt::format("Request processing duration = {} milliseconds. request = {}", millis, serialize(ctx.params)); + auto const msg = fmt::format( + "Request processing duration = {} milliseconds. request = {}", + millis, + serialize(util::removeSecret(ctx.params))); if (seconds > 10) log.error() << ctx.tag() << msg; diff --git a/src/util/JsonUtils.h b/src/util/JsonUtils.h new file mode 100644 index 00000000..ee6e20fb --- /dev/null +++ b/src/util/JsonUtils.h @@ -0,0 +1,53 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2023, the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#pragma once + +#include + +#include + +namespace util { + +inline boost::json::object +removeSecret(boost::json::object const& object) +{ + auto newObject = object; + auto const secretFields = {"secret", "seed", "seed_hex", "passphrase"}; + + if (newObject.contains("params") and newObject.at("params").is_array() and + not newObject.at("params").as_array().empty() and newObject.at("params").as_array()[0].is_object()) + { + for (auto const& secretField : secretFields) + { + if (newObject.at("params").as_array()[0].as_object().contains(secretField)) + newObject.at("params").as_array()[0].as_object()[secretField] = "*"; + } + } + // for websocket requests + for (auto const& secretField : secretFields) + { + if (newObject.contains(secretField)) + newObject[secretField] = "*"; + } + + return newObject; +} + +} // namespace util diff --git a/src/webserver/RPCExecutor.h b/src/webserver/RPCExecutor.h index 3e064200..295dde46 100644 --- a/src/webserver/RPCExecutor.h +++ b/src/webserver/RPCExecutor.h @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -125,7 +126,8 @@ private: std::shared_ptr connection) { log_.info() << connection->tag() << (connection->upgraded ? "ws" : "http") - << " received request from work queue: " << request << " ip = " << connection->clientIp; + << " received request from work queue: " << util::removeSecret(request) + << " ip = " << connection->clientIp; auto const id = request.contains("id") ? request.at("id") : nullptr; diff --git a/unittests/JsonUtilTest.cpp b/unittests/JsonUtilTest.cpp new file mode 100644 index 00000000..095ae675 --- /dev/null +++ b/unittests/JsonUtilTest.cpp @@ -0,0 +1,62 @@ +//------------------------------------------------------------------------------ +/* + This file is part of clio: https://github.com/XRPLF/clio + Copyright (c) 2023, the clio developers. + + Permission to use, copy, modify, and distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ +//============================================================================== + +#include + +#include +#include + +TEST(JsonUtils, RemoveSecrets) +{ + auto json = boost::json::parse(R"({ + "secret": "snoopy", + "seed": "woodstock", + "seed_hex": "charlie", + "passphrase": "lucy" + })") + .as_object(); + + auto json2 = util::removeSecret(json); + EXPECT_EQ(json2.at("secret").as_string(), "*"); + EXPECT_EQ(json2.at("seed").as_string(), "*"); + EXPECT_EQ(json2.at("seed_hex").as_string(), "*"); + EXPECT_EQ(json2.at("passphrase").as_string(), "*"); + + json = boost::json::parse(R"({ + "params": [ + { + "secret": "snoopy", + "seed": "woodstock", + "seed_hex": "charlie", + "passphrase": "lucy" + } + ] + })") + .as_object(); + + json2 = util::removeSecret(json); + EXPECT_TRUE(json2.contains("params")); + EXPECT_TRUE(json2.at("params").is_array()); + EXPECT_TRUE(json2.at("params").as_array().size() > 0); + json2 = json2.at("params").as_array()[0].as_object(); + EXPECT_EQ(json2.at("secret").as_string(), "*"); + EXPECT_EQ(json2.at("seed").as_string(), "*"); + EXPECT_EQ(json2.at("seed_hex").as_string(), "*"); + EXPECT_EQ(json2.at("passphrase").as_string(), "*"); +}