CVE-2022-4730 (Python, published 2022-12-27T15:15Z)
Description: value: A vulnerability was found in Graphite Web. It has been classified as problematic. Affected is an unknown function of the component Absolute Time Range Handler. The manipulation leads to cross site scripting. It is possible to launch the attack remotely. The exploit has been disclosed to the public and may be used. The name of the patch is 2f178f490e10efc03cd1d27c72f64ecab224eb23. It is recommended to apply a patch to fix this issue. The identifier of this vulnerability is VDB-216744.
Severity: nan (CVSS v3 base score: 5.4)
Weakness: CWE-79 \u2014 Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
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.
Repository: https://github.com/graphite-project/graphite-web
Files changed:
webapp/content/js/dashboard.js
webapp/graphite/dashboard/views.py
webapp/graphite/errors.py
webapp/graphite/util.py
webapp/tests/base.py
webapp/tests/test_dashboard.py
Commit message: Fix XSS in some dashboards queries (#2785)

* prevent xss on time range text fields

* prevent xss on dashbord queries

Vulnerable code:
    regex: /\d+/,
    regex: /\d+/,
    TimeRange.relativeStartQuantity = quantityField.getValue();
    TimeRange.relativeStartUnits = unitField.getValue();
    TimeRange.relativeUntilQuantity = untilQuantityField.getValue();
    TimeRange.relativeUntilUnits = untilUnitField.getValue();
# Replace special characters "&", "<" and ">" to HTML-safe sequences.
def escape(s):
    s = s.replace("&", "&amp;")  # Must be done first!
    s = s.replace("<", "&lt;")
    s = s.replace(">", "&gt;")
            return HttpResponseBadRequest(escape(msgStr))
        xss = response.content.find(b"<") != -1 or response.content.find(b">") != -1
        self.assertFalse(xss, msg=msg_prefix+str(response.content))
        # Save again after it now exists

Fixed code:
function htmlStriped(input) {
  return htmlEncode(input).replace(/\s/g, '_')
}

    regex: /^\d+$/,
    regex: /^\d+$/,
    TimeRange.relativeStartQuantity = htmlStriped(quantityField.getValue());
    TimeRange.relativeStartUnits = htmlStriped(unitField.getValue());
    TimeRange.relativeUntilQuantity = htmlStriped(untilQuantityField.getValue());
    TimeRange.relativeUntilUnits = htmlStriped(untilUnitField.getValue());
from graphite.errors import handleInputParameterError, str_param
@handleInputParameterError
  name = str_param('name', name)
@handleInputParameterError
  name = str_param('name', name)
@handleInputParameterError
  name = str_param('name', name)

@handleInputParameterError
  name = str_param('name', name)
  key = str_param('key', key)

@handleInputParameterError
  name = str_param('name', name)
@handleInputParameterError
  name = str_param('name', name)
@handleInputParameterError
  name = str_param('name', name)
@handleInputParameterError
  name = str_param('name', name)
from graphite.util import htmlEscape, is_unsafe_str
def safe_param(name, s):
    if is_unsafe_str(s):
        raise InputParameterError("{} contain unsafe symbols".format(name))
    return s


def is_unclean_str(s):
    for symbol in '&<>~!@#$%^*()`':
        if s.find(symbol) >= 0:
            return True
    return False

def str_param(name, s):
    if s is not None and is_unclean_str(s):
        raise InputParameterError("{} contain restricted symbols".format(name))
            return HttpResponseBadRequest(htmlEscape(msgStr))
# Replace special characters "&", "<" and ">" to HTML-safe sequences.
def htmlEscape(s):
    s = s.replace("&", "&amp;")  # Must be done first!
    s = s.replace("<", "&lt;")
    s = s.replace(">", "&gt;")
    return s


def is_unsafe_str(s):
    for symbol in '<>':
        if s.find(symbol) >= 0:
            return True
    return False


def is_unsafe_str(s):
    for symbol in '<>':
        if s.find(symbol) > 0:
            return True
        return False


        content = str(response.content)
        xss = is_unsafe_str(content)
        self.assertFalse(xss, msg=msg_prefix+content)
from graphite.util import htmlEscape

    def test_dashboard_save_temporary_xss_name(self):
        xssStr = htmlEscape('<img src=1 onerror=alert(1)>')
        url = '/graphite/dashboard/save_template/' + xssStr + '/testkey'

        request = copy.deepcopy(self.testtemplate)
        response = self.client.post(url, request)
        self.assertContains(response, 'name contain restricted symbols', status_code=400)

    def test_dashboard_save_temporary_xss_key(self):
        xssStr = htmlEscape('<img src=1 onerror=alert(1)>')
        url = '/graphite/dashboard/save_template/testtemplate/' + xssStr

        request = copy.deepcopy(self.testtemplate)
        response = self.client.post(url, request)
        self.assertContains(response, 'key contain restricted symbols', status_code=400)

        self.assertEqual(response.content, b'{"success": true}')
    # Save again after it now exists
        self.assertEqual(response.content, b'{"success": true}')
        self.assertEqual(response.content, b'{"success": true}')