fix: rate limit widget conversation transcript API (#15085)
## Description The widget conversation transcript endpoint (`POST /api/v1/widget/conversations/transcript`) has no rate limit. Every other comparable endpoint does: the agent-facing transcript API and the widget conversation-create and contact-update endpoints are all throttled. This gap lets a single client trigger a large burst of transcript emails from one conversation. This adds an IP-based throttle (5 requests/hour) for the endpoint, placed inside the existing widget-API throttle block so it inherits the `ENABLE_RACK_ATTACK_WIDGET_API` opt-out used by embedded/iframe clients. The limit is generous for legitimate use (a visitor emailing themselves a transcript) while stopping abusive loops. Throttled requests get the standard 429 the widget already handles. ## Type of change - [x] Bug fix (non-breaking change which fixes an issue) ## How Has This Been Tested? `config/initializers/rack_attack.rb` throttles have no existing specs in this file, so this follows the established convention (no new spec). Verified `ruby -c` and `rubocop` pass on the file. The new throttle mirrors the sibling widget throttles directly above it (same IP key, path guard, and structure). ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] My changes generate no new warnings --------- Co-authored-by: Sojan Jose <sojan@pepalo.com>
This commit is contained in:
co-authored by
Sojan Jose
parent
eae9841eb4
commit
71fffdd2b9
@@ -31,10 +31,11 @@ class Rack::Attack
|
||||
(default_allowed_ips + env_allowed_ips).include?(remote_ip)
|
||||
end
|
||||
|
||||
# Rails would allow requests to paths with extensions, so lets compare against the path with extension stripped
|
||||
# example /auth & /auth.json would both work
|
||||
# Rails allows paths with extensions and trailing slashes, so compare against a normalized path.
|
||||
# For example, /auth, /auth.json, and /auth/ should all use the same throttle.
|
||||
def path_without_extensions
|
||||
path[/^[^.]+/]
|
||||
normalized_path = path[/^[^.]+/]
|
||||
normalized_path == '/' ? normalized_path : normalized_path.sub(%r{/+\z}, '')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -188,6 +189,11 @@ class Rack::Attack
|
||||
throttle('widget?website_token={website_token}&cw_conversation={x-auth-token}', limit: 5, period: 1.hour) do |req|
|
||||
req.ip if req.path_without_extensions == '/widget' && ActionDispatch::Request.new(req.env).params['cw_conversation'].blank?
|
||||
end
|
||||
|
||||
## Prevent Transcript Bombing on Widget API ###
|
||||
throttle('api/v1/widget/conversations/transcript', limit: 5, period: 1.hour) do |req|
|
||||
req.ip if req.path_without_extensions == '/api/v1/widget/conversations/transcript' && req.post?
|
||||
end
|
||||
end
|
||||
|
||||
##-----------------------------------------------##
|
||||
|
||||
Reference in New Issue
Block a user