The Problem
Nonce replay protection was added to the MCP and A2A middleware (commit 4f7c307). The `POST /verify` endpoint at `cmd/server/main.go:95` calls `verify.Chain` directly and has no nonce check. Bundles can be replayed through `/verify` indefinitely.
This matters because `/verify` is not an internal endpoint. It is the primary integration point for the SDK and for any caller that does not go through the middleware. If a client captures a valid signed bundle, they can submit it to `/verify` as many times as they want, getting a valid `VerificationResult` back each time. Any downstream system that gates on a `{"valid":true}` response is bypassed.
Where It Breaks
```go
// cmd/server/main.go:119
result := verify.Chain(req.ChainBundle, reqDeps)
```
No nonce check before this line.
What Must Change
Wire the same `nonceStore` into the `/verify` handler. The check is already extracted into `checkNonceReplay()` in `pkg/middleware/decode.go` — one call, one return-if-true.
```go
if checkNonceReplay(w, req.Invocation, nonceStore) {
return
}
result := verify.Chain(req.ChainBundle, reqDeps)
```
The nonce store is already constructed in `main()` and available in scope.
Severity
HIGH. The nonce protection is inconsistent. The whole point of replay protection is that it applies everywhere. A middleware that rejects replays but a direct endpoint that accepts them is a bypass, not a defense.
The Problem
Nonce replay protection was added to the MCP and A2A middleware (commit 4f7c307). The `POST /verify` endpoint at `cmd/server/main.go:95` calls `verify.Chain` directly and has no nonce check. Bundles can be replayed through `/verify` indefinitely.
This matters because `/verify` is not an internal endpoint. It is the primary integration point for the SDK and for any caller that does not go through the middleware. If a client captures a valid signed bundle, they can submit it to `/verify` as many times as they want, getting a valid `VerificationResult` back each time. Any downstream system that gates on a `{"valid":true}` response is bypassed.
Where It Breaks
```go
// cmd/server/main.go:119
result := verify.Chain(req.ChainBundle, reqDeps)
```
No nonce check before this line.
What Must Change
Wire the same `nonceStore` into the `/verify` handler. The check is already extracted into `checkNonceReplay()` in `pkg/middleware/decode.go` — one call, one return-if-true.
```go
if checkNonceReplay(w, req.Invocation, nonceStore) {
return
}
result := verify.Chain(req.ChainBundle, reqDeps)
```
The nonce store is already constructed in `main()` and available in scope.
Severity
HIGH. The nonce protection is inconsistent. The whole point of replay protection is that it applies everywhere. A middleware that rejects replays but a direct endpoint that accepts them is a bypass, not a defense.