Module: LocalVault::PlaintextOutput

Defined in:
lib/localvault/plaintext_output.rb

Overview

Gate for printing plaintext secret values.

Plaintext may flow to an interactive terminal (a human is watching). When stdout is captured (pipe, command substitution, agent shell), a human can confirm with one keystroke on /dev/tty. An agent's shell has no /dev/tty, and there is deliberately no flag, environment variable, or config bypass: anything discoverable from the binary would be discovered by an agent.

Constant Summary collapse

TTY_PATH =
"/dev/tty".freeze

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.assume_ttyObject

In-process overrides for tests only — unreachable from the installed binary, unlike an env var or CLI flag would be.



15
16
17
# File 'lib/localvault/plaintext_output.rb', line 15

def assume_tty
  @assume_tty
end

.tty_overrideObject

IO, output: IO



16
17
18
# File 'lib/localvault/plaintext_output.rb', line 16

def tty_override
  @tty_override
end

Class Method Details

.confirm(prompt) ⇒ Object



29
30
31
32
33
34
35
36
# File 'lib/localvault/plaintext_output.rb', line 29

def self.confirm(prompt)
  with_tty do |input, output|
    output.write(prompt)
    output.flush
    answer = input.gets
    !answer.nil? && %w[y yes].include?(answer.strip.downcase)
  end
end

.permitted?(out: $stdout, purpose: "Print plaintext") ⇒ Boolean

Returns whether plaintext may be printed to out.

Parameters:

  • out (IO) (defaults to: $stdout)

    the stream plaintext would be written to

  • purpose (String) (defaults to: "Print plaintext")

    short description used in the /dev/tty prompt

Returns:

  • (Boolean)

    whether plaintext may be printed to out



22
23
24
25
26
27
# File 'lib/localvault/plaintext_output.rb', line 22

def self.permitted?(out: $stdout, purpose: "Print plaintext")
  return true if assume_tty
  return true if out.respond_to?(:tty?) && out.tty?

  confirm("#{purpose}? [y/N] ")
end

.with_tty(&block) ⇒ Object



38
39
40
41
42
43
44
45
46
# File 'lib/localvault/plaintext_output.rb', line 38

def self.with_tty(&block)
  if tty_override
    return yield(tty_override[:input], tty_override[:output])
  end

  File.open(TTY_PATH, "r+") { |tty| yield(tty, tty) }
rescue SystemCallError, IOError
  false
end