Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.26.1
require (
github.com/emersion/go-smtp v0.24.0
github.com/golang-jwt/jwt/v5 v5.3.1
github.com/klauspost/compress v1.18.0
github.com/modelcontextprotocol/go-sdk v1.4.0
github.com/prometheus/client_golang v1.23.2
github.com/prometheus/client_model v0.6.2
Expand Down
23 changes: 23 additions & 0 deletions modules/sentry/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"net/http"
"strings"

"github.com/klauspost/compress/zstd"

"github.com/buggregator/go-buggregator/internal/event"
)

Expand Down Expand Up @@ -324,9 +326,21 @@ func decompress(data []byte, encoding string) []byte {
if d, err := decompressZlib(data); err == nil {
return d
}
case "zstd":
if d, err := decompressZstd(data); err == nil {
return d
}
}

// Auto-detect by magic bytes (Sentry SDKs sometimes omit the header).
if len(data) >= 4 {
// Zstd magic: 0x28 0xb5 0x2f 0xfd
if data[0] == 0x28 && data[1] == 0xb5 && data[2] == 0x2f && data[3] == 0xfd {
if d, err := decompressZstd(data); err == nil {
return d
}
}
}
if len(data) >= 2 {
// Gzip magic: 0x1f 0x8b
if data[0] == 0x1f && data[1] == 0x8b {
Expand Down Expand Up @@ -354,6 +368,15 @@ func decompressGzip(data []byte) ([]byte, error) {
return io.ReadAll(r)
}

func decompressZstd(data []byte) ([]byte, error) {
r, err := zstd.NewReader(bytes.NewReader(data))
if err != nil {
return nil, err
}
defer r.Close()
return io.ReadAll(r)
}

func decompressZlib(data []byte) ([]byte, error) {
r, err := zlib.NewReader(bytes.NewReader(data))
if err != nil {
Expand Down
35 changes: 35 additions & 0 deletions modules/sentry/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"strconv"
"strings"
"testing"

"github.com/klauspost/compress/zstd"
)

func TestHandler_Priority(t *testing.T) {
Expand Down Expand Up @@ -152,6 +154,39 @@ func TestDecompress(t *testing.T) {
}
})

// sentry-php sends Content-Encoding: zstd by default since 4.x. Without zstd
// support the body stays compressed, fails to parse as an envelope and the
// event is dropped silently — the request still answers 200.
t.Run("zstd with header", func(t *testing.T) {
var buf bytes.Buffer
w, err := zstd.NewWriter(&buf)
if err != nil {
t.Fatalf("zstd writer: %v", err)
}
w.Write([]byte("zstd payload"))
w.Close()

got := decompress(buf.Bytes(), "zstd")
if string(got) != "zstd payload" {
t.Errorf("got %q, want %q", got, "zstd payload")
}
})

t.Run("zstd auto-detect by magic bytes", func(t *testing.T) {
var buf bytes.Buffer
w, err := zstd.NewWriter(&buf)
if err != nil {
t.Fatalf("zstd writer: %v", err)
}
w.Write([]byte("zstd auto-detected"))
w.Close()

got := decompress(buf.Bytes(), "")
if string(got) != "zstd auto-detected" {
t.Errorf("got %q, want %q", got, "zstd auto-detected")
}
})

t.Run("empty data", func(t *testing.T) {
got := decompress([]byte{}, "gzip")
if len(got) != 0 {
Expand Down
Loading