Commit Graph
27 Commits
Author SHA1 Message Date
Tobias GesellchenandClaude Opus 4.7 eab1b7a15a fix(security): close go/path-injection alerts via os.Root containment
The previous filepath.IsLocal-up-front pattern in safeJoin/safeJoin-equivalents
turned out not to satisfy CodeQL's go/path-injection rule — the post-validation
filepath.Join still constructs the joined string from tainted input, so the
analyser conservatively assumes the os.* sink that consumes it is tainted
too. Only one of 34 alerts closed on the previous attempt.

Switch to *os.Root (Go 1.24+, available on the project's 1.26.3 toolchain).
The Go runtime guarantees that operations on a Root cannot escape the
anchored directory regardless of what's in the relative path, and CodeQL has
a built-in model that recognises *os.Root.* methods as path-traversal
sanitisers. Result: every os.* sink in the datastore, marge, recorder,
mirror parity-mismatch writer, and docs handler is now reached only via a
*os.Root, which closes the rule-level alerts cleanly.

Changes per file:

* pkg/service/datastore/datastore.go — Adds a `root *os.Root` to DataStore,
  lazily opened at first use (after MkdirAll-ing baseDir) and closed by a
  new `(*DataStore).Close()`. Adds package-private helpers
  (rootStat / rootReadFile / rootWriteFile / rootMkdirAll / rootRemove /
  rootRemoveAll / rootRename / rootReadDir / rootOpen / rootExists) plus
  three exported wrappers (ReadDirUnderBase, MkdirAllUnderBase,
  WriteFileUnderBase) for the cross-package marge / handlers callers.
  Every os.* call that previously consumed safeJoin output now goes through
  these helpers. The post-join belt-and-suspenders prefix check inside
  safeJoin is preserved as a defence-in-depth fallback.

* pkg/service/marge/marge.go — Replaces the five `os.ReadDir(devicesDir)`
  call sites with `ds.ReadDirUnderBase(...)` so the datastore's root
  enforces containment.

* pkg/service/proxy/recorder.go — Mirrors the datastore pattern with its
  own `root *os.Root` anchored at Recorder.BaseDir, lazily opened. New
  helpers convert the eight existing `os.*` sites that consume sessionID
  / relPath / sanitizedSegments inputs. The earlier safeJoin (filepath.IsLocal
  pre-check) stays in place as the same belt-and-suspenders guard.

* pkg/service/handlers/handlers_docs.go — Opens a *os.Root at "docs" via
  sync.Once and reads file content (and SUMMARY.md sidebar) through it.
  Removes the prior filepath.IsLocal pre-check; the runtime now guarantees
  containment.

* pkg/service/handlers/mirror_middleware.go — Routes the parity-mismatch
  JSON write through `s.ds.WriteFileUnderBase` so the datastore's root
  performs the path-traversal sanitiser.

Behavioural fix: *os.File.ReadDir(-1) returns directory entries in
filesystem order, but os.ReadDir is documented to sort by name and at least
one regression test
(handlers.TestMargeAccountFullExcludesEmptyAmazonSource) depends on the
sorted contract. Both rootReadDir helpers explicitly sort by name to match.

All test suites pass for the touched packages; the unrelated
TestDocsConsistency failure about untracked working-tree docs is
pre-existing. golangci-lint reports 0 issues.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 15:18:24 +02:00
Tobias GesellchenandClaude Opus 4.7 339dc80bf1 feat(proxy): add UnsafeLogCredentialHeaders escape hatch for debugging
The previous commit made credential-header redaction unconditional in
proxy log output, which is the right safety floor for production but
inconvenient for local debugging when a developer wants to inspect
Authorization / Cookie / X-Bose-Token values flowing through the
service.

Add an explicit "I-know-what-I-am-doing" toggle:

* New LoggingProxy.UnsafeLogCredentialHeaders bool field.
* Default off — the redaction floor stays in place.
* Reads the LOG_PROXY_CREDENTIALS env var so a developer can flip it
  on without recompiling, mirroring the existing LOG_PROXY_BODY
  pattern.
* When true, formatHeaders skips both the always-sensitive floor and
  the broader Redact policy, so log lines contain raw header values.

CodeQL's go/clear-text-logging rule continues to be satisfied because
the default code path still redacts; only an explicit opt-in via
configuration produces unredacted output, mirroring how
AllowInsecureUpstreamTLS works.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 14:30:55 +02:00
Tobias GesellchenandClaude Opus 4.7 be45b3485d fix(security): always redact credential headers in proxy logs
CodeQL alert #43 (go/clear-text-logging) flagged that headers flow to
log.Printf in pkg/service/proxy/proxy.go. The existing implementation
only redacted when LoggingProxy.Redact was true — an opt-in. CodeQL is
right to flag this: the safety floor for credential-bearing headers
should not depend on caller configuration.

Split the sensitive-header list into two:

* alwaysSensitiveHeaders — Authorization, Proxy-Authorization, Cookie,
  Set-Cookie, X-Api-Key, X-Bose-Token. Redacted unconditionally,
  regardless of LoggingProxy.Redact.
* sensitiveHeaders — kept as a compatibility alias pointing at the same
  list, and still gated on Redact for any future use cases that want
  *additional* opt-in redaction beyond the floor.

Behaviour change is strict tightening: nothing that was previously
hidden becomes visible, and credentials that would have been logged
when Redact was false are now hidden by default.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 14:30:55 +02:00
Tobias GesellchenandClaude Opus 4.7 648eedefde fix(security): close go/path-injection alerts via filepath.IsLocal sanitiser
CodeQL flagged 34 go/path-injection alerts across datastore.go, marge.go,
recorder.go, handlers_docs.go and mirror_middleware.go. The existing
defences (DataStore.safeJoin's post-join prefix check, handlers_docs's
HasPrefix(filepath.Clean(...))) are functionally correct but sit
downstream of the join, so CodeQL's interprocedural taint tracking
treats every os.* sink that consumes them as still tainted.

Move the validation up-front using filepath.IsLocal, which CodeQL
recognises as a path-traversal sanitiser. IsLocal rejects absolute
paths, ".." segments, and (on Windows) reserved device names — the
same set the existing checks intended to block, just expressed in the
shape the analyser understands.

Changes:

* DataStore.safeJoin (datastore.go) — pre-validates each non-empty
  element with filepath.IsLocal before joining. Existing post-join
  prefix check stays as belt-and-suspenders. ~30 of the 34 alerts
  flow through this helper.

* Recorder (recorder.go) — adds a new (*Recorder).safeJoin method
  with the same sanitiser. getRecordingDir, DeleteSession,
  GetInteractionContent and ArchiveSession route through it; their
  signatures already returned error so plumbing it through is local.

* HandleDocs (handlers_docs.go) — replaces the post-join HasPrefix
  check with an up-front filepath.IsLocal gate.

* Mirror parity recorder (mirror_middleware.go) — also strips
  backslash separators (Windows) and gates the resulting filename
  component on filepath.IsLocal, falling back to "invalid" rather
  than letting malformed paths reach os.WriteFile.

No behaviour change for legitimate inputs (account IDs, device IDs,
session IDs, doc paths all satisfy IsLocal). Datastore and proxy
test suites pass; handler suite's pre-existing TestDocsConsistency
failure is unrelated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 14:30:55 +02:00
Tobias GesellchenandGitHub 1fecb3948e Refactor constants for sources and source providers (#168) 2026-04-17 19:08:50 +02:00
Tobias GesellchenandJunie 37eb23fc36 Merge existing device info in SaveDeviceInfo to preserve name on power-on
Co-authored-by: Junie <junie@jetbrains.com>
2026-03-17 22:59:20 +01:00
Tobias GesellchenandGitHub eb50e9b6f6 Decode SCMUDC event details (#97)
This should help understanding events from the SoundTouch app to the
speakers and from speakers to the BMX service.
2026-03-05 23:19:39 +01:00
Tobias Gesellchen 6211e34050 Improve parity with upstream Bose services 2026-02-26 21:08:14 +01:00
Tobias Gesellchen 5edab77209 feat: add comprehensive TLS certificate SAN support with wildcard domains
- Add RFC-compliant wildcard certificates (*.api.bose.io, *.api.bosecm.com) for automatic API coverage
- Include additional Bose production domains (worldwide.bose.com, music.api.bose.com, bose-prod.apigee.net)
- Implement TLS certificate request logging and wildcard domain matching logic
- Add detailed TLS handshake debugging with connection state tracking
- Wrap TLS listener with logging to capture certificate selection and handshake failures
- Update documentation with wildcard certificate coverage and debugging features
- Normalize test data to use consistent local IP addresses

This enables automatic coverage of all current and future Bose API subdomains
while providing comprehensive TLS debugging for DNS redirection troubleshooting.
2026-02-24 21:47:20 +01:00
Tobias Gesellchen 0090746b89 refactor: update recording filename format to include date
- Update `getRecordingPath` to use a timestamp format that includes the date (`20060102-150405.000`).
- Update `parseInteractionFile` and `getFullTimestamp` to handle both the new filename format and the legacy format for backward compatibility.
- Improved parsing logic to reliably extract date, time, and HTTP method from interaction filenames.
2026-02-24 11:49:04 +01:00
Tobias Gesellchen f7b74db3ea Make the linter happy 2026-02-19 08:44:26 +01:00
Tobias Gesellchen b7013a5ec8 Apply 'Redact Sensitive Headers' to recordings 2026-02-15 23:12:19 +01:00
Tobias Gesellchen 6ca206053f Add session download feature to web UI 2026-02-15 22:20:16 +01:00
Tobias Gesellchen 9a070da1ef Fix data race in RecordMiddleware and improve recorder robustness
This commit addresses the data race detected in TestRecordMiddleware: - Updated Recorder.Record to clone Request and Response objects (including bodies) before background processing. - Ensures background workers can safely access data after the main request handler has finished. - Enabled synchronous recording in handler tests to ensure deterministic results and avoid race conditions.
2026-02-15 21:51:55 +01:00
Tobias Gesellchen d4b518da23 Fix proxy and recorder tests by ensuring synchronous recording during testing
This commit addresses the test failures in pkg/service/proxy: - Ensures synchronous recording in tests by setting RECORDER_ASYNC=false. - Adds a Close() method to the Recorder for proper cleanup. - Fixes a panic in TestRecorder_Record_Redaction caused by race conditions.
2026-02-15 21:51:55 +01:00
Tobias Gesellchen 89bafd97b6 Optimize recording performance and add Soundcork proxy toggle
This commit introduces several key improvements: Performance Optimization (asynchronous recording), Legacy Proxy Control (Soundcork proxy toggle), X-Forwarded-For Sanitization, consistent Soundcork naming across the stack, and various code quality improvements.
2026-02-15 21:51:55 +01:00
Tobias Gesellchen 8a21db3517 Capture additional redirect methods and improve recorder functionality 2026-02-15 20:20:47 +01:00
Tobias Gesellchen f20cfcb319 Enhance interaction session management and cleanup UI 2026-02-15 16:52:44 +01:00
Tobias Gesellchen a453059d6d Enhance interaction recording and analysis features 2026-02-15 16:52:44 +01:00
Tobias Gesellchen 505e6dd760 Refactor data storage to use account-based hierarchy and update Web UI 2026-02-15 15:36:01 +01:00
Tobias Gesellchen dcf2e29c16 Fix linting issues and refactor for improved code quality 2026-02-14 12:39:39 +01:00
Tobias Gesellchen 9be1c7d588 Allow toggling HTTP interaction recording via CLI, environment, and Web UI 2026-02-14 12:39:39 +01:00
Tobias Gesellchen 133c07fefa Improve visibility of multiple devices in recordings by adding original value comments to .http files 2026-02-14 12:39:39 +01:00
Tobias Gesellchen ef90b4e848 Improve structure and re-usability of HTTP interaction recordings 2026-02-14 12:39:39 +01:00
Tobias Gesellchen 6504c301f6 Fix golangci-lint issues: error checking, JSON encoding, variable shadowing, and code structure
- Fixed critical error checking (errcheck) for file operations, HTTP responses, JSON operations
- Added proper JSON encoding error handling (errchkjson) in HTTP handlers
- Fixed built-in redefinition by renaming max variable to maxETag
- Optimized range loops to avoid copying large structs (gocritic)
- Resolved variable shadowing issues in multiple functions (govet)
- Improved code structure with nesting reduction (gocritic)
- Enhanced test robustness with proper error handling

Remaining issues are primarily style/documentation related (revive comments).
2026-02-07 22:36:50 +01:00
Tobias Gesellchen 210fd587de chore: run golangci-lint --fix and manually address remaining linting issues. Fixed bodyclose, errcheck, and contextcheck across the codebase. 2026-02-07 22:36:50 +01:00
Tobias Gesellchen 79ca666785 Merge Bose-SoundTouch-API (soundcork-go) into Bose-SoundTouch. Integrated service logic, created soundtouch-service command, embedded resources, updated docs, examples and CI/CD.
Commit history from `7204e619decc48df5dee91d18470934b50e389ac` to `f9b5ad3129831086b02bdf20a197ff4e2d098e2d`: https://github.com/gesellix/Bose-SoundTouch-API/compare/7204e619decc48df5dee91d18470934b50e389ac...f9b5ad3129831086b02bdf20a197ff4e2d098e2d

* f9b5ad3 - Tobias Gesellchen, 2026-02-07 : Rename module to gesellix/bose-soundtouch-api and update related files
* 5b3dbbb - Tobias Gesellchen, 2026-02-07 : docs: translate PLAN.md to English and fix preferredLanguage typo in marge.go
* 696b9c9 - Tobias Gesellchen, 2026-02-07 : feat(discovery): fetch serial number from speaker info if missing in discovery and update datastore tests
* 8ed78f0 - Tobias Gesellchen, 2026-02-07 : Consolidate proxy and main service on port 8000 and update related tests and UI
* 0e3abbb - Tobias Gesellchen, 2026-02-07 : feat(go): lowercase guessed hostnames for URL consistency
* ca1091f - Tobias Gesellchen, 2026-02-07 : feat(health): add health endpoint with VCS build information
* a432d53 - Tobias Gesellchen, 2026-02-07 : Rename mock token to soundcork-local-token and add documentation
* 3b5ee2f - Tobias Gesellchen, 2026-02-07 : Implement Phase 10: Stats API, Device Event Log, and advanced Marge functions
* bc96033 - Tobias Gesellchen, 2026-02-06 : chore
* c77864b - Tobias Gesellchen, 2026-02-06 : Document Golang header normalization behavior and ensure generic header casing preservation in proxy
* a54e7e7 - Tobias Gesellchen, 2026-02-06 : Ensure ETag header preserves casing (uppercase 'T') for case-sensitive devices
* 6265fbe - Tobias Gesellchen, 2026-02-06 : update dockerfile to be in sync with go.mod
* 5290bad - Tobias Gesellchen, 2026-02-06 : Implement proxy logging settings UI and complete Phase 8 quick wins (ETags, DataStore initialization)
* d7aa7f7 - Tobias Gesellchen, 2026-02-06 : Update PLAN.md with recent features and Phase 8 Upstream Parity tasks
* c8ae5e2 - Tobias Gesellchen, 2026-02-06 : Enhance Bose SoundTouch migration with proxying, remote services persistence, and improved diagnostics
* c53fa00 - Tobias Gesellchen, 2026-02-06 : Implement remote services persistence check and UI improvements for Bose SoundTouch migration
* ea5c348 - Tobias Gesellchen, 2026-02-02 : Ignore soundcork-go/data directory and include recent datastore fixes
* d162892 - Tobias Gesellchen, 2026-02-02 : Complete Phase 7: Automated Setup & UI refactoring. Implemented programmatic SSH/migration logic, added device discovery endpoints, created Web UI for speaker management, and refactored UI to use external HTML with Go embed.
* b528016 - Tobias Gesellchen, 2026-02-01 : Add GitHub workflow to publish Docker image to GHCR and update Dockerfile
* 2ee03da - Tobias Gesellchen, 2026-02-01 : Add GitHub Actions workflow for Go CI and update PLAN.md
* 439e2a9 - Tobias Gesellchen, 2026-02-01 : Refactor Go implementation: extract handlers and tests into dedicated files, add comprehensive unit and HTTP tests
* c0698fb - Tobias Gesellchen, 2026-02-01 : Add Docker telnet example and update IP consistency in documentation
* 028a02e - Tobias Gesellchen, 2026-02-01 : Fix older port number in README
* 264829d - Tobias Gesellchen, 2026-02-01 : Add setup-speaker.sh and update documentation to match issue #59
* 64306f9 - Tobias Gesellchen, 2026-02-01 : Implement device presets endpoint in Go
* b98a602 - Tobias Gesellchen, 2026-02-01 : Implement Phase 4: Datastore and Marge logic in Go
* b6e1bc9 - Tobias Gesellchen, 2026-02-01 : Implement Phase 3: BMX Streaming and Service Registry in Go
* f1b3dcf - Tobias Gesellchen, 2026-02-01 : Port core models and constants to Go
* 9eae655 - Tobias Gesellchen, 2026-02-01 : Implement static file serving for /media in Go
* cc73e50 - Tobias Gesellchen, 2026-02-01 : Fix Go service accessibility and improve Docker configuration
* e356bdd - Tobias Gesellchen, 2026-02-01 : Initialize Go migration: Phase 1 infrastructure, proxy-first routing, and root endpoint
2026-02-07 22:36:50 +01:00