Browse Source

Fix corrupted events with zero-filled IDs/pubkeys/sigs (v0.47.1)

- Add validation in GetEventIdBySerial to ensure sei value is 32 bytes
- Fix fallback-to-legacy bug: return error instead of attempting legacy
  unmarshal on compact format data when event ID lookup fails
- Add upfront validation in UnmarshalCompactEvent for eventId length
- Prevents events with all-zero IDs from being returned to clients

Files modified:
- pkg/database/serial_cache.go: Validate sei value is exactly 32 bytes
- pkg/database/fetch-events-by-serials.go: Return error for compact format
  when eventId missing instead of falling back to legacy unmarshal
- pkg/database/fetch-event-by-serial.go: Same fix for single event fetch
- pkg/database/compact_event.go: Validate eventId is 32 bytes upfront
- pkg/version/version: Bump to v0.47.1

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
main
woikos 6 days ago
parent
commit
8dfd25613d
No known key found for this signature in database
  1. 5
      pkg/database/compact_event.go
  2. 15
      pkg/database/fetch-event-by-serial.go
  3. 20
      pkg/database/fetch-events-by-serials.go
  4. 6
      pkg/database/serial_cache.go
  5. 2
      pkg/version/version

5
pkg/database/compact_event.go

@ -240,6 +240,11 @@ func readUint40(r io.Reader) (value uint64, err error) {
// The resolver is used to look up pubkeys and event IDs from serials. // The resolver is used to look up pubkeys and event IDs from serials.
// The eventId parameter is the full 32-byte event ID (from SerialEventId table). // The eventId parameter is the full 32-byte event ID (from SerialEventId table).
func UnmarshalCompactEvent(data []byte, eventId []byte, resolver SerialResolver) (ev *event.E, err error) { func UnmarshalCompactEvent(data []byte, eventId []byte, resolver SerialResolver) (ev *event.E, err error) {
// Validate eventId upfront to prevent returning events with zero IDs
if len(eventId) != 32 {
return nil, errors.New("invalid eventId: must be exactly 32 bytes")
}
r := bytes.NewReader(data) r := bytes.NewReader(data)
ev = new(event.E) ev = new(event.E)

15
pkg/database/fetch-event-by-serial.go

@ -44,9 +44,12 @@ func (d *D) FetchEventBySerial(ser *types.Uint40) (ev *event.E, err error) {
// Check if this is compact format // Check if this is compact format
if len(eventData) > 0 && eventData[0] == CompactFormatVersion { if len(eventData) > 0 && eventData[0] == CompactFormatVersion {
eventId, idErr := d.GetEventIdBySerial(ser) eventId, idErr := d.GetEventIdBySerial(ser)
if idErr == nil { if idErr != nil {
return UnmarshalCompactEvent(eventData, eventId, resolver) // Cannot decode compact format without event ID - return error
// DO NOT fall back to legacy unmarshal as compact format is not valid legacy format
return nil, fmt.Errorf("compact format inline but no event ID mapping for serial %d: %w", ser.Get(), idErr)
} }
return UnmarshalCompactEvent(eventData, eventId, resolver)
} }
// Legacy binary format // Legacy binary format
@ -106,10 +109,14 @@ func (d *D) FetchEventBySerial(ser *types.Uint40) (ev *event.E, err error) {
// Check if this is compact format // Check if this is compact format
if len(v) > 0 && v[0] == CompactFormatVersion { if len(v) > 0 && v[0] == CompactFormatVersion {
eventId, idErr := d.GetEventIdBySerial(ser) eventId, idErr := d.GetEventIdBySerial(ser)
if idErr == nil { if idErr != nil {
ev, err = UnmarshalCompactEvent(v, eventId, resolver) // Cannot decode compact format without event ID - return error
// DO NOT fall back to legacy unmarshal as compact format is not valid legacy format
err = fmt.Errorf("compact format evt but no event ID mapping for serial %d: %w", ser.Get(), idErr)
return return
} }
ev, err = UnmarshalCompactEvent(v, eventId, resolver)
return
} }
// Check if we have valid data before attempting to unmarshal // Check if we have valid data before attempting to unmarshal

20
pkg/database/fetch-events-by-serials.go

@ -149,12 +149,10 @@ func (d *D) fetchSmallEvent(txn *badger.Txn, ser *types.Uint40) (ev *event.E, er
resolver := NewDatabaseSerialResolver(d, d.serialCache) resolver := NewDatabaseSerialResolver(d, d.serialCache)
eventId, idErr := d.GetEventIdBySerial(ser) eventId, idErr := d.GetEventIdBySerial(ser)
if idErr != nil { if idErr != nil {
// Fall back to legacy unmarshal // Cannot decode compact format without event ID - return error
ev = new(event.E) // DO NOT fall back to legacy unmarshal as compact format is not valid legacy format
if err = ev.UnmarshalBinary(bytes.NewBuffer(eventData)); err != nil { log.W.F("fetchSmallEvent: compact format but no event ID mapping for serial %d: %v", ser.Get(), idErr)
return nil, err return nil, idErr
}
return ev, nil
} }
return UnmarshalCompactEvent(eventData, eventId, resolver) return UnmarshalCompactEvent(eventData, eventId, resolver)
} }
@ -196,12 +194,10 @@ func (d *D) fetchLegacyEvent(txn *badger.Txn, ser *types.Uint40) (ev *event.E, e
resolver := NewDatabaseSerialResolver(d, d.serialCache) resolver := NewDatabaseSerialResolver(d, d.serialCache)
eventId, idErr := d.GetEventIdBySerial(ser) eventId, idErr := d.GetEventIdBySerial(ser)
if idErr != nil { if idErr != nil {
// Fall back to legacy unmarshal // Cannot decode compact format without event ID - return error
ev = new(event.E) // DO NOT fall back to legacy unmarshal as compact format is not valid legacy format
if err = ev.UnmarshalBinary(bytes.NewBuffer(v)); err != nil { log.W.F("fetchLegacyEvent: compact format but no event ID mapping for serial %d: %v", ser.Get(), idErr)
return nil, err return nil, idErr
}
return ev, nil
} }
return UnmarshalCompactEvent(v, eventId, resolver) return UnmarshalCompactEvent(v, eventId, resolver)
} }

6
pkg/database/serial_cache.go

@ -251,7 +251,11 @@ func (d *D) GetEventIdBySerial(ser *types.Uint40) (eventId []byte, err error) {
} }
return item.Value(func(val []byte) error { return item.Value(func(val []byte) error {
eventId = make([]byte, len(val)) // Validate that the stored value is exactly 32 bytes
if len(val) != 32 {
return errors.New("corrupted event ID: expected 32 bytes")
}
eventId = make([]byte, 32)
copy(eventId, val) copy(eventId, val)
return nil return nil
}) })

2
pkg/version/version

@ -1 +1 @@
v0.47.0 v0.47.1

Loading…
Cancel
Save