# frozen_string_literal: true require_relative '../../../instrumentation/gateway/argument' require_relative '../../../../tracing/client_ip' require_relative '../../../../tracing/contrib/rack/header_collection' module Datadog module AppSec module Contrib module Rack module Gateway # Gateway Request argument. Normalized extration of data from Rack::Request class Request < Instrumentation::Gateway::Argument attr_reader :env def initialize(env) super @env = env end def request @request ||= ::Rack::Request.new(env) end def query # Downstream libddwaf expects keys and values to be extractable # separately so we can't use [[k, v], ...]. We also want to allow # duplicate keys, so we use [{k, v}, ...] instead. request.query_string.split('&').map do |e| k, v = e.split('=').map { |s| CGI.unescape(s) } { k => v } end end # Rack < 2.0 does not have :each_header # TODO: We need access to Rack here. We must make sure we are able to load AppSec without Rack, # TODO: while still ensure correctness in ths code path. if defined?(::Rack) && ::Rack::Request.instance_methods.include?(:each_header) def headers request.each_header.each_with_object({}) do |(k, v), h| h[k.gsub(/^HTTP_/, '').downcase.tr('_', '-')] = v if k =~ /^HTTP_/ end end else def headers request.env.each_with_object({}) do |(k, v), h| h[k.gsub(/^HTTP_/, '').downcase.tr('_', '-')] = v if k =~ /^HTTP_/ end end end def body request.body.read.tap { request.body.rewind } end def url request.url end def cookies request.cookies end def host request.host end def user_agent request.user_agent end def remote_addr env['REMOTE_ADDR'] end def form_hash # force form data processing request.POST if request.form_data? # usually Hash but can be a more complex # Hash when e.g coming from JSON env['rack.request.form_hash'] end def client_ip remote_ip = remote_addr headers = Datadog::Tracing::Contrib::Rack::Header::RequestHeaderCollection.new(env) result = Datadog::Tracing::ClientIp.raw_ip_from_request(headers, remote_ip) if result.raw_ip ip = Datadog::Tracing::ClientIp.strip_decorations(result.raw_ip) return unless Datadog::Tracing::ClientIp.valid_ip?(ip) ip end end end end end end end end