check / check (push) Waiting to run
cli.Entry() set time.Local to UTC and ended with memguard.Purge(), and TestEntryWipesBuffersAndReturnsExitCode ran it in the same process as the other tests. A timer left by an earlier test could read time.Local as it changed, which the race detector reported on some runs, and the purge could destroy a buffer still in use, the likeliest cause of a crash seen on another run. main() in cmd/secret now sets time.Local before it calls cli.Entry(); the test that runs the built binary with TZ=America/New_York still covers it. The Entry test runs cli.Entry() in a copy of the test binary started with SECRET_TEST_RUN_ENTRY set, which init picks up so that TestMain does not run there. Model: opus-5-5
125 lines
3.4 KiB
Go
125 lines
3.4 KiB
Go
package cli
|
|
|
|
import (
|
|
"os"
|
|
|
|
"github.com/awnumar/memguard"
|
|
"github.com/spf13/cobra"
|
|
"golang.org/x/sys/unix"
|
|
"golang.org/x/term"
|
|
"sneak.berlin/go/secret/internal/secret"
|
|
)
|
|
|
|
// Entry runs the secret CLI and returns the process exit code. It wipes
|
|
// every memguard buffer before it returns, so the caller must do nothing
|
|
// but exit with the code.
|
|
func Entry() int {
|
|
raiseLockedMemoryLimit()
|
|
|
|
// On SIGINT or SIGTERM memguard runs this function, wipes every buffer
|
|
// and exits with status 1. The passphrase prompt turns echo off on the
|
|
// controlling terminal until the read finishes, so a signal there would
|
|
// leave echo off. Only a process in the terminal's foreground process
|
|
// group may reset it: one in the background that tries is stopped
|
|
// instead of exiting.
|
|
terminal, terminalErr := os.Open(secret.TerminalPath)
|
|
|
|
var terminalState *term.State
|
|
|
|
if terminalErr == nil {
|
|
defer func() { _ = terminal.Close() }()
|
|
|
|
terminalState, terminalErr = term.GetState(int(terminal.Fd()))
|
|
}
|
|
|
|
memguard.CatchSignal(func(os.Signal) {
|
|
if terminalErr != nil {
|
|
return
|
|
}
|
|
|
|
foreground, err := unix.IoctlGetInt(int(terminal.Fd()), unix.TIOCGPGRP)
|
|
if err == nil && foreground == unix.Getpgrp() {
|
|
_ = term.Restore(int(terminal.Fd()), terminalState)
|
|
}
|
|
}, os.Interrupt, unix.SIGTERM)
|
|
|
|
defer memguard.Purge()
|
|
|
|
err := newRootCmd().Execute()
|
|
if err != nil {
|
|
return 1
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
// raiseLockedMemoryLimit raises this process's limit on locked memory, which
|
|
// holds every secret value, to the hard limit, the most a process may set
|
|
// without privileges. When that fails the limit stays as it was, and
|
|
// checkLockedMemoryLimit names it in its error.
|
|
func raiseLockedMemoryLimit() {
|
|
var limit unix.Rlimit
|
|
|
|
if unix.Getrlimit(unix.RLIMIT_MEMLOCK, &limit) == nil {
|
|
limit.Cur = limit.Max
|
|
_ = unix.Setrlimit(unix.RLIMIT_MEMLOCK, &limit)
|
|
}
|
|
}
|
|
|
|
func newRootCmd() *cobra.Command {
|
|
secret.Debug("newRootCmd starting")
|
|
|
|
cmd := &cobra.Command{
|
|
Use: "secret",
|
|
Short: "A simple secrets manager",
|
|
Long: `A simple secrets manager to store and retrieve sensitive ` +
|
|
`information securely.`,
|
|
// Cobra prints the error a command returns; Entry does not.
|
|
SilenceErrors: false,
|
|
// Usage belongs only to a command called wrongly. Cobra has
|
|
// checked its arguments and flag values before this runs, but
|
|
// checks required flags (ValidateRequiredFlags) and flag groups
|
|
// (ValidateFlagGroups) only after it, so both are checked here
|
|
// to keep usage for them. An error after that comes from running
|
|
// the command, and usage would only bury it. A subcommand that
|
|
// sets its own PersistentPreRun replaces this one.
|
|
PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {
|
|
err := cmd.ValidateRequiredFlags()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = cmd.ValidateFlagGroups()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
cmd.SilenceUsage = true
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
secret.Debug("Adding subcommands to root command")
|
|
// Add subcommands
|
|
cmd.AddCommand(NewInitCmd())
|
|
cmd.AddCommand(newGenerateCmd())
|
|
cmd.AddCommand(newVaultCmd())
|
|
cmd.AddCommand(newAddCmd())
|
|
cmd.AddCommand(newGetCmd())
|
|
cmd.AddCommand(newListCmd())
|
|
cmd.AddCommand(newRemoveCmd())
|
|
cmd.AddCommand(newMoveCmd())
|
|
cmd.AddCommand(newUnlockerCmd())
|
|
cmd.AddCommand(newImportCmd())
|
|
cmd.AddCommand(newEncryptCmd())
|
|
cmd.AddCommand(newDecryptCmd())
|
|
cmd.AddCommand(newVersionCmd())
|
|
cmd.AddCommand(newInfoCmd())
|
|
cmd.AddCommand(newCompletionCmd())
|
|
|
|
secret.Debug("newRootCmd completed")
|
|
|
|
return cmd
|
|
}
|