Ask for the passphrase on the controlling terminal, not stdin #165

Merged
clawbot merged 1 commits from fix/144-passphrase-from-tty into next 2026-10-14 23:02:26 +02:00
Collaborator

Fixes #144.

The passphrase and mnemonic prompts read stdin and refused when it was not a terminal, so echo v | secret add s1 --force, cat file | secret encrypt key and cat file.age | secret decrypt key failed at a terminal. They now open /dev/tty, the terminal the command runs in, and write the prompt and read the answer there, whatever stdin and stderr are. A process with no controlling terminal still fails with the message that names the variable to set; its cause now reads "there is no terminal to ask on". secret init and secret vault create no longer write a newline to stderr after the mnemonic, as the prompt ends that line on the terminal.

What the diff does not make obvious:

  • Entry() saved and restored the terminal settings of stdin, for Ctrl-C at the prompt. The prompt now turns echo off on /dev/tty, so with stdin piped, Ctrl-C there left echo off. Entry() now saves and restores /dev/tty instead.
  • The new tests run the binary in a new session whose controlling terminal is a pseudo-terminal that is not its stdin. The two tests that pin the no-terminal message run it in a new session with no terminal, wherever the tests run.
  • The [y/N] removal confirmations still read stdin, as the README documents.

Disclosures:

  • Not covered by a test: echo after Ctrl-C at the prompt with stdin piped. Checked by hand under a pseudo-terminal: echo is back on with this change, and stays off without the Entry() part.

Model: opus-5-5

Fixes https://git.eeqj.de/sneak/secret/issues/144. The passphrase and mnemonic prompts read stdin and refused when it was not a terminal, so `echo v | secret add s1 --force`, `cat file | secret encrypt key` and `cat file.age | secret decrypt key` failed at a terminal. They now open `/dev/tty`, the terminal the command runs in, and write the prompt and read the answer there, whatever stdin and stderr are. A process with no controlling terminal still fails with the message that names the variable to set; its cause now reads "there is no terminal to ask on". `secret init` and `secret vault create` no longer write a newline to stderr after the mnemonic, as the prompt ends that line on the terminal. What the diff does not make obvious: - `Entry()` saved and restored the terminal settings of stdin, for Ctrl-C at the prompt. The prompt now turns echo off on `/dev/tty`, so with stdin piped, Ctrl-C there left echo off. `Entry()` now saves and restores `/dev/tty` instead. - The new tests run the binary in a new session whose controlling terminal is a pseudo-terminal that is not its stdin. The two tests that pin the no-terminal message run it in a new session with no terminal, wherever the tests run. - The `[y/N]` removal confirmations still read stdin, as the README documents. Disclosures: - Not covered by a test: echo after Ctrl-C at the prompt with stdin piped. Checked by hand under a pseudo-terminal: echo is back on with this change, and stays off without the `Entry()` part. Model: opus-5-5
clawbot added the needs-review label 2026-10-14 21:01:13 +02:00
clawbot self-assigned this 2026-10-14 21:01:13 +02:00
Author
Collaborator
  1. README.md line 377: "With no terminal, as in a script or a CI job, the command fails and names the variable to set" is wrong for a script started from a terminal. That script runs in the terminal, so the command now asks there and waits instead of failing. Only a process with no controlling terminal (cron, a CI job, a service) gets the error. Acceptable: name only cases that really have no terminal, for example "With no controlling terminal, as under cron or in a CI job, ...".

  2. internal/cli/init.go lines 60 and 65 still assume the mnemonic prompt uses stdin and stderr. The debug message says "Failed to read mnemonic from stdin", but the mnemonic is no longer read from stdin. Line 65 writes a newline to stderr "after hidden input", but the input is now typed on the terminal and readFromTerminal already ends that line there, so secret init 2>log leaves a stray blank line in the log. Acceptable: remove line 65 and remove "from stdin" from the message.

Model: opus-5-5

1. `README.md` line 377: "With no terminal, as in a script or a CI job, the command fails and names the variable to set" is wrong for a script started from a terminal. That script runs in the terminal, so the command now asks there and waits instead of failing. Only a process with no controlling terminal (cron, a CI job, a service) gets the error. Acceptable: name only cases that really have no terminal, for example "With no controlling terminal, as under cron or in a CI job, ...". 2. `internal/cli/init.go` lines 60 and 65 still assume the mnemonic prompt uses stdin and stderr. The debug message says "Failed to read mnemonic from stdin", but the mnemonic is no longer read from stdin. Line 65 writes a newline to stderr "after hidden input", but the input is now typed on the terminal and `readFromTerminal` already ends that line there, so `secret init 2>log` leaves a stray blank line in the log. Acceptable: remove line 65 and remove "from stdin" from the message. Model: opus-5-5
clawbot added needs-rework and removed needs-review labels 2026-10-14 21:22:57 +02:00
clawbot force-pushed fix/144-passphrase-from-tty from d01a277448 to d0e2fb8b20 2026-10-14 21:48:24 +02:00 Compare
Author
Collaborator

Rework:

  1. README.md now says "With no controlling terminal, as under cron or in a CI job".
  2. internal/cli/init.go no longer writes the newline to stderr after the mnemonic, and the debug message drops "from stdin". New test TestInitWritesNoBlankLineAfterMnemonic runs secret init with stderr captured and fails without the fix. TODO.md and the PR body mention it.

Model: opus-5-5

Rework: 1. `README.md` now says "With no controlling terminal, as under cron or in a CI job". 2. `internal/cli/init.go` no longer writes the newline to stderr after the mnemonic, and the debug message drops "from stdin". New test `TestInitWritesNoBlankLineAfterMnemonic` runs `secret init` with stderr captured and fails without the fix. `TODO.md` and the PR body mention it. Model: opus-5-5
clawbot added needs-review and removed needs-rework labels 2026-10-14 21:48:48 +02:00
clawbot force-pushed fix/144-passphrase-from-tty from d0e2fb8b20 to 45d0fe584a 2026-10-14 22:03:52 +02:00 Compare
clawbot added 1 commit 2026-10-14 22:36:54 +02:00
The passphrase and mnemonic prompts open /dev/tty and ask there, so
`echo v | secret add s1 --force`, `secret encrypt` and `secret decrypt`
with piped input ask at a terminal instead of failing. Only a process
with no controlling terminal still gets the error naming the variable to
set. `secret init` and `secret vault create` no longer write a newline
to stderr after the mnemonic.

On SIGINT or SIGTERM, Entry() restores the settings of /dev/tty instead
of stdin, since that is where the prompt turns echo off.

The two tests that pin the no-terminal message run the binary in a new
session, so it has no terminal wherever the tests run.

Unverified by a test: the echo restore after Ctrl-C, checked by hand.

Model: opus-5-5
clawbot force-pushed fix/144-passphrase-from-tty from 45d0fe584a to 44d8edbb95 2026-10-14 22:36:54 +02:00 Compare
Author
Collaborator

Review passed.

Model: opus-5-5

Review passed. Model: opus-5-5
clawbot merged commit 46c395e045 into next 2026-10-14 23:02:26 +02:00
clawbot deleted branch fix/144-passphrase-from-tty 2026-10-14 23:02:28 +02:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sneak/secret#165