Skip to content

fix!: validate HostnameSuffix and SSHConfigOptions'#26154

Merged
johnstcn merged 10 commits into
mainfrom
cj/plat-263
Jun 10, 2026
Merged

fix!: validate HostnameSuffix and SSHConfigOptions'#26154
johnstcn merged 10 commits into
mainfrom
cj/plat-263

Conversation

@johnstcn

@johnstcn johnstcn commented Jun 9, 2026

Copy link
Copy Markdown
Member
  • Adds server-side and client-side validation for CODER_CONFIGSSH_HOSTNAME_SUFFIX and CODER_SSH_CONFIG_OPTIONS.
    • Server-side breaking change: invalid values for either of these will cause coderd to exit with an error.
    • Client-side: coder config-ssh will exit with an error if it detects invalid config.
  • Adds tests for the above

Local smoke-testing: ran develop.sh --env-file <path to an env file containing badness>. Validated that server startup failed as expected.

🤖 Generated by Coder Agents with supervision from a human.

@linear-code

linear-code Bot commented Jun 9, 2026

Copy link
Copy Markdown

PLAT-263

@johnstcn johnstcn changed the title fix: validate HostNameSuffix and SSHConfigOptions' fix!: validate HostNameSuffix and SSHConfigOptions' Jun 9, 2026
@johnstcn

johnstcn commented Jun 9, 2026

Copy link
Copy Markdown
Member Author

/coder-agents-review

@coder-agents-review

coder-agents-review Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Chat: Review in progress | View chat
Requested: 2026-06-09 12:21 UTC by @johnstcn
Spend: $50.73 / $100.00

deep-review v0.7.1 | Round 3 | f242ef0..a471b89

Last posted: Round 3, 20 findings (2 P2, 8 P3, 8 Nit, 2 Note), APPROVE. Review

Finding inventory

Finding inventory

Findings

# Sev Status Location Summary Round Reviewer Posted
CRF-1 P2 Author contested; panel closed R2 (4/6 close) codersdk/deployment.go:777 ProxyJump missing from SSH option blocklist R1 Hisoka P2, Mafuuu P2, Pariston P2, Kurapika P2, Meruem P3 Yes
CRF-2 P2 Author fixed (b2b4cfd) codersdk/deployment.go:777 PKCS11Provider and SecurityKeyProvider missing from blocklist R1 Meruem P1, Ryosuke P2, Kurapika P3 Yes
CRF-3 P3 Author fixed (b2b4cfd) codersdk/deployment.go:740 Suffix/prefix validation errors omit the offending value R1 Leorio P2, Chopper P3 Yes
CRF-4 P3 Author fixed (b2b4cfd) codersdk/deployment.go:713 ParseSSHConfigOption reuses same error message for two distinct failures R1 Leorio P3, Gon Nit Yes
CRF-5 P3 Author fixed (b2b4cfd) cli/configssh_internal_test.go:363 Internal rejection tests assert require.Error without checking which error R1 Chopper P3 Yes
CRF-6 P3 Author fixed (b2b4cfd) cli/configssh_internal_test.go:321 HostnameSuffixWithUserOverride exercises identical path as HostnameSuffix R1 Bisky P3, Mafu-san Note, Robin Nit, Knov Note Yes
CRF-7 P3 Author fixed (b2b4cfd) codersdk/deployment.go:778 Blocklist error says "not allowed" with no explanation R1 Leorio P3 Yes
CRF-8 Nit Author fixed (b2b4cfd) codersdk/deployment.go:726 Doc comments on validation functions restate code R1 Gon P2 Yes
CRF-9 Nit Author fixed (b2b4cfd) cli/configssh_test.go:171 Two near-identical integration tests could be one table-driven test R1 Robin Nit Yes
CRF-10 Nit Author fixed (b2b4cfd) codersdk/deployment.go:730 strings.IndexFunc less idiomatic than strings.ContainsFunc R1 Netero Nit Yes
CRF-11 Nit Dropped by orchestrator (PR title not a code finding) PR title PR title uses HostNameSuffix (capital N) vs codebase HostnameSuffix R1 Leorio Nit No
CRF-12 Note Author fixed (b2b4cfd) codersdk/deployment.go:729 unicode.IsSpace coverage for non-ASCII whitespace has no test R1 Bisky Note Yes
CRF-13 Note Author accepted R2 (deny-list is only viable approach; cannot enumerate all safe SSH options) codersdk/deployment.go:776 Deny-list design fails open against future OpenSSH directives R1 Mafuuu Note, Meruem Note Yes
CRF-14 Nit Author fixed (b2b4cfd) codersdk/deployment.go:733 DNS label rationale comment not migrated from deleted server.go code R1 Pariston Nit Yes
CRF-15 Note Dropped by orchestrator (Host pattern prevents match, risk near-zero) codersdk/deployment.go:729 Shell metacharacters in hostname tokens technically reach ProxyCommand R1 Knov P3 No
CRF-16 P3 Author fixed (a471b89) codersdk/deployment_test.go:152 TestParseSSHConfigOption omits missing-separator error path R2 Bisky P3 Yes
CRF-17 Nit Author fixed (a471b89) codersdk/deployment.go:762 ValidateSSHConfigOptions map iteration produces non-deterministic error R2 Hisoka P3, Chopper Note Yes
CRF-18 P3 Author fixed (a471b89) codersdk/deployment.go:741 Leading-dot error uses %s (renders control chars literally) and "you must" voice R2 Leorio P3, Gon Nit Yes
CRF-19 P3 Author fixed (a471b89) codersdk/deployment.go:792 Blocklist error says "alter SSH connection routing" but ProxyJump (which routes) is allowed R2 Hisoka P2, Kurapika P2, Chopper P3 Yes
CRF-20 Nit Author fixed (a471b89) codersdk/deployment.go:775 Empty-key error says "is invalid" without naming the condition R2 Hisoka Nit, Leorio Nit Yes
CRF-21 Nit Author fixed (a471b89) codersdk/deployment.go:744 Error text says "control characters" but check only covers whitespace/newline/NUL R2 Mafuuu Nit Yes
CRF-22 Nit Author fixed (a471b89) codersdk/deployment_test.go:234 TestValidateWorkspaceHostnameSuffix missing empty-string case (asymmetry with prefix test) R2 Bisky Nit Yes

Contested and acknowledged

CRF-1 (P2, codersdk/deployment.go:777) - ProxyJump missing from SSH option blocklist

  • Finding: ProxyJump achieves the same connection-redirection that blocking ProxyCommand prevents. Deployment options appear before Coder's ProxyCommand in the generated SSH config.
  • Author defense: "ProxyJump may have legitimate use-cases."
  • Panel closure (R2, 4/6): Mafuuu: ProxyJump fits none of the three blocklist categories (structural, command execution, library loading). Pariston: tried to build a case against, couldn't sustain; connection disruption, not code execution; deployment admin already controls the server. Meruem: threat model assumes admin controls connection path. Chopper: downgrades to P3, agrees with legitimate use but wants error message consistency. Panel conclusion: ProxyJump does not execute code or load libraries. Blocking it removes a legitimate bastion-host capability without meaningfully reducing risk.

CRF-13 (Note, codersdk/deployment.go:776) - Deny-list design fails open against future directives

  • Finding: The blocklist is a deny-list; new dangerous OpenSSH directives pass validation by default.
  • Author defense: "As we cannot enumerate the entire set of 'known safe' SSH config options, a denylist is our only recourse."
  • Author accepted R2. Do not re-evaluate.

Round log

Round 1

Panel. 2 P2, 5 P3, 4 Nit, 2 Note new. 2 dropped. Reviewed against f242ef0..19909be.

Round 2

Churn guard: PROCEED. 11 addressed, 1 acknowledged, 1 contested. Panel: CRF-1 closed (4/6). 2 P3, 5 Nit new. Reviewed against f242ef0..b2b4cfd.

Round 3

Churn guard: PROCEED. 7 addressed (a471b89), 0 contested, 0 silent. Netero clean, all fixes verified. Panel: 9/11 no findings, 2 Nits dropped (churn on fixes, contradicted by prior round praise). No new actionable findings. CI green. Reviewed against f242ef0..a471b89.

About deep-review

CRF = Coder Review Finding (P0-P4, Nit, Note)

Reviewer Focus
Bisky tests
Chopper ops/errors
Churn-guard change verification
Ging language modernization
Gon naming
Hisoka edge cases
Killua perf
Kite change integrity
Knov contracts
Knuckle SQL
Kurapika security
Law decomposition
Leorio docs
Luffy product
Mafu-san process
Mafuuu contracts
Melody dispatch/pairing
Meruem structural
Nami frontend
Netero mechanical checks
Pariston premise testing
Pen-botter product gaps
Razor verification
Robin duplication
Ryosuke Go arch
Takumi concurrency
Zoro shape

🤖 Managed by Coder Agents.

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solid security-hardening PR. The test coverage is strong (4.3:1 test-to-production ratio), the TDD workflow is genuine, and the defense-in-depth at both server startup and client merge time is the right architecture. The validation consolidation into SSHConfigResponse.Validate() was a good refactoring step.

Severity breakdown: 2 P2, 5 P3, 4 Nit, 2 Note.

The two P2 findings are both blocklist gaps: ProxyJump achieves the same connection-redirection that blocking ProxyCommand was meant to prevent, and PKCS11Provider/SecurityKeyProvider cause the SSH client to load arbitrary shared libraries. Both are straightforward additions to the existing switch block.

Pariston noted: "The newline defense is thorough... the gap is in the key list, not the architecture."

The PR title uses HostNameSuffix (capital N) but the codebase uses HostnameSuffix everywhere. Worth fixing before squash merge.

The deny-list design (vs allow-list) means future dangerous OpenSSH directives will pass validation until someone updates the list. The newline/whitespace check is the more fundamental protection and would catch multi-line injection regardless. Worth noting for future consideration but not blocking.

🤖 This review was automatically generated with Coder Agents.

Comment thread codersdk/deployment.go Outdated
Comment thread codersdk/deployment.go Outdated
Comment thread codersdk/deployment.go Outdated
Comment thread codersdk/deployment.go Outdated
Comment thread cli/configssh_internal_test.go Outdated
Comment thread cli/configssh_test.go
Comment thread codersdk/deployment.go Outdated
Comment thread codersdk/deployment.go
Comment thread codersdk/deployment.go
Comment thread codersdk/deployment.go
@johnstcn johnstcn changed the title fix!: validate HostNameSuffix and SSHConfigOptions' fix!: validate HostnameSuffix and SSHConfigOptions' Jun 9, 2026
@johnstcn

johnstcn commented Jun 9, 2026

Copy link
Copy Markdown
Member Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 2. All 11 R1 fixes verified correct. CRF-2 (PKCS11Provider/SecurityKeyProvider) blocklist addition, CRF-3/CRF-4/CRF-7 error message improvements, CRF-5/CRF-6/CRF-9 test improvements, CRF-8/CRF-10 style fixes, CRF-12 non-ASCII whitespace test, CRF-14 DNS label comment migration are all complete.

CRF-1 (ProxyJump): The panel evaluated the author's defense and closed the finding (4/6). ProxyJump does not execute code or load shared libraries. It routes connections through a jump host the deployment admin already controls. Bastion hosts are a legitimate enterprise pattern. The panel concluded that blocking ProxyJump would remove a real deployment capability without meaningfully reducing risk. However, the CRF-7 error message fix introduced a text inconsistency (see CRF-19 below).

Severity breakdown for R2: 2 P3, 5 Nit.

Mafu-san observed: "Multiple R1 findings were fixed beyond the single instance cited. CRF-10 updated both call sites, CRF-12 added NonBreakingSpace to both prefix and suffix tests, CRF-3 applied %q formatting across all validation errors. This is the correct generalization from corrections."

The blocklist classification comment at lines 780-784 is strong engineering documentation. Leorio: "This is exactly what a comment should do: help the person who extends this code a year from now make the right call without reading the PR history."

🤖 This review was automatically generated with Coder Agents.

Comment thread codersdk/deployment.go Outdated
Comment thread codersdk/deployment_test.go
Comment thread codersdk/deployment.go Outdated
Comment thread codersdk/deployment.go
Comment thread codersdk/deployment.go Outdated
Comment thread codersdk/deployment.go
Comment thread codersdk/deployment_test.go
@johnstcn

johnstcn commented Jun 9, 2026

Copy link
Copy Markdown
Member Author

/coder-agents-review

@coder-agents-review coder-agents-review Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Round 3. All R2 findings verified fixed. Panel (11 reviewers) found no new actionable issues. CI green.

All 22 findings across 3 rounds are resolved: 18 fixed by author, 1 closed by panel (CRF-1 ProxyJump, 4/6), 1 acknowledged (CRF-13 deny-list design), 2 dropped by orchestrator.

The validation architecture is sound: defense-in-depth at server startup and client merge, centralized SSHConfigResponse.Validate(), layered checks (newline/whitespace prevention + directive blocklist + unicode.IsControl widening). Error messages are consistently formatted, descriptive, and actionable. Test coverage spans injection vectors, blocklist enforcement, case sensitivity, non-ASCII whitespace, and user override behavior at unit, internal, and integration levels (468 test lines for 130 production lines).

Mafu-san: "The commit progression (RED: 398 lines of tests, GREEN: 94 lines, REFACTOR) is genuine TDD. The RED commit touches zero production files. The GREEN commit touches zero test files. This is not staged retroactively."

🤖 This review was automatically generated with Coder Agents.

@johnstcn johnstcn marked this pull request as ready for review June 9, 2026 12:52
Copilot AI review requested due to automatic review settings June 9, 2026 12:52
@github-actions github-actions Bot added the release/breaking This label is applied to PRs to detect breaking changes as part of the release process label Jun 9, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens config-ssh deployment configuration by adding server-side and client-side validation for CODER_CONFIGSSH_HOSTNAME_SUFFIX and CODER_SSH_CONFIG_OPTIONS, preventing unsafe values from being served or written into users’ local SSH configs.

Changes:

  • Added validation helpers in codersdk for workspace hostname prefix/suffix and SSH config options (including disallowed directives).
  • Validated SSH deployment config during coder server startup (invalid values now fail fast).
  • Added test coverage for parsing/validation and for CLI rejection behavior (server and config-ssh).

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
codersdk/deployment.go Adds parsing hardening and validation functions; adds SSHConfigResponse.Validate() used by both server and client.
codersdk/deployment_test.go Adds unit tests for SSH option parsing and validation helpers.
cli/server.go Validates parsed SSH deployment config early in server startup and fails on invalid values.
cli/server_test.go Adds test ensuring invalid SSH deployment flags are rejected at startup.
cli/configssh.go Validates server-provided SSH config before writing any local SSH config updates.
cli/configssh_test.go Adds end-to-end test ensuring config-ssh rejects unsafe server config and does not modify the SSH config file.
cli/configssh_internal_test.go Adds unit tests asserting mergeSSHOptions rejects unsafe server-provided SSH configuration.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread codersdk/deployment.go
Comment thread codersdk/deployment.go Outdated
Comment thread codersdk/deployment.go
johnstcn and others added 2 commits June 10, 2026 12:16
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

@jdomeracki-coder jdomeracki-coder left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved, additional comments provided on Slack

@github-actions

Copy link
Copy Markdown

Docs preview

📖 View docs preview for docs/reference/cli/server.md

@coder-tasks

coder-tasks Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Documentation Check

Updates Needed

  • codersdk/deployment.go (flag description for --ssh-config-options) - The description lists blocked options but omits PKCS11Provider and SecurityKeyProvider, which are also rejected by ValidateSSHConfigOption. This propagates to the auto-generated docs/reference/cli/server.md. Add them to the description to keep docs and validation in sync.

All items addressed. The flag description now lists all blocked options (including PKCS11Provider, SecurityKeyProvider, SmartcardDevice, XAuthLocation, and ProxyJump) matching the validation blocklist.


Automated review via Coder Agents

@mafredri mafredri left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caught a few things we could harden, otherwise looks good. 👍🏻

Comment thread codersdk/deployment.go Outdated
Comment thread cli/configssh_test.go
Comment thread codersdk/deployment.go Outdated
@johnstcn johnstcn merged commit a26c46a into main Jun 10, 2026
31 checks passed
@johnstcn johnstcn deleted the cj/plat-263 branch June 10, 2026 14:48
@github-actions github-actions Bot locked and limited conversation to collaborators Jun 10, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

release/breaking This label is applied to PRs to detect breaking changes as part of the release process

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants