Skip to content

Commit 32f5f4f

Browse files
Merge pull request #47 from browserstack/locsec/WI-a20414f5
fix: redact access key in public command accessor and inspect (CWE-312)
2 parents a506d5a + 9529fed commit 32f5f4f

2 files changed

Lines changed: 39 additions & 3 deletions

File tree

‎lib/browserstack/local.rb‎

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,27 @@ def stop
128128
@pid = nil
129129
end
130130

131+
# Public accessor used by callers for debugging/logging. Return the command
132+
# with the access key masked so it is never written to logs, CI artifacts or
133+
# error trackers (CWE-312). The real key is still used for execution via
134+
# start_command_args / start_command(false).
131135
def command
132-
start_command
136+
start_command(true)
133137
end
134138

135-
def start_command
136-
cmd = "#{@binary_path} -d start -logFile '#{@logfile}' #{@folder_flag} #{@key} #{@folder_path} #{@force_local_flag}"
139+
# Prevent Ruby's default #inspect from dumping @key when a Local instance is
140+
# logged or included in an exception payload (CWE-312).
141+
def inspect
142+
redacted = instance_variables.map do |var|
143+
value = var == :@key && !@key.to_s.empty? ? "[REDACTED]" : instance_variable_get(var)
144+
"#{var}=#{value.inspect}"
145+
end.join(", ")
146+
"#<#{self.class}:0x#{format('%016x', object_id << 1)} #{redacted}>"
147+
end
148+
149+
def start_command(redact = false)
150+
key = redact && !@key.to_s.empty? ? "[REDACTED]" : @key
151+
cmd = "#{@binary_path} -d start -logFile '#{@logfile}' #{@folder_flag} #{key} #{@folder_path} #{@force_local_flag}"
137152
cmd += " -localIdentifier #{@local_identifier_flag}" if @local_identifier_flag
138153
cmd += " #{@only_flag} #{@only_automate_flag}"
139154
cmd += " -proxyHost #{@proxy_host}" if @proxy_host

‎test/browserstack-local-test.rb‎

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,27 @@ def test_hosts
109109
assert_match /localhost\,8080\,0/, @bs_local.command
110110
end
111111

112+
# Regression for CWE-312: the public #command accessor must NOT expose the
113+
# access key — callers routinely log it to CI output / APM / error trackers.
114+
def test_command_redacts_access_key
115+
bs = BrowserStack::Local.new("MY_SECRET_ACCESS_KEY")
116+
refute_match /MY_SECRET_ACCESS_KEY/, bs.command
117+
assert_match /\[REDACTED\]/, bs.command
118+
end
119+
120+
# The real key must still reach the binary on the execution path.
121+
def test_start_command_keeps_key_for_execution
122+
bs = BrowserStack::Local.new("MY_SECRET_ACCESS_KEY")
123+
assert_match /MY_SECRET_ACCESS_KEY/, bs.start_command
124+
end
125+
126+
# Regression for CWE-312: default object inspection must not dump the key.
127+
def test_inspect_redacts_access_key
128+
bs = BrowserStack::Local.new("MY_SECRET_ACCESS_KEY")
129+
refute_match /MY_SECRET_ACCESS_KEY/, bs.inspect
130+
assert_match /\[REDACTED\]/, bs.inspect
131+
end
132+
112133
def teardown
113134
@bs_local.stop
114135
end

0 commit comments

Comments
 (0)