Skip to content

feat(run): --mask hides fetched values in the command's output - #169

Merged
groupsky merged 6 commits into
mainfrom
feat/run-mask
Nov 11, 2026
Merged

groupsky merged 6 commits into
mainfrom
feat/run-mask

Conversation

@groupsky

@groupsky groupsky commented Nov 11, 2026 •

Copy link
Copy Markdown
Contributor

Refs #18

Progress

  • nodkey run --mask replaces every fetched value in stdout and stderr with ***, also when a value is split across reads. (internal/client/mask.go, internal/client/runmask.go)
  • Tests with canary values, binary output and a value split across chunks. (mask_test.go: split at every byte, binary value cut at every place; runmask_test.go: a real sh with canaries, a value written in two writes with a pause, binary output, signal, start failure, failed output)
  • DESIGN.md (sections 3, 5, 15, 16), THREAT-MODEL.md (T15) and docs/setup.md updated.
  • Security review (2 rounds, all findings fixed) and code review (no blocking findings; a fuzz check against a simple reference mask found no difference).

Choices

  • --mask is opt-in. Without it, nodkey run still replaces itself with execve, as before, so nothing changes for current users.
  • With --mask, the command is a child of nodkey (os/exec with the two mask writers as Stdout and Stderr). This is the leanest way: os/exec already copies the pipes and closes them when a write fails, so the command gets EPIPE.
  • The mask writer holds back only the end of the output that may still become a value, so normal output is not delayed. No new dependency.
  • Signals: SIGTERM and SIGHUP are passed on to the command. SIGINT and SIGQUIT are caught and not passed on, because the terminal already sends them to the whole process group; passing them on would send them twice.

Review findings not fixed

  • Overlapping values (values ab and bcd, output abcd gives ***cd) show the rest of the second value. Real secrets rarely overlap; this is documented in DESIGN.md section 5 and in the code.
  • kill -INT sent to nodkey alone does not reach the command; a terminal sends SIGINT to the whole group. Documented.
  • SIGPIPE on a real closed fd 1 after the command ended is not tested: Go's rule applies only to fd 1 and 2, so it needs a child process. The code keeps SIGPIPE caught until all output is written.

@groupsky
groupsky marked this pull request as ready for review November 11, 2026 01:50
@groupsky
groupsky merged commit 4444bef into main Nov 11, 2026
8 checks passed
@groupsky
groupsky deleted the feat/run-mask branch November 11, 2026 01:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant