Skip to content

doLedgerEntry missing type validation on 'binary' parameter allows non-boolean values #7204

@mvadari

Description

@mvadari
          1. BUG-LQ006: doLedgerEntry missing type validation on 'binary' parameter allows non-boolean values

*Severity*: LOW

In doLedgerEntry (LedgerEntry.cpp line 944-945), the `binary` parameter is read with `context.params[jss::binary].asBool` without first checking `isBool`. While `asBool` doesn't throw (it does truthiness conversion — any non-zero integer, non-empty string, non-empty object/array all evaluate to true), this means that sending `"binary": 1` or `"binary": "yes"` or `"binary": {}` will all be treated as `true`. This is inconsistent with the `ledger` handler (Ledger.cpp lines 47-58) which explicitly validates `isBool` and returns `rpcINVALID_PARAMS` for non-boolean values. It's also inconsistent with the `ledger_data` handler (LedgerData.cpp lines 59-62) which also validates `isBool`.

*Root cause*: The doLedgerEntry handler skips type validation on the binary parameter, unlike other handlers in the same feature that properly check isBool first.

`src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp:943-945`
```cpp
bool bNodeBinary = false;
if (context.params.isMember(jss::binary))
bNodeBinary = context.params[jss::binary].asBool;
```

*Invariant gap*: Input validation should be consistent across all handlers for the same parameter. The binary parameter should always require a boolean type.

*Suggested fix*: Add a type check before calling asBool: `if (!context.params[jss::binary].isBool) return RPC::expected_field_error(jss::binary, "boolean");`

<details>
<summary>Triage Details</summary>

*Status*: ✅ Verified

Confirmed. LedgerEntry.cpp lines 943-945 use `context.params[jss::binary].asBool` without first checking `isBool`. The Ledger handler (lines 47-58) and LedgerData handler (lines 59-62) both validate `isBool` first. The impact is minimal — asBool performs truthiness conversion without throwing, so non-boolean values like integers or strings are accepted and silently converted. This is an input validation inconsistency, not a security vulnerability.

*Final severity*: INFORMATIONAL

*Action*: Backlog

Input validation inconsistency across handlers for the binary parameter. No security impact — asBool handles non-boolean types gracefully. Fix for API consistency.

Add `if (!context.params[jss::binary].isBool) return RPC::expected_field_error(jss::binary, "boolean");` before the asBool call, consistent with other handlers.

</details>


Additional Context

RPC Verdict Report

Pattern: IV — doLedgerEntry missing type validation on binary parameter

Handler: doLedgerEntry in rippled/src/xrpld/rpc/handlers/ledger/LedgerEntry.cpp:944-945

Registered Role: Role::USER — public | Transport: HTTP / WS

Verdict: ⚠️ Code Improvement Needed | Confidence: 86%

Root Cause

params[jss::binary] is read without isBool check. Non-boolean inputs cause Json::LogicError caught as rpcINTERNAL. Add isBool validation before asBool.

// LedgerEntry.cpp:944-945 — BUG: no isBool check
bool const bBinary = params[jss::binary].asBool;
// ^^^^^^^
// throws Json::LogicError if binary is a string, array, or object!
// e.g., params["binary"] = "yes" → rpcINTERNAL

Suggested Fix

// Add isBool guard:
if (params.isMember(jss::binary) && !params[jss::binary].isBool)
 return rpcError(rpcINVALID_PARAMS);
bool const bBinary = params.isMember(jss::binary) && params[jss::binary].asBool;

Reproduction Tests — src/test/rpc/LedgerEntry_test.cpp

void testLedgerEntryBinaryString(FeatureBitset features)
{
 testcase("ledger_entry: string binary returns rpcINVALID_PARAMS");
 using namespace jtx;

 Env env{*this, features};
 env.close;

 Json::Value params;
 params[jss::account_root] = env.master.human;
 params[jss::binary] = "yes"; // string instead of bool!

 auto jv = env.rpc("json", "ledger_entry", params.toStyledString);
 auto const& result = jv[jss::result];

 BEAST_EXPECT(result.isMember(jss::error));
 // BEFORE fix: rpcINTERNAL (Json::LogicError from asBool)
 // AFTER fix: rpcINVALID_PARAMS
 BEAST_EXPECT(result[jss::error_code].asInt != rpcINTERNAL);
}

void testLedgerEntryBinaryInteger(FeatureBitset features)
{
 testcase("ledger_entry: integer binary returns rpcINVALID_PARAMS");
 using namespace jtx;

 Env env{*this, features};
 env.close;

 Json::Value params;
 params[jss::account_root] = env.master.human;
 params[jss::binary] = 1; // integer instead of bool!

 auto jv = env.rpc("json", "ledger_entry", params.toStyledString);
 auto const& result = jv[jss::result];

 BEAST_EXPECT(result.isMember(jss::error));
 BEAST_EXPECT(result[jss::error_code].asInt != rpcINTERNAL);
}

void testLedgerEntryBinaryTrue(FeatureBitset features)
{
 testcase("ledger_entry: binary=true returns serialized hex");
 using namespace jtx;

 Env env{*this, features};
 env.close;

 Json::Value params;
 params[jss::account_root] = env.master.human;
 params[jss::binary] = true;

 auto jv = env.rpc("json", "ledger_entry", params.toStyledString);
 auto const& result = jv[jss::result];

 // Valid bool — should not return rpcINTERNAL
 if (result.isMember(jss::error))
 BEAST_EXPECT(result[jss::error_code].asInt != rpcINTERNAL);
 else
 BEAST_EXPECT(result.isMember(jss::node_binary));
}

void testLedgerEntryBinaryFalse(FeatureBitset features)
{
 testcase("ledger_entry: binary=false returns JSON object");
 using namespace jtx;

 Env env{*this, features};
 env.close;

 Json::Value params;
 params[jss::account_root] = env.master.human;
 params[jss::binary] = false;

 auto jv = env.rpc("json", "ledger_entry", params.toStyledString);
 auto const& result = jv[jss::result];

 if (result.isMember(jss::error))
 BEAST_EXPECT(result[jss::error_code].asInt != rpcINTERNAL);
 else
 BEAST_EXPECT(result.isMember(jss::node));
}

Build & Run

cmake --build rippled/build/Debug --target rippled
rippled/build/Debug/rippled --unittest ripple.rpc.LedgerEntry

Co-authored by Augment Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    AI TriageBugs and fixes that have been triaged via AI initiativesBug

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions