Browse Source

Fixes #5317 - Zammad not handling X-Forward-For that is a list

Martin Gruner 6 months ago
parent
commit
b7289d5df8
2 changed files with 58 additions and 1 deletions
  1. 12 1
      lib/sessions/event/base.rb
  2. 46 0
      spec/lib/sessions/event/base_spec.rb

+ 12 - 1
lib/sessions/event/base.rb

@@ -121,7 +121,18 @@ class Sessions::Event::Base
   end
 
   def remote_ip
-    @headers&.fetch('X-Forwarded-For', nil).presence
+    # Basic implementation of the algorithm recommended by
+    #   https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For#selecting_an_ip_address
+    #   because we cannot use Rails' request.remote_ip here (as it runs from websocket server without rack).
+    raw_header = @headers&.dig('X-Forwarded-For').presence
+
+    return if !raw_header
+
+    raw_header
+      .split(',')
+      .map(&:strip)
+      .difference(Rails.application.config.action_dispatch.trusted_proxies)
+      .last
   end
 
   def origin

+ 46 - 0
spec/lib/sessions/event/base_spec.rb

@@ -0,0 +1,46 @@
+# Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
+
+require 'rails_helper'
+
+RSpec.describe Sessions::Event::Base do
+
+  describe '#remote_ip' do
+    let(:instance) { described_class.new(headers:, clients: {}) }
+
+    context 'without X-Forwarded-For' do
+      let(:headers) { {} }
+
+      it 'returns no value' do
+        expect(instance.remote_ip).to be_nil
+      end
+    end
+
+    context 'with X-Forwarded-For' do
+      before do
+        allow(Rails.application.config.action_dispatch).to receive(:trusted_proxies).and_return(trusted_proxies)
+      end
+
+      let(:trusted_proxies) { ['127.0.0.1', '::1'] }
+
+      context 'with external IP' do
+
+        let(:headers) { { 'X-Forwarded-For' => '1.2.3.4 , 5.6.7.8, 127.0.0.1 , ::1' } }
+
+        it 'returns the correct value' do
+          expect(instance.remote_ip).to eq('5.6.7.8')
+        end
+      end
+
+      context 'without external IP' do
+
+        let(:headers) { { 'X-Forwarded-For' => ' 127.0.0.1 , ::1' } }
+
+        it 'returns no value' do
+          expect(instance.remote_ip).to be_nil
+        end
+      end
+
+    end
+
+  end
+end