@corpus-core/colibri-stateless 0.4.11 → 0.5.0

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/index.d.ts CHANGED
@@ -26,12 +26,14 @@ import { EthSubscribeSubscriptionType, EthNewFilterType } from './subscriptionMa
26
26
  import Strategy from './strategy.js';
27
27
  export { Strategy };
28
28
  export { ProviderRpcError, RequestArguments, Cache, C4Config, DataRequest, C4MethodType as MethodType, ProviderMessage, EthSubscribeSubscriptionType, EthNewFilterType };
29
+ export { TransactionVerifier, PrototypeProtection } from './transactionVerifier.js';
29
30
  export declare function handle_request(req: DataRequest, conf: C4Config): Promise<void>;
30
31
  export default class C4Client {
31
32
  config: C4Config;
32
33
  private eventEmitter;
33
34
  private connectionState;
34
35
  private subscriptionManager;
36
+ private static readonly CRITICAL_METHODS;
35
37
  constructor(config?: Partial<C4Config>);
36
38
  private getProoferConfig;
37
39
  private get flags();
package/index.js CHANGED
@@ -27,9 +27,12 @@ import { ConnectionState } from './connectionState.js';
27
27
  import { ProviderRpcError, MethodType as C4MethodType } from './types.js';
28
28
  import { SubscriptionManager } from './subscriptionManager.js';
29
29
  import Strategy from './strategy.js';
30
+ import { TransactionVerifier, PrototypeProtection } from './transactionVerifier.js';
30
31
  export { Strategy };
31
32
  // Re-export types needed by consumers of the C4Client module
32
33
  export { ProviderRpcError, C4MethodType as MethodType };
34
+ // Re-export transaction verification utilities
35
+ export { TransactionVerifier, PrototypeProtection } from './transactionVerifier.js';
33
36
  async function fetch_rpc(urls, payload, as_proof = false) {
34
37
  let last_error = "All nodes failed";
35
38
  for (const url of urls) {
@@ -190,10 +193,17 @@ export default class C4Client {
190
193
  eventEmitter;
191
194
  connectionState;
192
195
  subscriptionManager;
196
+ // Schutz vor Prototype Pollution durch Einfrieren kritischer Methoden
197
+ static CRITICAL_METHODS = ['rpc', 'request', 'verifyProof', 'createProof'];
198
+ static {
199
+ // Schutz vor Prototype Pollution anwenden
200
+ PrototypeProtection.protectClass(C4Client, C4Client.CRITICAL_METHODS);
201
+ }
193
202
  constructor(config) {
194
203
  const chainId = config?.chainId ? get_chain_id(config?.chainId + '') : 1;
195
204
  const chain_config = { ...default_config[chainId + ''] };
196
- this.config = {
205
+ // Schutz vor Config-Manipulation durch Deep-Freeze
206
+ const baseConfig = {
197
207
  chains: {},
198
208
  trusted_block_hashes: [],
199
209
  rpcs: chain_config.rpcs || [],
@@ -202,6 +212,9 @@ export default class C4Client {
202
212
  ...config, // User config overrides defaults
203
213
  chainId,
204
214
  };
215
+ // Schutz vor Config-Manipulation anwenden
216
+ PrototypeProtection.protectConfig(baseConfig, ['rpcs', 'beacon_apis', 'proofer', 'trusted_block_hashes']);
217
+ this.config = baseConfig;
205
218
  if (!this.config.warningHandler)
206
219
  this.config.warningHandler = async (req, message) => console.warn(message);
207
220
  if (!this.config.proofStrategy)
@@ -317,6 +330,10 @@ export default class C4Client {
317
330
  async rpc(method, args, method_type) {
318
331
  // eth_subscribe and eth_unsubscribe are handled by C4Client.request before this method is called.
319
332
  // This rpc method is for the underlying data fetching/proving.
333
+ // Spezielle Behandlung für eth_sendTransaction mit Verifikation
334
+ if (method === 'eth_sendTransaction' && this.config.verifyTransactions) {
335
+ return await TransactionVerifier.verifyAndSendTransaction(args[0], this.config, (method, args, methodType) => this.rpc(method, args, methodType), fetch_rpc);
336
+ }
320
337
  if (method_type === undefined)
321
338
  method_type = await this.getMethodSupport(method);
322
339
  switch (method_type) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@corpus-core/colibri-stateless",
3
- "version": "0.4.11",
3
+ "version": "0.5.0",
4
4
  "description": "Colibri is a stateless and trustless ethereum client, which is optimized for the mobile apps or embedded devices, because it does not hols the state, but verifies on demand.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Copyright (c) 2025 corpus.core
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ * this software and associated documentation files (the "Software"), to deal in
6
+ * the Software without restriction, including without limitation the rights to
7
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
+ * the Software, and to permit persons to whom the Software is furnished to do so,
9
+ * subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all
12
+ * copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
+ *
21
+ * SPDX-License-Identifier: MIT
22
+ */
23
+ import { MethodType as C4MethodType } from './types.js';
24
+ /**
25
+ * Transaction Verifier - Sichere Verifikation von eth_sendTransaction
26
+ *
27
+ * Implementiert eine Sicherheitsschicht gegen NPM Supply-Chain-Attacks:
28
+ * 1. Transaction über fallbackProvider signieren lassen
29
+ * 2. Signierte Transaction mit eth_decodeTransaction dekodieren
30
+ * 3. Dekodierte Parameter mit Original vergleichen
31
+ * 4. Bei Übereinstimmung: eth_sendRawTransaction ausführen
32
+ */
33
+ export interface TransactionVerifierConfig {
34
+ debug?: boolean;
35
+ chainId: number | string;
36
+ fallback_provider?: any;
37
+ rpcs: string[];
38
+ }
39
+ export declare class TransactionVerifier {
40
+ /**
41
+ * Verifizierte Ausführung von eth_sendTransaction
42
+ * 1. Transaction über fallbackProvider signieren lassen
43
+ * 2. Signierte Transaction mit eth_decodeTransaction dekodieren
44
+ * 3. Dekodierte Parameter mit Original vergleichen
45
+ * 4. eth_sendRawTransaction ausführen
46
+ */
47
+ static verifyAndSendTransaction(txObject: any, config: TransactionVerifierConfig, rpcMethod: (method: string, args: any[], method_type?: C4MethodType) => Promise<any>, fetchRpc: (urls: string[], payload: any, as_proof?: boolean) => Promise<any>): Promise<string>;
48
+ /**
49
+ * Vergleicht die Parameter einer ursprünglichen Transaction mit einer dekodierten signierten Transaction
50
+ */
51
+ private static compareTransactionParameters;
52
+ /**
53
+ * Normalisiert Hex-Werte für Vergleiche (entfernt führende Nullen, stellt 0x-Prefix sicher)
54
+ */
55
+ private static normalizeHexValue;
56
+ }
57
+ /**
58
+ * Prototype Protection - Schutz vor NPM Supply-Chain-Attacks
59
+ *
60
+ * Friert kritische Prototypen und Methoden ein, um Manipulation zu verhindern
61
+ */
62
+ export declare class PrototypeProtection {
63
+ /**
64
+ * Schützt eine Klasse vor Prototype Pollution
65
+ */
66
+ static protectClass<T>(targetClass: new (...args: any[]) => T, criticalMethods?: readonly string[]): void;
67
+ /**
68
+ * Schützt ein Konfigurationsobjekt vor Manipulation
69
+ */
70
+ static protectConfig(config: any, criticalArrays?: string[]): void;
71
+ }
72
+ /**
73
+ * Transaction Utilities - Hilfsfunktionen für Transaction-Handling
74
+ */
75
+ export declare class TransactionUtils {
76
+ /**
77
+ * Validiert ein Transaction-Objekt
78
+ */
79
+ static validateTransactionObject(txObject: any): boolean;
80
+ /**
81
+ * Bereinigt Transaction-Parameter für verschiedene Standards
82
+ */
83
+ static cleanupTransactionParams(txObject: any): any;
84
+ /**
85
+ * Erstellt eine Zusammenfassung einer Transaction für Logging
86
+ */
87
+ static summarizeTransaction(txObject: any): string;
88
+ }
89
+ declare const _default: {
90
+ TransactionVerifier: typeof TransactionVerifier;
91
+ PrototypeProtection: typeof PrototypeProtection;
92
+ TransactionUtils: typeof TransactionUtils;
93
+ };
94
+ export default _default;
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Copyright (c) 2025 corpus.core
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
5
+ * this software and associated documentation files (the "Software"), to deal in
6
+ * the Software without restriction, including without limitation the rights to
7
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8
+ * the Software, and to permit persons to whom the Software is furnished to do so,
9
+ * subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all
12
+ * copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20
+ *
21
+ * SPDX-License-Identifier: MIT
22
+ */
23
+ import { ProviderRpcError, MethodType as C4MethodType } from './types.js';
24
+ export class TransactionVerifier {
25
+ /**
26
+ * Verifizierte Ausführung von eth_sendTransaction
27
+ * 1. Transaction über fallbackProvider signieren lassen
28
+ * 2. Signierte Transaction mit eth_decodeTransaction dekodieren
29
+ * 3. Dekodierte Parameter mit Original vergleichen
30
+ * 4. eth_sendRawTransaction ausführen
31
+ */
32
+ static async verifyAndSendTransaction(txObject, config, rpcMethod, fetchRpc) {
33
+ if (config.debug)
34
+ console.log('[TransactionVerifier] Verifying transaction before sending:', txObject);
35
+ if (!config.fallback_provider) {
36
+ throw new ProviderRpcError(4203, 'Transaction verification requires fallback_provider to be configured');
37
+ }
38
+ try {
39
+ // 1. Transaction über fallbackProvider signieren lassen
40
+ const signedRawTx = await config.fallback_provider.request({
41
+ method: 'eth_signTransaction',
42
+ params: [txObject]
43
+ });
44
+ // 2. Signierte Transaction dekodieren
45
+ const decodedTx = await rpcMethod('eth_decodeTransaction', [signedRawTx], C4MethodType.LOCAL);
46
+ // 3. Dekodierte Parameter mit Original vergleichen
47
+ const isValid = this.compareTransactionParameters(txObject, decodedTx, config);
48
+ if (!isValid) {
49
+ throw new ProviderRpcError(4201, 'Signed transaction does not match original parameters');
50
+ }
51
+ // 4. Verifizierte Transaction senden
52
+ return await fetchRpc(config.rpcs, {
53
+ method: 'eth_sendRawTransaction',
54
+ params: [signedRawTx]
55
+ }, false);
56
+ }
57
+ catch (error) {
58
+ if (config.debug)
59
+ console.error('[TransactionVerifier] Transaction verification failed:', error);
60
+ throw new ProviderRpcError(4202, `Transaction verification failed: ${error.message}`);
61
+ }
62
+ }
63
+ /**
64
+ * Vergleicht die Parameter einer ursprünglichen Transaction mit einer dekodierten signierten Transaction
65
+ */
66
+ static compareTransactionParameters(original, decoded, config) {
67
+ if (config.debug) {
68
+ console.log('[TransactionVerifier] Comparing transaction parameters:');
69
+ console.log('Original:', original);
70
+ console.log('Decoded:', decoded);
71
+ }
72
+ // Kritische Parameter vergleichen
73
+ const criticalFields = ['to', 'value', 'data', 'gas', 'gasPrice', 'maxFeePerGas', 'maxPriorityFeePerGas', 'nonce'];
74
+ for (const field of criticalFields) {
75
+ if (original[field] !== undefined) {
76
+ // Normalisierung für Hex-Werte (0x-Prefix, Leading Zeros)
77
+ const originalValue = this.normalizeHexValue(original[field]);
78
+ const decodedValue = this.normalizeHexValue(decoded[field]);
79
+ if (originalValue !== decodedValue) {
80
+ if (config.debug) {
81
+ console.warn(`[TransactionVerifier] Transaction parameter mismatch in '${field}': original='${originalValue}', decoded='${decodedValue}'`);
82
+ }
83
+ return false;
84
+ }
85
+ }
86
+ }
87
+ // ChainId prüfen
88
+ if (decoded.chainId && parseInt(decoded.chainId, 16) !== parseInt(config.chainId)) {
89
+ if (config.debug) {
90
+ console.warn(`[TransactionVerifier] ChainId mismatch: expected=${config.chainId}, decoded=${parseInt(decoded.chainId, 16)}`);
91
+ }
92
+ return false;
93
+ }
94
+ return true;
95
+ }
96
+ /**
97
+ * Normalisiert Hex-Werte für Vergleiche (entfernt führende Nullen, stellt 0x-Prefix sicher)
98
+ */
99
+ static normalizeHexValue(value) {
100
+ if (value === undefined || value === null)
101
+ return '';
102
+ if (typeof value === 'number')
103
+ return '0x' + value.toString(16);
104
+ const str = value.toString();
105
+ if (!str.startsWith('0x'))
106
+ return '0x' + str;
107
+ // Führende Nullen entfernen, aber mindestens eine Ziffer behalten
108
+ const withoutPrefix = str.slice(2);
109
+ const normalized = withoutPrefix.replace(/^0+/, '') || '0';
110
+ return '0x' + normalized;
111
+ }
112
+ }
113
+ /**
114
+ * Prototype Protection - Schutz vor NPM Supply-Chain-Attacks
115
+ *
116
+ * Friert kritische Prototypen und Methoden ein, um Manipulation zu verhindern
117
+ */
118
+ export class PrototypeProtection {
119
+ /**
120
+ * Schützt eine Klasse vor Prototype Pollution
121
+ */
122
+ static protectClass(targetClass, criticalMethods = []) {
123
+ // Einfrieren des Prototypes
124
+ Object.freeze(targetClass.prototype);
125
+ // Kritische Methoden gegen Überschreibung schützen
126
+ criticalMethods.forEach(method => {
127
+ const descriptor = Object.getOwnPropertyDescriptor(targetClass.prototype, method);
128
+ if (descriptor) {
129
+ Object.defineProperty(targetClass.prototype, method, {
130
+ ...descriptor,
131
+ writable: false,
132
+ configurable: false
133
+ });
134
+ }
135
+ });
136
+ }
137
+ /**
138
+ * Schützt ein Konfigurationsobjekt vor Manipulation
139
+ */
140
+ static protectConfig(config, criticalArrays = []) {
141
+ // Kritische Arrays einfrieren
142
+ criticalArrays.forEach(arrayName => {
143
+ if (config[arrayName] && Array.isArray(config[arrayName])) {
144
+ Object.freeze(config[arrayName]);
145
+ }
146
+ });
147
+ }
148
+ }
149
+ /**
150
+ * Transaction Utilities - Hilfsfunktionen für Transaction-Handling
151
+ */
152
+ export class TransactionUtils {
153
+ /**
154
+ * Validiert ein Transaction-Objekt
155
+ */
156
+ static validateTransactionObject(txObject) {
157
+ if (!txObject || typeof txObject !== 'object')
158
+ return false;
159
+ // Mindestanforderungen
160
+ if (!txObject.to && !txObject.data)
161
+ return false; // Entweder to-Adresse oder data für Contract-Creation
162
+ return true;
163
+ }
164
+ /**
165
+ * Bereinigt Transaction-Parameter für verschiedene Standards
166
+ */
167
+ static cleanupTransactionParams(txObject) {
168
+ const cleaned = { ...txObject };
169
+ // EIP-1559 vs Legacy Gas-Handling
170
+ if (cleaned.maxFeePerGas || cleaned.maxPriorityFeePerGas) {
171
+ // EIP-1559 - gasPrice entfernen
172
+ delete cleaned.gasPrice;
173
+ }
174
+ else if (cleaned.gasPrice) {
175
+ // Legacy - EIP-1559 Felder entfernen
176
+ delete cleaned.maxFeePerGas;
177
+ delete cleaned.maxPriorityFeePerGas;
178
+ }
179
+ return cleaned;
180
+ }
181
+ /**
182
+ * Erstellt eine Zusammenfassung einer Transaction für Logging
183
+ */
184
+ static summarizeTransaction(txObject) {
185
+ const to = txObject.to || 'Contract Creation';
186
+ const value = txObject.value ? `${parseInt(txObject.value, 16)} wei` : '0 wei';
187
+ const gas = txObject.gas ? parseInt(txObject.gas, 16) : 'auto';
188
+ return `Transaction(to: ${to}, value: ${value}, gas: ${gas})`;
189
+ }
190
+ }
191
+ // Export aller Klassen als Default
192
+ export default {
193
+ TransactionVerifier,
194
+ PrototypeProtection,
195
+ TransactionUtils
196
+ };
package/types.d.ts CHANGED
@@ -57,6 +57,7 @@ export interface ChainConfig {
57
57
  verify?: (method: string, args: any[]) => boolean;
58
58
  pollingInterval?: number;
59
59
  proofStrategy?: ProofStrategy;
60
+ verifyTransactions?: boolean;
60
61
  }
61
62
  export interface EIP1193Client {
62
63
  request(args: RequestArguments): Promise<unknown>;