mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 23:09:44 -04:00
RPC: Strictly enforce the type of parameters passed by name
This commit is contained in:
parent
28023ff415
commit
7cd0315bc4
4 changed files with 21 additions and 7 deletions
|
@ -9,6 +9,7 @@
|
||||||
#include <any>
|
#include <any>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include <univalue.h>
|
#include <univalue.h>
|
||||||
#include <util/fs.h>
|
#include <util/fs.h>
|
||||||
|
@ -38,6 +39,9 @@ public:
|
||||||
std::optional<UniValue> id = UniValue::VNULL;
|
std::optional<UniValue> id = UniValue::VNULL;
|
||||||
std::string strMethod;
|
std::string strMethod;
|
||||||
UniValue params;
|
UniValue params;
|
||||||
|
//! List of original parameter names after transformNamedArguments is
|
||||||
|
//! called and params is changed from an object to an array.
|
||||||
|
std::vector<std::optional<std::string>> param_names;
|
||||||
enum Mode { EXECUTE, GET_HELP, GET_ARGS } mode = EXECUTE;
|
enum Mode { EXECUTE, GET_HELP, GET_ARGS } mode = EXECUTE;
|
||||||
std::string URI;
|
std::string URI;
|
||||||
std::string authUser;
|
std::string authUser;
|
||||||
|
|
|
@ -397,9 +397,11 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c
|
||||||
for (const auto& [argNamePattern, named_only]: argNames) {
|
for (const auto& [argNamePattern, named_only]: argNames) {
|
||||||
std::vector<std::string> vargNames = SplitString(argNamePattern, '|');
|
std::vector<std::string> vargNames = SplitString(argNamePattern, '|');
|
||||||
auto fr = argsIn.end();
|
auto fr = argsIn.end();
|
||||||
|
std::string fr_name;
|
||||||
for (const std::string & argName : vargNames) {
|
for (const std::string & argName : vargNames) {
|
||||||
fr = argsIn.find(argName);
|
fr = argsIn.find(argName);
|
||||||
if (fr != argsIn.end()) {
|
if (fr != argsIn.end()) {
|
||||||
|
fr_name = argName;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -424,6 +426,7 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c
|
||||||
// but not at the end (for backwards compatibility with calls
|
// but not at the end (for backwards compatibility with calls
|
||||||
// that act based on number of specified parameters).
|
// that act based on number of specified parameters).
|
||||||
out.params.push_back(UniValue());
|
out.params.push_back(UniValue());
|
||||||
|
out.param_names.emplace_back(std::nullopt);
|
||||||
}
|
}
|
||||||
hole = 0;
|
hole = 0;
|
||||||
if (!initial_param) initial_param = &argNamePattern;
|
if (!initial_param) initial_param = &argNamePattern;
|
||||||
|
@ -440,6 +443,7 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + fr->first + " conflicts with parameter " + options.getKeys().front());
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + fr->first + " conflicts with parameter " + options.getKeys().front());
|
||||||
}
|
}
|
||||||
out.params.push_back(*fr->second);
|
out.params.push_back(*fr->second);
|
||||||
|
out.param_names.emplace_back(fr_name);
|
||||||
argsIn.erase(fr);
|
argsIn.erase(fr);
|
||||||
}
|
}
|
||||||
if (!options.empty()) {
|
if (!options.empty()) {
|
||||||
|
|
|
@ -669,7 +669,7 @@ UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request) const
|
||||||
UniValue arg_mismatch{UniValue::VOBJ};
|
UniValue arg_mismatch{UniValue::VOBJ};
|
||||||
for (size_t i{0}; i < m_args.size(); ++i) {
|
for (size_t i{0}; i < m_args.size(); ++i) {
|
||||||
const auto& arg{m_args.at(i)};
|
const auto& arg{m_args.at(i)};
|
||||||
UniValue match{arg.MatchesType(request.params[i])};
|
UniValue match{arg.MatchesType(request.params[i], i < request.param_names.size() ? request.param_names[i] : std::nullopt)};
|
||||||
if (!match.isTrue()) {
|
if (!match.isTrue()) {
|
||||||
arg_mismatch.pushKV(strprintf("Position %s (%s)", i + 1, arg.m_names), std::move(match));
|
arg_mismatch.pushKV(strprintf("Position %s (%s)", i + 1, arg.m_names), std::move(match));
|
||||||
}
|
}
|
||||||
|
@ -923,18 +923,24 @@ static std::optional<UniValue::VType> ExpectedType(RPCArg::Type type)
|
||||||
NONFATAL_UNREACHABLE();
|
NONFATAL_UNREACHABLE();
|
||||||
}
|
}
|
||||||
|
|
||||||
UniValue RPCArg::MatchesType(const UniValue& request) const
|
UniValue RPCArg::MatchesType(const UniValue& request, const std::optional<std::string>& param_name) const
|
||||||
{
|
{
|
||||||
if (m_opts.skip_type_check) return true;
|
if (m_opts.skip_type_check) return true;
|
||||||
if (IsOptional() && request.isNull()) return true;
|
if (IsOptional() && request.isNull()) return true;
|
||||||
for (auto type : m_type_per_name.empty() ? std::vector<RPCArg::Type>{m_type} : m_type_per_name) {
|
const auto names = SplitString(m_names, '|');
|
||||||
const auto exp_type{ExpectedType(type)};
|
size_t i = 0;
|
||||||
|
do {
|
||||||
|
// If parameter was passed by name, only allow the specified type for
|
||||||
|
// that name. Otherwise allow any of the specified types.
|
||||||
|
if (param_name && i < names.size() && *param_name != names[i]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const auto exp_type{ExpectedType(i < m_type_per_name.size() ? m_type_per_name[i] : m_type)};
|
||||||
if (!exp_type) return true; // nothing to check
|
if (!exp_type) return true; // nothing to check
|
||||||
|
|
||||||
if (*exp_type == request.getType()) {
|
if (*exp_type == request.getType()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
} while (++i < names.size());
|
||||||
return strprintf("JSON value of type %s is not of expected type %s", uvTypeName(request.getType()), uvTypeName(*ExpectedType(m_type)));
|
return strprintf("JSON value of type %s is not of expected type %s", uvTypeName(request.getType()), uvTypeName(*ExpectedType(m_type)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -284,7 +284,7 @@ struct RPCArg {
|
||||||
* Check whether the request JSON type matches.
|
* Check whether the request JSON type matches.
|
||||||
* Returns true if type matches, or object describing error(s) if not.
|
* Returns true if type matches, or object describing error(s) if not.
|
||||||
*/
|
*/
|
||||||
UniValue MatchesType(const UniValue& request) const;
|
UniValue MatchesType(const UniValue& request, const std::optional<std::string>& param_name) const;
|
||||||
|
|
||||||
/** Return the first of all aliases */
|
/** Return the first of all aliases */
|
||||||
std::string GetFirstName() const;
|
std::string GetFirstName() const;
|
||||||
|
|
Loading…
Add table
Reference in a new issue