quantumcoin 7.0.9 → 7.0.11
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 +1 -5
- package/README.md +18 -3
- package/SPEC.md +2 -63
- package/config.js +10 -2
- package/examples/example.js +0 -5
- package/examples/example.ts +0 -5
- package/examples/node_modules/.bin/esbuild +16 -0
- package/examples/node_modules/.bin/esbuild.cmd +17 -0
- package/examples/node_modules/.bin/esbuild.ps1 +28 -0
- package/examples/node_modules/.bin/sdkgen +16 -0
- package/examples/node_modules/.bin/sdkgen.cmd +17 -0
- package/examples/node_modules/.bin/sdkgen.ps1 +28 -0
- package/examples/node_modules/.bin/tsx +16 -0
- package/examples/node_modules/.bin/tsx.cmd +17 -0
- package/examples/node_modules/.bin/tsx.ps1 +28 -0
- package/examples/node_modules/.package-lock.json +235 -0
- package/examples/node_modules/@esbuild/win32-x64/README.md +3 -0
- package/examples/node_modules/@esbuild/win32-x64/esbuild.exe +0 -0
- package/examples/node_modules/@esbuild/win32-x64/package.json +20 -0
- package/examples/node_modules/esbuild/LICENSE.md +21 -0
- package/examples/node_modules/esbuild/README.md +3 -0
- package/examples/node_modules/esbuild/bin/esbuild +223 -0
- package/examples/node_modules/esbuild/install.js +289 -0
- package/examples/node_modules/esbuild/lib/main.d.ts +716 -0
- package/examples/node_modules/esbuild/lib/main.js +2532 -0
- package/examples/node_modules/esbuild/package.json +49 -0
- package/examples/node_modules/get-tsconfig/LICENSE +21 -0
- package/examples/node_modules/get-tsconfig/README.md +235 -0
- package/examples/node_modules/get-tsconfig/dist/index.cjs +7 -0
- package/examples/node_modules/get-tsconfig/dist/index.d.cts +2088 -0
- package/examples/node_modules/get-tsconfig/dist/index.d.mts +2088 -0
- package/examples/node_modules/get-tsconfig/dist/index.mjs +7 -0
- package/examples/node_modules/get-tsconfig/package.json +46 -0
- package/examples/node_modules/quantum-coin-js-sdk/.github/workflows/publish-npmjs.yaml +22 -0
- package/examples/node_modules/quantum-coin-js-sdk/LICENSE +21 -0
- package/examples/node_modules/quantum-coin-js-sdk/LICENSE-wasm_exec.js.txt +30 -0
- package/examples/node_modules/quantum-coin-js-sdk/README.md +1665 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/README.md +14 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/conversion-example.js +19 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-create-contract.js +396 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-encode-decode-rlp.js +225 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-event-pack-unpack.js +391 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-misc.js +101 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-rpc-send-signRawTransaction.js +318 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-rpc-send.js +116 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-send.js +70 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-token-pack-unpack.js +961 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-wallet-version4.js +35 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example-wallet.js +43 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/example.js +405 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/package-lock.json +134 -0
- package/examples/node_modules/quantum-coin-js-sdk/example/package.json +15 -0
- package/examples/node_modules/quantum-coin-js-sdk/index.d.ts +1024 -0
- package/examples/node_modules/quantum-coin-js-sdk/index.js +3062 -0
- package/examples/node_modules/quantum-coin-js-sdk/package.json +34 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/encrypted-32.json +1 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/encrypted-36.json +1 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/encrypted-48.json +1 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/generate-verify-vectors.js +91 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/non-transactional.preinit.test.js +41 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/non-transactional.test.js +686 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/sign-raw-keytype5-context-null.test.js +107 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/sign-raw-transaction.test.js +196 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/sign-verify.test.js +311 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/transactional.relay.test.js +131 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/transactional.rpc.test.js +103 -0
- package/examples/node_modules/quantum-coin-js-sdk/tests/verify-vectors.json +95035 -0
- package/examples/node_modules/quantum-coin-js-sdk/wasmBase64.d.ts +9 -0
- package/examples/node_modules/quantum-coin-js-sdk/wasmBase64.js +16 -0
- package/examples/node_modules/quantum-coin-js-sdk/wasm_exec.d.ts +0 -0
- package/examples/node_modules/quantum-coin-js-sdk/wasm_exec.js +587 -0
- package/examples/node_modules/resolve-pkg-maps/LICENSE +21 -0
- package/examples/node_modules/resolve-pkg-maps/README.md +216 -0
- package/examples/node_modules/resolve-pkg-maps/dist/index.cjs +1 -0
- package/examples/node_modules/resolve-pkg-maps/dist/index.d.cts +11 -0
- package/examples/node_modules/resolve-pkg-maps/dist/index.d.mts +11 -0
- package/examples/node_modules/resolve-pkg-maps/dist/index.mjs +1 -0
- package/examples/node_modules/resolve-pkg-maps/package.json +42 -0
- package/examples/node_modules/seed-words/.github/workflows/publish-npmjs.yaml +22 -0
- package/examples/node_modules/seed-words/BUILD.md +7 -0
- package/examples/node_modules/seed-words/LICENSE +121 -0
- package/examples/node_modules/seed-words/README.md +67 -0
- package/examples/node_modules/seed-words/dist/seedwords.d.ts +39 -0
- package/examples/node_modules/seed-words/package.json +27 -0
- package/examples/node_modules/seed-words/seedwords.js +315 -0
- package/examples/node_modules/seed-words/seedwords.txt +65536 -0
- package/examples/node_modules/seed-words/tsconfig.json +21 -0
- package/examples/node_modules/tsx/LICENSE +21 -0
- package/examples/node_modules/tsx/README.md +32 -0
- package/examples/node_modules/tsx/dist/cjs/api/index.cjs +1 -0
- package/examples/node_modules/tsx/dist/cjs/api/index.d.cts +35 -0
- package/examples/node_modules/tsx/dist/cjs/api/index.d.mts +35 -0
- package/examples/node_modules/tsx/dist/cjs/api/index.mjs +1 -0
- package/examples/node_modules/tsx/dist/cjs/index.cjs +1 -0
- package/examples/node_modules/tsx/dist/cjs/index.mjs +1 -0
- package/examples/node_modules/tsx/dist/cli.cjs +54 -0
- package/examples/node_modules/tsx/dist/cli.mjs +55 -0
- package/examples/node_modules/tsx/dist/client-BQVF1NaW.mjs +1 -0
- package/examples/node_modules/tsx/dist/client-D6NvIMSC.cjs +1 -0
- package/examples/node_modules/tsx/dist/esm/api/index.cjs +1 -0
- package/examples/node_modules/tsx/dist/esm/api/index.d.cts +35 -0
- package/examples/node_modules/tsx/dist/esm/api/index.d.mts +35 -0
- package/examples/node_modules/tsx/dist/esm/api/index.mjs +1 -0
- package/examples/node_modules/tsx/dist/esm/index.cjs +2 -0
- package/examples/node_modules/tsx/dist/esm/index.mjs +2 -0
- package/examples/node_modules/tsx/dist/get-pipe-path-BHW2eJdv.mjs +1 -0
- package/examples/node_modules/tsx/dist/get-pipe-path-BoR10qr8.cjs +1 -0
- package/examples/node_modules/tsx/dist/index-7AaEi15b.mjs +14 -0
- package/examples/node_modules/tsx/dist/index-BWFBUo6r.cjs +1 -0
- package/examples/node_modules/tsx/dist/index-gbaejti9.mjs +1 -0
- package/examples/node_modules/tsx/dist/index-gckBtVBf.cjs +14 -0
- package/examples/node_modules/tsx/dist/lexer-DQCqS3nf.mjs +3 -0
- package/examples/node_modules/tsx/dist/lexer-DgIbo0BU.cjs +3 -0
- package/examples/node_modules/tsx/dist/loader.cjs +1 -0
- package/examples/node_modules/tsx/dist/loader.mjs +1 -0
- package/examples/node_modules/tsx/dist/node-features-_8ZFwP_x.mjs +1 -0
- package/examples/node_modules/tsx/dist/node-features-roYmp9jK.cjs +1 -0
- package/examples/node_modules/tsx/dist/package-CeBgXWuR.mjs +1 -0
- package/examples/node_modules/tsx/dist/package-Dxt5kIHw.cjs +1 -0
- package/examples/node_modules/tsx/dist/patch-repl.cjs +1 -0
- package/examples/node_modules/tsx/dist/patch-repl.mjs +1 -0
- package/examples/node_modules/tsx/dist/preflight.cjs +1 -0
- package/examples/node_modules/tsx/dist/preflight.mjs +1 -0
- package/examples/node_modules/tsx/dist/register-2sWVXuRQ.cjs +1 -0
- package/examples/node_modules/tsx/dist/register-B7jrtLTO.mjs +1 -0
- package/examples/node_modules/tsx/dist/register-CFH5oNdT.mjs +4 -0
- package/examples/node_modules/tsx/dist/register-D46fvsV_.cjs +4 -0
- package/examples/node_modules/tsx/dist/repl.cjs +3 -0
- package/examples/node_modules/tsx/dist/repl.mjs +3 -0
- package/examples/node_modules/tsx/dist/require-D4F1Lv60.cjs +1 -0
- package/examples/node_modules/tsx/dist/require-DQxpCAr4.mjs +1 -0
- package/examples/node_modules/tsx/dist/suppress-warnings.cjs +1 -0
- package/examples/node_modules/tsx/dist/suppress-warnings.mjs +1 -0
- package/examples/node_modules/tsx/dist/temporary-directory-B83uKxJF.cjs +1 -0
- package/examples/node_modules/tsx/dist/temporary-directory-CwHp0_NW.mjs +1 -0
- package/examples/node_modules/tsx/dist/types-Cxp8y2TL.d.ts +5 -0
- package/examples/node_modules/tsx/package.json +68 -0
- package/examples/offline-signing.js +0 -2
- package/examples/offline-signing.ts +0 -1
- package/examples/package-lock.json +424 -73
- package/examples/package.json +2 -2
- package/examples/wallet-offline.js +10 -9
- package/examples/wallet-offline.ts +1 -9
- package/generate-sdk.js +4 -6
- package/package.json +2 -2
- package/src/abi/interface.js +13 -7
- package/src/abi/js-abi-coder.js +23 -18
- package/src/constants.d.ts +0 -5
- package/src/constants.js +0 -7
- package/src/contract/contract-factory.js +9 -3
- package/src/contract/contract.js +9 -3
- package/src/errors/index.js +12 -0
- package/src/index.d.ts +0 -3
- package/src/providers/extra-providers.js +20 -6
- package/src/providers/json-rpc-provider.js +15 -5
- package/src/providers/provider.d.ts +0 -2
- package/src/providers/provider.js +1 -3
- package/src/utils/address.d.ts +0 -14
- package/src/utils/address.js +12 -49
- package/src/utils/hashing.d.ts +0 -6
- package/src/utils/hashing.js +8 -23
- package/src/utils/index.d.ts +0 -3
- package/src/utils/rlp.js +7 -4
- package/src/wallet/wallet.d.ts +11 -13
- package/src/wallet/wallet.js +135 -96
- package/test/security/malformed-input.test.js +295 -1
- package/test/unit/address-wallet.test.js +277 -128
- package/test/unit/address-wallet.test.ts +276 -127
- package/test/unit/hashing.test.js +0 -11
- package/test/unit/hashing.test.ts +0 -11
- package/test/unit/providers.test.js +3 -1
- package/test/unit/providers.test.ts +3 -1
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* @testCategory security
|
|
3
3
|
* @blockchainRequired false
|
|
4
4
|
* @transactional false
|
|
5
|
-
* @description Security tests for malformed input, edge cases, and invalid values
|
|
5
|
+
* @description Security tests for malformed input, edge cases, and invalid values.
|
|
6
|
+
* Covers all findings from the consolidated security audit.
|
|
6
7
|
* Run with VERBOSE=1 for test names.
|
|
7
8
|
*/
|
|
8
9
|
|
|
@@ -35,3 +36,296 @@ describe("Security: Malformed Input", () => {
|
|
|
35
36
|
});
|
|
36
37
|
});
|
|
37
38
|
|
|
39
|
+
describe("Security: C1 - Private key enumeration protection", () => {
|
|
40
|
+
logSuite("Security: C1 - Private key enumeration protection");
|
|
41
|
+
|
|
42
|
+
it("JSON.stringify(wallet) must NOT contain privateKey or seed", async () => {
|
|
43
|
+
logTest("JSON.stringify(wallet) must NOT contain privateKey or seed", {});
|
|
44
|
+
await Initialize(null);
|
|
45
|
+
const w = qc.Wallet.createRandom();
|
|
46
|
+
const json = JSON.stringify(w);
|
|
47
|
+
const parsed = JSON.parse(json);
|
|
48
|
+
|
|
49
|
+
assert.ok(!("privateKey" in parsed), "privateKey must not appear in JSON");
|
|
50
|
+
assert.ok(!("seed" in parsed), "seed must not appear in JSON");
|
|
51
|
+
assert.ok(!("signingKey" in parsed), "signingKey must not appear in JSON");
|
|
52
|
+
assert.ok(!("_qcWallet" in parsed), "_qcWallet must not appear in JSON");
|
|
53
|
+
assert.ok(!("_seed" in parsed), "_seed must not appear in JSON");
|
|
54
|
+
assert.ok(!("privateKeyBytes" in parsed), "privateKeyBytes must not appear in JSON");
|
|
55
|
+
|
|
56
|
+
assert.ok("address" in parsed, "address should be in JSON");
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("Object.keys(wallet) must not include secret properties", async () => {
|
|
60
|
+
logTest("Object.keys(wallet) must not include secret properties", {});
|
|
61
|
+
await Initialize(null);
|
|
62
|
+
const w = qc.Wallet.createRandom();
|
|
63
|
+
const keys = Object.keys(w);
|
|
64
|
+
|
|
65
|
+
assert.ok(!keys.includes("privateKey"), "privateKey must not be enumerable");
|
|
66
|
+
assert.ok(!keys.includes("seed"), "seed must not be enumerable");
|
|
67
|
+
assert.ok(!keys.includes("signingKey"), "signingKey must not be enumerable");
|
|
68
|
+
assert.ok(!keys.includes("_qcWallet"), "_qcWallet must not be enumerable");
|
|
69
|
+
assert.ok(!keys.includes("_seed"), "_seed must not be enumerable");
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("privateKey and seed are still accessible directly", async () => {
|
|
73
|
+
logTest("privateKey and seed are still accessible directly", {});
|
|
74
|
+
await Initialize(null);
|
|
75
|
+
const w = qc.Wallet.createRandom();
|
|
76
|
+
assert.equal(typeof w.privateKey, "string");
|
|
77
|
+
assert.ok(w.privateKey.startsWith("0x"));
|
|
78
|
+
assert.equal(typeof w.seed, "string");
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe("Security: C3 - Wallet.connect() preserves state", () => {
|
|
83
|
+
logSuite("Security: C3 - Wallet.connect() preserves state");
|
|
84
|
+
|
|
85
|
+
it("connect() preserves seed", async () => {
|
|
86
|
+
logTest("connect() preserves seed", {});
|
|
87
|
+
await Initialize(null);
|
|
88
|
+
const w = qc.Wallet.createRandom();
|
|
89
|
+
const seedBefore = w.seed;
|
|
90
|
+
|
|
91
|
+
const provider = new qc.JsonRpcProvider("http://127.0.0.1:9999", 123123);
|
|
92
|
+
const connected = w.connect(provider);
|
|
93
|
+
|
|
94
|
+
assert.equal(connected.seed, seedBefore, "seed must survive connect()");
|
|
95
|
+
assert.equal(connected.address, w.address, "address must survive connect()");
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe("Security: C4 - KDF string password handling", () => {
|
|
100
|
+
logSuite("Security: C4 - KDF string password handling");
|
|
101
|
+
|
|
102
|
+
it("pbkdf2 with plain string password does not crash", async () => {
|
|
103
|
+
logTest("pbkdf2 with plain string password does not crash", {});
|
|
104
|
+
const result = qc.pbkdf2("password123", "salt123", 1000, 32, "sha256");
|
|
105
|
+
assert.equal(typeof result, "string");
|
|
106
|
+
assert.ok(result.startsWith("0x"));
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("computeHmac with plain string key/data does not crash", async () => {
|
|
110
|
+
logTest("computeHmac with plain string key/data does not crash", {});
|
|
111
|
+
const result = qc.computeHmac("sha256", "mykey", "mydata");
|
|
112
|
+
assert.equal(typeof result, "string");
|
|
113
|
+
assert.ok(result.startsWith("0x"));
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("scryptSync with plain string password does not crash", async () => {
|
|
117
|
+
logTest("scryptSync with plain string password does not crash", {});
|
|
118
|
+
const result = qc.scryptSync("password123", "salt123", 1024, 8, 1, 32);
|
|
119
|
+
assert.equal(typeof result, "string");
|
|
120
|
+
assert.ok(result.startsWith("0x"));
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe("Security: H2 - Numeric precision in signTransaction", () => {
|
|
125
|
+
logSuite("Security: H2 - Numeric precision in signTransaction");
|
|
126
|
+
|
|
127
|
+
it("rejects number value above MAX_SAFE_INTEGER", async () => {
|
|
128
|
+
logTest("rejects number value above MAX_SAFE_INTEGER", {});
|
|
129
|
+
await Initialize(null);
|
|
130
|
+
const w = qc.Wallet.createRandom();
|
|
131
|
+
await assert.rejects(
|
|
132
|
+
() => w.signTransaction({ to: w.address, value: Number.MAX_SAFE_INTEGER + 1, nonce: 0, chainId: 123123 }),
|
|
133
|
+
/overflow/i,
|
|
134
|
+
);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it("accepts bigint value for large amounts", async () => {
|
|
138
|
+
logTest("accepts bigint value for large amounts", {});
|
|
139
|
+
await Initialize(null);
|
|
140
|
+
const w = qc.Wallet.createRandom();
|
|
141
|
+
const raw = await w.signTransaction({
|
|
142
|
+
to: w.address,
|
|
143
|
+
value: 999999999999999999999n,
|
|
144
|
+
nonce: 0,
|
|
145
|
+
chainId: 123123,
|
|
146
|
+
});
|
|
147
|
+
assert.equal(typeof raw, "string");
|
|
148
|
+
assert.ok(raw.startsWith("0x"));
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("accepts string value '0x' as zero", async () => {
|
|
152
|
+
logTest("accepts string value '0x' as zero", {});
|
|
153
|
+
await Initialize(null);
|
|
154
|
+
const w = qc.Wallet.createRandom();
|
|
155
|
+
const raw = await w.signTransaction({ to: w.address, value: "0x", nonce: 0, chainId: 123123 });
|
|
156
|
+
assert.equal(typeof raw, "string");
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe("Security: M3 - Password strength enforcement", () => {
|
|
161
|
+
logSuite("Security: M3 - Password strength enforcement");
|
|
162
|
+
|
|
163
|
+
it("encryptSync rejects password shorter than 12 characters", async () => {
|
|
164
|
+
logTest("encryptSync rejects password shorter than 12 characters", {});
|
|
165
|
+
await Initialize(null);
|
|
166
|
+
const w = qc.Wallet.createRandom();
|
|
167
|
+
assert.throws(() => w.encryptSync("short"), /password must be at least 12 characters/);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("encryptSync accepts password of 12+ characters", async () => {
|
|
171
|
+
logTest("encryptSync accepts password of 12+ characters", {});
|
|
172
|
+
await Initialize(null);
|
|
173
|
+
const w = qc.Wallet.createRandom();
|
|
174
|
+
const json = w.encryptSync("abcdefghijkl");
|
|
175
|
+
assert.equal(typeof json, "string");
|
|
176
|
+
assert.ok(json.length > 0);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
describe("Security: M4 - Message signing removed", () => {
|
|
181
|
+
logSuite("Security: M4 - Message signing removed");
|
|
182
|
+
|
|
183
|
+
it("hashMessage is not exported", () => {
|
|
184
|
+
logTest("hashMessage is not exported", {});
|
|
185
|
+
assert.equal(qc.hashMessage, undefined, "hashMessage should not be exported");
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it("verifyMessage is not exported", () => {
|
|
189
|
+
logTest("verifyMessage is not exported", {});
|
|
190
|
+
assert.equal(qc.verifyMessage, undefined, "verifyMessage should not be exported");
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("recoverAddress is not exported", () => {
|
|
194
|
+
logTest("recoverAddress is not exported", {});
|
|
195
|
+
assert.equal(qc.recoverAddress, undefined, "recoverAddress should not be exported");
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("MessagePrefix is not exported", () => {
|
|
199
|
+
logTest("MessagePrefix is not exported", {});
|
|
200
|
+
assert.equal(qc.MessagePrefix, undefined, "MessagePrefix should not be exported");
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
describe("Security: M6 - Error messages do not leak secrets", () => {
|
|
205
|
+
logSuite("Security: M6 - Error messages do not leak secrets");
|
|
206
|
+
|
|
207
|
+
it("fromKeys error does not contain actual key bytes", async () => {
|
|
208
|
+
logTest("fromKeys error does not contain actual key bytes", {});
|
|
209
|
+
await Initialize(null);
|
|
210
|
+
const fakeKey = "0xdeadbeef";
|
|
211
|
+
try {
|
|
212
|
+
qc.Wallet.fromKeys(fakeKey, fakeKey);
|
|
213
|
+
assert.fail("should have thrown");
|
|
214
|
+
} catch (e) {
|
|
215
|
+
assert.ok(!e.message.includes("deadbeef"), "error must not contain key material");
|
|
216
|
+
assert.ok(e.message.includes("[REDACTED]") || !e.message.includes("0x"), "value should be redacted");
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
describe("Security: M7 - _hexToBigInt handles '0x'", () => {
|
|
222
|
+
logSuite("Security: M7 - _hexToBigInt handles '0x'");
|
|
223
|
+
|
|
224
|
+
it("getBalance does not crash on '0x' response", async () => {
|
|
225
|
+
logTest("getBalance does not crash on '0x' response", {});
|
|
226
|
+
assert.equal(typeof qc.JsonRpcProvider, "function");
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
describe("Security: L3 - RLP depth limit", () => {
|
|
231
|
+
logSuite("Security: L3 - RLP depth limit");
|
|
232
|
+
|
|
233
|
+
it("rejects deeply nested RLP (depth > 64)", () => {
|
|
234
|
+
logTest("rejects deeply nested RLP (depth > 64)", {});
|
|
235
|
+
let inner = [];
|
|
236
|
+
for (let i = 0; i < 70; i++) {
|
|
237
|
+
inner = [inner];
|
|
238
|
+
}
|
|
239
|
+
const encoded = qc.encodeRlp(inner);
|
|
240
|
+
assert.throws(() => qc.decodeRlp(encoded), /maximum nesting depth exceeded/);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it("accepts RLP within depth limit", () => {
|
|
244
|
+
logTest("accepts RLP within depth limit", {});
|
|
245
|
+
const encoded = qc.encodeRlp([["hello", "world"], "test"]);
|
|
246
|
+
const decoded = qc.decodeRlp(encoded);
|
|
247
|
+
assert.ok(Array.isArray(decoded));
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
describe("Security: M2 - Seed phrase with invalid words", () => {
|
|
252
|
+
logSuite("Security: M2 - Seed phrase with invalid words");
|
|
253
|
+
|
|
254
|
+
it("rejects gibberish words of correct count", async () => {
|
|
255
|
+
logTest("rejects gibberish words of correct count", {});
|
|
256
|
+
await Initialize(null);
|
|
257
|
+
const gibberish = new Array(32).fill("xyzzyplugh");
|
|
258
|
+
assert.throws(
|
|
259
|
+
() => qc.Wallet.fromPhrase(gibberish),
|
|
260
|
+
{ message: /failed/i },
|
|
261
|
+
);
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
it("rejects empty string words of correct count", async () => {
|
|
265
|
+
logTest("rejects empty string words of correct count", {});
|
|
266
|
+
await Initialize(null);
|
|
267
|
+
const empty = new Array(32).fill("");
|
|
268
|
+
assert.throws(() => qc.Wallet.fromPhrase(empty));
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
describe("Security: L6 - Keccak-256 test vectors", () => {
|
|
273
|
+
logSuite("Security: L6 - Keccak-256 test vectors");
|
|
274
|
+
|
|
275
|
+
it("keccak256 of empty bytes matches known digest", async () => {
|
|
276
|
+
logTest("keccak256 of empty bytes matches known digest", {});
|
|
277
|
+
const result = qc.keccak256(new Uint8Array(0));
|
|
278
|
+
assert.equal(result, "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it("keccak256 of 'hello' matches known digest", async () => {
|
|
282
|
+
logTest("keccak256 of 'hello' matches known digest", {});
|
|
283
|
+
const bytes = new TextEncoder().encode("hello");
|
|
284
|
+
const result = qc.keccak256(bytes);
|
|
285
|
+
assert.equal(result, "0x1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8");
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it("sha256 produces correct length output", () => {
|
|
289
|
+
logTest("sha256 produces correct length output", {});
|
|
290
|
+
const result = qc.sha256(new Uint8Array([1, 2, 3]));
|
|
291
|
+
assert.equal(typeof result, "string");
|
|
292
|
+
assert.equal(result.length, 66);
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
describe("Security: H5 - BigInt in provider params", () => {
|
|
297
|
+
logSuite("Security: H5 - BigInt in provider params");
|
|
298
|
+
|
|
299
|
+
it("JsonRpcProvider serializes BigInt params without crashing", async (t) => {
|
|
300
|
+
logTest("JsonRpcProvider serializes BigInt params without crashing", {});
|
|
301
|
+
if (typeof fetch !== "function") {
|
|
302
|
+
t.skip("global fetch not available");
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
const originalFetch = fetch;
|
|
306
|
+
global.fetch = async (_url, init) => {
|
|
307
|
+
const body = JSON.parse(init.body);
|
|
308
|
+
assert.equal(body.params[0].value, "0x3e8");
|
|
309
|
+
const responseBody = JSON.stringify({ jsonrpc: "2.0", id: body.id, result: "0x1" });
|
|
310
|
+
return { ok: true, text: async () => responseBody, json: async () => JSON.parse(responseBody) };
|
|
311
|
+
};
|
|
312
|
+
try {
|
|
313
|
+
const p = new qc.JsonRpcProvider("http://example.invalid", 123123);
|
|
314
|
+
await p._perform("eth_call", [{ to: "0x" + "00".repeat(32), value: 1000n }]);
|
|
315
|
+
} finally {
|
|
316
|
+
global.fetch = originalFetch;
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
describe("Security: H6 - Per-instance RPC IDs", () => {
|
|
322
|
+
logSuite("Security: H6 - Per-instance RPC IDs");
|
|
323
|
+
|
|
324
|
+
it("different provider instances have independent ID counters", () => {
|
|
325
|
+
logTest("different provider instances have independent ID counters", {});
|
|
326
|
+
const p1 = new qc.JsonRpcProvider("http://a.invalid", 1);
|
|
327
|
+
const p2 = new qc.JsonRpcProvider("http://b.invalid", 1);
|
|
328
|
+
assert.equal(p1._rpcId, 1);
|
|
329
|
+
assert.equal(p2._rpcId, 1);
|
|
330
|
+
});
|
|
331
|
+
});
|