CVE-2023-30614: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: Pay is a payments engine for Ruby on Rails 6.0 and higher. In versions prior to 6.3.2 a payments info page of Pay is susceptible to reflected Cross-site scripting. An attacker could create a working URL that renders a javascript link to a user on a Rails application that integrates Pay. This URL could be distributed via email to specifically target certain individuals. If the targeted application contains a functionality to submit user-generated content (such as comments) the attacker could even distribute the URL using that functionality. This has been patched in version 6.3.2 and above. Users are advised to upgrade. There are no known workarounds for this vulnerability.
CWE description: The product does not neutralize or incorrectly neutralizes user-controllable input before it is placed in output that is used as a web page that is served to other users.
CVSS v3 base score: 6.1
Published: 2023-04-19T18:15Z
Repository: https://github.com/pay-rails/pay
Language: Ruby
Files changed: CHANGELOG.md, app/controllers/pay/payments_controller.rb, app/views/pay/payments/show.html.erb
Commit date: 2023-04-19 11:39:59 -0500
Commit message: Fix XSS vulnerability on Stripe payment page

Diff: diff --git a/CHANGELOG.md b/CHANGELOG.md
index d79029e..2171878 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,12 @@
 ℹ️ For upgrade instructions, read the [UPGRADE guide](./UPGRADE.md)
 
 ### Unreleased
 
+* [SECURITY] Fix XSS vulnerability in back parameter on Stripe payment page
+  Previously, an attacker could inject Javascript or redirect the user to any URL by changing the `back` parameter in the URL.
+  The `back` parameter is now sanitized and restricted to relative paths.
 * Remove unused attributes for `plan` and `quantity` in `app/models/pay/customer.rb`.
 * Add explicit requires for `active_support` and `action_mailer` in `lib/pay.rb`. This should provide better errors for anyone not requiring all of Rails.
 
 ### 6.3.1
 
diff --git a/app/controllers/pay/payments_controller.rb b/app/controllers/pay/payments_controller.rb
index 8b1907c..6d1f77d 100644
--- a/app/controllers/pay/payments_controller.rb
+++ b/app/controllers/pay/payments_controller.rb
@@ -1,10 +1,19 @@
 module Pay
   class PaymentsController < ApplicationController
     layout "pay/application"
 
+    before_action :set_redirect_to
+
     def show
-      @redirect_to = params[:back].presence || root_path
       @payment = Payment.from_id(params[:id])
     end
+
+    private
+
+    # Ensure the back parameter is a valid path
+    # This safely handles XSS or external redirects
+    def set_redirect_to
+      @redirect_to = URI.parse(params[:back].to_s).path || root_path
+    end
   end
 end
diff --git a/app/views/pay/payments/show.html.erb b/app/views/pay/payments/show.html.erb
index e4dea41..b456603 100644
--- a/app/views/pay/payments/show.html.erb
+++ b/app/views/pay/payments/show.html.erb
@@ -52,11 +52,11 @@
             <%=t "pay.requires_action.button", amount: @payment.amount_with_currency %>
           </button>
         </div>
       <% end %>
 
-      <%= link_to t("pay.back"), @redirect_to, class: "inline-block w-full px-4 py-3 bg-gray-100 hover:bg-gray-200 text-center text-gray-600 rounded-lg" %>
+      <%= sanitize link_to(t("pay.back"), @redirect_to, class: "inline-block w-full px-4 py-3 bg-gray-100 hover:bg-gray-200 text-center text-gray-600 rounded-lg") %>
     </div>
 
     <p class="text-center text-gray-500 text-sm">
       © <%= Date.current.year %> <%= Pay.business_name %> <%=t "pay.all_rights_reserved" %>
     </p>

Vulnerable code:       @redirect_to = params[:back].presence || root_path
      <%= link_to t("pay.back"), @redirect_to, class: "inline-block w-full px-4 py-3 bg-gray-100 hover:bg-gray-200 text-center text-gray-600 rounded-lg" %>
Fixed code: * [SECURITY] Fix XSS vulnerability in back parameter on Stripe payment page
  Previously, an attacker could inject Javascript or redirect the user to any URL by changing the `back` parameter in the URL.
  The `back` parameter is now sanitized and restricted to relative paths.
    before_action :set_redirect_to


    private

    # Ensure the back parameter is a valid path
    # This safely handles XSS or external redirects
    def set_redirect_to
      @redirect_to = URI.parse(params[:back].to_s).path || root_path
    end
      <%= sanitize link_to(t("pay.back"), @redirect_to, class: "inline-block w-full px-4 py-3 bg-gray-100 hover:bg-gray-200 text-center text-gray-600 rounded-lg") %>