Class: Datadog::AppSec::Contrib::Rails::Gateway::Request

Inherits:
Instrumentation::Gateway::Argument show all
Defined in:
lib/datadog/appsec/contrib/rails/gateway/request.rb

Overview

Gateway Request argument. Normalized extration of data from ActionDispatch::Request

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(request) ⇒ Request

Returns a new instance of Request.



15
16
17
18
# File 'lib/datadog/appsec/contrib/rails/gateway/request.rb', line 15

def initialize(request)
  super()
  @request = request
end

Instance Attribute Details

#request ⇒ Object (readonly)

Returns the value of attribute request.



13
14
15
# File 'lib/datadog/appsec/contrib/rails/gateway/request.rb', line 13

def request
  @request
end

Instance Method Details

#body_bytesize(limit) ⇒ Object

Returns the request body size in bytes using all available methods, or nil when the size cannot be measured within the limit

NOTE: The priority of the measurement is the following: raw posted data, raw form vars, size if known, raw Content-Length, then buffering to the limit if unknown-length



76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/datadog/appsec/contrib/rails/gateway/request.rb', line 76

def body_bytesize(limit)
  raw_body = env["RAW_POST_DATA"]
  return raw_body.bytesize if raw_body

  form_vars = env["rack.request.form_vars"]
  return form_vars.bytesize if form_vars

  io = request.body
  return 0 unless io
  return io.size if io.respond_to?(:size)

  # NOTE: Read raw `CONTENT_LENGTH` as {ActionDispatch::Request#content_length}
  #       drains `rack.input` into `RAW_POST_DATA` on chunked Transfer-Encoding
  content_length = env["CONTENT_LENGTH"]
  return content_length.to_i if content_length

  # NOTE: An already-read body (e.g. late-parsed multipart on Rack 3+) peeks
  #       as 0, so we skip byte_length but still collect the parsed body
  begin
    Rack::InputPeeker.peek_bytesize(env, limit: limit)
  rescue => e
    Datadog.logger.debug { "AppSec: Failed to measure Rails request body: #{e.class}: #{e.message}" }
    nil
  end
end

#env ⇒ Object



20
21
22
# File 'lib/datadog/appsec/contrib/rails/gateway/request.rb', line 20

def env
  request.env
end

#headers ⇒ Object



24
25
26
# File 'lib/datadog/appsec/contrib/rails/gateway/request.rb', line 24

def headers
  request.headers
end

#host ⇒ Object



28
29
30
# File 'lib/datadog/appsec/contrib/rails/gateway/request.rb', line 28

def host
  request.host
end

#parsed_body ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/datadog/appsec/contrib/rails/gateway/request.rb', line 40

def parsed_body
  # force body parameter parsing, which is done lazily by Rails
  request.parameters

  # usually Hash<String,String> but can be a more complex
  # Hash<String,String||Array||Hash> when e.g coming from JSON or
  # with Rails advanced param square bracket parsing
  body = request.env["action_dispatch.request.request_parameters"]

  return if body.nil?
  return body unless request.env["action_dispatch.request.path_parameters"]

  body.reject do |k, _v|
    request.env["action_dispatch.request.path_parameters"].key?(k)
  end
rescue => e
  Datadog.logger.debug { "AppSec: Failed to parse request body: #{e.class}: #{e.message}" }
  AppSec.telemetry.report(e, description: "AppSec: Failed to parse request body")

  nil
end

#remote_addr ⇒ Object



36
37
38
# File 'lib/datadog/appsec/contrib/rails/gateway/request.rb', line 36

def remote_addr
  request.remote_addr
end

#route_params ⇒ Object



62
63
64
65
66
67
68
# File 'lib/datadog/appsec/contrib/rails/gateway/request.rb', line 62

def route_params
  excluded = [:controller, :action]

  request.env.fetch("action_dispatch.request.path_parameters", {}).reject do |k, _v|
    excluded.include?(k)
  end
end

#user_agent ⇒ Object



32
33
34
# File 'lib/datadog/appsec/contrib/rails/gateway/request.rb', line 32

def user_agent
  request.user_agent
end