system_command: allow redacting secrets in the log

Add a new argument `secrets` to specify secret tokens, so we can redact them in the log.
This commit is contained in:
Cheng XU 2019-06-28 14:50:38 +08:00
parent 6fcd4734db
commit 9232ca4508
No known key found for this signature in database
GPG Key ID: B19F15830AB4E690
4 changed files with 24 additions and 7 deletions

View File

@ -1,6 +1,7 @@
# frozen_string_literal: true
require "shellwords"
require "utils"
class UsageError < RuntimeError
attr_reader :reason
@ -520,7 +521,7 @@ class ErrorDuringExecution < RuntimeError
attr_reader :status
attr_reader :output
def initialize(cmd, status:, output: nil)
def initialize(cmd, status:, output: nil, secrets: [])
@cmd = cmd
@status = status
@output = output
@ -531,7 +532,8 @@ class ErrorDuringExecution < RuntimeError
status
end
s = +"Failure while executing; `#{cmd.shelljoin.gsub(/\\=/, "=")}` exited with #{exitstatus}."
redacted_cmd = redact_secrets(cmd.shelljoin.gsub('\=', "="), secrets)
s = +"Failure while executing; `#{redacted_cmd}` exited with #{exitstatus}."
unless [*output].empty?
format_output_line = lambda do |type_line|

View File

@ -34,7 +34,7 @@ class SystemCommand
end
def run!
puts command.shelljoin.gsub(/\\=/, "=") if verbose? || ARGV.debug?
puts redact_secrets(command.shelljoin.gsub('\=', "="), @secrets) if verbose? || ARGV.debug?
@output = []
@ -54,7 +54,7 @@ class SystemCommand
end
def initialize(executable, args: [], sudo: false, env: {}, input: [], must_succeed: false,
print_stdout: false, print_stderr: true, verbose: false, **options)
print_stdout: false, print_stderr: true, verbose: false, secrets: [], **options)
@executable = executable
@args = args
@ -63,6 +63,7 @@ class SystemCommand
@print_stdout = print_stdout
@print_stderr = print_stderr
@verbose = verbose
@secrets = Array(secrets)
@must_succeed = must_succeed
options.assert_valid_keys!(:chdir)
@options = options
@ -106,9 +107,7 @@ class SystemCommand
def assert_success
return if @status.success?
raise ErrorDuringExecution.new(command,
status: @status,
output: @output)
raise ErrorDuringExecution.new(command, status: @status, output: @output, secrets: @secrets)
end
def expanded_args

View File

@ -252,5 +252,17 @@ describe SystemCommand do
expect(system_command(executable)).to be_a_success
end
end
context "when given arguments with secrets" do
it "does not leak the secrets" do
redacted_msg = /#{Regexp.escape("username:******")}/
expect do
described_class.run! "curl",
args: %w[--user username:hunter2],
verbose: true,
secrets: %w[hunter2]
end.to raise_error.with_message(redacted_msg).and output(redacted_msg).to_stdout
end
end
end
end

View File

@ -500,3 +500,7 @@ end
def command_help_lines(path)
path.read.lines.grep(/^#:/).map { |line| line.slice(2..-1) }
end
def redact_secrets(input, secrets)
secrets.reduce(input) { |str, secret| str.gsub secret, "******" }.freeze
end