fix(legacy): parse the SSH config instead of matching the exact snippet - #180
shawnplatform wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Warning
Changes suggested — 🟡 2 warnings · 🔵 1 minor point
🔍 Full review · 4 files reviewed
Verification
splitArgsruns on both the expected paths and the file'sIncludearguments, so the"""quoting produced byquoteFilePathstill compares consistently on both sides.- Host patterns and required wildcards are both lowercased before comparison, while paths keep their case, which matches ssh's case-insensitive host matching.
- A
Hostor non-allMatchline resets$scope, so anIncludein a later unrelated block is not credited to an earlier matching block. - The
is_arrayguard and theis_stringfilter onssh.domain_wildcardsrun beforeimplode, which accounts for the lower PHPStan baseline count.
The new SshConfigInspectorTest covers the parser in isolation. No test covers SshConfig::addUserSshConfig(), the Windows formattedPaths() variants, or ~ expansion when HOME differs from the passwd home.
Review details
- Commit: 78efa92
- Model: claude-opus-5-5
Review 1 of 10 for this pull request · View the full run
On login, addUserSshConfig() checked whether ~/.ssh/config already contained the CLI's Include by searching for the exact three-line snippet as a substring. Any deviation, such as extra options in the Host block or a missing trailing "Host *", failed the check, so the CLI prompted to rewrite the file on every login. Add SshConfigInspector, a small ssh_config parser that accepts the Include when it appears at the top level, in a "Match all" block, or in a Host block whose patterns cover all of the configured domain wildcards. It handles "=" syntax, quoted values, tilde expansion, mixed case, and CRLF line endings, and ignores comments, Match blocks, and Host blocks with negated patterns. Every suggested Include path must be present, so on Windows both the converted and raw path formats are still required. A "~" path counts for all of them, as each SSH client expands it to its own home directory; it is compared in a canonical form so that Windows separators and drive letters match. On Unix, "~" is expanded using the passwd home directory, as OpenSSH does, rather than HOME. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
78efa92 to
498f4b3
Compare
|
📋 PR Summary When a user logs in, the CLI checks whether Changes
|
There was a problem hiding this comment.
Note
Reviewed — No blocking findings · ⚪ 1 nitpick
🔁 Incremental (head + base moved) · 3 files reviewed
⚪ Nitpick
legacy/src/Service/SshConfig.php:445—getSshHomeDirectory()and its docblock were inserted betweengetUserSshConfigFilename()and that method's own docblock ("Returns the path to the user's global SSH config file. @return string"). The old docblock now sits as an orphan directly above the new one, andgetUserSshConfigFilename()has no docblock at all. Nothing breaks at runtime.
Verification
addUserSshConfig()now expands~with the passwd home fromposix_getpwuid(posix_geteuid()). HOME is used only on Windows, or when the posix functions are unavailable.includesPath()records a match for each deduplicated expected path in$found. It returns true only once every path is found, so a Windows config that includes just one of the twowindows_paths: bothforms is rejected.canonicalPath()convertsC:\Users\me/.upsun-cli/...and/c/Users/me/.upsun-cli/...to the same string. Because of that, a~Include on Windows now matches the pathsformattedPaths()produces.OsUtilis already imported in SshConfig.php. The Go/PHP version updates on the base branch do not touch SshConfig or SshConfigInspector.
This change adds testTildeWithDifferentHomeDirectory and testWindowsPaths to SshConfigInspectorTest, which run with the legacy phpunit suite in CI. No test covers getSshHomeDirectory() or the passwd lookup in SshConfig.php.
Review 2 of 10 for this pull request · View the full run
Summary
On login,
addUserSshConfig()checked whether~/.ssh/configalready contained the CLI'sIncludeby searching for the exact three-line snippet as a substring:Any deviation, such as extra options inside the
Hostblock or a missing trailingHost *, failed the check. The CLI then printed "Checking SSH configuration file" and prompted to rewrite the file on every login, even though the configuration was functionally correct. Answering "yes" would replace the block and drop the user's custom options.This PR replaces the substring match with a small
ssh_configparser,SshConfigInspector, which accepts theIncludewhen it appears:HostorMatchblock), or in aMatch allblock, orHostblock whose patterns contain every configured domain wildcard, or the catch-all*.It handles
Keyword=valuesyntax, quoted values,~expansion, mixed-case keywords and patterns, and CRLF line endings. It ignores comments,Matchblocks, andHostblocks with negated patterns (which could exclude hosts the CLI needs to configure).Typing the wildcards list properly also removed one baselined PHPStan error, so the baseline count is adjusted accordingly.
Test plan
SshConfigInspectorTestwith table-driven cases covering the exact snippet, blocks with extra options,=syntax, quoting, tilde expansion, top-level andMatch allincludes, CRLF, comments, missing wildcards, unrelated blocks,Matchblocks, negated patterns, and prefix-only paths~/.ssh/configcontaining the marked block with extraStrictHostKeyChecking/UserKnownHostsFile/LogLeveloptions: now validated, no promptphpstanlevel 10 clean,php-cs-fixercleanphpunit: the only failures are the two pre-existingDependenciesTestfailures that also fail onmain🤖 Generated with Claude Code