Skip to content

Commit d70a586

Browse files
authored
fix: specify --header-command when running coder start (#526)
1 parent 73f866d commit d70a586

File tree

5 files changed

+38
-24
lines changed

5 files changed

+38
-24
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+
### Fixed
6+
7+
- Use `--header-command` properly when starting a workspace.
8+
59
## [v1.9.1](https://github.com/coder/vscode-coder/releases/tag/v1.9.1) 2025-05-27
610

711
### Fixed

src/api.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import * as vscode from "vscode"
99
import * as ws from "ws"
1010
import { errToStr } from "./api-helper"
1111
import { CertificateError } from "./error"
12+
import { getHeaderArgs } from "./headers"
1213
import { getProxyForUrl } from "./proxy"
1314
import { Storage } from "./storage"
1415
import { expandPath } from "./util"
@@ -168,6 +169,7 @@ export async function startWorkspaceIfStoppedOrFailed(
168169
const startArgs = [
169170
"--global-config",
170171
globalConfigDir,
172+
...getHeaderArgs(vscode.workspace.getConfiguration()),
171173
"start",
172174
"--yes",
173175
workspace.owner_name + "/" + workspace.name,

src/headers.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import * as cp from "child_process"
2+
import * as os from "os"
23
import * as util from "util"
3-
4-
import { WorkspaceConfiguration } from "vscode"
4+
import type { WorkspaceConfiguration } from "vscode"
5+
import { escapeCommandArg } from "./util"
56

67
export interface Logger {
78
writeToCoderOutputChannel(message: string): void
@@ -25,6 +26,23 @@ export function getHeaderCommand(config: WorkspaceConfiguration): string | undef
2526
return cmd
2627
}
2728

29+
export function getHeaderArgs(config: WorkspaceConfiguration): string[] {
30+
// Escape a command line to be executed by the Coder binary, so ssh doesn't substitute variables.
31+
const escapeSubcommand: (str: string) => string =
32+
os.platform() === "win32"
33+
? // On Windows variables are %VAR%, and we need to use double quotes.
34+
(str) => escapeCommandArg(str).replace(/%/g, "%%")
35+
: // On *nix we can use single quotes to escape $VARS.
36+
// Note single quotes cannot be escaped inside single quotes.
37+
(str) => `'${str.replace(/'/g, "'\\''")}'`
38+
39+
const command = getHeaderCommand(config)
40+
if (!command) {
41+
return []
42+
}
43+
return ["--header-command", escapeSubcommand(command)]
44+
}
45+
2846
// TODO: getHeaders might make more sense to directly implement on Storage
2947
// but it is difficult to test Storage right now since we use vitest instead of
3048
// the standard extension testing framework which would give us access to vscode

src/remote.ts

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ import { extractAgents } from "./api-helper"
1414
import * as cli from "./cliManager"
1515
import { Commands } from "./commands"
1616
import { featureSetForVersion, FeatureSet } from "./featureSet"
17-
import { getHeaderCommand } from "./headers"
17+
import { getHeaderArgs } from "./headers"
1818
import { Inbox } from "./inbox"
1919
import { SSHConfig, SSHValues, mergeSSHConfigValues } from "./sshConfig"
2020
import { computeSSHProperties, sshSupportsSetEnv } from "./sshSupport"
2121
import { Storage } from "./storage"
22-
import { AuthorityPrefix, expandPath, findPort, parseRemoteAuthority } from "./util"
22+
import { AuthorityPrefix, escapeCommandArg, expandPath, findPort, parseRemoteAuthority } from "./util"
2323
import { WorkspaceMonitor } from "./workspaceMonitor"
2424

2525
export interface RemoteDetails extends vscode.Disposable {
@@ -611,32 +611,18 @@ export class Remote {
611611
const sshConfig = new SSHConfig(sshConfigFile)
612612
await sshConfig.load()
613613

614-
const escape = (str: string): string => `"${str.replace(/"/g, '\\"')}"`
615-
// Escape a command line to be executed by the Coder binary, so ssh doesn't substitute variables.
616-
const escapeSubcommand: (str: string) => string =
617-
os.platform() === "win32"
618-
? // On Windows variables are %VAR%, and we need to use double quotes.
619-
(str) => escape(str).replace(/%/g, "%%")
620-
: // On *nix we can use single quotes to escape $VARS.
621-
// Note single quotes cannot be escaped inside single quotes.
622-
(str) => `'${str.replace(/'/g, "'\\''")}'`
623-
624-
// Add headers from the header command.
625-
let headerArg = ""
626-
const headerCommand = getHeaderCommand(vscode.workspace.getConfiguration())
627-
if (typeof headerCommand === "string" && headerCommand.trim().length > 0) {
628-
headerArg = ` --header-command ${escapeSubcommand(headerCommand)}`
629-
}
614+
const headerArgs = getHeaderArgs(vscode.workspace.getConfiguration())
615+
const headerArgList = headerArgs.length > 0 ? ` ${headerArgs.join(" ")}` : ""
630616

631617
const hostPrefix = label ? `${AuthorityPrefix}.${label}--` : `${AuthorityPrefix}--`
632618

633619
const proxyCommand = featureSet.wildcardSSH
634-
? `${escape(binaryPath)}${headerArg} --global-config ${escape(
620+
? `${escapeCommandArg(binaryPath)}${headerArgList} --global-config ${escapeCommandArg(
635621
path.dirname(this.storage.getSessionTokenPath(label)),
636-
)} ssh --stdio --usage-app=vscode --disable-autostart --network-info-dir ${escape(this.storage.getNetworkInfoPath())}${await this.formatLogArg(logDir)} --ssh-host-prefix ${hostPrefix} %h`
637-
: `${escape(binaryPath)}${headerArg} vscodessh --network-info-dir ${escape(
622+
)} ssh --stdio --usage-app=vscode --disable-autostart --network-info-dir ${escapeCommandArg(this.storage.getNetworkInfoPath())}${await this.formatLogArg(logDir)} --ssh-host-prefix ${hostPrefix} %h`
623+
: `${escapeCommandArg(binaryPath)}${headerArgList} vscodessh --network-info-dir ${escapeCommandArg(
638624
this.storage.getNetworkInfoPath(),
639-
)}${await this.formatLogArg(logDir)} --session-token-file ${escape(this.storage.getSessionTokenPath(label))} --url-file ${escape(
625+
)}${await this.formatLogArg(logDir)} --session-token-file ${escapeCommandArg(this.storage.getSessionTokenPath(label))} --url-file ${escapeCommandArg(
640626
this.storage.getUrlPath(label),
641627
)} %h`
642628

src/util.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,7 @@ export function countSubstring(needle: string, haystack: string): number {
136136
}
137137
return count
138138
}
139+
140+
export function escapeCommandArg(arg: string): string {
141+
return `"${arg.replace(/"/g, '\\"')}"`
142+
}

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