diff --git a/lib/core/settings.py b/lib/core/settings.py index f0d981251..6b3bf99b1 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.8.28" +VERSION = "1.10.8.29" 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_configfile.py b/tests/test_configfile.py index c2b179148..b09242b95 100644 --- a/tests/test_configfile.py +++ b/tests/test_configfile.py @@ -104,19 +104,27 @@ class TestConfigFileRoundTrip(unittest.TestCase): handle, path = tempfile.mkstemp(suffix=".ini") os.close(handle) + # `conf` is the process-wide singleton every other test file shares, so this one saves and + # restores the WHOLE of it. Clearing it (or leaving a key behind) is not a local mistake: an + # emptied conf makes `conf.get("direct")` answer for the rest of the run, which silently turns + # urlencode() into the identity function in test files that run later. + saved = dict(conf) + try: conf.url = "http://127.0.0.1:1/?id=1" conf.commonTables = True conf.tablePrefix = "zzz" saveConfig(conf, path) - conf.clear() + conf.url, conf.commonTables, conf.tablePrefix = None, False, "overwritten" configFileParser(path) self.assertEqual(conf.url, "http://127.0.0.1:1/?id=1") self.assertTrue(conf.commonTables) self.assertEqual(conf.tablePrefix, "zzz") finally: + conf.clear() + conf.update(saved) os.unlink(path) diff --git a/tests/test_target_parsing.py b/tests/test_target_parsing.py index 5db31e8f1..da43993ca 100644 --- a/tests/test_target_parsing.py +++ b/tests/test_target_parsing.py @@ -161,9 +161,14 @@ class _TargetTestBase(unittest.TestCase): class TestRestoreMergedOptions(_TargetTestBase): def test_restores_each_option_from_mergedOptions(self): + # conf is restored as carefully as mergedOptions: this leaves 'VAL_' in conf otherwise, + # and conf.string / conf.textOnly are read by the shared page-comparison oracle - a stray + # sentinel there makes every later boolean differential in the process answer false saved = {} + savedConf = {} for opt in RESTORE_MERGED_OPTIONS: saved[opt] = mergedOptions.get(opt) + savedConf[opt] = conf.get(opt) mergedOptions[opt] = "VAL_%s" % opt conf[opt] = "tampered" try: @@ -174,6 +179,8 @@ class TestRestoreMergedOptions(_TargetTestBase): finally: for opt, v in saved.items(): mergedOptions[opt] = v + for opt, v in savedConf.items(): + conf[opt] = v class TestSetAuxOptions(_TargetTestBase): diff --git a/tests/test_xslt.py b/tests/test_xslt.py index 31d226f33..3907d2771 100644 --- a/tests/test_xslt.py +++ b/tests/test_xslt.py @@ -278,10 +278,32 @@ class XQueryTierTest(unittest.TestCase): class SwitchWiringTest(unittest.TestCase): def test_switch_is_registered_and_mutually_exclusive(self): + from lib.core.data import conf + from lib.core.exception import SqlmapSyntaxException from lib.core.optiondict import optDict + from lib.core.settings import NONSQL_TECHNIQUES + self.assertEqual(optDict["Techniques"].get("xslt"), "boolean") - self.assertIn('("--xslt", conf.xslt)', _source("lib", "core", "option.py")) + # exclusivity used to be asserted by grepping option.py for a literal `("--xslt", conf.xslt)` + # pair; the switches now come from one registry, so assert the PROPERTY instead of the spelling + self.assertIn("xslt", NONSQL_TECHNIQUES) + + saved = dict((_, conf.get(_)) for _ in NONSQL_TECHNIQUES) + try: + from lib.core.option import _basicOptionValidation + + for _ in NONSQL_TECHNIQUES: + conf[_] = False + conf.xslt = conf.nosql = True + try: + _basicOptionValidation() + except SqlmapSyntaxException as ex: + self.assertIn("--xslt", str(ex)) + else: + self.fail("'--xslt --nosql' was accepted") + finally: + conf.update(saved) def test_controller_dispatches_the_scan(self): self.assertIn("from lib.techniques.xslt.inject import xsltScan", _source("lib", "controller", "controller.py"))