Two re-assessment findings:
- verifyScannedQR checked only staleness (now - ts > maxAge), so a
future-dated timestamp bought a QR a longer validity window than a
fresh one. The freshness check is now symmetric.
- Inbound Nostr DMs had no client-side created_at validation; the age
bound relied entirely on relays honoring the subscription's `since`
filter. Both gift-wrap decrypt paths now drop rumors outside
[now - lookback - skew, now + skew]. The inner rumor timestamp is the
sender's true send time (only the outer gift wrap is randomized per
NIP-17), so the window mirrors exactly what an honest relay already
guarantees — a dishonest relay can no longer inject stale or
future-dated DMs. Existing mitigations (persistent gift-wrap dedup,
relay-side since) are unchanged; this closes the malicious-relay gap.
Co-authored-by: jack <jackjackbits@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>