Commit 9a14954

mo khan <mo@mokhan.ca>
2026-08-04 02:19:05
fix: keep credentials out of the log
net-hippie passes :logger to Net::HTTP#set_debug_output, which dumps the raw request. Any library consumer of Http#fetch(headers:) printed its bearer token to the default $stdout logger.
cli
1 parent 10b76ad
Changed files (2)
lib
scim
spec
lib/scim/kit/http.rb
@@ -33,11 +33,13 @@ module Scim
         Result.new(nil, { detail: error.message })
       end
 
+      # No :logger here on purpose -- net-hippie hands it to
+      # Net::HTTP#set_debug_output, which dumps raw requests (credentials
+      # included) to the log.
       def self.default_driver
         @default_driver ||= Net::Hippie::Client.new(
           follow_redirects: 0,
           headers: headers,
-          logger: Scim::Kit.logger,
           open_timeout: 1,
           read_timeout: 5
         )
spec/scim/kit/http_spec.rb
@@ -5,6 +5,47 @@ RSpec.describe Scim::Kit::Http do
 
   let(:uri) { URI(FFaker::Internet.uri('https')) }
 
+  describe 'request logging' do
+    let(:log) { StringIO.new }
+    let(:server) { TCPServer.new('127.0.0.1', 0) }
+
+    around do |example|
+      WebMock.disable!
+      original = Scim::Kit.logger
+      Scim::Kit.logger = Logger.new(log)
+      described_class.instance_variable_set(:@default_driver, nil)
+      example.run
+      Scim::Kit.logger = original
+      described_class.instance_variable_set(:@default_driver, nil)
+      WebMock.enable!
+      server.close
+    end
+
+    def respond_once
+      Thread.new do
+        socket = server.accept
+        loop { break if socket.gets.to_s.strip.empty? }
+        socket.print("HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\n{}")
+        socket.close
+      end
+    end
+
+    def fetch_with_credentials
+      responder = respond_once
+      described_class.new(retries: 0).fetch(
+        URI("http://127.0.0.1:#{server.addr[1]}/Users"),
+        headers: { 'Authorization' => 'Bearer s3cret' }
+      )
+      responder.join
+    end
+
+    it 'keeps credentials out of the log' do
+      fetch_with_credentials
+
+      expect(log.string).not_to include('s3cret')
+    end
+  end
+
   describe '#fetch' do
     context 'when the response is successful' do
       let(:body) { { id: '123' } }