From 20537c52c5811202c39a6fe5f7dcb0bee8af1838 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0tampar?= Date: Sun, 19 Jul 2026 12:44:38 +0200 Subject: [PATCH] Minor patch --- lib/core/common.py | 5 ++++- lib/core/settings.py | 2 +- tests/test_common.py | 17 +++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index ad9f5564c..33ef8aeae 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -74,6 +74,7 @@ from lib.core.dicts import DBMS_DICT from lib.core.dicts import DBWIRE_MODULES from lib.core.dicts import DEFAULT_DOC_ROOTS from lib.core.dicts import DEPRECATED_OPTIONS +from lib.core.dicts import HTML_ENTITIES from lib.core.dicts import OBSOLETE_OPTIONS from lib.core.dicts import SQL_STATEMENTS from lib.core.enums import ADJUST_TIME_DELAY @@ -629,7 +630,9 @@ def paramToDict(place, parameters=None): if place in conf.parameters and not parameters: parameters = conf.parameters[place] - parameters = re.sub(r"&(\w{1,4});", r"%s\g<1>%s" % (PARAMETER_AMP_MARKER, PARAMETER_SEMICOLON_MARKER), parameters) + # Note: shield real HTML entities (e.g. & — ’) from being split on the '&'/';' delimiter; + # match a named entity of any length but only when it is a genuine one, so a plain "&word;" still splits + parameters = re.sub(r"&(\w+);", lambda match: "%s%s%s" % (PARAMETER_AMP_MARKER, match.group(1), PARAMETER_SEMICOLON_MARKER) if match.group(1) in HTML_ENTITIES else match.group(0), parameters) if place == PLACE.COOKIE: splitParams = parameters.split(conf.cookieDel or DEFAULT_COOKIE_DELIMITER) else: diff --git a/lib/core/settings.py b/lib/core/settings.py index 750d36bd6..0df4f9279 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -20,7 +20,7 @@ from lib.core.enums import OS from thirdparty import six # sqlmap version (...) -VERSION = "1.10.7.123" +VERSION = "1.10.7.124" TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable" TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34} VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE) diff --git a/tests/test_common.py b/tests/test_common.py index e8d217627..b27e8acf3 100644 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -185,6 +185,23 @@ class TestParamToDict(unittest.TestCase): result = paramToDict(PLACE.GET, "lonely&id=1") self.assertEqual(list(result.items()), [("id", "1")]) + def test_html_entity_in_value_not_split(self): + # a genuine HTML entity in a value must not be split on its '&'/';' (any entity length) + result = paramToDict(PLACE.GET, "q=foo&bar&id=5") + self.assertEqual(list(result.items()), [("q", "foo&bar"), ("id", "5")]) + result = paramToDict(PLACE.GET, "q=foo—bar&id=5") + self.assertEqual(list(result.items()), [("q", "foo—bar"), ("id", "5")]) + + def test_html_entity_in_cookie_value_not_corrupted(self): + # regression: the entity's own ';' must not act as the cookie delimiter + result = paramToDict(PLACE.COOKIE, "token=a—b; id=5") + self.assertEqual(list(result.items()), [("token", "a—b"), ("id", "5")]) + + def test_non_entity_ampersand_still_splits(self): + # "&nope;" is not a real entity, so '&' remains a genuine delimiter + result = paramToDict(PLACE.GET, "a=1&nope=2") + self.assertEqual(list(result.items()), [("a", "1"), ("nope", "2")]) + class TestGetCharset(unittest.TestCase): """Inference charsets are fixed integer tables."""