From 0ed1cb43559537dc05ec5747e9c69435c5fe654b Mon Sep 17 00:00:00 2001 From: matst80 Date: Tue, 7 Apr 2026 12:44:55 +0200 Subject: [PATCH] some refinement --- .gitignore | 4 +- .semgrepignore | 5 + Dockerfile | 10 +- Makefile | 10 ++ cmd/server/main.go | 7 +- go.mod | 37 ++--- go.sum | 103 +++++++------ internal/collector/collector.go | 40 ++--- internal/collector/collector_test.go | 5 +- internal/storage/file_store_test.go | 4 +- internal/websocket/server.go | 10 +- k8s/deployment.yaml | 23 ++- semgrep-results.json | 209 +++++++++++++++++++++++++++ semgrep-rules.yml | 27 ++++ 14 files changed, 390 insertions(+), 104 deletions(-) create mode 100644 .semgrepignore create mode 100644 semgrep-results.json create mode 100644 semgrep-rules.yml diff --git a/.gitignore b/.gitignore index 66e8e43..1f639c9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ ./server -.DS_Store \ No newline at end of file +.DS_Store +otel-agent.log +./semgrep-results.json \ No newline at end of file diff --git a/.semgrepignore b/.semgrepignore new file mode 100644 index 0000000..cd9a940 --- /dev/null +++ b/.semgrepignore @@ -0,0 +1,5 @@ +otel-ui/node_modules/ +otel-ui/dist/ +vendor/ +.gemini/ +./server diff --git a/Dockerfile b/Dockerfile index 583d5f9..7e5d6d0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,11 +30,19 @@ FROM alpine:latest RUN apk --no-cache add ca-certificates -WORKDIR /root/ +# Create non-root user +RUN addgroup -S appgroup && adduser -S appuser -G appgroup + +WORKDIR /home/appuser/ COPY --from=go-builder /go-otel . COPY --from=go-builder /app/otel-ui/dist ./dist +# Ensure the appuser owns the files +RUN chown -R appuser:appgroup /home/appuser/ + +USER appuser + EXPOSE 8080 CMD ["./go-otel"] diff --git a/Makefile b/Makefile index a2ad30b..abebe17 100644 --- a/Makefile +++ b/Makefile @@ -6,3 +6,13 @@ lint: install-lint: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest + +fix: + golangci-lint run --fix ./... + +semgrep: + @which semgrep > /dev/null || (echo "semgrep not found, please install it: brew install semgrep") + semgrep scan --config auto --config semgrep-rules.yml + +semgrep-ci: + semgrep scan --config auto --config semgrep-rules.yml --error \ No newline at end of file diff --git a/cmd/server/main.go b/cmd/server/main.go index d106b1c..81a2760 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -6,6 +6,7 @@ import ( "log" "net" "net/http" + "os" "strings" "git.tornberg.me/go-otel/internal/collector" @@ -152,7 +153,11 @@ func main() { // Configure history size (default 100, can be made configurable) historySize := 100 - store := storage.NewFileStore("otel-agent.log") + logPath := os.Getenv("OTELLOG_PATH") + if logPath == "" { + logPath = "otel-agent.log" + } + store := storage.NewFileStore(logPath) coll := collector.New(nil, historySize, store) // Temporary nil for wsServer wsServer := websocket.New(coll) // Update collector with wsServer diff --git a/go.mod b/go.mod index d6249f3..27b207a 100644 --- a/go.mod +++ b/go.mod @@ -1,30 +1,33 @@ module git.tornberg.me/go-otel -go 1.25.1 +go 1.26 require ( github.com/gorilla/websocket v1.5.3 - go.opentelemetry.io/collector/consumer v1.45.0 - go.opentelemetry.io/collector/pdata v1.45.0 - go.opentelemetry.io/proto/otlp v1.9.0 - google.golang.org/grpc v1.76.0 - google.golang.org/protobuf v1.36.10 + github.com/natefinch/lumberjack v2.0.0+incompatible + go.opentelemetry.io/collector/consumer v1.55.0 + go.opentelemetry.io/collector/pdata v1.55.0 + go.opentelemetry.io/proto/otlp v1.10.0 + google.golang.org/grpc v1.80.0 + google.golang.org/protobuf v1.36.11 ) require ( - github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect - github.com/hashicorp/go-version v1.7.0 // indirect + github.com/BurntSushi/toml v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect + github.com/hashicorp/go-version v1.9.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect - github.com/natefinch/lumberjack v2.0.0+incompatible // indirect - go.opentelemetry.io/collector/featuregate v1.45.0 // indirect - go.opentelemetry.io/otel v1.38.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect + go.opentelemetry.io/collector/featuregate v1.55.0 // indirect + go.opentelemetry.io/otel v1.43.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/net v0.46.0 // indirect - golang.org/x/sys v0.37.0 // indirect - golang.org/x/text v0.30.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 // indirect + golang.org/x/net v0.52.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/text v0.35.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect + gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index cd0708c..d098e66 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,7 @@ +github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk= +github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -14,10 +18,10 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= -github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= -github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= +github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaXPSCnA= +github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -34,51 +38,58 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/collector/consumer v1.45.0 h1:TtqXxgW+1GSCwdoohq0fzqnfqrZBKbfo++1XRj8mrEA= -go.opentelemetry.io/collector/consumer v1.45.0/go.mod h1:pJzqTWBubwLt8mVou+G4/Hs23b3m425rVmld3LqOYpY= -go.opentelemetry.io/collector/featuregate v1.45.0 h1:D06hpf1F2KzKC+qXLmVv5e8IZpgCyZVeVVC8iOQxVmw= -go.opentelemetry.io/collector/featuregate v1.45.0/go.mod h1:d0tiRzVYrytB6LkcYgz2ESFTv7OktRPQe0QEQcPt1L4= -go.opentelemetry.io/collector/pdata v1.45.0 h1:q4XaISpeX640BcwXwb2mKOVw/gb67r22HjGWl8sbWsk= -go.opentelemetry.io/collector/pdata v1.45.0/go.mod h1:5q2f001YhwMQO8QvpFhCOa4Cq/vtwX9W4HRMsXkU/nE= -go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= -go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= -go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= -go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= -go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= -go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= -go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= -go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= -go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= -go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= -go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= -go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= -go.opentelemetry.io/proto/slim/otlp v1.8.0 h1:afcLwp2XOeCbGrjufT1qWyruFt+6C9g5SOuymrSPUXQ= -go.opentelemetry.io/proto/slim/otlp v1.8.0/go.mod h1:Yaa5fjYm1SMCq0hG0x/87wV1MP9H5xDuG/1+AhvBcsI= -go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.1.0 h1:Uc+elixz922LHx5colXGi1ORbsW8DTIGM+gg+D9V7HE= -go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.1.0/go.mod h1:VyU6dTWBWv6h9w/+DYgSZAPMabWbPTFTuxp25sM8+s0= -go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.1.0 h1:i8YpvWGm/Uq1koL//bnbJ/26eV3OrKWm09+rDYo7keU= -go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.1.0/go.mod h1:pQ70xHY/ZVxNUBPn+qUWPl8nwai87eWdqL3M37lNi9A= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/collector/consumer v1.55.0 h1:7Per8P4J0nlBrFVSXb+nwZ+egiel1BRtggZngyykGsM= +go.opentelemetry.io/collector/consumer v1.55.0/go.mod h1:Qrn5fDp/HpDmUp+l2RGKsdKyOPlgGlaZPKvw/z9FfEc= +go.opentelemetry.io/collector/featuregate v1.55.0 h1:s/bE8135+8GZpVlQ9qLXQjvprE9KNOGsLhNkqm+EDEU= +go.opentelemetry.io/collector/featuregate v1.55.0/go.mod h1:PS7zY/zaCb28EqciePVwRHVhc3oKortTFXsi3I6ee4g= +go.opentelemetry.io/collector/internal/testutil v0.149.0 h1:OWfUPO3NFKSaJtz/SBZph/2ENHbr/VbzzlBadKUhm8o= +go.opentelemetry.io/collector/internal/testutil v0.149.0/go.mod h1:Jkjs6rkqs973LqgZ0Fe3zrokQRKULYXPIf4HuqStiEE= +go.opentelemetry.io/collector/pdata v1.55.0 h1:WBgye8bo8koUyV9Vmp/r2Q3lgDezdsgfKDQAaM1oT2I= +go.opentelemetry.io/collector/pdata v1.55.0/go.mod h1:6jPrbM4tuliCPACDznjFtxnnHisfKfzwrBVoeuESYuk= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= +go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= +go.opentelemetry.io/proto/slim/otlp v1.10.0 h1:iR97Vs/ZDR+y9TfuP9b1XBtdPWeC+OMslIBmhcLU7jM= +go.opentelemetry.io/proto/slim/otlp v1.10.0/go.mod h1:lV9250stpjYLPNA5viFabIgP2QlUGRT1GdTgAf8SIUk= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.3.0 h1:RUF5rO0hAlgiJt1fzQVzcVs3vZVNHIcMLgOgG4rWNcQ= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.3.0/go.mod h1:I89cynRj8y+383o7tEQVg2SVA6SRgDVIouWPUVXjx0U= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.3.0 h1:CQvJSldHRUN6Z8jsUeYv8J0lXRvygALXIzsmAeCcZE0= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.3.0/go.mod h1:xSQ+mEfJe/GjK1LXEyVOoSI1N9JV9ZI923X5kup43W4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= -gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= -google.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101 h1:vk5TfqZHNn0obhPIYeS+cxIFKFQgser/M2jnI+9c6MM= -google.golang.org/genproto/googleapis/api v0.0.0-20251103181224-f26f9409b101/go.mod h1:E17fc4PDhkr22dE3RgnH2hEubUaky6ZwW4VhANxyspg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101 h1:tRPGkdGHuewF4UisLzzHHr1spKw92qLM98nIzxbC0wY= -google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= -google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= -google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA= +google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= +google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= +gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/collector/collector.go b/internal/collector/collector.go index 24254cf..f930944 100644 --- a/internal/collector/collector.go +++ b/internal/collector/collector.go @@ -47,7 +47,7 @@ func (c *Collector) SetBroadcaster(broadcaster websocket.Broadcaster) { c.broadcaster = broadcaster } -func (c *Collector) addToHistory(dataType websocket.DataType, data interface{}) { +func (c *Collector) addToHistory(dataType websocket.DataType, data json.RawMessage) { entry := websocket.HistoryEntry{ Type: string(dataType), Timestamp: time.Now().UnixNano(), @@ -149,9 +149,8 @@ func (c *Collector) SearchHistory(ctx context.Context, query string, limit int) if query == "" { filtered = append(filtered, entry) } else { - // Basic string filter for in-memory - data, _ := json.Marshal(entry.Data) - if strings.Contains(strings.ToLower(string(data)), lowerQuery) { + // Basic string filter for in-memory, no need to re-marshal RawMessage + if strings.Contains(strings.ToLower(string(entry.Data)), lowerQuery) { filtered = append(filtered, entry) } } @@ -180,17 +179,12 @@ func (c *Collector) ConsumeTraces(ctx context.Context, td ptrace.Traces) error { return err } - // Parse to interface{} for history storage - var data interface{} - if err := json.Unmarshal(buf, &data); err != nil { - log.Printf("failed to unmarshal traces for history: %v", err) - } else { - c.addToHistory(websocket.DataTypeTraces, data) - } + // Store and broadcast raw bytes as json.RawMessage to avoid unsafe interface{} usage + c.addToHistory(websocket.DataTypeTraces, buf) entry := websocket.Entry{ Type: string(websocket.DataTypeTraces), - Data: data, + Data: buf, } c.broadcaster.Broadcast(&entry) @@ -206,17 +200,12 @@ func (c *Collector) ConsumeMetrics(ctx context.Context, md pmetric.Metrics) erro return err } - // Parse to interface{} for history storage - var data interface{} - if err := json.Unmarshal(buf, &data); err != nil { - log.Printf("failed to unmarshal metrics for history: %v", err) - } else { - c.addToHistory(websocket.DataTypeMetrics, data) - } + // Store and broadcast raw bytes as json.RawMessage to avoid unsafe interface{} usage + c.addToHistory(websocket.DataTypeMetrics, buf) entry := websocket.Entry{ Type: string(websocket.DataTypeMetrics), - Data: data, + Data: buf, } c.broadcaster.Broadcast(&entry) @@ -232,17 +221,12 @@ func (c *Collector) ConsumeLogs(ctx context.Context, ld plog.Logs) error { return err } - // Parse to interface{} for history storage - var data interface{} - if err := json.Unmarshal(buf, &data); err != nil { - log.Printf("failed to unmarshal logs for history: %v", err) - } else { - c.addToHistory(websocket.DataTypeLogs, data) - } + // Store and broadcast raw bytes as json.RawMessage to avoid unsafe interface{} usage + c.addToHistory(websocket.DataTypeLogs, buf) entry := websocket.Entry{ Type: string(websocket.DataTypeLogs), - Data: data, + Data: buf, } c.broadcaster.Broadcast(&entry) diff --git a/internal/collector/collector_test.go b/internal/collector/collector_test.go index 6ed2df3..eccdbcd 100644 --- a/internal/collector/collector_test.go +++ b/internal/collector/collector_test.go @@ -2,6 +2,7 @@ package collector import ( "context" + "encoding/json" "testing" "time" @@ -29,7 +30,7 @@ func TestCollectorWithStore(t *testing.T) { store := &mockStore{} coll := New(nil, 10, store) - coll.addToHistory(websocket.DataTypeLogs, map[string]interface{}{"msg": "test"}) + coll.addToHistory(websocket.DataTypeLogs, json.RawMessage(`{"msg": "test"}`)) // Give it a moment since it's asynchronous timeout := time.After(100 * time.Millisecond) @@ -55,7 +56,7 @@ func TestCollectorHistoryLimit(t *testing.T) { coll := New(nil, 10, nil) for i := 0; i < 10; i++ { - coll.addToHistory(websocket.DataTypeLogs, map[string]interface{}{"msg": i}) + coll.addToHistory(websocket.DataTypeLogs, json.RawMessage(`{"msg": "test"}`)) } history := coll.GetHistory(10) diff --git a/internal/storage/file_store_test.go b/internal/storage/file_store_test.go index 1cc1bf8..e39961a 100644 --- a/internal/storage/file_store_test.go +++ b/internal/storage/file_store_test.go @@ -21,12 +21,12 @@ func TestFileStore(t *testing.T) { entry1 := websocket.HistoryEntry{ Type: "logs", Timestamp: 100, - Data: map[string]interface{}{"msg": "hello world"}, + Data: json.RawMessage(`{"msg":"hello world"}`), } entry2 := websocket.HistoryEntry{ Type: "traces", Timestamp: 200, - Data: map[string]interface{}{"msg": "trace event"}, + Data: json.RawMessage(`{"msg":"trace event"}`), } if err := fs.Append(ctx, entry1); err != nil { diff --git a/internal/websocket/server.go b/internal/websocket/server.go index da6168a..9348579 100644 --- a/internal/websocket/server.go +++ b/internal/websocket/server.go @@ -27,15 +27,15 @@ type HistoryProvider interface { // HistoryEntry represents a single archived telemetry entry. type HistoryEntry struct { - Type string `json:"type"` - Timestamp int64 `json:"timestamp"` - Data interface{} `json:"data"` + Type string `json:"type"` + Timestamp int64 `json:"timestamp"` + Data json.RawMessage `json:"data"` } // Entry represents a telemetry update sent to clients. type Entry struct { - Type string `json:"type"` - Data interface{} `json:"data"` + Type string `json:"type"` + Data json.RawMessage `json:"data"` } // Broadcaster interface for sending telemetry updates to all connected clients. diff --git a/k8s/deployment.yaml b/k8s/deployment.yaml index 752de63..96e31cb 100644 --- a/k8s/deployment.yaml +++ b/k8s/deployment.yaml @@ -12,9 +12,22 @@ spec: labels: app: otel-debug-receiver spec: + securityContext: + runAsNonRoot: true + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 containers: - name: otel-debug image: matst80/otel-debug:latest + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 ports: - containerPort: 8080 name: http @@ -25,6 +38,11 @@ spec: env: - name: GRPC_GO_LOG_SEVERITY_LEVEL value: "info" + - name: OTELLOG_PATH + value: "/logs/otel-agent.log" + volumeMounts: + - name: log-dir + mountPath: /logs livenessProbe: httpGet: path: /health @@ -43,4 +61,7 @@ spec: cpu: "100m" limits: memory: "256Mi" - cpu: "500m" \ No newline at end of file + cpu: "500m" + volumes: + - name: log-dir + emptyDir: {} diff --git a/semgrep-results.json b/semgrep-results.json new file mode 100644 index 0000000..ab4c230 --- /dev/null +++ b/semgrep-results.json @@ -0,0 +1,209 @@ +{ + "version": "1.157.0", + "results": [], + "errors": [ + { + "code": 3, + "level": "warn", + "type": "Syntax error", + "message": "Syntax error at line semgrep-results.json:1:\n missing element", + "path": "semgrep-results.json" + }, + { + "code": 3, + "level": "warn", + "type": [ + "PartialParsing", + [ + { + "path": ".gitea/workflows/build.yaml", + "start": { + "line": 36, + "col": 23, + "offset": 1047 + }, + "end": { + "line": 36, + "col": 26, + "offset": 1050 + } + }, + { + "path": ".gitea/workflows/build.yaml", + "start": { + "line": 37, + "col": 26, + "offset": 1047 + }, + "end": { + "line": 37, + "col": 29, + "offset": 1050 + } + }, + { + "path": ".gitea/workflows/build.yaml", + "start": { + "line": 38, + "col": 26, + "offset": 1047 + }, + "end": { + "line": 38, + "col": 29, + "offset": 1050 + } + } + ] + ], + "message": "Syntax error at line .gitea/workflows/build.yaml:36:\n When parsing a snippet as Bash for metavariable-pattern in rule 'yaml.github-actions.security.curl-eval.curl-eval', `${{` was unexpected", + "path": ".gitea/workflows/build.yaml", + "spans": [ + { + "file": ".gitea/workflows/build.yaml", + "start": { + "line": 36, + "col": 23, + "offset": 1047 + }, + "end": { + "line": 36, + "col": 26, + "offset": 1050 + } + }, + { + "file": ".gitea/workflows/build.yaml", + "start": { + "line": 37, + "col": 26, + "offset": 1047 + }, + "end": { + "line": 37, + "col": 29, + "offset": 1050 + } + }, + { + "file": ".gitea/workflows/build.yaml", + "start": { + "line": 38, + "col": 26, + "offset": 1047 + }, + "end": { + "line": 38, + "col": 29, + "offset": 1050 + } + } + ] + } + ], + "paths": { + "scanned": [ + ".DS_Store", + ".dockerignore", + ".gitea/workflows/build.yaml", + ".gitignore", + ".golangci.yml", + ".semgrepignore", + "Dockerfile", + "Makefile", + "README.md", + "agents_example.md", + "build.sh", + "go.mod", + "go.sum", + "index.html", + "internal/collector/collector.go", + "internal/collector/collector_test.go", + "internal/collector/handlers.go", + "internal/storage/file_store.go", + "internal/storage/file_store_test.go", + "internal/storage/store.go", + "internal/websocket/server.go", + "k8s/deployment.yaml", + "k8s/ingress.yaml", + "k8s/service.yaml", + "semgrep-results.json", + "semgrep-rules.yml", + "test.sh" + ] + }, + "time": { + "rules": [], + "rules_parse_time": 6.65862512588501, + "profiling_times": { + "config_time": 7.022655010223389, + "core_time": 7.331943988800049, + "ignores_time": 0.0019888877868652344, + "total_time": 14.365644931793213 + }, + "parsing_time": { + "total_time": 0.0, + "per_file_time": { + "mean": 0.0, + "std_dev": 0.0 + }, + "very_slow_stats": { + "time_ratio": 0.0, + "count_ratio": 0.0 + }, + "very_slow_files": [] + }, + "scanning_time": { + "total_time": 0.3914341926574707, + "per_file_time": { + "mean": 0.005436586009131538, + "std_dev": 0.0003555799509652799 + }, + "very_slow_stats": { + "time_ratio": 0.0, + "count_ratio": 0.0 + }, + "very_slow_files": [] + }, + "matching_time": { + "total_time": 0.0, + "per_file_and_rule_time": { + "mean": 0.0, + "std_dev": 0.0 + }, + "very_slow_stats": { + "time_ratio": 0.0, + "count_ratio": 0.0 + }, + "very_slow_rules_on_files": [] + }, + "tainting_time": { + "total_time": 0.0, + "per_def_and_rule_time": { + "mean": 0.0, + "std_dev": 0.0 + }, + "very_slow_stats": { + "time_ratio": 0.0, + "count_ratio": 0.0 + }, + "very_slow_rules_on_defs": [] + }, + "fixpoint_timeouts": [], + "prefiltering": { + "project_level_time": 0.0, + "file_level_time": 0.0, + "rules_with_project_prefilters_ratio": 0.0, + "rules_with_file_prefilters_ratio": 0.9292490118577075, + "rules_selected_ratio": 0.1324110671936759, + "rules_matched_ratio": 0.1324110671936759 + }, + "targets": [], + "total_bytes": 0, + "max_memory_bytes": 11722574784 + }, + "engine_requested": "OSS", + "interfile_languages_used": [], + "skipped_rules": [], + "profiling_results": [] +} \ No newline at end of file diff --git a/semgrep-rules.yml b/semgrep-rules.yml new file mode 100644 index 0000000..87d4b6a --- /dev/null +++ b/semgrep-rules.yml @@ -0,0 +1,27 @@ +rules: + - id: go-otel-span-end-defer + message: "Potential missing `defer span.End()`. Spans should be closed to avoid memory leaks." + languages: [go] + severity: WARNING + patterns: + - pattern: | + $SPAN, $ERR := $TRACER.Start(...) + ... + - pattern-not: | + $SPAN, $ERR := $TRACER.Start(...) + defer $SPAN.End() + ... + - pattern-not: | + $SPAN, $ERR := $TRACER.Start(...) + ... + $SPAN.End() + + - id: no-direct-log-fatal + message: "Avoid using `log.Fatal` in internal packages. Use errors to propagate up to main." + languages: [go] + severity: ERROR + pattern: log.Fatal(...) + paths: + exclude: + - "main.go" + - "cmd/**"