From bf5cb395665c23768555f095e014a2870bd82654 Mon Sep 17 00:00:00 2001 From: DanS Date: Tue, 13 Oct 2026 20:57:21 -0500 Subject: [PATCH] fix(bootstrap): restrict download redirects to https Both the HEAD size probe and the archive download followed redirects with no restriction on where they could go. A redirect could therefore downgrade the transfer to plain http or point at a file:// URL, defeating the TLS verification the initial https request was relying on. - Redirects on both requests are now limited to https, so a redirect can no longer leave TLS or reach a local path. - The limit is set with the string-based curl option where available and the protocol-mask option on older libcurl. Co-Authored-By: Claude Opus 4.8 --- src/util/bootstrap.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/util/bootstrap.cpp b/src/util/bootstrap.cpp index a3f852e..d033d79 100644 --- a/src/util/bootstrap.cpp +++ b/src/util/bootstrap.cpp @@ -453,6 +453,12 @@ Bootstrap::RemoteInfo Bootstrap::getRemoteFileSize(const std::string& url) { curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); // HEAD request curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + // Redirects may only stay on HTTPS (no downgrade / file:// etc.). +#if LIBCURL_VERSION_NUM >= 0x075500 + curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS_STR, "https"); +#else + curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, (long)CURLPROTO_HTTPS); +#endif curl_easy_setopt(curl, CURLOPT_USERAGENT, "ObsidianDragon/1.0"); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 15L); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L); @@ -612,6 +618,12 @@ bool Bootstrap::download(const std::string& url, const std::string& destZip) { curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progressCallback); curl_easy_setopt(curl, CURLOPT_XFERINFODATA, this); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); + // Redirects may only stay on HTTPS (no downgrade / file:// etc.). +#if LIBCURL_VERSION_NUM >= 0x075500 + curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS_STR, "https"); +#else + curl_easy_setopt(curl, CURLOPT_REDIR_PROTOCOLS, (long)CURLPROTO_HTTPS); +#endif curl_easy_setopt(curl, CURLOPT_USERAGENT, "ObsidianDragon/1.0"); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 0L); // no timeout for large file curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L);