mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 14:59:39 -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 <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <univalue.h>
|
||||
#include <util/fs.h>
|
||||
|
@ -38,6 +39,9 @@ public:
|
|||
std::optional<UniValue> id = UniValue::VNULL;
|
||||
std::string strMethod;
|
||||
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;
|
||||
std::string URI;
|
||||
std::string authUser;
|
||||
|
|
|
@ -397,9 +397,11 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c
|
|||
for (const auto& [argNamePattern, named_only]: argNames) {
|
||||
std::vector<std::string> vargNames = SplitString(argNamePattern, '|');
|
||||
auto fr = argsIn.end();
|
||||
std::string fr_name;
|
||||
for (const std::string & argName : vargNames) {
|
||||
fr = argsIn.find(argName);
|
||||
if (fr != argsIn.end()) {
|
||||
fr_name = argName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -424,6 +426,7 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c
|
|||
// but not at the end (for backwards compatibility with calls
|
||||
// that act based on number of specified parameters).
|
||||
out.params.push_back(UniValue());
|
||||
out.param_names.emplace_back(std::nullopt);
|
||||
}
|
||||
hole = 0;
|
||||
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());
|
||||
}
|
||||
out.params.push_back(*fr->second);
|
||||
out.param_names.emplace_back(fr_name);
|
||||
argsIn.erase(fr);
|
||||
}
|
||||
if (!options.empty()) {
|
||||
|
|
|
@ -669,7 +669,7 @@ UniValue RPCHelpMan::HandleRequest(const JSONRPCRequest& request) const
|
|||
UniValue arg_mismatch{UniValue::VOBJ};
|
||||
for (size_t i{0}; i < m_args.size(); ++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()) {
|
||||
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();
|
||||
}
|
||||
|
||||
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 (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 exp_type{ExpectedType(type)};
|
||||
const auto names = SplitString(m_names, '|');
|
||||
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 == request.getType()) {
|
||||
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)));
|
||||
}
|
||||
|
||||
|
|
|
@ -284,7 +284,7 @@ struct RPCArg {
|
|||
* Check whether the request JSON type matches.
|
||||
* 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 */
|
||||
std::string GetFirstName() const;
|
||||
|
|
Loading…
Add table
Reference in a new issue