Skip to content

Fix 6751 loglevel param type validation - #7923

Draft
SoubeDev wants to merge 3 commits into
XRPLF:developfrom
SoubeDev:fix-6751-loglevel-param-type-validation
Draft

Fix 6751 loglevel param type validation#7923
SoubeDev wants to merge 3 commits into
XRPLF:developfrom
SoubeDev:fix-6751-loglevel-param-type-validation

Conversation

@SoubeDev

Copy link
Copy Markdown

High Level Overview of Change

Fixes #6751

doLogLevel read both of its parameters with an unguarded asString(). This adds
isString() guards to severity and partition so malformed values return
invalidParams instead of a generic internal error, and rejects the empty partition
name. Adds a LogLevel RPC test suite, which did not previously exist.

Context of Change

json::Value::asString() throws json::Error for arrays and objects, and silently
stringifies every scalar (Int -> "42", Boolean -> "true", Null -> ""). The
throw was swallowed by callMethod's catch-all, so a malformed value produced a generic
internal error rather than invalidParams, and the request was recorded as a server
fault in the perf log.

The two parameters were broken to different degrees:

  • severity was only half broken. A coerced scalar still failed Logs::fromString
    and came back as invalidParams, so the outcome was accidentally correct; only arrays
    and objects misbehaved.
  • partition had no downstream validation at all, and Logs::get does an emplace,
    so it creates the sink on demand. A coerced value therefore permanently added a
    partition literally named 42, 1.500000, true or "" to the map, and
    partitionSeverities() then reported it in every subsequent log_level response.

The empty partition name is rejected for the same reason the type check exists: "" is
not a partition anyone can have meant, and accepting it wedges a nameless sink into the
listing forever.

This makes the handler stricter. A severity or partition given as a number, a bool,
or null was previously coerced and is now rejected. The commandline is unaffected:
RPCParser::parseLogLevel builds both fields from argv via asString(), so they are
always strings on that path.

Also drops the second isMember(jss::partition) test, which was unconditionally true
because the preceding branch already returned, along with the unreachable
return rpcError(RpcInvalidParams); it guarded. No behavior change.

This is the same class of defect, in the same admin handler tree, as the blacklist
threshold and get_counts min_count fixes, and follows their shape.

API Impact

log_level is admin-only, so the affected surface is small. The change is filed under
Bugfixes in API-CHANGELOG.md, consistent with the blacklist and get_counts entries
for the same class of fix.

  • Public API: New feature (new methods and/or new fields)
  • Public API: Breaking change (in general, breaking changes should only impact the next api_version)
  • libxrpl change (any change that may affect libxrpl or dependents of libxrpl)
  • Peer protocol change (must be backward compatible or bump the peer protocol version)

Before / After

request before after
{"severity": ["array"]} internal invalidParams
{"severity": 42} invalidParams invalidParams (unchanged)
{"severity": "warn", "partition": 42} success, creates a partition named 42 invalidParams
{"severity": "warn", "partition": null} success, creates a partition named "" invalidParams
{"severity": "warn", "partition": ""} success, creates a partition named "" invalidParams
{"severity": "warn", "partition": {}} internal invalidParams

Test Plan

New suite xrpl.rpc.LogLevel (src/test/rpc/LogLevel_test.cpp), 5 cases / 84
assertions, modelled on BlackList_test.cpp. There was no handler-level test for
log_level before this; the existing RPCCall_test cases cover only the
commandline-to-JSON parse and never reach the handler.

Coverage: the levels listing; every alias Logs::fromString accepts and the name
Logs::toString reports back; the base / mixed-case BASE distinction; and the
rejected-value tables for both parameters. The invalid-partition case additionally
asserts that none of 42, 1.500000, true or "" appear as partition names in the
listing afterwards — that assertion is what pins the sink-map pollution.

./xrpld --unittest=xrpl.rpc.LogLevel     # 84 tests, 0 failures
./xrpld --unittest=xrpl.rpc.RPCCall      # 2085 tests, 0 failures (CLI parser unaffected)

Verified the suite fails against the unpatched handler: 18 failures, including all four
sink-pollution assertions, confirming those partitions were really being created.

Future Tasks

The API-CHANGELOG.md entry uses an #NNNN placeholder and needs a follow-up commit
pointing it at this PR number.

SoubeDev and others added 3 commits July 31, 2026 12:01
doLogLevel read both parameters with an unguarded asString(), which throws
for arrays and objects and silently stringifies every scalar. The throw was
swallowed by callMethod's catch-all, so a malformed value produced a generic
internal error rather than invalidParams, and the request was recorded as a
server fault in the perf log.

The severity path was only half broken: a coerced scalar still failed
Logs::fromString and came back as invalidParams, so just arrays and objects
misbehaved. The partition path had no downstream validation at all, and
Logs::get creates a sink on demand, so a coerced value permanently added a
partition named "42", "true" or "" to the map that log_level itself then
reported on every subsequent call.

Guard both conversions with an isString() check and return invalidParams when
it fails. Reject the empty partition name for the same reason the type check
exists: it is not a partition anyone can have meant, and accepting it wedges a
nameless sink into the listing forever.

This makes the handler stricter: a severity or partition given as a number, a
bool, or null was previously coerced and is now rejected. The commandline
builds both fields from argv via asString(), so they are always strings there
and the parser is unaffected.

Also drop the second isMember(partition) test, which was unconditionally true
because the preceding branch already returned, along with the unreachable
return it guarded. No behavior change.

Add a LogLevel test suite, which did not exist; the RPCCall cases cover only
the commandline-to-JSON parse and never reach the handler.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The entry was added with a placeholder because the pull request did not
exist when the fix was committed. Point it at XRPLF#7922.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LogLevel missing type validation on severity and partition params

1 participant