Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0e2fb8b20 |
No files matched your search
@@ -374,8 +374,8 @@ variables.
|
|||||||
|
|
||||||
The prompt asks on the terminal the command runs in, not on stdin, so it also
|
The prompt asks on the terminal the command runs in, not on stdin, so it also
|
||||||
asks when stdin is a pipe, as in `echo value | secret add name --force` or
|
asks when stdin is a pipe, as in `echo value | secret add name --force` or
|
||||||
`cat file | secret encrypt key`. With no terminal, as in a script or a CI job,
|
`cat file | secret encrypt key`. With no controlling terminal, as under cron or
|
||||||
the command fails and names the variable to set.
|
in a CI job, the command fails and names the variable to set.
|
||||||
|
|
||||||
## Security Features
|
## Security Features
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,9 @@ https://git.eeqj.de/sneak/secret/milestone/12
|
|||||||
(https://git.eeqj.de/sneak/secret/issues/144). Only a process with no
|
(https://git.eeqj.de/sneak/secret/issues/144). Only a process with no
|
||||||
controlling terminal gets the error that names the variable to set. On SIGINT
|
controlling terminal gets the error that names the variable to set. On SIGINT
|
||||||
or SIGTERM, `Entry()` restores the settings of that terminal, not of stdin,
|
or SIGTERM, `Entry()` restores the settings of that terminal, not of stdin,
|
||||||
since that is where the prompt turns echo off.
|
since that is where the prompt turns echo off. `secret init` and
|
||||||
|
`secret vault create` no longer write a newline to stderr after the mnemonic
|
||||||
|
is typed, since the prompt ends that line on the terminal.
|
||||||
- 2026-10-14: A `secret add --force` that cannot unlock the vault, given a wrong
|
- 2026-10-14: A `secret add --force` that cannot unlock the vault, given a wrong
|
||||||
passphrase or mnemonic, leaves the secret as it was
|
passphrase or mnemonic, leaves the secret as it was
|
||||||
(https://git.eeqj.de/sneak/secret/issues/136). The new version used to be
|
(https://git.eeqj.de/sneak/secret/issues/136). The new version used to be
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"log/slog"
|
"log/slog"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/awnumar/memguard"
|
"github.com/awnumar/memguard"
|
||||||
@@ -57,13 +56,11 @@ func (cli *Instance) promptMnemonic() (*memguard.LockedBuffer, func(), error) {
|
|||||||
// Read mnemonic securely without echo
|
// Read mnemonic securely without echo
|
||||||
mnemonicBuffer, err := secret.ReadMnemonic("Enter your BIP39 mnemonic phrase: ")
|
mnemonicBuffer, err := secret.ReadMnemonic("Enter your BIP39 mnemonic phrase: ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
secret.Debug("Failed to read mnemonic from stdin", "error", err)
|
secret.Debug("Failed to read mnemonic", "error", err)
|
||||||
|
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprintln(os.Stderr) // Add newline after hidden input
|
|
||||||
|
|
||||||
return mnemonicBuffer, mnemonicBuffer.Destroy, nil
|
return mnemonicBuffer, mnemonicBuffer.Destroy, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,6 +67,77 @@ func TestPassphraseAskedAtTerminalWithPipedStdin(t *testing.T) {
|
|||||||
assert.Equal(t, "a document\n", decrypted)
|
assert.Equal(t, "a document\n", decrypted)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestInitWritesNoBlankLineAfterMnemonic runs `secret init 2>log` at a
|
||||||
|
// terminal: the terminal is its controlling terminal and none of stdin,
|
||||||
|
// stdout and stderr. The mnemonic is typed on the terminal and the prompt
|
||||||
|
// ends its line there, so stderr must not get a newline for it as well, a
|
||||||
|
// blank line in the log. pty.Open is described above
|
||||||
|
// TestRemoveIgnoresTerminalOnStdout.
|
||||||
|
func TestInitWritesNoBlankLineAfterMnemonic(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
ctx, cancel := context.WithTimeout(t.Context(), commandWait)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
stateDir := t.TempDir()
|
||||||
|
|
||||||
|
//nolint:gosec // G204: test executes the freshly built secret binary
|
||||||
|
cmd := exec.CommandContext(ctx, secretBinaryPath(t), "init")
|
||||||
|
cmd.Env = []string{
|
||||||
|
secret.EnvStateDir + "=" + stateDir,
|
||||||
|
secret.EnvUnlockPassphrase + "=" + testPassphrase,
|
||||||
|
"PATH=" + os.Getenv("PATH"),
|
||||||
|
"HOME=" + os.Getenv("HOME"),
|
||||||
|
}
|
||||||
|
|
||||||
|
ptmx, tty, err := pty.Open()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
defer func() { _ = ptmx.Close() }()
|
||||||
|
|
||||||
|
deadline, _ := ctx.Deadline()
|
||||||
|
require.NoError(t, ptmx.SetReadDeadline(deadline))
|
||||||
|
|
||||||
|
var stderr strings.Builder
|
||||||
|
|
||||||
|
cmd.Stderr = &stderr
|
||||||
|
cmd.ExtraFiles = []*os.File{tty}
|
||||||
|
// Ctty is a descriptor in the new process: 3, the first of ExtraFiles.
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true, Setctty: true, Ctty: 3}
|
||||||
|
|
||||||
|
require.NoError(t, cmd.Start())
|
||||||
|
|
||||||
|
_ = tty.Close()
|
||||||
|
|
||||||
|
var (
|
||||||
|
shown []byte
|
||||||
|
char byte
|
||||||
|
)
|
||||||
|
|
||||||
|
terminal := bufio.NewReader(ptmx)
|
||||||
|
for !bytes.HasSuffix(shown, []byte("Enter your BIP39 mnemonic phrase: ")) {
|
||||||
|
char, err = terminal.ReadByte()
|
||||||
|
require.NoError(t, err, "secret init did not ask on the terminal: %s",
|
||||||
|
shown)
|
||||||
|
|
||||||
|
shown = append(shown, char)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = ptmx.WriteString(testMnemonic + "\n")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// The read ends once the command has exited and so closed the terminal.
|
||||||
|
rest, _ := io.ReadAll(terminal)
|
||||||
|
|
||||||
|
err = cmd.Wait()
|
||||||
|
require.NoError(t, err, "secret init: %s%s%s", shown, rest, stderr.String())
|
||||||
|
|
||||||
|
assert.True(t, strings.HasPrefix(stderr.String(),
|
||||||
|
"Initialized secrets manager at: "+stateDir+"\n\n"+
|
||||||
|
"Default vault created and configured\n"),
|
||||||
|
"stderr: %q", stderr.String())
|
||||||
|
}
|
||||||
|
|
||||||
// runAtTerminal runs the secret binary with args and input on stdin, in a
|
// runAtTerminal runs the secret binary with args and input on stdin, in a
|
||||||
// new session whose controlling terminal is a new terminal, which is also
|
// new session whose controlling terminal is a new terminal, which is also
|
||||||
// its stderr: a command typed at a terminal with its input piped. pty.Open
|
// its stderr: a command typed at a terminal with its input piped. pty.Open
|
||||||
|
|||||||
Reference in new issue
Block a user