Commit Graph
4 Commits
Author SHA1 Message Date
Tobias GesellchenandClaude Sonnet 4.6 3d8e08d11a sec5d: sanitize log-injection in soundtouchweb, stockholm, zeroconf
Fixes CodeQL go/log-injection alerts in three packages.

Adds logutil.go with a package-private sanitizeLog helper to each.

pkg/service/soundtouchweb/discovery.go (2 call sites):
- host, source (device fetch failure)
- source, info.Name, info.Type, host (device added)

pkg/service/soundtouchweb/websocket.go (9 call sites):
- deviceID across connect/disconnect/upgrade/read/ping/status messages

pkg/service/stockholm/bridge.go (2 call sites):
- method, clientID (dispatch trace)
- clientID, msg (log bridge method)

pkg/service/stockholm/discovery.go (2 call sites):
- host (fetch failure)
- host, info.MargeAccountUUID, expectedAccountID (skipping device)

pkg/service/stockholm/static.go (1 call site):
- r.URL.Path (path-traversal rejection)

pkg/service/zeroconf/zeroconf.go (2 call sites):
- username (logAddUserNoOp)
- username, server, ct, cl, bodySummary (logAddUserFailure)

No behaviour change. golangci-lint and make check pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 16:52:53 +02:00
Tobias Gesellchen b95bdae751 feat: Add RadioBrowser integration alongside TuneIn support
- Refactors BMX service to support multiple radio providers
- Adds RadioBrowser.com API integration with search and browse
- Splits TuneIn logic into separate module for better organization
- Adds new web UI components for radio station discovery
- Includes new SVG icons for RadioBrowser branding
2026-05-18 22:34:26 +02:00
Tobias GesellchenandClaude Opus 4.7 75118d9a92 fix(soundtouch-web): keep device WebSocket alive across disconnects
ConnectDeviceWebSocket was a one-shot: connect, wait for disconnect,
log, return. Once the device-side WebSocket died (idle timeout, blip,
speaker reboot), the goroutine ended and conn.WebSocket stayed
pointing at the (now-dead) client — which made the duplicate-spawn
guard `if device.WebSocket == nil` at the five callsites in
handler.go correctly skip spawning, but with nothing else trying to
reconnect, the speaker's status flow froze for the rest of the
process's lifetime. The browser kept receiving status_update
messages on the 5 s ticker (HandleWebSocket), but every payload
carried the same stale data the service last knew.

Symptom: load the page, NowPlaying shows fresh state; some minutes
later, the speaker switches presets or tracks but NowPlaying never
updates — even though playback itself works because those are
one-shot HTTP calls that don't depend on the WebSocket.

Fix: wrap the connect-and-wait in a for-loop with exponential
backoff (1 s → 30 s cap, reset on every successful connect). The
goroutine now lives for the device entry's lifetime; conn.WebSocket
is updated on each successful reconnect and never cleared, so the
existing guards keep working without spawning duplicate loops.

Pre-existing main bug — preserved by the relocation, surfaced when
testing the rebased branch. Fix is contained to the one function;
behaviour is byte-identical for the happy path (one connect, no
disconnect ever).

go build ./... clean. go test ./... clean (only pre-existing
TestDocsConsistency fails). golangci-lint run ./... 0 issues.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 22:34:26 +02:00
Tobias GesellchenandClaude Opus 4.7 42257aeebc refactor(soundtouch-web): relocate handlers/webtypes to pkg/service/soundtouchweb
Mechanical relocation only — zero semantic change. Sets up the package
layout that the future Preact-UI rewrite (branch `app`) wants, while
preserving every line of main's current logic. Subsequent commits will
land the additive parts (frontend rewrite, recents, zones, bass control)
on top of this clean base.

Moves (`git mv`, content unchanged except package decl):

  cmd/soundtouch-web/handlers/handlers.go      → pkg/service/soundtouchweb/handler.go
  cmd/soundtouch-web/handlers/handlers_test.go → pkg/service/soundtouchweb/handler_test.go
  cmd/soundtouch-web/handlers/websocket.go     → pkg/service/soundtouchweb/websocket.go
  cmd/soundtouch-web/handlers/registry_test.go → pkg/service/soundtouchweb/registry_test.go
  cmd/soundtouch-web/webtypes/types.go         → pkg/service/soundtouchweb/webtypes/types.go
  cmd/soundtouch-web/webtypes/types_test.go    → pkg/service/soundtouchweb/webtypes/types_test.go
  cmd/soundtouch-web/webtypes/status_test.go   → pkg/service/soundtouchweb/webtypes/status_test.go
  cmd/soundtouch-web/static/img/tunein-{dark,mono}.svg → pkg/service/soundtouchweb/static/img/

Adjustments:

- `package handlers` → `package soundtouchweb` in the 4 moved handler-tier
  files (plus their package-doc comments).
- Import paths rewritten in cmd/soundtouch-web/{main.go,spa_test.go} and
  in the moved files themselves: cmd/soundtouch-web/{handlers,webtypes}
  → pkg/service/soundtouchweb/{,webtypes}.
- `handlers.` selector renamed to `soundtouchweb.` in the callers.
- `.golangci.yml` errcheck waiver extended from `cmd/.*\.go` to also
  cover `pkg/service/soundtouchweb/.*\.go`. Same code that the
  cmd-tier waiver applied to; same waiver follows it. Documented as
  a carry-over with the intent to tighten in a follow-up review.

Not changed:

- `cmd/soundtouch-web/main.go` keeps the `//go:embed static` pointing at
  the still-vanilla `cmd/soundtouch-web/static/`. The frontend rewrite
  (Preact UI) lands in a later commit; this one is mechanical.
- `cmd/soundtouch-web/resolve_bind_addr_test.go` stays put — it tests
  main.go-local flag plumbing.

go build ./... clean. go test ./... clean (only pre-existing
TestDocsConsistency fails, untracked-file issue, unrelated).
golangci-lint run ./... 0 issues.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 22:34:26 +02:00