You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add Grape::Mountable marker to identify a Grape app (#2793)
* Add Grape::Mountable marker to identify a Grape app
Grape distinguished a mounted Grape app from a bare Rack app by duck-typing
on an incidental internal method (`respond_to?(:inheritable_setting)`).
That is brittle: Grape::API and Grape::API::Instance share no common
ancestor and don't even respond to the same methods (mount_instance vs
inheritable_setting/endpoints), so there was no single predicate and the
checks keyed on a settings method that has nothing to do with mountability.
Introduce a Grape::Mountable marker extended by both Grape::API and
Grape::API::Instance, giving one explicit `is_a?(Grape::Mountable)`
predicate. Route the identity checks through it: Endpoint#bare_rack_app?
(used for forward_match) and the mount branch in DSL::Routing.
Capability checks that go on to call a stage-specific method are left as
respond_to? on purpose (a Mountable does not respond to every such method):
`respond_to?(:mount_instance)` still gates the remountable-class stage,
`respond_to?(:endpoints)` still guards reading endpoints, and the settings
inheritance check still guards calling inheritable_setting.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Harden mounted-app auth surface and error HTML escaping
Two security-relevant fixes surfaced by a review of Grape::API:
- Grape::Middleware::Error#rack_response only escaped the error message
when the response content-type was exactly 'text/html', so a
parameterized value such as 'text/html; charset=utf-8' skipped escaping
and could reflect an unescaped message into an HTML response. Compare the
media type only.
- A bare Rack app mounted with `mount` is called directly and never goes
through the endpoint middleware stack, so an API's authentication
middleware does not wrap it and the mount is reachable unauthenticated.
Mounted Grape APIs are unaffected. Warn at compile time when a bare Rack
app is mounted under configured authentication so the bypass isn't silent.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Daniel (dB.) Doubrovkine <dblock@dblock.org>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+2Lines changed: 2 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -12,6 +12,7 @@
12
12
*[#2782](https://github.com/ruby-grape/grape/pull/2782): Remove the dead `format` keyword from `Grape::Router::Pattern` - [@ericproulx](https://github.com/ericproulx).
13
13
*[#2783](https://github.com/ruby-grape/grape/pull/2783): Read a route's `version`, `anchor` and `requirements` from its pattern instead of storing them again on the route - [@ericproulx](https://github.com/ericproulx).
14
14
*[#2784](https://github.com/ruby-grape/grape/pull/2784): Move HEAD route creation into `Grape::Router::Route#to_head` - [@ericproulx](https://github.com/ericproulx).
15
+
*[#2793](https://github.com/ruby-grape/grape/pull/2793): Add a `Grape::Mountable` marker to identify a Grape app instead of duck-typing on `respond_to?(:inheritable_setting)` - [@ericproulx](https://github.com/ericproulx).
15
16
*[#2792](https://github.com/ruby-grape/grape/pull/2792): Move `Grape::Path` to `Grape::Router::Pattern::Path` and add a `Grape::Router::Pattern.build` factory (`Grape::Path` kept as a deprecated constant) - [@ericproulx](https://github.com/ericproulx).
16
17
*[#2785](https://github.com/ruby-grape/grape/pull/2785): Make route `params` a first-class endpoint input and split `Grape::Router::Route#params` into `#params` (declared definitions) and `#params_for(input)` (extracted values) - [@ericproulx](https://github.com/ericproulx).
17
18
*[#2786](https://github.com/ruby-grape/grape/pull/2786): Make route `requirements` and `anchor` explicit keyword arguments and first-class endpoint inputs instead of opaque `route_options` keys - [@ericproulx](https://github.com/ericproulx).
@@ -20,6 +21,7 @@
20
21
#### Fixes
21
22
22
23
*[#2767](https://github.com/ruby-grape/grape/pull/2767): Update rubocop to 1.88.0 and rubocop-rspec to 3.10.2 - [@ericproulx](https://github.com/ericproulx).
24
+
*[#2789](https://github.com/ruby-grape/grape/pull/2789): Escape error messages served with a parameterized `text/html` content-type (e.g. `text/html; charset=utf-8`) and warn when a bare Rack app is mounted under authentication - [@ericproulx](https://github.com/ericproulx).
23
25
*[#2790](https://github.com/ruby-grape/grape/pull/2790): Stop mutating `Grape::Router`'s internal maps at request time for HTTP methods with no routes (data race on JRuby/TruffleRuby and unbounded growth) - [@ericproulx](https://github.com/ericproulx).
24
26
*[#2791](https://github.com/ruby-grape/grape/pull/2791): Build HEAD/OPTIONS/405 helper routes from a copy of the inheritable settings instead of temporarily mutating the shared class-level settings during compilation - [@ericproulx](https://github.com/ericproulx).
0 commit comments