Fixing CI/CD errors

This commit is contained in:
Miroslav Štampar
2026-08-13 19:27:32 +02:00
parent 92c2ee47ea
commit ebf4b48337
7 changed files with 62 additions and 43 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ from lib.core.enums import OS
from thirdparty import six
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
VERSION = "1.10.8.32"
VERSION = "1.10.8.33"
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)
+31
View File
@@ -114,6 +114,37 @@ def reset_dbms():
kb.dbmsVersion = [UNKNOWN_DBMS_VERSION]
# --- patching shared singletons (agent, unescaper, ...) ---
_MISSING = object()
def save_attrs(obj, *names):
"""Snapshot attributes of a shared singleton so tearDown can restore them EXACTLY.
agent.payload and friends are methods on the CLASS: patching one sets an *instance* attribute
that shadows it, and restoring by assignment leaves that instance attribute behind holding a
bound method. Harmless on its own - but invisible state, and a later module whose cleanup keys
on "did this attribute exist before me?" then reads True, skips its own del, and leaks its stub
into every module that follows (test_techniques + test_checks did exactly that to
test_union_engine, whose real payloads silently became the string "PAYLOAD").
Pair with restore_attrs(); it deletes what was not there before and restores what was.
"""
return [(obj, name, obj.__dict__.get(name, _MISSING)) for name in names]
def restore_attrs(saved):
"""Undo save_attrs(), leaving obj.__dict__ exactly as it was found."""
for obj, name, value in saved:
if value is _MISSING:
obj.__dict__.pop(name, None)
else:
setattr(obj, name, value)
# --- property/fuzz testing harness (shared so individual test files don't each reinvent it) ---
_PROPERTY_BASE = 0x51A1
+5 -10
View File
@@ -27,7 +27,7 @@ import time
import unittest
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from _testutils import bootstrap
from _testutils import bootstrap, save_attrs, restore_attrs
bootstrap()
import lib.controller.checks as checks
@@ -88,13 +88,11 @@ class _ChecksTestBase(unittest.TestCase):
def setUp(self):
self._snap = _snapshot()
# remember the real seams so monkeypatches can't leak. agent.payload /
# addPayloadDelimiters are class methods on a shared singleton: patching
# sets an *instance* attribute, so it's restored by deleting that
# attribute (reassigning would leave a stale bound method behind).
# addPayloadDelimiters are class methods on a shared singleton, so they need
# save_attrs()/restore_attrs() rather than a plain reassignment (see _testutils).
self._origQueryPage = checks.Request.queryPage
self._origGetPage = checks.Request.getPage
self._agentHadPayload = "payload" in checks.agent.__dict__
self._agentHadAddDelims = "addPayloadDelimiters" in checks.agent.__dict__
self._savedAgent = save_attrs(checks.agent, "payload", "addPayloadDelimiters")
self._origReadInput = checks.readInput
self._origDbmsErr = checks.wasLastResponseDBMSError
self._origHttpErr = checks.wasLastResponseHTTPError
@@ -111,10 +109,7 @@ class _ChecksTestBase(unittest.TestCase):
def tearDown(self):
checks.Request.queryPage = self._origQueryPage
checks.Request.getPage = self._origGetPage
if not self._agentHadPayload and "payload" in checks.agent.__dict__:
del checks.agent.payload
if not self._agentHadAddDelims and "addPayloadDelimiters" in checks.agent.__dict__:
del checks.agent.addPayloadDelimiters
restore_attrs(self._savedAgent)
checks.readInput = self._origReadInput
checks.wasLastResponseDBMSError = self._origDbmsErr
checks.wasLastResponseHTTPError = self._origHttpErr
+8 -6
View File
@@ -38,7 +38,7 @@ import time
import unittest
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from _testutils import bootstrap, set_dbms, reset_dbms
from _testutils import bootstrap, set_dbms, reset_dbms, save_attrs, restore_attrs
bootstrap()
from lib.core.agent import agent
@@ -131,7 +131,7 @@ class _DnsCase(unittest.TestCase):
# (agent.prefixQuery/agent.payload, needing a full kb.injection). That plumbing is
# generic - not DNS logic - and the mock oracle ignores the payload, so stub it to a
# pass-through; the DNS-specific snippet/substring/chunking still runs for real.
self._saved_prefixQuery, self._saved_payload = agent.prefixQuery, agent.payload
self._s_agent = save_attrs(agent, "prefixQuery", "payload")
agent.prefixQuery = lambda expression, *a, **k: expression
agent.payload = lambda place=None, parameter=None, value=None, newValue=None, where=None: newValue or ""
set_dbms(self.DBMS_NAME)
@@ -148,7 +148,7 @@ class _DnsCase(unittest.TestCase):
dnsmod.randomStr = self._saved_randomStr
dnstestmod.randomInt = self._saved_randomInt
dnsmod.hashDBRetrieve, dnsmod.hashDBWrite = self._saved_hdbR, self._saved_hdbW
agent.prefixQuery, agent.payload = self._saved_prefixQuery, self._saved_payload
restore_attrs(self._s_agent)
def _install_oracle(self, secret, working=True, force=None):
"""
@@ -293,10 +293,12 @@ class TestDnsLabelInvariant(_DnsCase):
# "SUBSTRING((...) FROM 1 FOR 13)"; the substring LENGTH argument (the source's real
# chunk_length) is the last integer literal in it. Capture it per iteration so the oracle
# emits a chunk of exactly that size - the source's arithmetic, not a copy of it.
saved_hexConvertField = agent.hexConvertField
real_hexConvertField = agent.hexConvertField # the spy delegates to it
saved_hexConvertField = save_attrs(agent, "hexConvertField")
def spy_hexConvertField(field):
source_chunk_lengths.append(int(re.findall(r"\d+", field)[-1]))
return saved_hexConvertField(field)
return real_hexConvertField(field)
agent.hexConvertField = spy_hexConvertField
def oracle(payload=None, *args, **kwargs):
@@ -328,7 +330,7 @@ class TestDnsLabelInvariant(_DnsCase):
try:
result = dnsmod.dnsUse("%s AND %d=%d", "user()")
finally:
agent.hexConvertField = saved_hexConvertField
restore_attrs(saved_hexConvertField)
# round-trip must still work (the source must actually reassemble what it chunked)
self.assertEqual(result, secret)
+3 -3
View File
@@ -16,7 +16,7 @@ import sys
import unittest
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from _testutils import bootstrap
from _testutils import bootstrap, save_attrs, restore_attrs
bootstrap()
from lib.core.settings import (JSON_RECOGNITION_REGEX, JSON_LIKE_RECOGNITION_REGEX,
@@ -219,7 +219,7 @@ def _drive_hpp(payload, name="id"):
captured["value"] = value
raise _Sentinel()
orig_remove = agent.removePayloadDelimiters
saved_remove = save_attrs(agent, "removePayloadDelimiters")
agent.removePayloadDelimiters = _capture
try:
conf.direct = False
@@ -238,7 +238,7 @@ def _drive_hpp(payload, name="id"):
except _Sentinel:
pass
finally:
agent.removePayloadDelimiters = orig_remove
restore_attrs(saved_remove)
_ = re.escape(PAYLOAD_DELIMITER)
return re.search(r"(?s)%s(?P<result>.*?)%s" % (_, _), captured["value"]).group("result")
+7 -23
View File
@@ -30,7 +30,7 @@ import tempfile
import unittest
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from _testutils import bootstrap, set_dbms, reset_dbms
from _testutils import bootstrap, set_dbms, reset_dbms, save_attrs, restore_attrs
bootstrap()
from lib.core.data import conf, kb
@@ -94,9 +94,7 @@ class _UnionCase(unittest.TestCase):
self._scounters = kb.get("counters")
self._sinj_data = kb.injection.data
self._shashdb = conf.get("hashDB")
self._s_forge = agent.forgeUnionQuery
self._s_concat = agent.concatQuery
self._s_payload = agent.payload
self._s_agent = save_attrs(agent, "forgeUnionQuery", "concatQuery", "payload")
self._s_escape = unescaper.escape
for k, v in _UU_CONF.items():
@@ -131,9 +129,7 @@ class _UnionCase(unittest.TestCase):
kb.counters = self._scounters
kb.injection.data = self._sinj_data
conf.hashDB = self._shashdb
agent.forgeUnionQuery = self._s_forge
agent.concatQuery = self._s_concat
agent.payload = self._s_payload
restore_attrs(self._s_agent)
unescaper.escape = self._s_escape
def _install_page(self, page):
@@ -325,11 +321,8 @@ class _UnionLimitCase(unittest.TestCase):
self._sinj_data = kb.injection.data
self._shashdb = conf.get("hashDB")
self._sbatch = conf.get("batch")
self._s_forge = agent.forgeUnionQuery
self._s_concat = agent.concatQuery
self._s_payload = agent.payload
self._s_agent = save_attrs(agent, "forgeUnionQuery", "concatQuery", "payload", "_lastexpr")
self._s_escape = unescaper.escape
self._s_lastexpr = getattr(agent, "_lastexpr", None)
self._s_initTechnique = uu.initTechnique
for k, v in _UU_CONF_LIMIT.items():
@@ -372,11 +365,8 @@ class _UnionLimitCase(unittest.TestCase):
kb.counters = self._scounters
kb.injection.data = self._sinj_data
conf.hashDB = self._shashdb
agent.forgeUnionQuery = self._s_forge
agent.concatQuery = self._s_concat
agent.payload = self._s_payload
restore_attrs(self._s_agent)
unescaper.escape = self._s_escape
agent._lastexpr = self._s_lastexpr
uu.initTechnique = self._s_initTechnique
if self._s_columns is None:
@@ -500,10 +490,7 @@ class _ErrorCase(unittest.TestCase):
self._shashdb = conf.get("hashDB")
self._sbatch = conf.get("batch")
self._s_prefix = agent.prefixQuery
self._s_suffix = agent.suffixQuery
self._s_payload = agent.payload
self._s_nullcast = agent.nullAndCastField
self._s_agent = save_attrs(agent, "prefixQuery", "suffixQuery", "payload", "nullAndCastField")
self._s_escape = unescaper.escape
# restore thread state we touch
@@ -557,10 +544,7 @@ class _ErrorCase(unittest.TestCase):
kb.injection.data = self._sinj_data
conf.hashDB = self._shashdb
agent.prefixQuery = self._s_prefix
agent.suffixQuery = self._s_suffix
agent.payload = self._s_payload
agent.nullAndCastField = self._s_nullcast
restore_attrs(self._s_agent)
unescaper.escape = self._s_escape
td = getCurrentThreadData()
+7
View File
@@ -26,6 +26,7 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from _testutils import bootstrap, set_dbms, reset_dbms
bootstrap()
from lib.core.agent import agent
from lib.core.data import conf, kb
from lib.core.datatype import AttribDict
from lib.core.enums import PAYLOAD, PLACE
@@ -74,6 +75,12 @@ class TestOrderByColumnCount(unittest.TestCase):
ut.Request.queryPage = self._sqp
def _detect(self, true_count):
# canary: a leaked agent.payload stub (another module patching the shared singleton and
# restoring it by assignment) makes every probe below identical, so the ORDER BY oracle turns
# unusable and this test used to fail as a bare 'None != 25'. Name the cause instead.
self.assertNotIn("payload", agent.__dict__,
"agent.payload is stubbed - a test module leaked it (see _testutils.save_attrs)")
def oracle(payload=None, place=None, content=False, raise404=True, **kwargs):
m = re.search(r"ORDER BY (\d+)", payload or "")
cols = int(m.group(1)) if m else 1