Files
keyfunc/internal/cli/ssh/install_test.go
T
clawbot 11a6050ac7
check / check (push) Failing after 2s
ssh install refuses stray arguments before -- and a symlinked authorized_keys (closes #61)
Anything but the host before --, or a second argument when there is no --, used to reach sftp in front of the host; it is now refused before the mnemonic is read or any connection is made. That includes "install -- host", which names no host before --.

The first listing of ~/.ssh is now a long one, ls -n, which the client formats itself whatever the server, so an authorized_keys that is a symlink shows as one. It is refused before any upload, since the rename would have replaced the link and left the file it points at without the key. The README says so.

Model: opus-5-5
2026-10-04 15:58:13 +00:00

142 lines
4.2 KiB
Go

//nolint:testpackage // absent is what these wordings are read by
package ssh
import "testing"
// What a session says besides its report on the file that was asked
// for: sftp echoes the command it is running, and ssh warns about an
// identity file it cannot find in the words of a missing file even
// though the session goes on to authenticate.
const (
echoed = `sftp> get .ssh/authorized_keys "/tmp/keyfunc/authorized_keys"
`
listed = "sftp> ls -n .ssh\n"
warning = `Warning: Identity file /gone not accessible: ` +
"No such file or directory.\n"
)
// TestAbsenceIsReadOnlyFromWhatSFTPSaidAboutAuthorizedKeys holds the
// wordings the OpenSSH client was seen to use against a real server:
// a file it cannot find is reported one way, naming the path the
// server expanded, and everything else it says is a failure.
func TestAbsenceIsReadOnlyFromWhatSFTPSaidAboutAuthorizedKeys(t *testing.T) {
t.Parallel()
sessions := map[string]struct {
said string
want bool
}{
"the file is not there": {
said: echoed +
`File "/home/someone/.ssh/authorized_keys" not found.` + "\n",
want: true,
},
"the file is not there, named as it was asked for": {
said: echoed + `File ".ssh/authorized_keys" not found.` + "\n",
want: true,
},
"the file is not there and an identity file is not either": {
said: warning + echoed +
`File "/home/someone/.ssh/authorized_keys" not found.` + "\n",
want: true,
},
"the file is there and cannot be read": {
said: echoed +
`remote open "/home/someone/.ssh/authorized_keys": ` +
"Permission denied\n",
want: false,
},
"only an identity file is not there": {
said: warning + echoed +
`remote open "/home/someone/.ssh/authorized_keys": ` +
"Permission denied\n",
want: false,
},
"some other file is not there": {
said: echoed + `File "/home/someone/.ssh/known_hosts" not found.` +
"\n",
want: false,
},
"the connection did not come up": {
said: "ssh: connect to host example.com port 22: " +
"Connection refused\nConnection closed\n",
want: false,
},
}
for name, session := range sessions {
t.Run(name, func(t *testing.T) {
t.Parallel()
if absent(session.said) != session.want {
t.Errorf(
"read as absent: %t, wanted %t, from:\n%s",
!session.want, session.want, session.said,
)
}
})
}
}
// TestTheDirectoryIsReadAsAbsentOnlyFromTheListingSayingSo holds the
// wordings the OpenSSH client was seen to use when a listing fails: a
// directory it cannot find is reported one way, and one it cannot read
// another, and only the first is read as a host with no .ssh yet. A
// .ssh that can be read but not entered lists as empty, and the
// listing of .ssh/. that follows reports that path, not .ssh, as not
// found.
func TestTheDirectoryIsReadAsAbsentOnlyFromTheListingSayingSo(t *testing.T) {
t.Parallel()
listings := map[string]struct {
said string
want bool
}{
"the directory is not there": {
said: listed + `Can't ls: "/home/someone/.ssh" not found` + "\n",
want: true,
},
"the directory is not there, named as it was asked for": {
said: listed + `Can't ls: ".ssh" not found` + "\n",
want: true,
},
"the directory is not there and an identity file is not either": {
said: warning + listed +
`Can't ls: "/home/someone/.ssh" not found` + "\n",
want: true,
},
"the directory is there and cannot be read": {
said: listed +
`remote readdir("/home/someone/.ssh/"): Permission denied` + "\n",
want: false,
},
"the directory is there and cannot be entered": {
said: listed + "sftp> ls -1 .ssh/.\n" +
`Can't ls: "/home/someone/.ssh/." not found` + "\n",
want: false,
},
"some other directory is not there": {
said: listed + `Can't ls: "/home/someone/.config" not found` + "\n",
want: false,
},
"the connection did not come up": {
said: "ssh: connect to host example.com port 22: " +
"Connection refused\nConnection closed\n",
want: false,
},
}
for name, listing := range listings {
t.Run(name, func(t *testing.T) {
t.Parallel()
if listingNotFound(listing.said, directory) != listing.want {
t.Errorf(
"read as absent: %t, wanted %t, from:\n%s",
!listing.want, listing.want, listing.said,
)
}
})
}
}