@agirails/sdk 2.0.1-beta → 2.0.2
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/LICENSE +190 -0
- package/README.md +116 -108
- package/bin/actp +10 -0
- package/dist/ACTPClient.d.ts +456 -33
- package/dist/ACTPClient.d.ts.map +1 -1
- package/dist/ACTPClient.js +477 -93
- package/dist/ACTPClient.js.map +1 -1
- package/dist/abi/AgentRegistry.json +782 -0
- package/dist/abi/EscrowVault.json +106 -38
- package/dist/abi/IdentityRegistry.json +316 -0
- package/dist/adapters/BaseAdapter.d.ts +231 -0
- package/dist/adapters/BaseAdapter.d.ts.map +1 -0
- package/dist/adapters/BaseAdapter.js +393 -0
- package/dist/adapters/BaseAdapter.js.map +1 -0
- package/dist/adapters/BeginnerAdapter.d.ts +152 -0
- package/dist/adapters/BeginnerAdapter.d.ts.map +1 -0
- package/dist/adapters/BeginnerAdapter.js +168 -0
- package/dist/adapters/BeginnerAdapter.js.map +1 -0
- package/dist/adapters/IntermediateAdapter.d.ts +211 -0
- package/dist/adapters/IntermediateAdapter.d.ts.map +1 -0
- package/dist/adapters/IntermediateAdapter.js +260 -0
- package/dist/adapters/IntermediateAdapter.js.map +1 -0
- package/dist/adapters/index.d.ts +15 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +26 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/builders/DeliveryProofBuilder.d.ts +60 -1
- package/dist/builders/DeliveryProofBuilder.d.ts.map +1 -1
- package/dist/builders/DeliveryProofBuilder.js +81 -5
- package/dist/builders/DeliveryProofBuilder.js.map +1 -1
- package/dist/builders/QuoteBuilder.d.ts +101 -0
- package/dist/builders/QuoteBuilder.d.ts.map +1 -1
- package/dist/builders/QuoteBuilder.js +120 -3
- package/dist/builders/QuoteBuilder.js.map +1 -1
- package/dist/builders/index.d.ts +4 -0
- package/dist/builders/index.d.ts.map +1 -1
- package/dist/builders/index.js +4 -0
- package/dist/builders/index.js.map +1 -1
- package/dist/cli/commands/balance.d.ts +13 -0
- package/dist/cli/commands/balance.d.ts.map +1 -0
- package/dist/cli/commands/balance.js +89 -0
- package/dist/cli/commands/balance.js.map +1 -0
- package/dist/cli/commands/batch.d.ts +24 -0
- package/dist/cli/commands/batch.d.ts.map +1 -0
- package/dist/cli/commands/batch.js +424 -0
- package/dist/cli/commands/batch.js.map +1 -0
- package/dist/cli/commands/config.d.ts +13 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +192 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/init.d.ts +19 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +143 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/mint.d.ts +13 -0
- package/dist/cli/commands/mint.d.ts.map +1 -0
- package/dist/cli/commands/mint.js +91 -0
- package/dist/cli/commands/mint.js.map +1 -0
- package/dist/cli/commands/pay.d.ts +18 -0
- package/dist/cli/commands/pay.d.ts.map +1 -0
- package/dist/cli/commands/pay.js +87 -0
- package/dist/cli/commands/pay.js.map +1 -0
- package/dist/cli/commands/simulate.d.ts +32 -0
- package/dist/cli/commands/simulate.d.ts.map +1 -0
- package/dist/cli/commands/simulate.js +290 -0
- package/dist/cli/commands/simulate.js.map +1 -0
- package/dist/cli/commands/time.d.ts +29 -0
- package/dist/cli/commands/time.d.ts.map +1 -0
- package/dist/cli/commands/time.js +252 -0
- package/dist/cli/commands/time.js.map +1 -0
- package/dist/cli/commands/tx.d.ts +16 -0
- package/dist/cli/commands/tx.d.ts.map +1 -0
- package/dist/cli/commands/tx.js +379 -0
- package/dist/cli/commands/tx.js.map +1 -0
- package/dist/cli/commands/watch.d.ts +20 -0
- package/dist/cli/commands/watch.d.ts.map +1 -0
- package/dist/cli/commands/watch.js +160 -0
- package/dist/cli/commands/watch.js.map +1 -0
- package/dist/cli/index.d.ts +17 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +104 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/utils/client.d.ts +70 -0
- package/dist/cli/utils/client.d.ts.map +1 -0
- package/dist/cli/utils/client.js +240 -0
- package/dist/cli/utils/client.js.map +1 -0
- package/dist/cli/utils/config.d.ts +91 -0
- package/dist/cli/utils/config.d.ts.map +1 -0
- package/dist/cli/utils/config.js +240 -0
- package/dist/cli/utils/config.js.map +1 -0
- package/dist/cli/utils/output.d.ts +174 -0
- package/dist/cli/utils/output.d.ts.map +1 -0
- package/dist/cli/utils/output.js +380 -0
- package/dist/cli/utils/output.js.map +1 -0
- package/dist/config/networks.d.ts +28 -0
- package/dist/config/networks.d.ts.map +1 -1
- package/dist/config/networks.js +60 -12
- package/dist/config/networks.js.map +1 -1
- package/dist/errors/index.d.ts +165 -2
- package/dist/errors/index.d.ts.map +1 -1
- package/dist/errors/index.js +260 -2
- package/dist/errors/index.js.map +1 -1
- package/dist/index.d.ts +61 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +141 -36
- package/dist/index.js.map +1 -1
- package/dist/level0/Provider.d.ts +106 -0
- package/dist/level0/Provider.d.ts.map +1 -0
- package/dist/level0/Provider.js +10 -0
- package/dist/level0/Provider.js.map +1 -0
- package/dist/level0/ServiceDirectory.d.ts +74 -0
- package/dist/level0/ServiceDirectory.d.ts.map +1 -0
- package/dist/level0/ServiceDirectory.js +122 -0
- package/dist/level0/ServiceDirectory.js.map +1 -0
- package/dist/level0/index.d.ts +10 -0
- package/dist/level0/index.d.ts.map +1 -0
- package/dist/level0/index.js +15 -0
- package/dist/level0/index.js.map +1 -0
- package/dist/level0/provide.d.ts +51 -0
- package/dist/level0/provide.d.ts.map +1 -0
- package/dist/level0/provide.js +113 -0
- package/dist/level0/provide.js.map +1 -0
- package/dist/level0/request.d.ts +53 -0
- package/dist/level0/request.d.ts.map +1 -0
- package/dist/level0/request.js +462 -0
- package/dist/level0/request.js.map +1 -0
- package/dist/level1/Agent.d.ts +472 -0
- package/dist/level1/Agent.d.ts.map +1 -0
- package/dist/level1/Agent.js +1091 -0
- package/dist/level1/Agent.js.map +1 -0
- package/dist/level1/index.d.ts +10 -0
- package/dist/level1/index.d.ts.map +1 -0
- package/dist/level1/index.js +30 -0
- package/dist/level1/index.js.map +1 -0
- package/dist/level1/pricing/PriceCalculator.d.ts +62 -0
- package/dist/level1/pricing/PriceCalculator.d.ts.map +1 -0
- package/dist/level1/pricing/PriceCalculator.js +237 -0
- package/dist/level1/pricing/PriceCalculator.js.map +1 -0
- package/dist/level1/pricing/PricingStrategy.d.ts +179 -0
- package/dist/level1/pricing/PricingStrategy.d.ts.map +1 -0
- package/dist/level1/pricing/PricingStrategy.js +11 -0
- package/dist/level1/pricing/PricingStrategy.js.map +1 -0
- package/dist/level1/types/Job.d.ts +166 -0
- package/dist/level1/types/Job.d.ts.map +1 -0
- package/dist/level1/types/Job.js +11 -0
- package/dist/level1/types/Job.js.map +1 -0
- package/dist/level1/types/Options.d.ts +258 -0
- package/dist/level1/types/Options.d.ts.map +1 -0
- package/dist/level1/types/Options.js +8 -0
- package/dist/level1/types/Options.js.map +1 -0
- package/dist/level1/types/index.d.ts +8 -0
- package/dist/level1/types/index.d.ts.map +1 -0
- package/dist/level1/types/index.js +8 -0
- package/dist/level1/types/index.js.map +1 -0
- package/dist/protocol/ACTPKernel.d.ts +229 -2
- package/dist/protocol/ACTPKernel.d.ts.map +1 -1
- package/dist/protocol/ACTPKernel.js +367 -33
- package/dist/protocol/ACTPKernel.js.map +1 -1
- package/dist/protocol/AgentRegistry.d.ts +177 -0
- package/dist/protocol/AgentRegistry.d.ts.map +1 -0
- package/dist/protocol/AgentRegistry.js +449 -0
- package/dist/protocol/AgentRegistry.js.map +1 -0
- package/dist/protocol/DIDManager.d.ts +289 -0
- package/dist/protocol/DIDManager.d.ts.map +1 -0
- package/dist/protocol/DIDManager.js +481 -0
- package/dist/protocol/DIDManager.js.map +1 -0
- package/dist/protocol/DIDResolver.d.ts +236 -0
- package/dist/protocol/DIDResolver.d.ts.map +1 -0
- package/dist/protocol/DIDResolver.js +495 -0
- package/dist/protocol/DIDResolver.js.map +1 -0
- package/dist/protocol/EASHelper.d.ts +57 -2
- package/dist/protocol/EASHelper.d.ts.map +1 -1
- package/dist/protocol/EASHelper.js +230 -37
- package/dist/protocol/EASHelper.js.map +1 -1
- package/dist/protocol/EscrowVault.d.ts +93 -2
- package/dist/protocol/EscrowVault.d.ts.map +1 -1
- package/dist/protocol/EscrowVault.js +122 -33
- package/dist/protocol/EscrowVault.js.map +1 -1
- package/dist/protocol/EventMonitor.d.ts +45 -1
- package/dist/protocol/EventMonitor.d.ts.map +1 -1
- package/dist/protocol/EventMonitor.js +64 -8
- package/dist/protocol/EventMonitor.js.map +1 -1
- package/dist/protocol/MessageSigner.d.ts +116 -2
- package/dist/protocol/MessageSigner.d.ts.map +1 -1
- package/dist/protocol/MessageSigner.js +215 -9
- package/dist/protocol/MessageSigner.js.map +1 -1
- package/dist/protocol/ProofGenerator.d.ts +93 -0
- package/dist/protocol/ProofGenerator.d.ts.map +1 -1
- package/dist/protocol/ProofGenerator.js +194 -9
- package/dist/protocol/ProofGenerator.js.map +1 -1
- package/dist/protocol/QuoteBuilder.d.ts +8 -0
- package/dist/protocol/QuoteBuilder.d.ts.map +1 -1
- package/dist/protocol/QuoteBuilder.js +8 -0
- package/dist/protocol/QuoteBuilder.js.map +1 -1
- package/dist/runtime/BlockchainRuntime.d.ts +360 -0
- package/dist/runtime/BlockchainRuntime.d.ts.map +1 -0
- package/dist/runtime/BlockchainRuntime.js +767 -0
- package/dist/runtime/BlockchainRuntime.js.map +1 -0
- package/dist/runtime/IACTPRuntime.d.ts +271 -0
- package/dist/runtime/IACTPRuntime.d.ts.map +1 -0
- package/dist/runtime/IACTPRuntime.js +15 -0
- package/dist/runtime/IACTPRuntime.js.map +1 -0
- package/dist/runtime/MockRuntime.d.ts +445 -0
- package/dist/runtime/MockRuntime.d.ts.map +1 -0
- package/dist/runtime/MockRuntime.js +1065 -0
- package/dist/runtime/MockRuntime.js.map +1 -0
- package/dist/runtime/MockStateManager.d.ts +233 -0
- package/dist/runtime/MockStateManager.d.ts.map +1 -0
- package/dist/runtime/MockStateManager.js +533 -0
- package/dist/runtime/MockStateManager.js.map +1 -0
- package/dist/runtime/index.d.ts +14 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +42 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/types/MockState.d.ts +167 -0
- package/dist/runtime/types/MockState.d.ts.map +1 -0
- package/dist/runtime/types/MockState.js +43 -0
- package/dist/runtime/types/MockState.js.map +1 -0
- package/dist/types/agent.d.ts +76 -0
- package/dist/types/agent.d.ts.map +1 -0
- package/dist/types/agent.js +8 -0
- package/dist/types/agent.js.map +1 -0
- package/dist/types/did.d.ts +192 -0
- package/dist/types/did.d.ts.map +1 -0
- package/dist/types/did.js +38 -0
- package/dist/types/did.js.map +1 -0
- package/dist/types/eip712.d.ts +34 -0
- package/dist/types/eip712.d.ts.map +1 -1
- package/dist/types/eip712.js +31 -5
- package/dist/types/eip712.js.map +1 -1
- package/dist/types/escrow.d.ts +17 -10
- package/dist/types/escrow.d.ts.map +1 -1
- package/dist/types/index.d.ts +5 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +8 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/message.d.ts +32 -0
- package/dist/types/message.d.ts.map +1 -1
- package/dist/types/message.js +4 -0
- package/dist/types/message.js.map +1 -1
- package/dist/types/state.d.ts +28 -0
- package/dist/types/state.d.ts.map +1 -1
- package/dist/types/state.js +37 -6
- package/dist/types/state.js.map +1 -1
- package/dist/types/transaction.d.ts +17 -0
- package/dist/types/transaction.d.ts.map +1 -1
- package/dist/utils/ErrorRecoveryGuide.d.ts +125 -0
- package/dist/utils/ErrorRecoveryGuide.d.ts.map +1 -0
- package/dist/utils/ErrorRecoveryGuide.js +579 -0
- package/dist/utils/ErrorRecoveryGuide.js.map +1 -0
- package/dist/utils/Helpers.d.ts +453 -0
- package/dist/utils/Helpers.d.ts.map +1 -0
- package/dist/utils/Helpers.js +623 -0
- package/dist/utils/Helpers.js.map +1 -0
- package/dist/utils/IPFSClient.d.ts +113 -0
- package/dist/utils/IPFSClient.d.ts.map +1 -1
- package/dist/utils/IPFSClient.js +128 -7
- package/dist/utils/IPFSClient.js.map +1 -1
- package/dist/utils/Logger.d.ts +195 -0
- package/dist/utils/Logger.d.ts.map +1 -0
- package/dist/utils/Logger.js +382 -0
- package/dist/utils/Logger.js.map +1 -0
- package/dist/utils/NonceManager.d.ts +234 -1
- package/dist/utils/NonceManager.d.ts.map +1 -1
- package/dist/utils/NonceManager.js +372 -7
- package/dist/utils/NonceManager.js.map +1 -1
- package/dist/utils/RateLimiter.d.ts +253 -0
- package/dist/utils/RateLimiter.d.ts.map +1 -0
- package/dist/utils/RateLimiter.js +424 -0
- package/dist/utils/RateLimiter.js.map +1 -0
- package/dist/utils/ReceivedNonceTracker.d.ts +175 -0
- package/dist/utils/ReceivedNonceTracker.d.ts.map +1 -1
- package/dist/utils/ReceivedNonceTracker.js +261 -5
- package/dist/utils/ReceivedNonceTracker.js.map +1 -1
- package/dist/utils/SDKLifecycle.d.ts +156 -0
- package/dist/utils/SDKLifecycle.d.ts.map +1 -0
- package/dist/utils/SDKLifecycle.js +347 -0
- package/dist/utils/SDKLifecycle.js.map +1 -0
- package/dist/utils/SecureNonce.d.ts +57 -0
- package/dist/utils/SecureNonce.d.ts.map +1 -0
- package/dist/utils/SecureNonce.js +80 -0
- package/dist/utils/SecureNonce.js.map +1 -0
- package/dist/utils/Semaphore.d.ts +123 -0
- package/dist/utils/Semaphore.d.ts.map +1 -0
- package/dist/utils/Semaphore.js +247 -0
- package/dist/utils/Semaphore.js.map +1 -0
- package/dist/utils/UsedAttestationTracker.d.ts +167 -0
- package/dist/utils/UsedAttestationTracker.d.ts.map +1 -0
- package/dist/utils/UsedAttestationTracker.js +309 -0
- package/dist/utils/UsedAttestationTracker.js.map +1 -0
- package/dist/utils/canonicalJson.d.ts +22 -0
- package/dist/utils/canonicalJson.d.ts.map +1 -1
- package/dist/utils/canonicalJson.js +26 -3
- package/dist/utils/canonicalJson.js.map +1 -1
- package/dist/utils/computeTypeHash.d.ts +14 -0
- package/dist/utils/computeTypeHash.d.ts.map +1 -1
- package/dist/utils/computeTypeHash.js +19 -2
- package/dist/utils/computeTypeHash.js.map +1 -1
- package/dist/utils/fsSafe.d.ts +14 -0
- package/dist/utils/fsSafe.d.ts.map +1 -0
- package/dist/utils/fsSafe.js +89 -0
- package/dist/utils/fsSafe.js.map +1 -0
- package/dist/utils/index.d.ts +15 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +51 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/security.d.ts +147 -0
- package/dist/utils/security.d.ts.map +1 -0
- package/dist/utils/security.js +391 -0
- package/dist/utils/security.js.map +1 -0
- package/dist/utils/validation.d.ts +40 -0
- package/dist/utils/validation.d.ts.map +1 -1
- package/dist/utils/validation.js +184 -7
- package/dist/utils/validation.js.map +1 -1
- package/package.json +54 -37
- package/src/ACTPClient.ts +692 -178
- package/src/abi/AgentRegistry.json +782 -0
- package/src/abi/EscrowVault.json +106 -38
- package/src/abi/IdentityRegistry.json +316 -0
- package/src/adapters/BaseAdapter.ts +473 -0
- package/src/adapters/BeginnerAdapter.ts +232 -0
- package/src/adapters/IntermediateAdapter.ts +316 -0
- package/src/adapters/index.ts +25 -0
- package/src/builders/DeliveryProofBuilder.ts +3 -2
- package/src/cli/commands/balance.ts +110 -0
- package/src/cli/commands/batch.ts +487 -0
- package/src/cli/commands/config.ts +231 -0
- package/src/cli/commands/init.ts +161 -0
- package/src/cli/commands/mint.ts +116 -0
- package/src/cli/commands/pay.ts +113 -0
- package/src/cli/commands/simulate.ts +345 -0
- package/src/cli/commands/time.ts +303 -0
- package/src/cli/commands/tx.ts +448 -0
- package/src/cli/commands/watch.ts +211 -0
- package/src/cli/index.ts +116 -0
- package/src/cli/utils/client.ts +249 -0
- package/src/cli/utils/config.ts +282 -0
- package/src/cli/utils/output.ts +465 -0
- package/src/config/networks.ts +32 -9
- package/src/errors/index.ts +298 -1
- package/src/index.ts +207 -71
- package/src/level0/Provider.ts +117 -0
- package/src/level0/ServiceDirectory.ts +131 -0
- package/src/level0/index.ts +10 -0
- package/src/level0/provide.ts +131 -0
- package/src/level0/request.ts +494 -0
- package/src/level1/Agent.ts +1432 -0
- package/src/level1/index.ts +10 -0
- package/src/level1/pricing/PriceCalculator.ts +255 -0
- package/src/level1/pricing/PricingStrategy.ts +198 -0
- package/src/level1/types/Job.ts +179 -0
- package/src/level1/types/Options.ts +291 -0
- package/src/level1/types/index.ts +8 -0
- package/src/protocol/ACTPKernel.ts +175 -23
- package/src/protocol/AgentRegistry.ts +559 -0
- package/src/protocol/DIDManager.ts +629 -0
- package/src/protocol/DIDResolver.ts +554 -0
- package/src/protocol/EASHelper.ts +230 -46
- package/src/protocol/EscrowVault.ts +68 -50
- package/src/protocol/EventMonitor.ts +44 -15
- package/src/protocol/MessageSigner.ts +193 -13
- package/src/protocol/ProofGenerator.ts +223 -4
- package/src/runtime/BlockchainRuntime.ts +993 -0
- package/src/runtime/IACTPRuntime.ts +284 -0
- package/src/runtime/MockRuntime.ts +1244 -0
- package/src/runtime/MockStateManager.ts +576 -0
- package/src/runtime/index.ts +25 -0
- package/src/runtime/types/MockState.ts +227 -0
- package/src/types/agent.ts +79 -0
- package/src/types/did.ts +223 -0
- package/src/types/escrow.ts +12 -11
- package/src/types/index.ts +5 -1
- package/src/types/state.ts +12 -3
- package/src/types/transaction.ts +4 -1
- package/src/utils/ErrorRecoveryGuide.ts +675 -0
- package/src/utils/Helpers.ts +688 -0
- package/src/utils/IPFSClient.ts +122 -5
- package/src/utils/Logger.ts +484 -0
- package/src/utils/NonceManager.ts +305 -8
- package/src/utils/RateLimiter.ts +534 -0
- package/src/utils/ReceivedNonceTracker.ts +170 -0
- package/src/utils/SDKLifecycle.ts +416 -0
- package/src/utils/SecureNonce.ts +78 -0
- package/src/utils/Semaphore.ts +276 -0
- package/src/utils/UsedAttestationTracker.ts +387 -0
- package/src/utils/fsSafe.ts +75 -0
- package/src/utils/index.ts +80 -0
- package/src/utils/security.ts +418 -0
- package/src/utils/validation.ts +164 -0
- package/src/__tests__/ProofGenerator.test.ts +0 -124
- package/src/__tests__/QuoteBuilder.test.ts +0 -516
- package/src/__tests__/StateMachine.test.ts +0 -82
- package/src/__tests__/builders/DeliveryProofBuilder.test.ts +0 -581
- package/src/__tests__/integration/ACTPClient.test.ts +0 -263
- package/src/__tests__/integration.test.ts +0 -289
- package/src/__tests__/protocol/EASHelper.test.ts +0 -472
- package/src/__tests__/protocol/EventMonitor.test.ts +0 -382
- package/src/__tests__/security/ACTPKernel.security.test.ts +0 -1167
- package/src/__tests__/security/EscrowVault.security.test.ts +0 -570
- package/src/__tests__/security/MessageSigner.security.test.ts +0 -286
- package/src/__tests__/security/NonceReplay.security.test.ts +0 -501
- package/src/__tests__/security/validation.security.test.ts +0 -376
- package/src/__tests__/utils/IPFSClient.test.ts +0 -262
- package/src/__tests__/utils/NonceManager.test.ts +0 -205
- package/src/__tests__/utils/canonicalJson.test.ts +0 -153
|
@@ -228,6 +228,10 @@ export class InMemoryReceivedNonceTracker implements IReceivedNonceTracker {
|
|
|
228
228
|
* - Reject duplicate nonces (replay attack)
|
|
229
229
|
* - Allows non-sequential nonces (nonce gaps are OK)
|
|
230
230
|
*
|
|
231
|
+
* SECURITY FIX (NEW-H-2): Max size enforcement to prevent memory exhaustion
|
|
232
|
+
* SECURITY FIX (HIGH-2): Global total entries limit to prevent DoS via many sender combinations
|
|
233
|
+
* SECURITY FIX (H-2): Rate limiting per sender to prevent flood attacks
|
|
234
|
+
*
|
|
231
235
|
* Trade-off:
|
|
232
236
|
* - Higher memory usage (stores every nonce)
|
|
233
237
|
* - More flexible (allows out-of-order delivery)
|
|
@@ -237,8 +241,94 @@ export class SetBasedReceivedNonceTracker implements IReceivedNonceTracker {
|
|
|
237
241
|
// Map: sender -> messageType -> Set of used nonces
|
|
238
242
|
private usedNonces: Map<string, Map<string, Set<string>>> = new Map();
|
|
239
243
|
|
|
244
|
+
// SECURITY FIX (NEW-H-2): Maximum entries per sender+messageType
|
|
245
|
+
private readonly maxSizePerType: number;
|
|
246
|
+
|
|
247
|
+
// SECURITY FIX (HIGH-2): Global limit across ALL sender+messageType combinations
|
|
248
|
+
private readonly maxTotalEntries: number;
|
|
249
|
+
private totalEntries: number = 0;
|
|
250
|
+
|
|
251
|
+
// SECURITY FIX (H-2): Rate limiting per sender
|
|
252
|
+
// Map: sender -> { count: number, windowStart: number }
|
|
253
|
+
private rateLimitState: Map<string, { count: number; windowStart: number }> = new Map();
|
|
254
|
+
private readonly maxNoncesPerMinute: number;
|
|
255
|
+
private readonly rateLimitWindowMs: number = 60000; // 1 minute window
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Create set-based tracker with optional max size
|
|
259
|
+
* @param maxSizePerType - Maximum nonces per sender+messageType (default: 10,000)
|
|
260
|
+
* @param maxTotalEntries - Maximum total nonces across all combinations (default: 100,000)
|
|
261
|
+
* @param maxNoncesPerMinute - Maximum nonces per sender per minute (default: 100)
|
|
262
|
+
*/
|
|
263
|
+
constructor(
|
|
264
|
+
maxSizePerType: number = 10000,
|
|
265
|
+
maxTotalEntries: number = 100000,
|
|
266
|
+
maxNoncesPerMinute: number = 100
|
|
267
|
+
) {
|
|
268
|
+
if (maxSizePerType <= 0) {
|
|
269
|
+
throw new Error('maxSizePerType must be positive');
|
|
270
|
+
}
|
|
271
|
+
if (maxTotalEntries <= 0) {
|
|
272
|
+
throw new Error('maxTotalEntries must be positive');
|
|
273
|
+
}
|
|
274
|
+
if (maxNoncesPerMinute <= 0) {
|
|
275
|
+
throw new Error('maxNoncesPerMinute must be positive');
|
|
276
|
+
}
|
|
277
|
+
this.maxSizePerType = maxSizePerType;
|
|
278
|
+
this.maxTotalEntries = maxTotalEntries;
|
|
279
|
+
this.maxNoncesPerMinute = maxNoncesPerMinute;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* SECURITY FIX (H-2): Check rate limit for sender
|
|
284
|
+
* @param sender - Sender DID
|
|
285
|
+
* @returns true if rate limit exceeded
|
|
286
|
+
*/
|
|
287
|
+
private checkRateLimit(sender: string): boolean {
|
|
288
|
+
const now = Date.now();
|
|
289
|
+
const state = this.rateLimitState.get(sender);
|
|
290
|
+
|
|
291
|
+
if (!state) {
|
|
292
|
+
// First nonce from this sender
|
|
293
|
+
this.rateLimitState.set(sender, { count: 1, windowStart: now });
|
|
294
|
+
return false; // Not rate limited
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Check if window expired (reset counter)
|
|
298
|
+
if (now - state.windowStart >= this.rateLimitWindowMs) {
|
|
299
|
+
state.count = 1;
|
|
300
|
+
state.windowStart = now;
|
|
301
|
+
return false; // Not rate limited
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Increment counter
|
|
305
|
+
state.count++;
|
|
306
|
+
|
|
307
|
+
// Check if rate limit exceeded
|
|
308
|
+
return state.count > this.maxNoncesPerMinute;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* SECURITY FIX (H-2): Periodic cleanup of rate limit state
|
|
313
|
+
* Removes expired rate limit entries (older than 5 minutes)
|
|
314
|
+
*/
|
|
315
|
+
private cleanupRateLimitState(): void {
|
|
316
|
+
const now = Date.now();
|
|
317
|
+
const expiryThreshold = 5 * 60 * 1000; // 5 minutes
|
|
318
|
+
|
|
319
|
+
for (const [sender, state] of this.rateLimitState.entries()) {
|
|
320
|
+
if (now - state.windowStart > expiryThreshold) {
|
|
321
|
+
this.rateLimitState.delete(sender);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
240
326
|
/**
|
|
241
327
|
* Validate and record a received nonce
|
|
328
|
+
*
|
|
329
|
+
* SECURITY FIX (NEW-H-2): Automatic cleanup when max size reached
|
|
330
|
+
* SECURITY FIX (HIGH-2): Global limit check to prevent DoS
|
|
331
|
+
* SECURITY FIX (H-2): Rate limiting per sender (max 100 nonces/minute)
|
|
242
332
|
*/
|
|
243
333
|
validateAndRecord(sender: string, messageType: string, nonce: string): NonceValidationResult {
|
|
244
334
|
// Validate nonce format
|
|
@@ -250,6 +340,34 @@ export class SetBasedReceivedNonceTracker implements IReceivedNonceTracker {
|
|
|
250
340
|
};
|
|
251
341
|
}
|
|
252
342
|
|
|
343
|
+
// SECURITY FIX (H-2): Rate limit check (BEFORE global limit to avoid unnecessary work)
|
|
344
|
+
if (this.checkRateLimit(sender)) {
|
|
345
|
+
return {
|
|
346
|
+
valid: false,
|
|
347
|
+
reason: `Rate limit exceeded for sender ${sender}: ` +
|
|
348
|
+
`Maximum ${this.maxNoncesPerMinute} nonces per minute allowed. ` +
|
|
349
|
+
`This may indicate a flood attack or misconfigured client. ` +
|
|
350
|
+
`Wait 1 minute before retrying.`,
|
|
351
|
+
receivedNonce: nonce
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// SECURITY FIX (H-2): Periodic cleanup every 100 validations (amortized cost)
|
|
356
|
+
if (this.totalEntries % 100 === 0) {
|
|
357
|
+
this.cleanupRateLimitState();
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
// SECURITY FIX (HIGH-2): Check global limit BEFORE adding
|
|
361
|
+
if (this.totalEntries >= this.maxTotalEntries) {
|
|
362
|
+
return {
|
|
363
|
+
valid: false,
|
|
364
|
+
reason: `Global nonce tracker limit reached (${this.maxTotalEntries} entries). ` +
|
|
365
|
+
`This may indicate a DoS attack or need for cleanup. ` +
|
|
366
|
+
`Current usage: ${this.totalEntries} entries across ${this.getCombinationCount()} sender+type combinations.`,
|
|
367
|
+
receivedNonce: nonce
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
|
|
253
371
|
// Get sender's nonce map
|
|
254
372
|
let senderNonces = this.usedNonces.get(sender);
|
|
255
373
|
if (!senderNonces) {
|
|
@@ -273,11 +391,53 @@ export class SetBasedReceivedNonceTracker implements IReceivedNonceTracker {
|
|
|
273
391
|
};
|
|
274
392
|
}
|
|
275
393
|
|
|
394
|
+
// SECURITY FIX (NEW-H-2): Auto-cleanup if max size per type reached
|
|
395
|
+
if (usedSet.size >= this.maxSizePerType) {
|
|
396
|
+
// Keep only last 80% of entries (sorted by nonce value)
|
|
397
|
+
const keepCount = Math.floor(this.maxSizePerType * 0.8);
|
|
398
|
+
const sortedNonces = Array.from(usedSet).sort((a, b) => {
|
|
399
|
+
const aVal = BigInt(a);
|
|
400
|
+
const bVal = BigInt(b);
|
|
401
|
+
return aVal < bVal ? -1 : aVal > bVal ? 1 : 0;
|
|
402
|
+
});
|
|
403
|
+
const removedCount = usedSet.size - keepCount;
|
|
404
|
+
usedSet = new Set(sortedNonces.slice(-keepCount));
|
|
405
|
+
senderNonces.set(messageType, usedSet);
|
|
406
|
+
// SECURITY FIX (HIGH-2): Update global counter
|
|
407
|
+
this.totalEntries -= removedCount;
|
|
408
|
+
}
|
|
409
|
+
|
|
276
410
|
// Valid nonce - record it
|
|
277
411
|
usedSet.add(nonce);
|
|
412
|
+
// SECURITY FIX (HIGH-2): Update global counter
|
|
413
|
+
this.totalEntries++;
|
|
278
414
|
return { valid: true };
|
|
279
415
|
}
|
|
280
416
|
|
|
417
|
+
/**
|
|
418
|
+
* Get number of sender+messageType combinations (for monitoring)
|
|
419
|
+
* SECURITY FIX (HIGH-2): Monitoring method
|
|
420
|
+
*/
|
|
421
|
+
private getCombinationCount(): number {
|
|
422
|
+
let count = 0;
|
|
423
|
+
this.usedNonces.forEach(senderMap => {
|
|
424
|
+
count += senderMap.size;
|
|
425
|
+
});
|
|
426
|
+
return count;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Get memory usage statistics
|
|
431
|
+
* SECURITY FIX (HIGH-2): Monitoring method for DoS detection
|
|
432
|
+
*/
|
|
433
|
+
getMemoryUsage(): { totalEntries: number; combinations: number; maxTotalEntries: number } {
|
|
434
|
+
return {
|
|
435
|
+
totalEntries: this.totalEntries,
|
|
436
|
+
combinations: this.getCombinationCount(),
|
|
437
|
+
maxTotalEntries: this.maxTotalEntries
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
|
|
281
441
|
/**
|
|
282
442
|
* Check if a nonce has been used
|
|
283
443
|
*/
|
|
@@ -327,6 +487,11 @@ export class SetBasedReceivedNonceTracker implements IReceivedNonceTracker {
|
|
|
327
487
|
reset(sender: string, messageType: string): void {
|
|
328
488
|
const senderNonces = this.usedNonces.get(sender);
|
|
329
489
|
if (senderNonces) {
|
|
490
|
+
const usedSet = senderNonces.get(messageType);
|
|
491
|
+
if (usedSet) {
|
|
492
|
+
// SECURITY FIX (HIGH-2): Update global counter
|
|
493
|
+
this.totalEntries -= usedSet.size;
|
|
494
|
+
}
|
|
330
495
|
senderNonces.delete(messageType);
|
|
331
496
|
if (senderNonces.size === 0) {
|
|
332
497
|
this.usedNonces.delete(sender);
|
|
@@ -339,6 +504,8 @@ export class SetBasedReceivedNonceTracker implements IReceivedNonceTracker {
|
|
|
339
504
|
*/
|
|
340
505
|
clearAll(): void {
|
|
341
506
|
this.usedNonces.clear();
|
|
507
|
+
// SECURITY FIX (HIGH-2): Reset global counter
|
|
508
|
+
this.totalEntries = 0;
|
|
342
509
|
}
|
|
343
510
|
|
|
344
511
|
/**
|
|
@@ -377,8 +544,11 @@ export class SetBasedReceivedNonceTracker implements IReceivedNonceTracker {
|
|
|
377
544
|
});
|
|
378
545
|
|
|
379
546
|
// Keep only the last N nonces
|
|
547
|
+
const removedCount = usedSet.size - keepLast;
|
|
380
548
|
const toKeep = new Set(sortedNonces.slice(-keepLast));
|
|
381
549
|
senderNonces.set(messageType, toKeep);
|
|
550
|
+
// SECURITY FIX (HIGH-2): Update global counter
|
|
551
|
+
this.totalEntries -= removedCount;
|
|
382
552
|
}
|
|
383
553
|
}
|
|
384
554
|
|
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SDKLifecycle - Proper cleanup and resource management
|
|
3
|
+
*
|
|
4
|
+
* SECURITY FIX (M-8): Ensures proper cleanup on SDK shutdown:
|
|
5
|
+
* - Connection cleanup
|
|
6
|
+
* - Pending request handling
|
|
7
|
+
* - Memory release
|
|
8
|
+
* - Graceful shutdown
|
|
9
|
+
*
|
|
10
|
+
* @module utils/SDKLifecycle
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { sdkLogger, Logger } from './Logger';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Disposable resource interface
|
|
17
|
+
*/
|
|
18
|
+
export interface Disposable {
|
|
19
|
+
/** Cleanup method */
|
|
20
|
+
dispose(): Promise<void> | void;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Shutdown handler function
|
|
25
|
+
*/
|
|
26
|
+
export type ShutdownHandler = () => Promise<void> | void;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Lifecycle event types
|
|
30
|
+
*/
|
|
31
|
+
export type LifecycleEvent =
|
|
32
|
+
| 'initializing'
|
|
33
|
+
| 'ready'
|
|
34
|
+
| 'shutting-down'
|
|
35
|
+
| 'shutdown'
|
|
36
|
+
| 'error';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Lifecycle event listener
|
|
40
|
+
*/
|
|
41
|
+
export type LifecycleListener = (event: LifecycleEvent, data?: unknown) => void;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* SDK Lifecycle Manager
|
|
45
|
+
*
|
|
46
|
+
* Manages SDK initialization, shutdown, and resource cleanup.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* const lifecycle = new SDKLifecycle();
|
|
51
|
+
*
|
|
52
|
+
* // Register cleanup handlers
|
|
53
|
+
* lifecycle.onShutdown(async () => {
|
|
54
|
+
* await closeConnections();
|
|
55
|
+
* });
|
|
56
|
+
*
|
|
57
|
+
* // Register disposable resources
|
|
58
|
+
* lifecycle.registerDisposable(myResource);
|
|
59
|
+
*
|
|
60
|
+
* // Later, initiate shutdown
|
|
61
|
+
* await lifecycle.shutdown();
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export class SDKLifecycle {
|
|
65
|
+
private state: LifecycleEvent = 'initializing';
|
|
66
|
+
private shutdownHandlers: ShutdownHandler[] = [];
|
|
67
|
+
private disposables: Disposable[] = [];
|
|
68
|
+
private listeners: LifecycleListener[] = [];
|
|
69
|
+
private shutdownPromise: Promise<void> | null = null;
|
|
70
|
+
private readonly logger: Logger;
|
|
71
|
+
private isShuttingDown = false;
|
|
72
|
+
|
|
73
|
+
// SECURITY FIX (NEW-HIGH-2): Store handler references for cleanup
|
|
74
|
+
private processHandlers: {
|
|
75
|
+
sigint?: () => void;
|
|
76
|
+
sigterm?: () => void;
|
|
77
|
+
uncaughtException?: (error: Error) => void;
|
|
78
|
+
unhandledRejection?: (reason: unknown) => void;
|
|
79
|
+
} = {};
|
|
80
|
+
private processHandlersRegistered = false;
|
|
81
|
+
|
|
82
|
+
constructor(logger?: Logger) {
|
|
83
|
+
this.logger = logger ?? sdkLogger.child('Lifecycle');
|
|
84
|
+
|
|
85
|
+
// Register process shutdown handlers
|
|
86
|
+
this.registerProcessHandlers();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get current lifecycle state
|
|
91
|
+
*/
|
|
92
|
+
getState(): LifecycleEvent {
|
|
93
|
+
return this.state;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Check if SDK is ready for operations
|
|
98
|
+
*/
|
|
99
|
+
isReady(): boolean {
|
|
100
|
+
return this.state === 'ready';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Mark SDK as ready
|
|
105
|
+
*/
|
|
106
|
+
markReady(): void {
|
|
107
|
+
this.state = 'ready';
|
|
108
|
+
this.emit('ready');
|
|
109
|
+
this.logger.info('SDK ready');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Register a shutdown handler
|
|
114
|
+
*
|
|
115
|
+
* @param handler - Function to call during shutdown
|
|
116
|
+
* @returns Unregister function
|
|
117
|
+
*/
|
|
118
|
+
onShutdown(handler: ShutdownHandler): () => void {
|
|
119
|
+
this.shutdownHandlers.push(handler);
|
|
120
|
+
|
|
121
|
+
return () => {
|
|
122
|
+
const index = this.shutdownHandlers.indexOf(handler);
|
|
123
|
+
if (index > -1) {
|
|
124
|
+
this.shutdownHandlers.splice(index, 1);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Register a disposable resource
|
|
131
|
+
*
|
|
132
|
+
* @param disposable - Resource with dispose() method
|
|
133
|
+
* @returns Unregister function
|
|
134
|
+
*/
|
|
135
|
+
registerDisposable(disposable: Disposable): () => void {
|
|
136
|
+
this.disposables.push(disposable);
|
|
137
|
+
|
|
138
|
+
return () => {
|
|
139
|
+
const index = this.disposables.indexOf(disposable);
|
|
140
|
+
if (index > -1) {
|
|
141
|
+
this.disposables.splice(index, 1);
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Add lifecycle event listener
|
|
148
|
+
*
|
|
149
|
+
* @param listener - Event listener function
|
|
150
|
+
* @returns Unregister function
|
|
151
|
+
*/
|
|
152
|
+
addListener(listener: LifecycleListener): () => void {
|
|
153
|
+
this.listeners.push(listener);
|
|
154
|
+
|
|
155
|
+
return () => {
|
|
156
|
+
const index = this.listeners.indexOf(listener);
|
|
157
|
+
if (index > -1) {
|
|
158
|
+
this.listeners.splice(index, 1);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Emit lifecycle event
|
|
165
|
+
*/
|
|
166
|
+
private emit(event: LifecycleEvent, data?: unknown): void {
|
|
167
|
+
for (const listener of this.listeners) {
|
|
168
|
+
try {
|
|
169
|
+
listener(event, data);
|
|
170
|
+
} catch (error) {
|
|
171
|
+
this.logger.error('Lifecycle listener error', { event }, error as Error);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Initiate graceful shutdown
|
|
178
|
+
*
|
|
179
|
+
* @param timeout - Maximum time to wait for cleanup (ms)
|
|
180
|
+
* @returns Promise that resolves when shutdown is complete
|
|
181
|
+
*/
|
|
182
|
+
async shutdown(timeout = 30000): Promise<void> {
|
|
183
|
+
// Prevent multiple shutdown calls
|
|
184
|
+
if (this.shutdownPromise) {
|
|
185
|
+
return this.shutdownPromise;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (this.isShuttingDown) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
this.isShuttingDown = true;
|
|
193
|
+
this.state = 'shutting-down';
|
|
194
|
+
this.emit('shutting-down');
|
|
195
|
+
this.logger.info('SDK shutting down...');
|
|
196
|
+
|
|
197
|
+
this.shutdownPromise = this.executeShutdown(timeout);
|
|
198
|
+
return this.shutdownPromise;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Execute shutdown sequence
|
|
203
|
+
*/
|
|
204
|
+
private async executeShutdown(timeout: number): Promise<void> {
|
|
205
|
+
const startTime = Date.now();
|
|
206
|
+
const errors: Error[] = [];
|
|
207
|
+
|
|
208
|
+
// Create timeout promise
|
|
209
|
+
const timeoutPromise = new Promise<void>((_, reject) => {
|
|
210
|
+
setTimeout(() => {
|
|
211
|
+
reject(new Error(`Shutdown timed out after ${timeout}ms`));
|
|
212
|
+
}, timeout);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
try {
|
|
216
|
+
// Run shutdown handlers first (in reverse order - LIFO)
|
|
217
|
+
const handlersPromise = (async () => {
|
|
218
|
+
for (let i = this.shutdownHandlers.length - 1; i >= 0; i--) {
|
|
219
|
+
try {
|
|
220
|
+
const elapsed = Date.now() - startTime;
|
|
221
|
+
if (elapsed >= timeout) {
|
|
222
|
+
this.logger.warn('Shutdown timeout reached, skipping remaining handlers');
|
|
223
|
+
break;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const handler = this.shutdownHandlers[i];
|
|
227
|
+
await handler();
|
|
228
|
+
} catch (error) {
|
|
229
|
+
this.logger.error('Shutdown handler error', {}, error as Error);
|
|
230
|
+
errors.push(error as Error);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Dispose resources (in reverse order)
|
|
235
|
+
for (let i = this.disposables.length - 1; i >= 0; i--) {
|
|
236
|
+
try {
|
|
237
|
+
const elapsed = Date.now() - startTime;
|
|
238
|
+
if (elapsed >= timeout) {
|
|
239
|
+
this.logger.warn('Shutdown timeout reached, skipping remaining disposables');
|
|
240
|
+
break;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const disposable = this.disposables[i];
|
|
244
|
+
await disposable.dispose();
|
|
245
|
+
} catch (error) {
|
|
246
|
+
this.logger.error('Disposable cleanup error', {}, error as Error);
|
|
247
|
+
errors.push(error as Error);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
})();
|
|
251
|
+
|
|
252
|
+
// Race against timeout
|
|
253
|
+
await Promise.race([handlersPromise, timeoutPromise]);
|
|
254
|
+
|
|
255
|
+
} catch (error) {
|
|
256
|
+
if ((error as Error).message.includes('timed out')) {
|
|
257
|
+
this.logger.warn('Shutdown timed out, forcing cleanup');
|
|
258
|
+
} else {
|
|
259
|
+
errors.push(error as Error);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// SECURITY FIX (NEW-HIGH-2): Remove process handlers to prevent memory leaks
|
|
264
|
+
this.unregisterProcessHandlers();
|
|
265
|
+
|
|
266
|
+
// Clear registrations
|
|
267
|
+
this.shutdownHandlers = [];
|
|
268
|
+
this.disposables = [];
|
|
269
|
+
|
|
270
|
+
this.state = 'shutdown';
|
|
271
|
+
this.emit('shutdown');
|
|
272
|
+
this.logger.info('SDK shutdown complete', { errors: errors.length });
|
|
273
|
+
|
|
274
|
+
if (errors.length > 0) {
|
|
275
|
+
this.logger.error('Shutdown completed with errors', {
|
|
276
|
+
errorCount: errors.length,
|
|
277
|
+
errors: errors.map((e) => e.message),
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Register process-level shutdown handlers
|
|
284
|
+
*
|
|
285
|
+
* SECURITY FIX (NEW-HIGH-2): Store handler references for cleanup
|
|
286
|
+
* to prevent memory leaks when multiple SDKLifecycle instances are created.
|
|
287
|
+
*/
|
|
288
|
+
private registerProcessHandlers(): void {
|
|
289
|
+
// Only register in Node.js environment
|
|
290
|
+
if (typeof process === 'undefined') {
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// SECURITY FIX (NEW-HIGH-2): Prevent duplicate registrations
|
|
295
|
+
if (this.processHandlersRegistered) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// SECURITY FIX (NEW-HIGH-2): Create named handlers we can remove later
|
|
300
|
+
this.processHandlers.sigint = () => {
|
|
301
|
+
this.logger.info('Received SIGINT, initiating shutdown');
|
|
302
|
+
this.shutdown().then(() => {
|
|
303
|
+
process.exit(0);
|
|
304
|
+
}).catch((error) => {
|
|
305
|
+
this.logger.error('Shutdown error', {}, error);
|
|
306
|
+
process.exit(1);
|
|
307
|
+
});
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
this.processHandlers.sigterm = () => {
|
|
311
|
+
this.logger.info('Received SIGTERM, initiating shutdown');
|
|
312
|
+
this.shutdown().then(() => {
|
|
313
|
+
process.exit(0);
|
|
314
|
+
}).catch((error) => {
|
|
315
|
+
this.logger.error('Shutdown error', {}, error);
|
|
316
|
+
process.exit(1);
|
|
317
|
+
});
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
this.processHandlers.uncaughtException = (error: Error) => {
|
|
321
|
+
this.logger.error('Uncaught exception', {}, error);
|
|
322
|
+
this.emit('error', error);
|
|
323
|
+
this.shutdown().finally(() => {
|
|
324
|
+
process.exit(1);
|
|
325
|
+
});
|
|
326
|
+
};
|
|
327
|
+
|
|
328
|
+
this.processHandlers.unhandledRejection = (reason: unknown) => {
|
|
329
|
+
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
330
|
+
this.logger.error('Unhandled rejection', {}, error);
|
|
331
|
+
this.emit('error', error);
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
// Handle termination signals
|
|
335
|
+
process.on('SIGINT', this.processHandlers.sigint);
|
|
336
|
+
process.on('SIGTERM', this.processHandlers.sigterm);
|
|
337
|
+
|
|
338
|
+
// Handle uncaught errors
|
|
339
|
+
process.on('uncaughtException', this.processHandlers.uncaughtException);
|
|
340
|
+
process.on('unhandledRejection', this.processHandlers.unhandledRejection);
|
|
341
|
+
|
|
342
|
+
this.processHandlersRegistered = true;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Unregister process-level shutdown handlers
|
|
347
|
+
*
|
|
348
|
+
* SECURITY FIX (NEW-HIGH-2): Remove handlers to prevent memory leaks
|
|
349
|
+
*/
|
|
350
|
+
private unregisterProcessHandlers(): void {
|
|
351
|
+
if (typeof process === 'undefined' || !this.processHandlersRegistered) {
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
if (this.processHandlers.sigint) {
|
|
356
|
+
process.removeListener('SIGINT', this.processHandlers.sigint);
|
|
357
|
+
}
|
|
358
|
+
if (this.processHandlers.sigterm) {
|
|
359
|
+
process.removeListener('SIGTERM', this.processHandlers.sigterm);
|
|
360
|
+
}
|
|
361
|
+
if (this.processHandlers.uncaughtException) {
|
|
362
|
+
process.removeListener('uncaughtException', this.processHandlers.uncaughtException);
|
|
363
|
+
}
|
|
364
|
+
if (this.processHandlers.unhandledRejection) {
|
|
365
|
+
process.removeListener('unhandledRejection', this.processHandlers.unhandledRejection);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
this.processHandlers = {};
|
|
369
|
+
this.processHandlersRegistered = false;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Create a disposable wrapper for any cleanup function
|
|
374
|
+
*/
|
|
375
|
+
static createDisposable(cleanup: () => Promise<void> | void): Disposable {
|
|
376
|
+
return {
|
|
377
|
+
dispose: cleanup,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Global SDK lifecycle manager instance
|
|
384
|
+
*/
|
|
385
|
+
export const sdkLifecycle = new SDKLifecycle();
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Convenience function to run cleanup on shutdown
|
|
389
|
+
*
|
|
390
|
+
* @example
|
|
391
|
+
* ```typescript
|
|
392
|
+
* const cleanup = onShutdown(async () => {
|
|
393
|
+
* await closeDatabase();
|
|
394
|
+
* });
|
|
395
|
+
*
|
|
396
|
+
* // Later, to remove the handler:
|
|
397
|
+
* cleanup();
|
|
398
|
+
* ```
|
|
399
|
+
*/
|
|
400
|
+
export function onShutdown(handler: ShutdownHandler): () => void {
|
|
401
|
+
return sdkLifecycle.onShutdown(handler);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Convenience function to register disposable
|
|
406
|
+
*/
|
|
407
|
+
export function registerDisposable(disposable: Disposable): () => void {
|
|
408
|
+
return sdkLifecycle.registerDisposable(disposable);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Convenience function to shutdown SDK
|
|
413
|
+
*/
|
|
414
|
+
export function shutdownSDK(timeout?: number): Promise<void> {
|
|
415
|
+
return sdkLifecycle.shutdown(timeout);
|
|
416
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SecureNonce - Cryptographically secure nonce generation
|
|
3
|
+
*
|
|
4
|
+
* SECURITY FIX (NEW-H-3): Provides secure random nonce generation
|
|
5
|
+
* to prevent weak randomness vulnerabilities in EIP-712 message signing.
|
|
6
|
+
*
|
|
7
|
+
* Reference: V7 Re-Audit NEW-H-3 (Weak Random Nonce Generation)
|
|
8
|
+
*
|
|
9
|
+
* @module utils/SecureNonce
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { ethers } from 'ethers';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Generate a cryptographically secure random nonce (bytes32)
|
|
16
|
+
*
|
|
17
|
+
* Uses ethers.js randomBytes() which:
|
|
18
|
+
* - Uses Node.js crypto.randomBytes() (CSPRNG)
|
|
19
|
+
* - Uses Web Crypto API in browsers (window.crypto.getRandomValues)
|
|
20
|
+
* - Guaranteed to be cryptographically secure
|
|
21
|
+
*
|
|
22
|
+
* @returns 32-byte hex string (0x...)
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* import { generateSecureNonce } from '@agirails/sdk';
|
|
27
|
+
*
|
|
28
|
+
* const nonce = generateSecureNonce();
|
|
29
|
+
* console.log(nonce); // 0x1234...abcd (64 hex chars)
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export function generateSecureNonce(): string {
|
|
33
|
+
return ethers.hexlify(ethers.randomBytes(32));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Validate nonce format (must be bytes32)
|
|
38
|
+
*
|
|
39
|
+
* @param nonce - Nonce to validate
|
|
40
|
+
* @returns true if valid bytes32 format
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* isValidNonce('0x' + '00'.repeat(32)); // true
|
|
45
|
+
* isValidNonce('0x1234'); // false (too short)
|
|
46
|
+
* isValidNonce('not-hex'); // false (invalid format)
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export function isValidNonce(nonce: string): boolean {
|
|
50
|
+
return /^0x[a-fA-F0-9]{64}$/.test(nonce);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Generate an array of secure nonces
|
|
55
|
+
*
|
|
56
|
+
* @param count - Number of nonces to generate
|
|
57
|
+
* @returns Array of bytes32 hex strings
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* const nonces = generateSecureNonces(10);
|
|
62
|
+
* console.log(nonces.length); // 10
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export function generateSecureNonces(count: number): string[] {
|
|
66
|
+
if (count <= 0) {
|
|
67
|
+
throw new Error('Count must be positive');
|
|
68
|
+
}
|
|
69
|
+
if (count > 10000) {
|
|
70
|
+
throw new Error('Count exceeds maximum allowed (10000)');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const nonces: string[] = [];
|
|
74
|
+
for (let i = 0; i < count; i++) {
|
|
75
|
+
nonces.push(generateSecureNonce());
|
|
76
|
+
}
|
|
77
|
+
return nonces;
|
|
78
|
+
}
|