Skip to content

fix(scm): resolve GitHub SSH aliases#2957

Open
ppanphper wants to merge 1 commit into
AgentWrapper:mainfrom
ppanphper:fix/github-ssh-alias
Open

fix(scm): resolve GitHub SSH aliases#2957
ppanphper wants to merge 1 commit into
AgentWrapper:mainfrom
ppanphper:fix/github-ssh-alias

Conversation

@ppanphper

Copy link
Copy Markdown

Summary

  • resolve unknown SSH host aliases through ssh -G before parsing GitHub repository origins
  • accept aliases only when their resolved hostname is a supported GitHub host
  • cache SSH host resolution results to avoid repeated subprocess calls from the SCM observer
  • cover SCP-style and ssh:// remotes, rejected non-GitHub aliases, failures, and caching

Why

Projects using remotes such as git@github-work:owner/repo.git were reported as having no supported SCM origin. As a result, the observer never discovered merged pull requests and affected AO sessions remained open.

Testing

  • GIT_CONFIG_GLOBAL=/dev/null go test -race ./internal/adapters/scm/github ./internal/observe/scm ./internal/service/session ./internal/daemon
  • GIT_CONFIG_GLOBAL=/dev/null npm run lint

GIT_CONFIG_GLOBAL=/dev/null isolates the suite from local Git URL rewrite rules.

@somewherelostt

Copy link
Copy Markdown
Collaborator

Thanks for contributing to Agent Orchestrator.

This PR is being picked up by the current external contributor on-call pair:

If someone is already working on this, please continue as usual.
The on-call pair is added for visibility, tracking, and support, not to take over the work.
If you need help with review, direction, reproduction, or next steps, please tag @illegalcall and @Pulkit7070 here.

For faster context or live questions, you can also join the AO Discord.

Join the session here:
https://discord.gg/H6ZDcUXmq

Come by if you want to see what is being built, ask questions, or just hang around with the community.

@Pulkit7070 Pulkit7070 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed at head fb36f0c. Ran from a clean checkout of this branch:

  • go test ./internal/adapters/scm/github -count=1 -> ok (0.66s)
  • go vet ./internal/adapters/scm/github -> clean
  • go build ./... -> ok

The approach (inject SSHHostResolver, cache resolutions, ssh -G in prod) is sound and the tests are well structured.

Overlap with #2963 and #2965: both of those are stacked directly on this commit (fb36f0c) and are byte-for-byte identical to each other (same head SHA 2dc6bbc, git diff pr2963 pr2965 is empty). They keep this PR's design and fix the three issues I flag below. If #2963/#2965 lands, this PR is fully absorbed. Recommendation: consolidate. Prefer landing one of #2963/#2965 (they are the same commit) over this one, or fold their fixes into this branch and close the other two. Right now three PRs are competing to change the same three files.

Three correctness issues that #2963/#2965 already address:

  1. Alias case is lowercased before ssh -G, which breaks case-sensitive OpenSSH Host matching (line 664).
  2. Failed lookups are cached permanently, a sticky negative cache (line 683).
  3. ssh -G host is run without --, so an alias beginning with - is parsed as an ssh flag (argument injection); no leading-dash guard (line 691).

Details inline.

}

func (p *Provider) resolveSSHHost(host string) (string, bool) {
host = strings.ToLower(strings.TrimSpace(host))

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

strings.ToLower on the alias before resolution breaks case-sensitive lookup. OpenSSH Host patterns are case-sensitive, so an alias configured as Host GitHub-Work will not match after being lowercased to github-work, and ssh -G github-work returns the alias itself as hostname (no match), so resolution silently fails for any mixed-case alias. Your test scp ssh alias does not catch this because its injected resolver ignores the host argument. #2963/#2965 keep original casing for the resolver and lowercase only for the GitHub-host comparison and the normalized key.

if p.sshHosts == nil {
p.sshHosts = make(map[string]sshHostResolution)
}
p.sshHosts[host] = sshHostResolution{host: resolved, ok: ok}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This caches failures too (ok=false is stored), so a transient ssh -G failure (timeout, ssh briefly unavailable) is cached for the lifetime of the Provider and never retried. A GitHub-backed alias then stays unresolved until the daemon restarts. This is exactly the sticky negative cache #2965 calls out and fixes by not caching failures, letting the next observer poll recover.

func resolveSSHConfigHost(host string) (string, bool) {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
out, err := aoprocess.CommandContext(ctx, "ssh", "-G", host).Output()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

ssh -G host runs without a -- terminator and there is no guard rejecting hosts that start with -. A remote such as git@-oProxyCommand=...:owner/repo reaches here as host -oProxyCommand=... and ssh parses it as an option (argument injection from a remote URL). #2963/#2965 reject leading-dash hosts in both resolveSSHHost and resolveSSHConfigHost and pass ssh -G -- host. Worth adopting.

@AllBeingsFuture

Copy link
Copy Markdown

Closing as subset of #2965. #2965 includes this change plus SSH alias case + no sticky negative cache. Keeper is #2965.

@AllBeingsFuture

Copy link
Copy Markdown

Team review: this PR is a subset of #2965 (SSH alias case + no sticky negative cache). Please close in favor of #2965 when convenient — AllBeingsFuture cannot close this head (author is @ppanphper).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants