CVE-2023-49277: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') (CWE-79)
value: dpaste is an open source pastebin application written in Python using the Django framework. A security vulnerability has been identified in the expires parameter of the dpaste API, allowing for a POST Reflected XSS attack. This vulnerability can be exploited by an attacker to execute arbitrary JavaScript code in the context of a user's browser, potentially leading to unauthorized access, data theft, or other malicious activities. Users are strongly advised to upgrade to dpaste release v3.8 or later versions, as dpaste versions older than v3.8 are susceptible to the identified security vulnerability. No known workarounds have been identified, and applying the patch is the most effective way to remediate the 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-12-01T21:15Z
Repository: https://github.com/DarrenOfficial/dpaste
Language: Python
Files changed: dpaste/views.py
Commit date: 2023-11-18 11:32:12 -0500
Commit message: fix: correct escaping of api responses

The API did not respond as intended. :D
Co-authored-by: Brian Ferri brian.ferri19@gmail.com>

Diff: diff --git a/dpaste/views.py b/dpaste/views.py
index 2e5fdff..123c12d 100644
--- a/dpaste/views.py
+++ b/dpaste/views.py
@@ -11,10 +11,11 @@ from django.http import (
     HttpResponseRedirect,
 )
 from django.shortcuts import get_object_or_404, render
 from django.urls import reverse
 from django.utils import timezone
+from django.utils.html import escape
 from django.utils.cache import add_never_cache_headers, patch_cache_control
 from django.utils.translation import gettext
 from django.views.generic import FormView
 from django.views.generic.base import TemplateView, View
 from django.views.generic.detail import DetailView
@@ -288,12 +289,11 @@ class APIView(View):
             )
 
         # A lexer is given, check if its valid at all
         if lexer and lexer not in highlight.LEXER_KEYS:
             return HttpResponseBadRequest(
-                'Invalid lexer "%s" given. Valid lexers are: %s'
-                % (lexer, ", ".join(highlight.LEXER_KEYS))
+                f'Invalid lexer choice "{escape(lexer)}" given. Valid lexer values are: {", ".join(highlight.LEXER_KEYS)}'
             )
 
         # No lexer is given, but we have a filename, try to get the lexer
         #  out of it. In case Pygments cannot determine the lexer of the
         # filename, we fallback to 'plain' code.
@@ -306,13 +306,11 @@ class APIView(View):
 
         if expires:
             expire_options = [str(i) for i in dict(config.EXPIRE_CHOICES)]
             if expires not in expire_options:
                 return HttpResponseBadRequest(
-                    'Invalid expire choice "{}" given. Valid values are: {}'.format(
-                        expires, ", ".join(expire_options)
-                    )
+                    f'Invalid expire choice "{escape(expires)}" given. Valid expire values are: {", ".join(expire_options)}'
                 )
             expires, expire_type = get_expire_values(expires)
         else:
             expires = datetime.datetime.now() + datetime.timedelta(seconds=60 * 60 * 24)
             expire_type = Snippet.EXPIRE_TIME
@@ -350,6 +348,6 @@ def handler404(request, exception=None, template_name="dpaste/404.html"):
 def handler500(request, template_name="dpaste/500.html"):
     context = {}
     context.update(config.extra_template_context)
     response = render(request, template_name, context, status=500)
     add_never_cache_headers(response)
-    return response
+    return response
\ No newline at end of file

Vulnerable code:                 'Invalid lexer "%s" given. Valid lexers are: %s'
                % (lexer, ", ".join(highlight.LEXER_KEYS))
                    'Invalid expire choice "{}" given. Valid values are: {}'.format(
                        expires, ", ".join(expire_options)
                    )
    return response
Fixed code: from django.utils.html import escape
                f'Invalid lexer choice "{escape(lexer)}" given. Valid lexer values are: {", ".join(highlight.LEXER_KEYS)}'
                    f'Invalid expire choice "{escape(expires)}" given. Valid expire values are: {", ".join(expire_options)}'
    return response