Skip to content

Commit a923f58

Browse files
authored
fix: enforce Content-Type to accept only binary responses (#174)
Add validation for CLI downloads that ensures the Content-Type header is indicating a binary stream (`application/octet-stream`), including common variants with parameters. This prevents saving unexpected HTML or other non-binary responses (e.g., from frontend dev servers on :8080) as binaries, improving reliability and providing clearer error feedback.
1 parent 3b88d15 commit a923f58

File tree

4 files changed

+29
-17
lines changed

4 files changed

+29
-17
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## Unreleased
44

5+
### Changed
6+
7+
- content-type is now enforced when downloading the CLI to accept only binary responses
8+
59
## 0.6.1 - 2025-08-11
610

711
### Added

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
version=0.6.1
1+
version=0.6.2
22
group=com.coder.toolbox
33
name=coder-toolbox

src/main/kotlin/com/coder/toolbox/cli/downloader/CoderDownloadService.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,13 @@ class CoderDownloadService(
5151

5252
return when (response.code()) {
5353
HTTP_OK -> {
54+
val contentType = response.headers()["Content-Type"]?.lowercase()
55+
if (contentType?.startsWith("application/octet-stream") != true) {
56+
throw ResponseException(
57+
"Invalid content type '$contentType' when downloading CLI from $remoteBinaryURL. Expected application/octet-stream.",
58+
response.code()
59+
)
60+
}
5461
context.logger.info("Downloading binary to temporary $cliTempDst")
5562
response.saveToDisk(cliTempDst, showTextProgress, buildVersion)?.makeExecutable()
5663
DownloadResult.Downloaded(remoteBinaryURL, cliTempDst)

src/test/kotlin/com/coder/toolbox/cli/CoderCLIManagerTest.kt

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ internal class CoderCLIManagerTest {
137137
}
138138

139139
val body = response.toByteArray()
140+
exchange.responseHeaders["Content-Type"] = "application/octet-stream"
140141
exchange.sendResponseHeaders(code, if (code == HttpURLConnection.HTTP_OK) body.size.toLong() else -1)
141142
exchange.responseBody.write(body)
142143
exchange.close()
@@ -197,11 +198,11 @@ internal class CoderCLIManagerTest {
197198
val ccm = CoderCLIManager(
198199
context.copy(
199200
settingsStore = CoderSettingsStore(
200-
pluginTestSettingsStore(
201-
DATA_DIRECTORY to tmpdir.resolve("cli-dir-fail-to-write").toString(),
202-
),
203-
Environment(),
204-
context.logger
201+
pluginTestSettingsStore(
202+
DATA_DIRECTORY to tmpdir.resolve("cli-dir-fail-to-write").toString(),
203+
),
204+
Environment(),
205+
context.logger
205206
)
206207
),
207208
url
@@ -307,11 +308,11 @@ internal class CoderCLIManagerTest {
307308
val ccm = CoderCLIManager(
308309
context.copy(
309310
settingsStore = CoderSettingsStore(
310-
pluginTestSettingsStore(
311-
DATA_DIRECTORY to tmpdir.resolve("does-not-exist").toString(),
312-
),
313-
Environment(),
314-
context.logger
311+
pluginTestSettingsStore(
312+
DATA_DIRECTORY to tmpdir.resolve("does-not-exist").toString(),
313+
),
314+
Environment(),
315+
context.logger
315316
)
316317
),
317318
URL("https://foo")
@@ -329,12 +330,12 @@ internal class CoderCLIManagerTest {
329330
val ccm = CoderCLIManager(
330331
context.copy(
331332
settingsStore = CoderSettingsStore(
332-
pluginTestSettingsStore(
333-
FALLBACK_ON_CODER_FOR_SIGNATURES to "allow",
334-
DATA_DIRECTORY to tmpdir.resolve("overwrite-cli").toString(),
335-
),
336-
Environment(),
337-
context.logger
333+
pluginTestSettingsStore(
334+
FALLBACK_ON_CODER_FOR_SIGNATURES to "allow",
335+
DATA_DIRECTORY to tmpdir.resolve("overwrite-cli").toString(),
336+
),
337+
Environment(),
338+
context.logger
338339
)
339340
),
340341
url

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy