Skip to content

fix(api): harden external input validation - #21

Open
0xbigapple wants to merge 4 commits into
developfrom
fix/api-input-hardening
Open

fix(api): harden external input validation#21
0xbigapple wants to merge 4 commits into
developfrom
fix/api-input-hardening

Conversation

@0xbigapple

@0xbigapple 0xbigapple commented Aug 19, 2026

Copy link
Copy Markdown
Owner

What does this PR do?

This PR hardens externally reachable API inputs before expensive or ambiguous processing:

  • validates contract-query addresses before database access and Base58Check formatting
  • limits string-form integer inputs before BigDecimal conversion
  • validates Permission_id exactly, rejects strings longer than 64 characters, and preserves existing supported numeric forms
  • limits shielded TRC20 amount strings before BigInteger conversion
  • validates shielded transfer spend/receive counts before serialization loops
  • rejects malformed reward and brokerage addresses consistently across FullNode, Solidity, and PBFT HTTP APIs
  • returns bounded validation messages without echoing oversized attacker-controlled values

Why are these changes required?

Malformed inputs could previously trigger disproportionate CPU work, silently truncate or wrap numeric values, or return plausible results for structurally invalid addresses.

These changes reject invalid inputs before expensive conversion, encoding, storage access, or repeated buffer merging.

Compatibility notes:

  • normal valid inputs retain their existing behavior
  • Permission_id values with fractions, integer overflow, explicit null, or string representations longer than 64 characters are rejected
  • shielded TRC20 APIs remain controlled by the existing node.allowShieldedTransactionApi flag, which is disabled by default
  • no protocol, protobuf, storage, dependency, configuration, or HTTP status-code changes are introduced

This PR has been tested by:

  • Unit Tests
  • Manual Testing

Follow up

Extra details


Summary by cubic

Harden external API input validation to reject malformed addresses and ambiguous numeric fields early while preserving legacy defaults and behavior. Reward/brokerage now reject only malformed addresses; blank or missing addresses keep the prior default response.

  • Addresses: org.tron.core.services.http.Util#getAddress validates Base58Check and 41... hex with length/prefix checks and returns null for blank input; servlets use processAddressParamError to emit bounded errors (“Invalid address” or “INVALID JSON body”) without echoing caller input. org.tron.core.Wallet#getContract and #getContractInfo short‑circuit on invalid address bytes.
  • Numeric inputs: enforce a 64‑character cap on quoted integers before BigDecimal in Util#getJsonLongValue. Permission_id must be an exact 32‑bit integer; legacy exact string forms remain; fractions, scientific strings, overflow, explicit null, and overlong strings are rejected. If absent or <= 0, the transaction is unchanged.
  • Shielded TRC20: cap amount strings at 80 characters before BigInteger. Require transfer spend/receive counts in 1–2, and require exactly one receive description for mint. Existing node.allowShieldedTransactionApi gate still applies.
  • No protocol, protobuf, storage, dependency, configuration, or HTTP status‑code changes. Core changes are in org.tron.core.services.http.Util, org.tron.core.Wallet, and org.tron.core.zen.ShieldedTRC20ParametersBuilder; added regression tests cover boundaries and mirrored endpoints across FullNode, Solidity, and PBFT.

Written for commit d2e1cdb. Summary will update on new commits.

Review in cubic

Validate Permission_id conversion, addresses, numeric strings, and shielded transfer cardinality before expensive or ambiguous processing.

Add deterministic boundary, compatibility, and mirror endpoint regression tests.
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai

coderabbitai Bot commented Aug 19, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: cc7bfb26-090b-459f-b9e9-e9ef776e0fc8


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

3 issues found across 15 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="framework/src/main/java/org/tron/core/Wallet.java">

<violation number="1" location="framework/src/main/java/org/tron/core/Wallet.java:4223">
P2: In getTriggerInputForShieldedTRC20Contract, getBigIntegerFromString(request.getAmount()) is not wrapped in try/catch, unlike the two other callers of getBigIntegerFromString. An amount string longer than 80 characters now lets an undeclared IllegalArgumentException escape instead of the ContractValidateException previously produced by checkBigIntegerRange. Only the outer servlet/RPC catch blocks keep it from failing, and the error type/message changes. Catch the exception here and rethrow as ContractValidateException for consistency with the other callers.</violation>
</file>

<file name="framework/src/main/java/org/tron/core/services/http/Util.java">

<violation number="1" location="framework/src/main/java/org/tron/core/services/http/Util.java:446">
P2: The new 64-character limit guards only string-form values (`rawValue instanceof String`). A bare numeric JSON literal with many digits is parsed by Jackson into a numeric node, bypasses the guard, and still undergoes full BigDecimal/BigInteger conversion before the exactness check rejects it. If the stated intent is to bound conversion cost before `BigDecimal`, extend the check to non-string numeric primitives (e.g. reject oversized `Number` values before `getBigDecimal`), and confirm the Jackson number-length limit actually caps this path.</violation>
</file>

<file name="framework/src/test/java/org/tron/core/services/interfaceOnSolidity/http/RewardBrokerageAddressValidationTest.java">

<violation number="1" location="framework/src/test/java/org/tron/core/services/interfaceOnSolidity/http/RewardBrokerageAddressValidationTest.java:42">
P3: The Solidity and PBFT test files duplicate the entire harness (reflection-backed wallet factory, request builder, inject helper, and the reward/brokerage rejection test) differing only in the wallet type and injected field name. Extract a shared base class or parameterized harness to avoid maintaining the same assertions in two places.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

if (trimmedIn.length() == 0) {
return BigInteger.ZERO;
}
if (trimmedIn.length() > 80) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: In getTriggerInputForShieldedTRC20Contract, getBigIntegerFromString(request.getAmount()) is not wrapped in try/catch, unlike the two other callers of getBigIntegerFromString. An amount string longer than 80 characters now lets an undeclared IllegalArgumentException escape instead of the ContractValidateException previously produced by checkBigIntegerRange. Only the outer servlet/RPC catch blocks keep it from failing, and the error type/message changes. Catch the exception here and rethrow as ContractValidateException for consistency with the other callers.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At framework/src/main/java/org/tron/core/Wallet.java, line 4223:

<comment>In getTriggerInputForShieldedTRC20Contract, getBigIntegerFromString(request.getAmount()) is not wrapped in try/catch, unlike the two other callers of getBigIntegerFromString. An amount string longer than 80 characters now lets an undeclared IllegalArgumentException escape instead of the ContractValidateException previously produced by checkBigIntegerRange. Only the outer servlet/RPC catch blocks keep it from failing, and the error type/message changes. Catch the exception here and rethrow as ContractValidateException for consistency with the other callers.</comment>

<file context>
@@ -4214,6 +4220,9 @@ private BigInteger getBigIntegerFromString(String in) {
     if (trimmedIn.length() == 0) {
       return BigInteger.ZERO;
     }
+    if (trimmedIn.length() > 80) {
+      throw new IllegalArgumentException("invalid shielded amount");
+    }
</file context>

int permissionId;
try {
Object rawValue = jsonObject.get(PERMISSION_ID);
if (rawValue instanceof String

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The new 64-character limit guards only string-form values (rawValue instanceof String). A bare numeric JSON literal with many digits is parsed by Jackson into a numeric node, bypasses the guard, and still undergoes full BigDecimal/BigInteger conversion before the exactness check rejects it. If the stated intent is to bound conversion cost before BigDecimal, extend the check to non-string numeric primitives (e.g. reject oversized Number values before getBigDecimal), and confirm the Jackson number-length limit actually caps this path.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At framework/src/main/java/org/tron/core/services/http/Util.java, line 446:

<comment>The new 64-character limit guards only string-form values (`rawValue instanceof String`). A bare numeric JSON literal with many digits is parsed by Jackson into a numeric node, bypasses the guard, and still undergoes full BigDecimal/BigInteger conversion before the exactness check rejects it. If the stated intent is to bound conversion cost before `BigDecimal`, extend the check to non-string numeric primitives (e.g. reject oversized `Number` values before `getBigDecimal`), and confirm the Jackson number-length limit actually caps this path.</comment>

<file context>
@@ -433,12 +437,27 @@ public static String getHexString(final String string) {
+    int permissionId;
+    try {
+      Object rawValue = jsonObject.get(PERMISSION_ID);
+      if (rawValue instanceof String
+          && ((String) rawValue).length() > MAX_JSON_INTEGER_VALUE_LENGTH) {
+        throw new InvalidParameterException(INVALID_PERMISSION_ID);
</file context>

}

@Test
public void rewardAndBrokerageMirrorsRejectMalformedAddresses() throws Exception {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The Solidity and PBFT test files duplicate the entire harness (reflection-backed wallet factory, request builder, inject helper, and the reward/brokerage rejection test) differing only in the wallet type and injected field name. Extract a shared base class or parameterized harness to avoid maintaining the same assertions in two places.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At framework/src/test/java/org/tron/core/services/interfaceOnSolidity/http/RewardBrokerageAddressValidationTest.java, line 42:

<comment>The Solidity and PBFT test files duplicate the entire harness (reflection-backed wallet factory, request builder, inject helper, and the reward/brokerage rejection test) differing only in the wallet type and injected field name. Extract a shared base class or parameterized harness to avoid maintaining the same assertions in two places.</comment>

<file context>
@@ -0,0 +1,57 @@
+  }
+
+  @Test
+  public void rewardAndBrokerageMirrorsRejectMalformedAddresses() throws Exception {
+    GetRewardOnSolidityServlet reward = new GetRewardOnSolidityServlet();
+    inject(reward, "walletOnSolidity", walletOnSolidity());
</file context>

getAddress returned an error for a blank or absent address, changing the
response of getreward and getbrokerage on all three interfaces. Restore the
null return, bound the hex branch before decoding, and report every decode
failure with a fixed message so the offending character is not echoed.

The servlets now handle the address parameter and the service call in
separate try blocks, so a failure further in is no longer reported to the
caller as a bad address.
The transfer branch got a cardinality guard; the mint branch on the same
endpoint still indexed element 0 of a caller-supplied repeated field.
The authoritative bound is checkBigIntegerRange; this only keeps the string
short enough to convert cheaply.
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.

1 participant