VYPR
Moderate severityNVD Advisory· Published Jul 5, 2018· Updated Aug 5, 2024

CVE-2018-3769

CVE-2018-3769

Description

Grape gem for Ruby fails to escape user-controlled input in the 'format' parameter when returning HTML error messages, leading to XSS.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Grape gem for Ruby fails to escape user-controlled input in the 'format' parameter when returning HTML error messages, leading to XSS.

Vulnerability

CVE-2018-3769 is a cross-site scripting (XSS) vulnerability in the grape gem for Ruby [1]. The bug exists in the error handling middleware: when the server returns an HTML error response, the user-supplied format parameter is directly embedded in the error message without HTML escaping. This affects versions prior to the fix that introduced ERB::Util.html_escape for HTML content types [2]. The vulnerable code path is reachable by requesting an unsupported or invalid format (e.g., format=txt).

Exploitation

An attacker does not need authentication or any special access. The only requirement is that the vulnerable application uses the Grape framework and that an attacker can control the format query parameter or route extension. By sending a request with a malicious payload in the format parameter (e.g., format=), the server will reflect that payload in the HTML error response. The attacker would need to trick a user into visiting such a crafted URL; no user interaction beyond normal browsing is required for the reflected XSS to trigger in the victim's browser.

Impact

Successful exploitation allows an attacker to execute arbitrary JavaScript in the context of the victim's browser. This can lead to session hijacking, theft of sensitive cookies, phishing redirections, or defacement. The impact depends on the application's session and security context, but because the attack is Reflected XSS, it typically requires social engineering (e.g., sending a crafted link) to achieve full compromise.

Mitigation

The Grape project fixed this issue in commit 6876b71 [2]. The fix adds ActiveSupport::CoreExt::String::OutputSafety and uses ERB::Util.html_escape on the error message when the response content type is HTML. The advisory database indicates the fix was included in an unspecified release following the commit [4]. Users should upgrade to Grape version 1.0.3 or later, which contains the patch [3]. No known workarounds were provided, and the vulnerability is not listed in CISA's Known Exploited Vulnerabilities (KEV) catalog as of the last check.

AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
grapeRubyGems
< 1.1.01.1.0

Affected products

2
  • ghsa-coords
    Range: < 1.1.0
  • Ruby Grape/ruby-grape ruby gemv5
    Range: >= 1.0.3

Patches

1
6876b71efc7b

When returning an HTML error, make sure it's safe (#1763)

https://github.com/ruby-grape/grapeCaleb TennisMay 26, 2018via ghsa
4 files changed · +36 6
  • CHANGELOG.md+2 0 modified
    @@ -6,6 +6,8 @@
     
     #### Fixes
     
    +
    +* [#1762](https://github.com/ruby-grape/grape/pull/1763): Fix unsafe HTML rendering on errors - [@ctennis](https://github.com/ctennis).
     * [#1759](https://github.com/ruby-grape/grape/pull/1759): Update appraisal for rails_edge - [@zvkemp](https://github.com/zvkemp).
     * [#1758](https://github.com/ruby-grape/grape/pull/1758): Fix expanding load_path in gemspec - [@2maz](https://github.com/2maz).
     * Your contribution here.
    
  • lib/grape/middleware/error.rb+4 0 modified
    @@ -1,4 +1,5 @@
     require 'grape/middleware/base'
    +require 'active_support/core_ext/string/output_safety'
     
     module Grape
       module Middleware
    @@ -69,6 +70,9 @@ def error_response(error = {})
           end
     
           def rack_response(message, status = options[:default_status], headers = { Grape::Http::Headers::CONTENT_TYPE => content_type })
    +        if headers[Grape::Http::Headers::CONTENT_TYPE] == TEXT_HTML
    +          message = ERB::Util.html_escape(message)
    +        end
             Rack::Response.new([message], status, headers).finish
           end
     
    
  • spec/grape/api_spec.rb+26 2 modified
    @@ -2142,7 +2142,11 @@ def self.call(message, _backtrace, _option, _env, _original_exception)
           end
           get '/excel.json'
           expect(last_response.status).to eq(406)
    -      expect(last_response.body).to eq("The requested format 'txt' is not supported.")
    +      if ActiveSupport::VERSION::MAJOR == 3
    +        expect(last_response.body).to eq('The requested format &#x27;txt&#x27; is not supported.')
    +      else
    +        expect(last_response.body).to eq('The requested format &#39;txt&#39; is not supported.')
    +      end
         end
       end
     
    @@ -3524,7 +3528,27 @@ def before
           end
           get '/something'
           expect(last_response.status).to eq(406)
    -      expect(last_response.body).to eq("{\"error\":\"The requested format 'txt' is not supported.\"}")
    +      if ActiveSupport::VERSION::MAJOR == 3
    +        expect(last_response.body).to eq('{&quot;error&quot;:&quot;The requested format &#x27;txt&#x27; is not supported.&quot;}')
    +      else
    +        expect(last_response.body).to eq('{&quot;error&quot;:&quot;The requested format &#39;txt&#39; is not supported.&quot;}')
    +      end
    +    end
    +  end
    +
    +  context 'with unsafe HTML format specified' do
    +    it 'escapes the HTML' do
    +      subject.content_type :json, 'application/json'
    +      subject.get '/something' do
    +        'foo'
    +      end
    +      get '/something?format=<script>blah</script>'
    +      expect(last_response.status).to eq(406)
    +      if ActiveSupport::VERSION::MAJOR == 3
    +        expect(last_response.body).to eq('The requested format &#x27;&lt;script&gt;blah&lt;/script&gt;&#x27; is not supported.')
    +      else
    +        expect(last_response.body).to eq('The requested format &#39;&lt;script&gt;blah&lt;/script&gt;&#39; is not supported.')
    +      end
         end
       end
     
    
  • spec/grape/middleware/exception_spec.rb+4 4 modified
    @@ -192,7 +192,7 @@ def app
         end
         it 'is possible to return errors in jsonapi format' do
           get '/'
    -      expect(last_response.body).to eq('{"error":"rain!"}')
    +      expect(last_response.body).to eq('{&quot;error&quot;:&quot;rain!&quot;}')
         end
       end
     
    @@ -207,8 +207,8 @@ def app
     
         it 'is possible to return hash errors in jsonapi format' do
           get '/'
    -      expect(['{"error":"rain!","detail":"missing widget"}',
    -              '{"detail":"missing widget","error":"rain!"}']).to include(last_response.body)
    +      expect(['{&quot;error&quot;:&quot;rain!&quot;,&quot;detail&quot;:&quot;missing widget&quot;}',
    +              '{&quot;detail&quot;:&quot;missing widget&quot;,&quot;error&quot;:&quot;rain!&quot;}']).to include(last_response.body)
         end
       end
     
    @@ -258,7 +258,7 @@ def app
         end
         it 'is possible to specify a custom formatter' do
           get '/'
    -      expect(last_response.body).to eq('{:custom_formatter=>"rain!"}')
    +      expect(last_response.body).to eq('{:custom_formatter=&gt;&quot;rain!&quot;}')
         end
       end
     
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.