diff --git a/java/src/main/java/org/tron/common/utils/Utils.java b/java/src/main/java/org/tron/common/utils/Utils.java index a6e8ac4f..0a08fc39 100644 --- a/java/src/main/java/org/tron/common/utils/Utils.java +++ b/java/src/main/java/org/tron/common/utils/Utils.java @@ -138,7 +138,7 @@ public class Utils { public static final int MIN_LENGTH = 2; public static final int MAX_LENGTH = 14; - public static final String VERSION = " v4.9.6"; + public static final String VERSION = " v4.9.9"; public static final String TRANSFER_METHOD_ID = "a9059cbb"; private static SecureRandom random = new SecureRandom(); diff --git a/java/src/main/java/org/tron/walletcli/Client.java b/java/src/main/java/org/tron/walletcli/Client.java index 91f7687c..48d0720f 100755 --- a/java/src/main/java/org/tron/walletcli/Client.java +++ b/java/src/main/java/org/tron/walletcli/Client.java @@ -38,6 +38,7 @@ import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.parser.ParserConfig; import com.beust.jcommander.JCommander; import com.beust.jcommander.Parameter; import com.google.common.primitives.Longs; @@ -125,6 +126,14 @@ public class Client { + static { + // fastjson 1.x honors the "@type" hint in JSON.parseObject/parseArray even when the target is a + // JSONObject, which is the entry point for autoType deserialization gadgets. wallet-cli feeds + // remote HTTP/WebSocket payloads (GasFree, multi-sign) into those calls and never relies on + // "@type" itself, so safe mode rejects the hint outright and removes the gadget surface. + ParserConfig.getGlobalInstance().setSafeMode(true); + } + private WalletApiWrapper walletApiWrapper = new WalletApiWrapper(); private static int retryTime = 3; diff --git a/java/src/test/java/org/tron/common/utils/TransactionJsonSafeModeTest.java b/java/src/test/java/org/tron/common/utils/TransactionJsonSafeModeTest.java new file mode 100644 index 00000000..ee19d1c7 --- /dev/null +++ b/java/src/test/java/org/tron/common/utils/TransactionJsonSafeModeTest.java @@ -0,0 +1,88 @@ +package org.tron.common.utils; + +import com.alibaba.fastjson.JSONObject; +import com.google.protobuf.Any; +import com.google.protobuf.ByteString; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.tron.api.GrpcAPI.TransactionExtention; +import org.tron.api.GrpcAPI.TransactionSignWeight; +import org.tron.protos.Protocol; +import org.tron.protos.contract.BalanceContract.TransferContract; +import org.tron.trident.proto.Chain; +import org.tron.trident.proto.Response; +import org.tron.walletcli.Client; + +/** + * Transaction rendering runs protobuf output through JSON.parseObject, and a Transaction carries its + * contract in a protobuf Any. Safe mode rejects any payload containing "@type", so this guards that + * our JsonFormat never emits that key and transaction display keeps working. + */ +public class TransactionJsonSafeModeTest { + + @BeforeClass + public static void enableSafeMode() throws Exception { + Class.forName(Client.class.getName()); + } + + private static Protocol.Transaction transferTransaction() { + TransferContract transfer = TransferContract.newBuilder() + .setOwnerAddress(ByteString.copyFrom(new byte[]{0x41, 0x01, 0x02, 0x03})) + .setToAddress(ByteString.copyFrom(new byte[]{0x41, 0x04, 0x05, 0x06})) + .setAmount(1_000_000L) + .build(); + Protocol.Transaction.Contract contract = Protocol.Transaction.Contract.newBuilder() + .setType(Protocol.Transaction.Contract.ContractType.TransferContract) + .setParameter(Any.pack(transfer)) + .build(); + return Protocol.Transaction.newBuilder() + .setRawData(Protocol.Transaction.raw.newBuilder() + .setTimestamp(1_700_000_000_000L) + .addContract(contract)) + .build(); + } + + @Test + public void printsTransactionWithProtobufAnyUnderSafeMode() { + JSONObject json = Utils.printTransactionToJSON(transferTransaction(), true); + + Assert.assertNotNull(json); + Assert.assertFalse("JsonFormat must not emit an @type key", json.toJSONString().contains("@type")); + Assert.assertTrue(json.toJSONString().contains("contract")); + } + + @Test + public void printsTransactionStringUnderSafeMode() { + String text = Utils.printTransaction(transferTransaction()); + + Assert.assertNotNull(text); + Assert.assertFalse(text.contains("@type")); + } + + @Test + public void printsTransactionSignWeightUnderSafeMode() { + TransactionSignWeight signWeight = TransactionSignWeight.newBuilder() + .setTransaction(TransactionExtention.newBuilder() + .setTransaction(transferTransaction())) + .build(); + + String text = Utils.printTransactionSignWeight(signWeight); + + Assert.assertNotNull(text); + Assert.assertFalse(text.contains("@type")); + } + + @Test + public void printsTransactionApprovedListUnderSafeMode() throws Exception { + Response.TransactionApprovedList approved = Response.TransactionApprovedList.newBuilder() + .setTransaction(Response.TransactionExtention.newBuilder() + .setTransaction(Chain.Transaction.parseFrom(transferTransaction().toByteArray()))) + .build(); + + String text = Utils.printTransactionApprovedList(approved); + + Assert.assertNotNull(text); + Assert.assertFalse(text.contains("@type")); + } +} diff --git a/java/src/test/java/org/tron/walletcli/FastjsonSafeModeTest.java b/java/src/test/java/org/tron/walletcli/FastjsonSafeModeTest.java new file mode 100644 index 00000000..3eeb7412 --- /dev/null +++ b/java/src/test/java/org/tron/walletcli/FastjsonSafeModeTest.java @@ -0,0 +1,43 @@ +package org.tron.walletcli; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONException; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.parser.ParserConfig; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class FastjsonSafeModeTest { + + @BeforeClass + public static void loadClient() throws Exception { + // Client's static initializer is what enables safe mode for the shipped binary. + Class.forName(Client.class.getName()); + } + + @Test + public void clientEnablesFastjsonSafeMode() { + Assert.assertTrue(ParserConfig.getGlobalInstance().isSafeMode()); + } + + @Test + public void safeModeRejectsAutoTypeHint() { + String payload = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\"," + + "\"dataSourceName\":\"ldap://127.0.0.1:1389/exploit\",\"autoCommit\":true}"; + try { + JSON.parseObject(payload); + Assert.fail("safe mode must reject the @type autoType hint"); + } catch (JSONException e) { + Assert.assertTrue(e.getMessage(), e.getMessage().contains("safeMode")); + } + } + + @Test + public void safeModeStillParsesOrdinaryPayloads() { + JSONObject parsed = JSON.parseObject("{\"address\":\"TXyz\",\"balance\":42}"); + + Assert.assertEquals("TXyz", parsed.getString("address")); + Assert.assertEquals(42, parsed.getIntValue("balance")); + } +}