Update error handling and logging in message processing
Some checks failed
Go / build (push) Has been cancelled
Go / release (push) Has been cancelled

- Modified the HandleMessage function to avoid logging actual message content and instead send generic error notices to clients for invalid or malformed messages, enhancing security by preventing exposure of binary data.
- Updated the NostrEscape function to escape all control characters to ensure valid JSON, preventing parsing errors with binary data.
- Adjusted policy checks to handle hex-encoded pubkeys correctly, ensuring compatibility with the updated encoding scheme.
- Introduced blackout period for relay connections after exceeding maximum reconnection delays, improving connection stability and management.
This commit is contained in:
2025-10-28 19:12:02 +00:00
parent a735bd3d5e
commit f5d13a6807
6 changed files with 135 additions and 35 deletions

View File

@@ -20,31 +20,50 @@ package text
// - A form feed, 0x0C, as \f
//
// UTF-8 should be used for encoding.
//
// NOTE: We also escape all other control characters (0x00-0x1F excluding those above)
// to ensure valid JSON, even though NIP-01 doesn't require it. This prevents
// JSON parsing errors when events with binary data in content are sent to relays.
func NostrEscape(dst, src []byte) []byte {
l := len(src)
for i := 0; i < l; i++ {
c := src[i]
switch {
case c == '"':
if c == '"' {
dst = append(dst, '\\', '"')
case c == '\\':
} else if c == '\\' {
// if i+1 < l && src[i+1] == 'u' || i+1 < l && src[i+1] == '/' {
if i+1 < l && src[i+1] == 'u' {
dst = append(dst, '\\')
} else {
dst = append(dst, '\\', '\\')
}
case c == '\b':
} else if c == '\b' {
dst = append(dst, '\\', 'b')
case c == '\t':
} else if c == '\t' {
dst = append(dst, '\\', 't')
case c == '\n':
} else if c == '\n' {
dst = append(dst, '\\', 'n')
case c == '\f':
} else if c == '\f' {
dst = append(dst, '\\', 'f')
case c == '\r':
} else if c == '\r' {
dst = append(dst, '\\', 'r')
default:
} else if c < 32 {
// Escape all other control characters (0x00-0x1F except those handled above) as \uXXXX
// This ensures valid JSON even when content contains binary data
dst = append(dst, '\\', 'u', '0', '0')
hexHigh := (c >> 4) & 0x0F
hexLow := c & 0x0F
if hexHigh < 10 {
dst = append(dst, byte('0'+hexHigh))
} else {
dst = append(dst, byte('a'+(hexHigh-10)))
}
if hexLow < 10 {
dst = append(dst, byte('0'+hexLow))
} else {
dst = append(dst, byte('a'+(hexLow-10)))
}
} else {
dst = append(dst, c)
}
}
@@ -91,14 +110,46 @@ func NostrUnescape(dst []byte) (b []byte) {
dst[w] = '\r'
w++
// special cases for non-nip-01 specified json escapes (must be
// preserved for ID generation).
case c == 'u':
dst[w] = '\\'
w++
dst[w] = 'u'
w++
case c == '/':
// special cases for non-nip-01 specified json escapes (must be
// preserved for ID generation).
case c == 'u':
// Check if this is a \u0000-\u001F sequence we generated
if r+4 < len(dst) && dst[r+1] == '0' && dst[r+2] == '0' {
// Extract hex digits
hexHigh := dst[r+3]
hexLow := dst[r+4]
var val byte
if hexHigh >= '0' && hexHigh <= '9' {
val = (hexHigh - '0') << 4
} else if hexHigh >= 'a' && hexHigh <= 'f' {
val = (hexHigh - 'a' + 10) << 4
} else if hexHigh >= 'A' && hexHigh <= 'F' {
val = (hexHigh - 'A' + 10) << 4
}
if hexLow >= '0' && hexLow <= '9' {
val |= hexLow - '0'
} else if hexLow >= 'a' && hexLow <= 'f' {
val |= hexLow - 'a' + 10
} else if hexLow >= 'A' && hexLow <= 'F' {
val |= hexLow - 'A' + 10
}
// Only decode if it's a control character (0x00-0x1F)
if val < 32 {
dst[w] = val
w++
r += 4 // Skip the u00XX part
continue
}
}
// Not our generated \u0000-\u001F, preserve as-is
dst[w] = '\\'
w++
dst[w] = 'u'
w++
case c == '/':
dst[w] = '\\'
w++
dst[w] = '/'