Skip to content

Commit d27fe7f

Browse files
committed
fix: specify --header-command when running coder start (#526)
1 parent d3f6636 commit d27fe7f

File tree

5 files changed

+39
-23
lines changed

5 files changed

+39
-23
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
@@ -12,6 +12,7 @@ import * as vscode from "vscode";
1212
import * as ws from "ws";
1313
import { errToStr } from "./api-helper";
1414
import { CertificateError } from "./error";
15+
import { getHeaderArgs } from "./headers";
1516
import { getProxyForUrl } from "./proxy";
1617
import { Storage } from "./storage";
1718
import { expandPath } from "./util";
@@ -185,6 +186,7 @@ export async function startWorkspaceIfStoppedOrFailed(
185186
const startArgs = [
186187
"--global-config",
187188
globalConfigDir,
189+
...getHeaderArgs(vscode.workspace.getConfiguration()),
188190
"start",
189191
"--yes",
190192
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;
@@ -28,6 +29,23 @@ export function getHeaderCommand(
2829
return cmd;
2930
}
3031

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

src/remote.ts

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,14 @@ import { extractAgents } from "./api-helper";
2020
import * as cli from "./cliManager";
2121
import { Commands } from "./commands";
2222
import { featureSetForVersion, FeatureSet } from "./featureSet";
23-
import { getHeaderCommand } from "./headers";
23+
import { getHeaderArgs } from "./headers";
2424
import { Inbox } from "./inbox";
2525
import { SSHConfig, SSHValues, mergeSSHConfigValues } from "./sshConfig";
2626
import { computeSSHProperties, sshSupportsSetEnv } from "./sshSupport";
2727
import { Storage } from "./storage";
2828
import {
2929
AuthorityPrefix,
30+
escapeCommandArg,
3031
expandPath,
3132
findPort,
3233
parseRemoteAuthority,
@@ -758,34 +759,21 @@ export class Remote {
758759
const sshConfig = new SSHConfig(sshConfigFile);
759760
await sshConfig.load();
760761

761-
const escape = (str: string): string => `"${str.replace(/"/g, '\\"')}"`;
762-
// Escape a command line to be executed by the Coder binary, so ssh doesn't substitute variables.
763-
const escapeSubcommand: (str: string) => string =
764-
os.platform() === "win32"
765-
? // On Windows variables are %VAR%, and we need to use double quotes.
766-
(str) => escape(str).replace(/%/g, "%%")
767-
: // On *nix we can use single quotes to escape $VARS.
768-
// Note single quotes cannot be escaped inside single quotes.
769-
(str) => `'${str.replace(/'/g, "'\\''")}'`;
770-
771-
// Add headers from the header command.
772-
let headerArg = "";
773-
const headerCommand = getHeaderCommand(vscode.workspace.getConfiguration());
774-
if (typeof headerCommand === "string" && headerCommand.trim().length > 0) {
775-
headerArg = ` --header-command ${escapeSubcommand(headerCommand)}`;
776-
}
762+
const headerArgs = getHeaderArgs(vscode.workspace.getConfiguration());
763+
const headerArgList =
764+
headerArgs.length > 0 ? ` ${headerArgs.join(" ")}` : "";
777765

778766
const hostPrefix = label
779767
? `${AuthorityPrefix}.${label}--`
780768
: `${AuthorityPrefix}--`;
781769

782770
const proxyCommand = featureSet.wildcardSSH
783-
? `${escape(binaryPath)}${headerArg} --global-config ${escape(
771+
? `${escapeCommandArg(binaryPath)}${headerArgList} --global-config ${escapeCommandArg(
784772
path.dirname(this.storage.getSessionTokenPath(label)),
785-
)} ssh --stdio --usage-app=vscode --disable-autostart --network-info-dir ${escape(this.storage.getNetworkInfoPath())}${await this.formatLogArg(logDir)} --ssh-host-prefix ${hostPrefix} %h`
786-
: `${escape(binaryPath)}${headerArg} vscodessh --network-info-dir ${escape(
773+
)} ssh --stdio --usage-app=vscode --disable-autostart --network-info-dir ${escapeCommandArg(this.storage.getNetworkInfoPath())}${await this.formatLogArg(logDir)} --ssh-host-prefix ${hostPrefix} %h`
774+
: `${escapeCommandArg(binaryPath)}${headerArgList} vscodessh --network-info-dir ${escapeCommandArg(
787775
this.storage.getNetworkInfoPath(),
788-
)}${await this.formatLogArg(logDir)} --session-token-file ${escape(this.storage.getSessionTokenPath(label))} --url-file ${escape(
776+
)}${await this.formatLogArg(logDir)} --session-token-file ${escapeCommandArg(this.storage.getSessionTokenPath(label))} --url-file ${escapeCommandArg(
789777
this.storage.getUrlPath(label),
790778
)} %h`;
791779

src/util.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,7 @@ export function countSubstring(needle: string, haystack: string): number {
143143
}
144144
return count;
145145
}
146+
147+
export function escapeCommandArg(arg: string): string {
148+
return `"${arg.replace(/"/g, '\\"')}"`;
149+
}

0 commit comments

Comments
 (0)