|
3 | 3 |
|
4 | 4 | require "action_dispatch/middleware/host_authorization" |
5 | 5 | require_relative "../enums/event" |
| 6 | +require_relative "../log/security/blocked_host" |
6 | 7 |
|
7 | 8 | module LogStruct |
8 | 9 | module Integrations |
@@ -34,56 +35,46 @@ def self.setup(config) |
34 | 35 | return nil unless config.enabled |
35 | 36 | return nil unless config.integrations.enable_host_authorization |
36 | 37 |
|
37 | | - # In test environment, ensure HostAuthorization does not block requests |
38 | | - # from the default integration test hosts. Allow all hosts explicitly. |
39 | | - if ::Rails.env.test? && ::Rails.application.config.respond_to?(:hosts) |
40 | | - begin |
41 | | - ::Rails.application.config.hosts << /.*\z/ |
42 | | - rescue |
43 | | - # best-effort; ignore if hosts not configurable |
44 | | - end |
45 | | - # Additionally, exclude all requests from HostAuthorization in test |
46 | | - begin |
47 | | - ::Rails.application.config.host_authorization ||= {} |
48 | | - ::Rails.application.config.host_authorization[:exclude] = ->(_request) { true } |
49 | | - rescue |
50 | | - # best-effort |
51 | | - end |
52 | | - end |
53 | | - |
54 | 38 | # Define the response app as a separate variable to fix block alignment |
55 | 39 | response_app = lambda do |env| |
56 | 40 | request = ::ActionDispatch::Request.new(env) |
57 | 41 | # Include the blocked hosts app configuration in the log entry |
58 | 42 | # This can be helpful later when reviewing logs. |
59 | 43 | blocked_hosts = env["action_dispatch.blocked_hosts"] |
60 | 44 |
|
61 | | - # Create a security error to be handled |
62 | | - blocked_host_error = ::ActionController::BadRequest.new( |
63 | | - "Blocked host detected: #{request.host}" |
64 | | - ) |
| 45 | + # Build allowed_hosts array |
| 46 | + allowed_hosts_array = T.let(nil, T.nilable(T::Array[String])) |
| 47 | + if blocked_hosts.respond_to?(:allowed_hosts) |
| 48 | + allowed_hosts_array = blocked_hosts.allowed_hosts |
| 49 | + end |
| 50 | + |
| 51 | + # Get allow_ip_hosts value |
| 52 | + allow_ip_hosts_value = T.let(nil, T.nilable(T::Boolean)) |
| 53 | + if blocked_hosts.respond_to?(:allow_ip_hosts) |
| 54 | + allow_ip_hosts_value = blocked_hosts.allow_ip_hosts |
| 55 | + end |
65 | 56 |
|
66 | | - # Create request context hash |
67 | | - context = { |
| 57 | + # Create structured log entry for blocked host |
| 58 | + log_entry = LogStruct::Log::Security::BlockedHost.new( |
| 59 | + message: "Blocked host detected: #{request.host}", |
68 | 60 | blocked_host: request.host, |
69 | | - client_ip: request.ip, |
70 | | - x_forwarded_for: request.x_forwarded_for, |
71 | | - http_method: request.method, |
72 | 61 | path: request.path, |
| 62 | + http_method: request.method, |
| 63 | + source_ip: request.ip, |
73 | 64 | user_agent: request.user_agent, |
74 | | - allowed_hosts: blocked_hosts.allowed_hosts, |
75 | | - allow_ip_hosts: blocked_hosts.allow_ip_hosts |
76 | | - } |
77 | | - |
78 | | - # Handle error according to configured mode (log, report, raise) |
79 | | - LogStruct.handle_exception( |
80 | | - blocked_host_error, |
81 | | - source: Source::Security, |
82 | | - context: context |
| 65 | + referer: request.referer, |
| 66 | + request_id: request.request_id, |
| 67 | + x_forwarded_for: request.x_forwarded_for, |
| 68 | + allowed_hosts: allowed_hosts_array&.empty? ? nil : allowed_hosts_array, |
| 69 | + allow_ip_hosts: allow_ip_hosts_value |
83 | 70 | ) |
84 | 71 |
|
| 72 | + # Log the blocked host |
| 73 | + LogStruct.warn(log_entry) |
| 74 | + |
85 | 75 | # Use pre-defined headers and response if we are only logging or reporting |
86 | | - [FORBIDDEN_STATUS, RESPONSE_HEADERS, [RESPONSE_HTML]] |
| 76 | + # Dup the headers so they can be modified by downstream middleware |
| 77 | + [FORBIDDEN_STATUS, RESPONSE_HEADERS.dup, [RESPONSE_HTML]] |
87 | 78 | end |
88 | 79 |
|
89 | 80 | # Merge our response_app into existing host_authorization config to preserve excludes |
|
0 commit comments