mirror of
https://github.com/MeshCore-Beacon/beacon-server.git
synced 2026-10-16 22:30:43 +00:00
An observer sending NUL-padded status strings had every status update rejected by Postgres, freezing its status and telemetry. Strip them from the text fields and the stored JSON, and warn with the fields affected. Also drop the timestamp-clamp, empty-timestamp and malformed-IATA logs to debug; they're expected and outnumbered inserted packets several times over.
65 lines
2.3 KiB
Go
65 lines
2.3 KiB
Go
// Copyright 2026 Beacon Contributors
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
package ingest
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"log/slog"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
type malformedTopicMessage struct{ topic string }
|
|
|
|
func (m malformedTopicMessage) Duplicate() bool { return false }
|
|
func (m malformedTopicMessage) Qos() byte { return 0 }
|
|
func (m malformedTopicMessage) Retained() bool { return false }
|
|
func (m malformedTopicMessage) Topic() string { return m.topic }
|
|
func (m malformedTopicMessage) MessageID() uint16 { return 0 }
|
|
func (m malformedTopicMessage) Payload() []byte { return nil }
|
|
func (m malformedTopicMessage) Ack() {}
|
|
|
|
func TestMalformedIATALogNamesSource(t *testing.T) {
|
|
for _, format := range []string{"json", "text"} {
|
|
for _, iata := range []string{"XX", "XX\r\nforged=true"} {
|
|
t.Run(format+"/"+strconv.Quote(iata), func(t *testing.T) {
|
|
var output bytes.Buffer
|
|
opts := &slog.HandlerOptions{Level: slog.LevelDebug}
|
|
var handler slog.Handler = slog.NewJSONHandler(&output, opts)
|
|
if format == "text" {
|
|
handler = slog.NewTextHandler(&output, opts)
|
|
}
|
|
previous := slog.Default()
|
|
slog.SetDefault(slog.New(handler))
|
|
t.Cleanup(func() { slog.SetDefault(previous) })
|
|
worker, _ := newTestWorker()
|
|
topic := "meshcore/" + iata + "/0011223344556677/packets"
|
|
worker.handleMessage(malformedTopicMessage{topic: topic})
|
|
if bytes.Count(output.Bytes(), []byte("\n")) != 1 || bytes.ContainsRune(output.Bytes(), '\r') {
|
|
t.Fatalf("log did not remain one escaped record: %q", output.String())
|
|
}
|
|
if format == "json" {
|
|
var record map[string]any
|
|
if err := json.Unmarshal(output.Bytes(), &record); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if record["iata"] != iata || record["topic"] != topic || record["msg"] != "dropped packet with malformed IATA" {
|
|
t.Fatalf("log lost its source: %v", record)
|
|
}
|
|
} else {
|
|
expectedIATA, expectedTopic := iata, topic
|
|
if strings.ContainsAny(iata, "\r\n") {
|
|
expectedIATA, expectedTopic = strconv.Quote(iata), strconv.Quote(topic)
|
|
}
|
|
if !strings.Contains(output.String(), "iata="+expectedIATA) || !strings.Contains(output.String(), "topic="+expectedTopic) {
|
|
t.Fatalf("log lost its source: %q", output.String())
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|