README child-mnemonic vector and host-key note; ssh install refuses a ~/.ssh it cannot enter (closes #51)
check / check (push) Successful in 2m4s
check / check (push) Successful in 2m4s
The README now gives the child mnemonic keyfunc prints for the abandon ... about test mnemonic at index 0, checked by the README vectors test; the BIP-85 specification vector stays, marked as starting from a master key keyfunc cannot take. It also says ssh install needs the host key in known_hosts already, and how to get round that. ssh install now also lists ~/.ssh/. on its first connection and refuses, before any upload, a ~/.ssh it can read but not enter, which sftp shows as empty; a file where ~/.ssh belongs is refused the same way. Model: opus-5-5 Co-authored-by: clawbot <sneak+clawbot@sneak.cloud>
This commit was merged in pull request #55.
This commit is contained in:
+52
-18
@@ -52,7 +52,7 @@ const (
|
||||
)
|
||||
|
||||
// notADirectory is what a test puts where the .ssh directory belongs
|
||||
// to make a step of the write session fail.
|
||||
// to make a .ssh that is listed but cannot be entered.
|
||||
const notADirectory = "a file where the directory belongs\n"
|
||||
|
||||
// missingIdentity is a path with no file at it, handed to sftp after
|
||||
@@ -106,9 +106,11 @@ const marker = "KEYFUNC_TEST_MARKER"
|
||||
// another for a file that is there and cannot be read, which is a
|
||||
// failure. A directory shut to the user is stood in for by mode 000,
|
||||
// which the listing reads off the mode itself so that the test does not
|
||||
// turn on the user it runs as. An -i naming a file that is not here
|
||||
// draws the warning ssh writes for it, which carries the wording of a
|
||||
// missing file into a session that goes on to authenticate.
|
||||
// turn on the user it runs as, and one the user can enter but not write
|
||||
// to by mode 500, which the put reads off the same way. An -i naming a
|
||||
// file that is not here draws the warning ssh writes for it, which
|
||||
// carries the wording of a missing file into a session that goes on to
|
||||
// authenticate.
|
||||
const installer = `
|
||||
[ -n "$KEYFUNC_TEST_ENVIRONMENT" ] && env > "$KEYFUNC_TEST_ENVIRONMENT"
|
||||
previous=
|
||||
@@ -160,7 +162,13 @@ while IFS= read -r line; do
|
||||
printf 'remote open "%s": Permission denied\n' "$home/$2" >&2
|
||||
fi
|
||||
;;
|
||||
put) cp "$2" "$home/$3" 2>/dev/null || worked=no ;;
|
||||
put)
|
||||
if [ "$(stat -c '%a' "$(dirname "$home/$3")")" = 500 ]; then
|
||||
worked=no
|
||||
else
|
||||
cp "$2" "$home/$3" 2>/dev/null || worked=no
|
||||
fi
|
||||
;;
|
||||
mkdir) mkdir "$home/$2" 2>/dev/null || worked=no ;;
|
||||
chmod) chmod "$2" "$home/$3" 2>/dev/null || worked=no ;;
|
||||
rename) mv "$home/$2" "$home/$3" 2>/dev/null || worked=no ;;
|
||||
@@ -329,6 +337,29 @@ func TestAnUnreadableDirectoryIsNotWrittenInto(t *testing.T) {
|
||||
require.Equal(t, 1, connections(t, pretend))
|
||||
}
|
||||
|
||||
func TestADirectoryThatCannotBeEnteredIsRefusedBeforeAnyUpload(t *testing.T) {
|
||||
t.Setenv(mnemonic.Variable, example())
|
||||
|
||||
pretend := pretendHost(t)
|
||||
|
||||
// A file where .ssh belongs is listed and cannot be entered, which is
|
||||
// how sftp sees a directory that can be read but not entered: the
|
||||
// listing of .ssh comes up and the listing of .ssh/. finds nothing.
|
||||
inTheWay := filepath.Join(pretend.home, keptUnder)
|
||||
require.NoError(t,
|
||||
os.WriteFile(inTheWay, []byte(notADirectory), fileMode),
|
||||
)
|
||||
|
||||
printed, _, err := attempt(t, host)
|
||||
require.ErrorIs(t, err, ssh.ErrCannotEnter)
|
||||
require.Empty(t, printed)
|
||||
|
||||
// The read and nothing after it: no upload was tried, and what was
|
||||
// on the host is still what is on the host.
|
||||
require.Equal(t, 1, connections(t, pretend))
|
||||
require.Equal(t, notADirectory, read(t, inTheWay))
|
||||
}
|
||||
|
||||
func TestAnExistingDirectoryKeepsItsModeAndIsNotRemade(t *testing.T) {
|
||||
t.Setenv(mnemonic.Variable, example())
|
||||
|
||||
@@ -392,27 +423,30 @@ func TestAFailedStepNamesTheUploadedFileAndChangesNothing(t *testing.T) {
|
||||
|
||||
pretend := pretendHost(t)
|
||||
|
||||
// A file where the .ssh directory belongs: the listing shows it and
|
||||
// so the directory reads as already there, but then the put has
|
||||
// nowhere to put anything, so the write session ends at the put.
|
||||
inTheWay := filepath.Join(pretend.home, keptUnder)
|
||||
require.NoError(t,
|
||||
os.WriteFile(inTheWay, []byte(notADirectory), fileMode),
|
||||
)
|
||||
// A .ssh that can be listed and entered but not written to: the
|
||||
// fetch finds no file in it, and then the put has nowhere to put
|
||||
// anything, so the write session ends at the put.
|
||||
const unwritable = 0o500
|
||||
|
||||
directory := filepath.Join(pretend.home, keptUnder)
|
||||
require.NoError(t, os.Mkdir(directory, unwritable))
|
||||
|
||||
printed, said, err := attempt(t, host)
|
||||
require.Error(t, err)
|
||||
require.Empty(t, printed)
|
||||
require.Contains(t, said, "put failed")
|
||||
|
||||
// The put is the first and last command the write session got to,
|
||||
// and the file it was uploading is the one the message names.
|
||||
// The put, after the three commands of the fetch, is the first and
|
||||
// last command the write session got to, and the file it was
|
||||
// uploading is the one the message names.
|
||||
sent := recorded(t, pretend.batch)
|
||||
require.Len(t, sent, 3)
|
||||
require.Equal(t, "put", strings.Fields(sent[2])[0])
|
||||
require.Contains(t, err.Error(), strings.Fields(sent[2])[2])
|
||||
require.Len(t, sent, 4)
|
||||
require.Equal(t, "put", strings.Fields(sent[3])[0])
|
||||
require.Contains(t, err.Error(), strings.Fields(sent[3])[2])
|
||||
|
||||
require.Equal(t, notADirectory, read(t, inTheWay))
|
||||
left, err := os.ReadDir(directory)
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, left)
|
||||
|
||||
// The same run again, this way for the status it ends with.
|
||||
given := os.Args
|
||||
|
||||
Reference in New Issue
Block a user