From 7eb06ce6354044d0e2a408b0ac8428845d29703f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miroslav=20=C5=A0tampar?= Date: Fri, 18 Sep 2026 00:53:16 +0200 Subject: [PATCH] Fixes #6122 --- lib/core/common.py | 2 +- lib/core/settings.py | 2 +- tests/test_common.py | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 2 deletions(-) diff --git a/lib/core/common.py b/lib/core/common.py index fb4ec825d..ad437291a 100644 --- a/lib/core/common.py +++ b/lib/core/common.py @@ -997,7 +997,7 @@ def setColor(message, color=None, bold=False, level=None, istty=None): if bold or color: retVal = colored(message, color=color, on_color=None, attrs=("bold",) if bold else None) - elif level: + elif level and hasattr(LOGGER_HANDLER, "colorize"): try: level = getattr(logging, level, None) except: diff --git a/lib/core/settings.py b/lib/core/settings.py index 48c86e0fc..176d98252 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.9.13" +VERSION = "1.10.9.14" 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 be4ad2d61..ddede42ab 100644 --- a/tests/test_common.py +++ b/tests/test_common.py @@ -113,6 +113,7 @@ from lib.core.common import ( safeSQLIdentificatorNaming, saveConfig, serializeObject, + setColor, setTechnique, splitFields, trimAlphaNum, @@ -1784,6 +1785,49 @@ class TestCheckOldOptions(unittest.TestCase): self.assertIsNone(checkOldOptions(["-u", "http://test.invalid/?id=1", "--banner"])) +class TestSetColorHandlerMismatch(unittest.TestCase): + """ + Regression test for issue #6122: setColor() decided whether to colorize purely from + conf.disableColoring/IS_TTY, independent of whether the installed LOGGER_HANDLER actually + supports colorize() (a plain logging.StreamHandler - installed for --disable-coloring, or as + the ansistrm-unavailable fallback - never defines it). A multiprocessing hash-cracking worker + hit exactly this mismatch (conf.disableColoring not carried over into the worker) and crashed + with an AttributeError that got misreported as "there was a problem while hashing entry". + """ + + def setUp(self): + import lib.core.common as common_mod + self._common_mod = common_mod + self._saved_handler = common_mod.LOGGER_HANDLER + self._saved_disableColoring = conf.get("disableColoring") + + def tearDown(self): + self._common_mod.LOGGER_HANDLER = self._saved_handler + conf.disableColoring = self._saved_disableColoring + + def test_plain_handler_without_colorize_does_not_raise(self): + import logging + self._common_mod.LOGGER_HANDLER = logging.StreamHandler() # no .colorize(), like the --disable-coloring handler + conf.disableColoring = False # the desync: coloring "should" apply, but handler can't + result = setColor("[INFO] current status: abcde", istty=True) # must not raise + self.assertIsInstance(result, str) + + def test_colorizing_handler_still_used(self): + # sanity check: a handler that DOES define colorize() is unaffected by the guard + calls = [] + + class _FakeColorizingHandler(object): + def colorize(self, message, levelno, force=False): + calls.append((message, levelno, force)) + return "COLORIZED" + + self._common_mod.LOGGER_HANDLER = _FakeColorizingHandler() + conf.disableColoring = False + result = setColor("[INFO] current status: abcde", istty=True) + self.assertEqual(result, "COLORIZED") + self.assertEqual(len(calls), 1) + + if __name__ == "__main__": unittest.main(verbosity=2)