cmd/tailscale/cli: prompt for y/n when attempting risky action

Previously, when attempting a risky action, the CLI printed a 5 second countdown saying
"Continuing in 5 seconds...". When the countdown finished, the CLI aborted rather than
continuing.

To avoid confusion, but also avoid accidentally continuing if someone (or an automated
process) fails to manually abort within the countdown, we now explicitly prompt for a
y/n response on whether or not to continue.

Updates #15445

Co-authored-by: Kot C <kot@kot.pink>
Signed-off-by: Percy Wegmann <percy@tailscale.com>
This commit is contained in:
Percy Wegmann 2025-09-02 09:25:21 -05:00 committed by Percy Wegmann
parent dbc54addd0
commit 42a215e12a

View File

@ -7,15 +7,11 @@ import (
"context" "context"
"errors" "errors"
"flag" "flag"
"fmt"
"os"
"os/signal"
"runtime" "runtime"
"strings" "strings"
"syscall"
"time"
"tailscale.com/ipn" "tailscale.com/ipn"
"tailscale.com/util/prompt"
"tailscale.com/util/testenv" "tailscale.com/util/testenv"
) )
@ -57,11 +53,6 @@ func isRiskAccepted(riskType, acceptedRisks string) bool {
var errAborted = errors.New("aborted, no changes made") var errAborted = errors.New("aborted, no changes made")
// riskAbortTimeSeconds is the number of seconds to wait after displaying the
// risk message before continuing with the operation.
// It is used by the presentRiskToUser function below.
const riskAbortTimeSeconds = 5
// presentRiskToUser displays the risk message and waits for the user to cancel. // presentRiskToUser displays the risk message and waits for the user to cancel.
// It returns errorAborted if the user aborts. In tests it returns errAborted // It returns errorAborted if the user aborts. In tests it returns errAborted
// immediately unless the risk has been explicitly accepted. // immediately unless the risk has been explicitly accepted.
@ -75,22 +66,10 @@ func presentRiskToUser(riskType, riskMessage, acceptedRisks string) error {
outln(riskMessage) outln(riskMessage)
printf("To skip this warning, use --accept-risk=%s\n", riskType) printf("To skip this warning, use --accept-risk=%s\n", riskType)
interrupt := make(chan os.Signal, 1) if prompt.YesNo("Continue?") {
signal.Notify(interrupt, syscall.SIGINT) return nil
var msgLen int
for left := riskAbortTimeSeconds; left > 0; left-- {
msg := fmt.Sprintf("\rContinuing in %d seconds...", left)
msgLen = len(msg)
printf("%s", msg)
select {
case <-interrupt:
printf("\r%s\r", strings.Repeat("x", msgLen+1))
return errAborted
case <-time.After(time.Second):
continue
} }
}
printf("\r%s\r", strings.Repeat(" ", msgLen))
return errAborted return errAborted
} }