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_KEY =
'action_dispatch.route_uri_pattern'
- RAILS_ROUTES_KEY =
'action_dispatch.routes'
- RAILS_FORMAT_SUFFIX =
'(.:format)'
Class Method Summary collapse
-
.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`).
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)"
WARNING: This method works only after the request has been routed.
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/datadog/appsec/api_security/route_extractor.rb', line 39 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].delete_suffix(RAILS_FORMAT_SUFFIX) elsif request.env.key?(RAILS_ROUTES_KEY) 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 request.path end end |