Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit1dd7d2c

Browse files
committed
Prevent early variable expansion in header command
1 parent71d3405 commit1dd7d2c

File tree

6 files changed

+74
-23
lines changed

6 files changed

+74
-23
lines changed

‎src/main/kotlin/com/coder/gateway/cli/CoderCLIManager.kt‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@ package com.coder.gateway.cli
33
importcom.coder.gateway.cli.ex.MissingVersionException
44
importcom.coder.gateway.cli.ex.ResponseException
55
importcom.coder.gateway.cli.ex.SSHConfigFormatException
6-
importcom.coder.gateway.settings.CoderSettings
76
importcom.coder.gateway.services.CoderSettingsState
7+
importcom.coder.gateway.settings.CoderSettings
88
importcom.coder.gateway.util.CoderHostnameVerifier
99
importcom.coder.gateway.util.InvalidVersionException
10-
importcom.coder.gateway.util.SemVer
1110
importcom.coder.gateway.util.OS
11+
importcom.coder.gateway.util.SemVer
1212
importcom.coder.gateway.util.coderSocketFactory
1313
importcom.coder.gateway.util.escape
14+
importcom.coder.gateway.util.escapeSubcommand
1415
importcom.coder.gateway.util.getHeaders
1516
importcom.coder.gateway.util.getOS
1617
importcom.coder.gateway.util.safeHost
@@ -228,7 +229,7 @@ class CoderCLIManager(
228229
escape(localBinaryPath.toString()),
229230
"--global-config", escape(coderConfigPath.toString()),
230231
if (settings.headerCommand.isNotBlank())"--header-command"elsenull,
231-
if (settings.headerCommand.isNotBlank())escape(settings.headerCommand)elsenull,
232+
if (settings.headerCommand.isNotBlank())escapeSubcommand(settings.headerCommand)elsenull,
232233
"ssh","--stdio")
233234
val blockContent= workspaceNames.joinToString(
234235
System.lineSeparator(),
Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
packagecom.coder.gateway.util
22

33
/**
4-
* Escape a command argument to be used in the ProxyCommand of an SSH
5-
* config. Surround with double quotes if the argument contains whitespace
6-
* and escape any existing double quotes.
4+
* Escape an argument to be used in the ProxyCommand of an SSH config.
5+
*
6+
* Escaping happens by surrounding with double quotes if the argument contains
7+
* whitespace and escaping any existing double quotes regardless of whitespace.
78
*
89
* Throws if the argument is invalid.
910
*/
@@ -15,4 +16,33 @@ fun escape(s: String): String {
1516
return"\""+ s.replace("\"","\\\"")+"\""
1617
}
1718
return s.replace("\"","\\\"")
18-
}
19+
}
20+
21+
/**
22+
* Escape an argument to be executed by the Coder binary such that expansions
23+
* happen in the binary and not in SSH.
24+
*
25+
* Escaping happens by wrapping in single quotes on Linux and escaping % on
26+
* Windows.
27+
*
28+
* Throws if the argument is invalid.
29+
*/
30+
funescapeSubcommand(s:String):String {
31+
if (s.contains("\n")) {
32+
throwException("argument cannot contain newlines")
33+
}
34+
returnif (getOS()==OS.WINDOWS) {
35+
// On Windows variables are in the format %VAR%. % is interpreted by
36+
// SSH as a special sequence and can be escaped with %%. Do not use
37+
// single quotes on Windows; they appear to only be used literally.
38+
return escape(s).replace("%","%%")
39+
}else {
40+
// On *nix and similar systems variables are in the format $VAR. SSH
41+
// will expand these before executing the proxy command; we can prevent
42+
// this by using single quotes. You cannot escape single quotes inside
43+
// single quotes, so if there are existing quotes you end the current
44+
// quoted string, output an escaped quote, then start the quoted string
45+
// again.
46+
"'"+ s.replace("'","'\\''")+"'"
47+
}
48+
}

‎src/test/fixtures/outputs/header-command-windows.conf‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# --- START CODER JETBRAINS test.coder.invalid
22
Host coder-jetbrains--header--test.coder.invalid
3-
ProxyCommand /tmp/coder-gateway/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-gateway/test.coder.invalid/config --header-command "C:\Program Files\My Header Command\\"also has quotes\"\HeaderCommand.exe" ssh --stdio header
3+
ProxyCommand /tmp/coder-gateway/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-gateway/test.coder.invalid/config --header-command "\"C:\Program Files\My Header Command\HeaderCommand.exe\" --url \"%%CODER_URL%%\" --test=\"foo bar\"" ssh --stdio header
44
ConnectTimeout 0
55
StrictHostKeyChecking no
66
UserKnownHostsFile /dev/null

‎src/test/fixtures/outputs/header-command.conf‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# --- START CODER JETBRAINS test.coder.invalid
22
Host coder-jetbrains--header--test.coder.invalid
3-
ProxyCommand /tmp/coder-gateway/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-gateway/test.coder.invalid/config --header-command"my-header-command\"test\"" ssh --stdio header
3+
ProxyCommand /tmp/coder-gateway/test.coder.invalid/coder-linux-amd64 --global-config /tmp/coder-gateway/test.coder.invalid/config --header-command'my-header-command--url="$CODER_URL" --test="foo bar" --literal='\''$CODER_URL'\''' ssh --stdio header
44
ConnectTimeout 0
55
StrictHostKeyChecking no
66
UserKnownHostsFile /dev/null

‎src/test/kotlin/com/coder/gateway/cli/CoderCLIManagerTest.kt‎

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,6 @@ package com.coder.gateway.cli
33
importcom.coder.gateway.cli.ex.MissingVersionException
44
importcom.coder.gateway.cli.ex.ResponseException
55
importcom.coder.gateway.cli.ex.SSHConfigFormatException
6-
importkotlin.test.Test
7-
importkotlin.test.assertContains
8-
importkotlin.test.assertEquals
9-
importkotlin.test.assertFailsWith
10-
importkotlin.test.assertFalse
11-
importkotlin.test.assertNotEquals
12-
importkotlin.test.assertTrue
13-
importorg.junit.jupiter.api.assertDoesNotThrow
14-
importorg.junit.jupiter.api.BeforeAll
15-
166
importcom.coder.gateway.services.CoderSettingsState
177
importcom.coder.gateway.settings.CoderSettings
188
importcom.coder.gateway.util.InvalidVersionException
@@ -24,13 +14,22 @@ import com.coder.gateway.util.sha1
2414
importcom.coder.gateway.util.toURL
2515
importcom.google.gson.JsonSyntaxException
2616
importcom.sun.net.httpserver.HttpServer
17+
importorg.junit.jupiter.api.BeforeAll
18+
importorg.junit.jupiter.api.assertDoesNotThrow
19+
importorg.zeroturnaround.exec.InvalidExitValueException
20+
importorg.zeroturnaround.exec.ProcessInitException
2721
importjava.net.HttpURLConnection
2822
importjava.net.InetSocketAddress
2923
importjava.net.URL
3024
importjava.nio.file.AccessDeniedException
3125
importjava.nio.file.Path
32-
importorg.zeroturnaround.exec.InvalidExitValueException
33-
importorg.zeroturnaround.exec.ProcessInitException
26+
importkotlin.test.Test
27+
importkotlin.test.assertContains
28+
importkotlin.test.assertEquals
29+
importkotlin.test.assertFailsWith
30+
importkotlin.test.assertFalse
31+
importkotlin.test.assertNotEquals
32+
importkotlin.test.assertTrue
3433

3534
internalclassCoderCLIManagerTest {
3635
privatefunmkbin(version:String):String {
@@ -253,8 +252,11 @@ internal class CoderCLIManagerTest {
253252
SSHTest(listOf("foo-bar"),"no-blocks","append-no-blocks","no-blocks",null),
254253
SSHTest(listOf("foo-bar"),"no-related-blocks","append-no-related-blocks","no-related-blocks",null),
255254
SSHTest(listOf("foo-bar"),"no-newline","append-no-newline","no-blocks",null),
256-
SSHTest(listOf("header"),null,"header-command","blank","my-header-command\"test\""),
257-
SSHTest(listOf("header"),null,"header-command-windows","blank","""C:\Program Files\My Header Command\"also has quotes"\HeaderCommand.exe"""),
255+
if (getOS()==OS.WINDOWS) {
256+
SSHTest(listOf("header"),null,"header-command-windows","blank",""""C:\Program Files\My Header Command\HeaderCommand.exe" --url="%CODER_URL%" --test="foo bar"""")
257+
}else {
258+
SSHTest(listOf("header"),null,"header-command","blank","my-header-command --url=\"\$CODER_URL\" --test=\"foo bar\" --literal='\$CODER_URL'")
259+
}
258260
)
259261

260262
val newlineRe="\r?\n".toRegex()

‎src/test/kotlin/com/coder/gateway/util/EscapeTest.kt‎

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,22 @@ internal class EscapeTest {
1919
assertEquals(it.value, escape(it.key))
2020
}
2121
}
22+
23+
@Test
24+
funtestEscapeSubcommand() {
25+
val tests=if (getOS()==OS.WINDOWS) {
26+
mapOf(
27+
"auth.exe --url=%CODER_URL%" to"\"auth.exe --url=%%CODER_URL%%\"",
28+
"\"my auth.exe\" --url=%CODER_URL%" to"\"\\\"my auth.exe\\\" --url=%%CODER_URL%%\"",
29+
)
30+
}else {
31+
mapOf(
32+
"auth --url=\$CODER_URL" to"'auth --url=\$CODER_URL'",
33+
"'my auth program' --url=\$CODER_URL" to"''\\''my auth program'\\'' --url=\$CODER_URL'",
34+
)
35+
}
36+
tests.forEach {
37+
assertEquals(it.value, escapeSubcommand(it.key))
38+
}
39+
}
2240
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp