Skip to content

Commit ebf4b48

Browse files
committed
Fixing CI/CD errors
1 parent 92c2ee4 commit ebf4b48

7 files changed

Lines changed: 62 additions & 43 deletions

File tree

lib/core/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from thirdparty import six
2121

2222
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
23-
VERSION = "1.10.8.32"
23+
VERSION = "1.10.8.33"
2424
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
2525
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
2626
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)

tests/_testutils.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,37 @@ def reset_dbms():
114114
kb.dbmsVersion = [UNKNOWN_DBMS_VERSION]
115115

116116

117+
# --- patching shared singletons (agent, unescaper, ...) ---
118+
119+
_MISSING = object()
120+
121+
122+
def save_attrs(obj, *names):
123+
"""Snapshot attributes of a shared singleton so tearDown can restore them EXACTLY.
124+
125+
agent.payload and friends are methods on the CLASS: patching one sets an *instance* attribute
126+
that shadows it, and restoring by assignment leaves that instance attribute behind holding a
127+
bound method. Harmless on its own - but invisible state, and a later module whose cleanup keys
128+
on "did this attribute exist before me?" then reads True, skips its own del, and leaks its stub
129+
into every module that follows (test_techniques + test_checks did exactly that to
130+
test_union_engine, whose real payloads silently became the string "PAYLOAD").
131+
132+
Pair with restore_attrs(); it deletes what was not there before and restores what was.
133+
"""
134+
135+
return [(obj, name, obj.__dict__.get(name, _MISSING)) for name in names]
136+
137+
138+
def restore_attrs(saved):
139+
"""Undo save_attrs(), leaving obj.__dict__ exactly as it was found."""
140+
141+
for obj, name, value in saved:
142+
if value is _MISSING:
143+
obj.__dict__.pop(name, None)
144+
else:
145+
setattr(obj, name, value)
146+
147+
117148
# --- property/fuzz testing harness (shared so individual test files don't each reinvent it) ---
118149

119150
_PROPERTY_BASE = 0x51A1

tests/test_checks.py

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
import unittest
2828

2929
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
30-
from _testutils import bootstrap
30+
from _testutils import bootstrap, save_attrs, restore_attrs
3131
bootstrap()
3232

3333
import lib.controller.checks as checks
@@ -88,13 +88,11 @@ class _ChecksTestBase(unittest.TestCase):
8888
def setUp(self):
8989
self._snap = _snapshot()
9090
# remember the real seams so monkeypatches can't leak. agent.payload /
91-
# addPayloadDelimiters are class methods on a shared singleton: patching
92-
# sets an *instance* attribute, so it's restored by deleting that
93-
# attribute (reassigning would leave a stale bound method behind).
91+
# addPayloadDelimiters are class methods on a shared singleton, so they need
92+
# save_attrs()/restore_attrs() rather than a plain reassignment (see _testutils).
9493
self._origQueryPage = checks.Request.queryPage
9594
self._origGetPage = checks.Request.getPage
96-
self._agentHadPayload = "payload" in checks.agent.__dict__
97-
self._agentHadAddDelims = "addPayloadDelimiters" in checks.agent.__dict__
95+
self._savedAgent = save_attrs(checks.agent, "payload", "addPayloadDelimiters")
9896
self._origReadInput = checks.readInput
9997
self._origDbmsErr = checks.wasLastResponseDBMSError
10098
self._origHttpErr = checks.wasLastResponseHTTPError
@@ -111,10 +109,7 @@ def setUp(self):
111109
def tearDown(self):
112110
checks.Request.queryPage = self._origQueryPage
113111
checks.Request.getPage = self._origGetPage
114-
if not self._agentHadPayload and "payload" in checks.agent.__dict__:
115-
del checks.agent.payload
116-
if not self._agentHadAddDelims and "addPayloadDelimiters" in checks.agent.__dict__:
117-
del checks.agent.addPayloadDelimiters
112+
restore_attrs(self._savedAgent)
118113
checks.readInput = self._origReadInput
119114
checks.wasLastResponseDBMSError = self._origDbmsErr
120115
checks.wasLastResponseHTTPError = self._origHttpErr

tests/test_dns_engine.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
import unittest
3939

4040
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
41-
from _testutils import bootstrap, set_dbms, reset_dbms
41+
from _testutils import bootstrap, set_dbms, reset_dbms, save_attrs, restore_attrs
4242
bootstrap()
4343

4444
from lib.core.agent import agent
@@ -131,7 +131,7 @@ def setUp(self):
131131
# (agent.prefixQuery/agent.payload, needing a full kb.injection). That plumbing is
132132
# generic - not DNS logic - and the mock oracle ignores the payload, so stub it to a
133133
# pass-through; the DNS-specific snippet/substring/chunking still runs for real.
134-
self._saved_prefixQuery, self._saved_payload = agent.prefixQuery, agent.payload
134+
self._s_agent = save_attrs(agent, "prefixQuery", "payload")
135135
agent.prefixQuery = lambda expression, *a, **k: expression
136136
agent.payload = lambda place=None, parameter=None, value=None, newValue=None, where=None: newValue or ""
137137
set_dbms(self.DBMS_NAME)
@@ -148,7 +148,7 @@ def tearDown(self):
148148
dnsmod.randomStr = self._saved_randomStr
149149
dnstestmod.randomInt = self._saved_randomInt
150150
dnsmod.hashDBRetrieve, dnsmod.hashDBWrite = self._saved_hdbR, self._saved_hdbW
151-
agent.prefixQuery, agent.payload = self._saved_prefixQuery, self._saved_payload
151+
restore_attrs(self._s_agent)
152152

153153
def _install_oracle(self, secret, working=True, force=None):
154154
"""
@@ -293,10 +293,12 @@ def spy_randomStr(length=4, alphabet=None, **kw):
293293
# "SUBSTRING((...) FROM 1 FOR 13)"; the substring LENGTH argument (the source's real
294294
# chunk_length) is the last integer literal in it. Capture it per iteration so the oracle
295295
# emits a chunk of exactly that size - the source's arithmetic, not a copy of it.
296-
saved_hexConvertField = agent.hexConvertField
296+
real_hexConvertField = agent.hexConvertField # the spy delegates to it
297+
saved_hexConvertField = save_attrs(agent, "hexConvertField")
298+
297299
def spy_hexConvertField(field):
298300
source_chunk_lengths.append(int(re.findall(r"\d+", field)[-1]))
299-
return saved_hexConvertField(field)
301+
return real_hexConvertField(field)
300302
agent.hexConvertField = spy_hexConvertField
301303

302304
def oracle(payload=None, *args, **kwargs):
@@ -328,7 +330,7 @@ def oracle(payload=None, *args, **kwargs):
328330
try:
329331
result = dnsmod.dnsUse("%s AND %d=%d", "user()")
330332
finally:
331-
agent.hexConvertField = saved_hexConvertField
333+
restore_attrs(saved_hexConvertField)
332334

333335
# round-trip must still work (the source must actually reassemble what it chunked)
334336
self.assertEqual(result, secret)

tests/test_payload_marking.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import unittest
1717

1818
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
19-
from _testutils import bootstrap
19+
from _testutils import bootstrap, save_attrs, restore_attrs
2020
bootstrap()
2121

2222
from lib.core.settings import (JSON_RECOGNITION_REGEX, JSON_LIKE_RECOGNITION_REGEX,
@@ -219,7 +219,7 @@ def _capture(value):
219219
captured["value"] = value
220220
raise _Sentinel()
221221

222-
orig_remove = agent.removePayloadDelimiters
222+
saved_remove = save_attrs(agent, "removePayloadDelimiters")
223223
agent.removePayloadDelimiters = _capture
224224
try:
225225
conf.direct = False
@@ -238,7 +238,7 @@ def _capture(value):
238238
except _Sentinel:
239239
pass
240240
finally:
241-
agent.removePayloadDelimiters = orig_remove
241+
restore_attrs(saved_remove)
242242

243243
_ = re.escape(PAYLOAD_DELIMITER)
244244
return re.search(r"(?s)%s(?P<result>.*?)%s" % (_, _), captured["value"]).group("result")

tests/test_techniques.py

Lines changed: 7 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import unittest
3131

3232
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
33-
from _testutils import bootstrap, set_dbms, reset_dbms
33+
from _testutils import bootstrap, set_dbms, reset_dbms, save_attrs, restore_attrs
3434
bootstrap()
3535

3636
from lib.core.data import conf, kb
@@ -94,9 +94,7 @@ def setUp(self):
9494
self._scounters = kb.get("counters")
9595
self._sinj_data = kb.injection.data
9696
self._shashdb = conf.get("hashDB")
97-
self._s_forge = agent.forgeUnionQuery
98-
self._s_concat = agent.concatQuery
99-
self._s_payload = agent.payload
97+
self._s_agent = save_attrs(agent, "forgeUnionQuery", "concatQuery", "payload")
10098
self._s_escape = unescaper.escape
10199

102100
for k, v in _UU_CONF.items():
@@ -131,9 +129,7 @@ def tearDown(self):
131129
kb.counters = self._scounters
132130
kb.injection.data = self._sinj_data
133131
conf.hashDB = self._shashdb
134-
agent.forgeUnionQuery = self._s_forge
135-
agent.concatQuery = self._s_concat
136-
agent.payload = self._s_payload
132+
restore_attrs(self._s_agent)
137133
unescaper.escape = self._s_escape
138134

139135
def _install_page(self, page):
@@ -325,11 +321,8 @@ def setUp(self):
325321
self._sinj_data = kb.injection.data
326322
self._shashdb = conf.get("hashDB")
327323
self._sbatch = conf.get("batch")
328-
self._s_forge = agent.forgeUnionQuery
329-
self._s_concat = agent.concatQuery
330-
self._s_payload = agent.payload
324+
self._s_agent = save_attrs(agent, "forgeUnionQuery", "concatQuery", "payload", "_lastexpr")
331325
self._s_escape = unescaper.escape
332-
self._s_lastexpr = getattr(agent, "_lastexpr", None)
333326
self._s_initTechnique = uu.initTechnique
334327

335328
for k, v in _UU_CONF_LIMIT.items():
@@ -372,11 +365,8 @@ def tearDown(self):
372365
kb.counters = self._scounters
373366
kb.injection.data = self._sinj_data
374367
conf.hashDB = self._shashdb
375-
agent.forgeUnionQuery = self._s_forge
376-
agent.concatQuery = self._s_concat
377-
agent.payload = self._s_payload
368+
restore_attrs(self._s_agent)
378369
unescaper.escape = self._s_escape
379-
agent._lastexpr = self._s_lastexpr
380370
uu.initTechnique = self._s_initTechnique
381371

382372
if self._s_columns is None:
@@ -500,10 +490,7 @@ def setUp(self):
500490
self._shashdb = conf.get("hashDB")
501491
self._sbatch = conf.get("batch")
502492

503-
self._s_prefix = agent.prefixQuery
504-
self._s_suffix = agent.suffixQuery
505-
self._s_payload = agent.payload
506-
self._s_nullcast = agent.nullAndCastField
493+
self._s_agent = save_attrs(agent, "prefixQuery", "suffixQuery", "payload", "nullAndCastField")
507494
self._s_escape = unescaper.escape
508495

509496
# restore thread state we touch
@@ -557,10 +544,7 @@ def tearDown(self):
557544
kb.injection.data = self._sinj_data
558545
conf.hashDB = self._shashdb
559546

560-
agent.prefixQuery = self._s_prefix
561-
agent.suffixQuery = self._s_suffix
562-
agent.payload = self._s_payload
563-
agent.nullAndCastField = self._s_nullcast
547+
restore_attrs(self._s_agent)
564548
unescaper.escape = self._s_escape
565549

566550
td = getCurrentThreadData()

tests/test_union_engine.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from _testutils import bootstrap, set_dbms, reset_dbms
2727
bootstrap()
2828

29+
from lib.core.agent import agent
2930
from lib.core.data import conf, kb
3031
from lib.core.datatype import AttribDict
3132
from lib.core.enums import PAYLOAD, PLACE
@@ -74,6 +75,12 @@ def tearDown(self):
7475
ut.Request.queryPage = self._sqp
7576

7677
def _detect(self, true_count):
78+
# canary: a leaked agent.payload stub (another module patching the shared singleton and
79+
# restoring it by assignment) makes every probe below identical, so the ORDER BY oracle turns
80+
# unusable and this test used to fail as a bare 'None != 25'. Name the cause instead.
81+
self.assertNotIn("payload", agent.__dict__,
82+
"agent.payload is stubbed - a test module leaked it (see _testutils.save_attrs)")
83+
7784
def oracle(payload=None, place=None, content=False, raise404=True, **kwargs):
7885
m = re.search(r"ORDER BY (\d+)", payload or "")
7986
cols = int(m.group(1)) if m else 1

0 commit comments

Comments
 (0)