Files
tiennm99 8223f40b16 feat(server): counters, readiness, drain mode and a version stamp
expvar counters for connections, rooms, games, submissions by rejection
reason, eliminations, chat, joins and bot moves, served on a separate
debug address so they never sit on the public mux, plus one structured
word_rejected log line per refused word carrying the normalized word and
its link. GET /readyz flips to 503 while draining; SIGTERM stops new
rooms, waits up to NOITU_DRAIN_TIMEOUT for live games, then shuts down.
GET /version and the startup log carry the build's git describe.

Fuzz targets for the frame decoder, the text sanitizer and Vietnamese
normalization; the last one found that composing before lowercasing
could leave a non-NFC result, now recomposed after lowering.

CI runs on dev as well as main, gates gofmt and golangci-lint, tracks the
buf major instead of an exact pin, and dependabot watches every
ecosystem. The lint findings that had been hidden by the default
per-issue cap are fixed.
2026-09-21 01:17:52 +07:00

45 lines
1.4 KiB
Go

package wsapi
import (
"errors"
"testing"
"github.com/coder/websocket"
"google.golang.org/protobuf/proto"
)
// FuzzDecode guards the one function that turns arbitrary bytes off the wire
// into a message the rest of the server trusts. The invariant Decode's own
// comment promises is narrower than "never panics" — a non-binary frame is
// always ErrNotBinary, and anything else either fails cleanly or comes back
// as a real message — but a panic here would take the reader goroutine down
// with a client that sent nothing but noise, so the fuzzer is left free to
// find one if it can.
func FuzzDecode(f *testing.F) {
for _, m := range clientVariants() {
raw, err := proto.Marshal(m)
if err != nil {
f.Fatalf("seed marshal: %v", err)
}
f.Add(int(websocket.MessageBinary), raw)
}
f.Add(int(websocket.MessageText), []byte("not protobuf"))
f.Add(int(websocket.MessageBinary), []byte{})
f.Add(int(websocket.MessageBinary), []byte{0xff, 0xff, 0xff, 0x01})
f.Add(0, []byte{0x01, 0x02})
f.Fuzz(func(t *testing.T, typ int, raw []byte) {
msg, err := Decode(websocket.MessageType(typ), raw)
if websocket.MessageType(typ) != websocket.MessageBinary {
if !errors.Is(err, ErrNotBinary) {
t.Fatalf("Decode(typ=%d, ...) err = %v, want ErrNotBinary", typ, err)
}
return
}
if err == nil && msg == nil {
t.Fatalf("Decode(%q) returned neither an error nor a message", raw)
}
})
}