mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
agentssh's CommandEnv, sftpHandler, and agentproc each resolved the session working directory on their own and had drifted: sftpHandler used the configured directory without checking it exists and bypassed the injected EnvInfoer, while the others stat-checked and fell back to home. Home and shell lookups could also skip the EnvInfoer seam through the exported usershell.HomeDir and Get. Resolve through a single usershell.ResolveWorkingDirectory and confine host home and shell lookups to usershell, so SSH sessions, the process API, and tests can no longer diverge and the injected environment is always honored. This also fixes SFTP landing in a configured directory that no longer exists. Refs coder/coder#26099
32 lines
944 B
Go
32 lines
944 B
Go
package usershell
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"golang.org/x/xerrors"
|
|
)
|
|
|
|
// get resolves the user's shell via dscl, falling back to $SHELL. It
|
|
// backs SystemEnvInfo.Shell. Callers resolve the shell through an
|
|
// EnvInfoer.
|
|
func get(username string) (string, error) {
|
|
// This command will output "UserShell: /bin/zsh" if successful, we
|
|
// can ignore the error since we have fallback behavior.
|
|
if !filepath.IsLocal(username) {
|
|
return "", xerrors.Errorf("username is nonlocal path: %s", username)
|
|
}
|
|
//nolint: gosec // input checked above
|
|
out, _ := exec.Command("dscl", ".", "-read", filepath.Join("/Users", username), "UserShell").Output() //nolint:gocritic
|
|
s, ok := strings.CutPrefix(string(out), "UserShell: ")
|
|
if ok {
|
|
return strings.TrimSpace(s), nil
|
|
}
|
|
if s = os.Getenv("SHELL"); s != "" {
|
|
return s, nil
|
|
}
|
|
return "", xerrors.Errorf("shell for user %q not found via dscl or in $SHELL", username)
|
|
}
|