VYPR
High severity7.5NVD Advisory· Published Mar 31, 2026· Updated Apr 3, 2026

CVE-2026-34209

CVE-2026-34209

Description

mppx is a TypeScript interface for machine payments protocol. Prior to version 0.4.11, the tempo/session cooperative close handler validated the close voucher amount using "<" instead of "<=" against the on-chain settled amount. An attacker could submit a close voucher exactly equal to the settled amount, which would be accepted without committing any new funds, effectively closing or griefing the channel for free. This issue has been patched in version 0.4.11.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
mppxnpm
< 0.4.110.4.11

Affected products

1
  • cpe:2.3:a:wevm:mppx:*:*:*:*:*:node.js:*:*
    Range: <0.4.11

Patches

1
94088246ee18

Merge commit from fork

https://github.com/wevm/mppxtmmMar 26, 2026via ghsa
3 files changed · +41 3
  • .changeset/close-voucher-settled.md+5 0 added
    @@ -0,0 +1,5 @@
    +---
    +'mppx': patch
    +---
    +
    +Fixed close voucher validation to reject vouchers equal to the on-chain settled amount.
    
  • src/tempo/server/Session.test.ts+29 0 modified
    @@ -1143,6 +1143,35 @@ describe.runIf(isLocalnet)('session', () => {
           ).rejects.toThrow('close voucher amount must be >=')
         })
     
    +    test('rejects close equal to on-chain settled amount', async () => {
    +      const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
    +      const server = createServer()
    +
    +      // Open with 1M voucher (matches openServerChannel default)
    +      await openServerChannel(server, channelId, serializedTransaction)
    +
    +      // Settle on-chain so settled becomes 1000000
    +      const settleTxHash = await settle(store, client, channelId, { escrowContract })
    +      await waitForTransactionReceipt(client, { hash: settleTxHash })
    +
    +      // Try to close with voucher == on-chain settled — should be rejected
    +      // because replaying the settled amount doesn't commit new funds
    +      await expect(
    +        server.verify({
    +          credential: {
    +            challenge: makeChallenge({ id: 'challenge-2', channelId }),
    +            payload: {
    +              action: 'close' as const,
    +              channelId,
    +              cumulativeAmount: '1000000',
    +              signature: await signTestVoucher(channelId, 1000000n),
    +            },
    +          },
    +          request: makeRequest(),
    +        }),
    +      ).rejects.toThrow('close voucher amount must be >')
    +    })
    +
         test('rejects close exceeding on-chain deposit', async () => {
           const { channelId, serializedTransaction } = await createSignedOpenTransaction(10000000n)
           const server = createServer()
    
  • src/tempo/server/Session.ts+7 3 modified
    @@ -815,10 +815,14 @@ async function handleClose(
         throw new ChannelClosedError({ reason: 'channel is finalized on-chain' })
       }
     
    -  const minCloseAmount = channel.spent > onChain.settled ? channel.spent : onChain.settled
    -  if (voucher.cumulativeAmount < minCloseAmount) {
    +  if (voucher.cumulativeAmount < channel.spent) {
         throw new VerificationFailedError({
    -      reason: `close voucher amount must be >= ${minCloseAmount} (max of spent and on-chain settled)`,
    +      reason: `close voucher amount must be >= ${channel.spent} (spent)`,
    +    })
    +  }
    +  if (voucher.cumulativeAmount <= onChain.settled) {
    +    throw new VerificationFailedError({
    +      reason: `close voucher amount must be > ${onChain.settled} (on-chain settled)`,
         })
       }
     
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.