Module: Datadog::AppSec::APISecurity::RouteExtractor
- Defined in:
- lib/datadog/appsec/api_security/route_extractor.rb
Overview
This is a helper module to extract the route pattern from the Rack::Request.
Constant Summary collapse
- SINATRA_ROUTE_KEY =
'sinatra.route'- SINATRA_ROUTE_SEPARATOR =
' '- GRAPE_ROUTE_KEY =
'grape.routing_args'- RAILS_ROUTE_URI_PATTERN_KEY =
'action_dispatch.route_uri_pattern'- RAILS_ROUTE_KEY =
Rails 8.1.1+
'action_dispatch.route'- RAILS_ROUTES_KEY =
'action_dispatch.routes'- RAILS_PATH_PARAMS_KEY =
'action_dispatch.request.path_parameters'- RAILS_FORMAT_SUFFIX =
'(.:format)'
Class Method Summary collapse
-
.route_pattern(request) ⇒ Object
HACK: We rely on the fact that each contrib will modify
request.envand store information sufficient to compute the canonical route (ex:/users/:id).
Class Method Details
.route_pattern(request) ⇒ Object
HACK: We rely on the fact that each contrib will modify request.env
and store information sufficient to compute the canonical
route (ex: /users/:id).
When contribs like Sinatra or Grape are used, they could be mounted
into the Rails app, hence you can see the use of the `script_name`
that will contain the path prefix of the mounted app.
Rack
does not support named arguments, so we have to use `path`
Sinatra
uses `sinatra.route` with a string like "GET /users/:id"
Grape
uses `grape.routing_args` with a hash with a `:route_info` key
that contains a `Grape::Router::Route` object that contains
`Grape::Router::Pattern` object with an `origin` method
Rails < 7.1 (slow path)
uses `action_dispatch.routes` to store `ActionDispatch::Routing::RouteSet`
which can recognize requests
Rails > 7.1 (fast path)
uses `action_dispatch.route_uri_pattern` with a string like
"/users/:id(.:format)"
Rails > 8.1.1 (fast path)
uses `action_dispatch.route` to store the ActionDispatch::Journey::Route
that matched when the request was routed
WARNING: This method works only after the request has been routed.
WARNING: In Rails > 7.1 when a route was not found,
action_dispatch.route_uri_pattern will not be set.
In Rails < 7.1 it also will not be set even if a route was found,
but in this case action_dispatch.request.path_parameters won't be empty.
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
# File 'lib/datadog/appsec/api_security/route_extractor.rb', line 51 def self.route_pattern(request) if request.env.key?(GRAPE_ROUTE_KEY) pattern = request.env[GRAPE_ROUTE_KEY][:route_info]&.pattern&.origin "#{request.script_name}#{pattern}" elsif request.env.key?(SINATRA_ROUTE_KEY) pattern = request.env[SINATRA_ROUTE_KEY].split(SINATRA_ROUTE_SEPARATOR, 2)[1] "#{request.script_name}#{pattern}" elsif request.env.key?(RAILS_ROUTE_KEY) request.env[RAILS_ROUTE_KEY].path.spec.to_s.delete_suffix(RAILS_FORMAT_SUFFIX) elsif request.env.key?(RAILS_ROUTE_URI_PATTERN_KEY) request.env[RAILS_ROUTE_URI_PATTERN_KEY].delete_suffix(RAILS_FORMAT_SUFFIX) elsif request.env.key?(RAILS_ROUTES_KEY) && !request.env.fetch(RAILS_PATH_PARAMS_KEY, {}).empty? # NOTE: In Rails < 7.1 this `request` argument will be a Rack::Request, # it does not have all the methods that ActionDispatch::Request has. # Before trying to use the router to recognize the route, we need to # create a new ActionDispatch::Request from the request env # # NOTE: Rails mutates HEAD request by changing the method to GET # and uses it for route recognition to check if the route is defined request = request.env[RAILS_ROUTES_KEY].request_class.new(request.env) pattern = request.env[RAILS_ROUTES_KEY].router .recognize(request) { |route, _| break route.path.spec.to_s } # NOTE: If rails is unable to recognize request it returns empty Array pattern = nil if pattern&.empty? # NOTE: If rails can't recognize the request, we are going to fallback # to generic request path (pattern || request.path).delete_suffix(RAILS_FORMAT_SUFFIX) else Tracing::Contrib::Rack::RouteInference.read_or_infer(request.env) end rescue => e AppSec.telemetry&.report(e, description: 'AppSec: Could not extract route pattern') nil end |