From ab5c98117000b66ed260f764126a0f1d8ef0bb1f Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Sep 2026 11:46:36 +0000 Subject: [PATCH] fix: keep signed tokens out of download logs HttpDownloader's wolfSSL path logged each request URL in full, so a redirected OPDS download printed its signed ?token= query string. The three URL log lines now print the URL up to the query string. Co-Authored-By: Claude Opus 5.5 (1M context) Claude-Session: https://claude.ai/code/session_01XgLRarbAmBVepTPKCK7mgd --- CHANGELOG.md | 4 ++++ src/network/HttpDownloader.cpp | 10 +++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b770254f96..4b2e4d4e95b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -120,6 +120,10 @@ - Changing global font or page layout settings from the pull-down panel on touch devices now updates the open book when it inherits those settings. - KOReader authentication now rejects unexpectedly large server responses to avoid crashes. +### Security + +- Download logs show a URL without its query string, so signed download tokens (such as `?token=` on a redirected OPDS book download) no longer end up in serial or saved logs. + ## [v1.6.0] - 2026-09-21 ### Added diff --git a/src/network/HttpDownloader.cpp b/src/network/HttpDownloader.cpp index 54716b6415c..e2ccc4744f1 100644 --- a/src/network/HttpDownloader.cpp +++ b/src/network/HttpDownloader.cpp @@ -12,6 +12,7 @@ #include #include +#include #include #include #include @@ -32,6 +33,9 @@ constexpr uint32_t DOWNLOAD_IDLE_TIMEOUT_MS = 30000; constexpr size_t DEFAULT_DOWNLOAD_BUFFER_SIZE = 2048; constexpr uint8_t MAX_REDIRECTS = 5; +// Length to log with %.*s: the query string can hold a signed download token. +int pathOnly(const std::string& url) { return static_cast(std::min(url.find('?'), url.size())); } + void logNetworkState(const char* phase) { LOG_DBG("HTTP", "%s: heap free=%u maxAlloc=%u wifi=%d rssi=%d", phase, ESP.getFreeHeap(), ESP.getMaxAllocHeap(), static_cast(WiFi.status()), WiFi.status() == WL_CONNECTED ? WiFi.RSSI() : 0); @@ -154,7 +158,7 @@ HttpDownloader::DownloadError runGetWolfSsl(const std::string& url, const std::s // existing KOSync transport; cross-origin hops omit Basic credentials. http.setInsecure(); if (!http.begin(currentUrl)) { - LOG_ERR("HTTP", "wolfSSL rejected URL: %s", currentUrl.c_str()); + LOG_ERR("HTTP", "wolfSSL rejected URL: %.*s", pathOnly(currentUrl), currentUrl.c_str()); return HttpDownloader::HTTP_ERROR; } // Replace SecureHttpClient's built-in User-Agent so strict servers receive @@ -172,7 +176,7 @@ HttpDownloader::DownloadError runGetWolfSsl(const std::string& url, const std::s http.addHeader("Authorization", std::string("Basic ") + encoded.c_str()); } - LOG_DBG("HTTP", "wolfSSL GET: %s", currentUrl.c_str()); + LOG_DBG("HTTP", "wolfSSL GET: %.*s", pathOnly(currentUrl), currentUrl.c_str()); const int status = http.GET( [&http, &sink, &progressNotifier](const uint8_t* data, const size_t len) { const int responseStatus = http.getStatus(); @@ -202,7 +206,7 @@ HttpDownloader::DownloadError runGetWolfSsl(const std::string& url, const std::s return HttpDownloader::HTTP_ERROR; } if (status < 0) { - LOG_ERR("HTTP", "wolfSSL request failed: %s", currentUrl.c_str()); + LOG_ERR("HTTP", "wolfSSL request failed: %.*s", pathOnly(currentUrl), currentUrl.c_str()); logNetworkState("wolfSSL request failure"); return HttpDownloader::HTTP_ERROR; }