Skip to content
Closed

. #1

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
5 changes: 5 additions & 0 deletions .jules/sentinel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## 2024-05-20 - Exposing Internals via Error Logs

**Vulnerability:** External API errors returning raw JSON response bodies were logged and bubbled up directly to the UI, risking exposure of internals, partial keys, or implementation details.
**Learning:** Returning exception stack traces or raw upstream provider responses directly in exceptions that bubble up to UI leaks internals, violates "Fail securely" principle.
**Prevention:** Catch external API exceptions and throw a generic error message (e.g. `IOException("OpenAI API error ${resp.code}")`) instead of including the raw upstream JSON body.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class RingCompanionService : CompanionDeviceService() {

@Inject lateinit var ringBLEClient: RingBLEClient

@android.annotation.SuppressLint("NewApi")
override fun onDevicePresenceEvent(event: DevicePresenceEvent) {
when (event.event) {
DevicePresenceEvent.EVENT_BLE_APPEARED -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,8 @@ class OpenAIClient(

val resp = http.newCall(req).execute()
if (!resp.isSuccessful) {
val text = resp.body?.string() ?: "(no body)"
Log.e("OpenAIClient", "HTTP ${resp.code} from $responsesUrl — body: $text")
throw IOException("OpenAI API error ${resp.code}: $text")
Log.e("OpenAIClient", "HTTP ${resp.code} from $responsesUrl")
throw IOException("OpenAI API error ${resp.code}")
}

val responseBody = resp.body ?: throw IOException("Empty response body")
Expand Down Expand Up @@ -113,9 +112,8 @@ class OpenAIClient(
if (out != null && out.length() > 0) completedOutputArr = out
}
"error" -> {
val msg = event.optString("message", data)
Log.e("OpenAIClient", "SSE error event: $msg")
throw IOException("API stream error: $msg")
Log.e("OpenAIClient", "SSE error event received")
throw IOException("API stream error")
}
}
} catch (ioe: IOException) { throw ioe } catch (_: Exception) { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ private suspend fun requestToken(vararg params: Pair<String, String>): OAuthToke
val req = Request.Builder().url(TOKEN_URL).post(body).build()
val resp = http.newCall(req).execute()
val text = resp.body?.string() ?: error("Empty token response")
if (!resp.isSuccessful) error("Token request failed ${resp.code}: $text")
if (!resp.isSuccessful) error("Token request failed ${resp.code}")
val j = JSONObject(text)
OAuthTokens(
accessToken = j.getString("access_token"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -538,7 +538,7 @@ fun RecordSummaryScreen(sessionId: String, navController: NavController, vm: Rec
val gpsPoints by vm.gpsPoints.collectAsState()
var showDeleteDialog by remember { mutableStateOf(false) }

remember(sessionId) { vm.load(sessionId) }
LaunchedEffect(sessionId) { vm.load(sessionId) }

Box(
modifier = Modifier
Expand Down
Loading