Skip to content

Fix security vulnerabilities and bugs#28

Closed
chamika wants to merge 1 commit into
mainfrom
fix-security-and-bugs
Closed

Fix security vulnerabilities and bugs#28
chamika wants to merge 1 commit into
mainfrom
fix-security-and-bugs

Conversation

@chamika

@chamika chamika commented Jun 30, 2026

Copy link
Copy Markdown
Owner

Addresses security findings and functional bugs found during a code scan. All 291 unit tests pass.

Security

  • Access token leaked to logcat — the stream URL (logged in DashTuneMusicService) and album-art remote URLs (logged in AlbumArtContentProvider) embed the Jellyfin access token as an api_key query param. Release builds don't strip logs (isMinifyEnabled = false), so the token was recoverable via logcat. Now logs track ids / URL paths only.
  • Exported media session granted privileged commands to any calleronConnect unconditionally added LOGIN_COMMAND and SYNC_COMMAND to every controller. Since the service is exported="true", any installed app could trigger login/sync. These two commands are now restricted to our own package; repeat/shuffle stay available to all controllers (needed by system UI).
  • Thread-unsafe ContentProvider stateAlbumArtContentProvider.uriMap was a plain map mutated from many binder/coroutine threads while clearCache iterated it (CME / lost entries). Switched to ConcurrentHashMap and added a canonical-path containment check as defense-in-depth against traversal.
  • allowBackup disabled — prevents cached library metadata / SharedPreferences from being included in device/cloud backups.

Bugs

  • QuickConnect code corrupted / could crash — the code was stored as an Int, which dropped leading zeros (user shown/typing the wrong code → auth fails) and could throw StringIndexOutOfBoundsException in formatting. Now carried as a String end-to-end with length-safe formatting.
  • Uncaught QuickConnect failures — init/polling is now wrapped in try/catch and the auth response is null-safe, instead of !! crashing the coroutine on a transient error or missing field.
  • Possible crash in onSetMediaItems — the non-audiobook single-item path could pass index -1 to MediaItemsWithStartPosition. Added coerceAtLeast(0) (matching the audiobook branch).
  • Authenticator.getAuthToken returned the wrong token — it read the (intentionally empty) account password instead of the token. Now returns the real token via peekAuthToken(TOKEN_TYPE).
  • ContentProvider latch waiters — threads waiting on an in-progress download now check file.exists() after the await instead of opening a possibly-missing file.

Notes

  • Cleartext traffic (usesCleartextTraffic="true") was intentionally left enabled. Self-hosted Jellyfin servers are commonly reached over plain HTTP on a LAN; disabling cleartext would break that core use case. The risk (credentials/token interception on http:// servers) is a deliberate product tradeoff worth a follow-up (e.g. a network-security-config scoped to trusted hosts, or an HTTPS-preferred warning at sign-in).

Testing

  • ./gradlew :automotive:testDebugUnitTest — 291 passed.
  • AuthenticatorTest updated to the corrected token-retrieval behavior.

🤖 Generated with Claude Code

Security:
- Stop logging stream/album-art URLs that carry the Jellyfin access token
  (api_key); log track ids / paths instead. Release builds don't strip logs.
- Restrict privileged session commands (LOGIN, SYNC) to our own package in
  onConnect; the media session is exported, so any app could previously trigger
  them. Repeat/shuffle remain available to all controllers.
- Harden AlbumArtContentProvider: ConcurrentHashMap for the static uri map
  (was a plain map mutated from many threads) and a canonical-path containment
  check as defense-in-depth against traversal.
- Disable allowBackup so cached library metadata / prefs aren't included in
  device backups.

Bugs:
- QuickConnect code is now carried as a String end-to-end. Parsing it to an Int
  dropped leading zeros (user saw/typed the wrong code) and could crash with
  StringIndexOutOfBoundsException on short values.
- Wrap QuickConnect init/polling in try/catch and make the auth response
  null-safe; a transient error or missing field previously crashed the coroutine.
- Clamp the single-item start index in onSetMediaItems with coerceAtLeast(0) to
  avoid an IllegalArgumentException when the selected id isn't in the resolved list.
- Authenticator.getAuthToken now returns the real token via peekAuthToken
  (TOKEN_TYPE) instead of the intentionally-empty account password.
- AlbumArtContentProvider latch waiters check file.exists() after the await
  before opening, instead of throwing a raw FileNotFoundException on failed
  downloads.

Tests:
- Update AuthenticatorTest to the corrected token-retrieval behavior.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@chamika

chamika commented Jul 2, 2026

Copy link
Copy Markdown
Owner Author

Closing as #30 addresses the same issues

@chamika chamika closed this Jul 2, 2026
@chamika chamika deleted the fix-security-and-bugs branch July 2, 2026 16:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant