mirror of
https://github.com/chatwoot/chatwoot.git
synced 2026-06-04 21:02:35 +08:00
This hardens the development/test Swagger docs endpoint by ensuring requested files are resolved only within the `swagger/` directory. This did not affect production security because the Swagger controller only renders files in development or test environments; production already returns `404`. The change still closes the scanner finding and prevents future automated reports from flagging the development-only path. ## Closes Addresses: GHSA-xhp7-ggjq-p2rg ## How to reproduce 1. Start Chatwoot locally in development. 2. Visit `/swagger/%2Fetc%2Fpasswd`. 3. Before this change, the endpoint could render files outside the Swagger directory in development/test. ## What changed - Resolve Swagger file requests relative to `Rails.root/swagger`. - Return `404` when the resolved path is outside the Swagger directory or does not point to a file. - Strip leading slashes from derived request paths. - Add a request spec for the encoded absolute-path case. ## How to test 1. Start the app locally. 2. Visit `/swagger` and confirm the ReDoc page loads. 3. Visit `/swagger/swagger.json` and confirm the Swagger JSON loads. 4. Visit `/swagger/%2Fetc%2Fpasswd` and confirm it returns `404` with no file contents. Note: `bundle exec rspec spec/controllers/swagger_controller_spec.rb` was passing locally earlier during this fix. A final rerun before opening the PR was blocked because local Postgres on `localhost:5432` was not accepting connections. Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
24 lines
689 B
Ruby
24 lines
689 B
Ruby
class SwaggerController < ApplicationController
|
|
def respond
|
|
if Rails.env.development? || Rails.env.test?
|
|
swagger_root = Rails.root.join('swagger')
|
|
file_path = swagger_root.join(derived_path).cleanpath
|
|
|
|
return head :not_found unless file_path.to_s.start_with?("#{swagger_root}/") && file_path.file?
|
|
|
|
render inline: file_path.read
|
|
else
|
|
head :not_found
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def derived_path
|
|
params[:path] ||= 'index.html'
|
|
path = Rack::Utils.clean_path_info(params[:path]).delete_prefix('/')
|
|
path << ".#{Rack::Utils.clean_path_info(params[:format]).delete_prefix('/')}" unless path.ends_with?(params[:format].to_s)
|
|
path
|
|
end
|
|
end
|