Skip to content

Commit 3cfba46

Browse files
committed
Dealing with ResourceWarning and DeprecationWarning
1 parent 8473e2e commit 3cfba46

3 files changed

Lines changed: 65 additions & 4 deletions

File tree

‎lib/core/option.py‎

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -847,6 +847,24 @@ def _listTamperingFunctions():
847847
comment = match.group(1).strip()
848848
dataToStdout("* %s - %s\n" % (setColor(os.path.basename(script), "yellow"), re.sub(r" *\n *", " ", comment.split("\n\n")[0].strip())))
849849

850+
def _argSpec(function):
851+
"""
852+
Cross Py2/Py3 argument-name introspection - inspect.getargspec() is deprecated since
853+
Python 3.0 and removed entirely since 3.11, while inspect.getfullargspec() (used here
854+
whenever available) covers the same 'args'/'keywords' need without the warning/removal
855+
"""
856+
857+
return inspect.getfullargspec(function) if hasattr(inspect, "getfullargspec") else inspect.getargspec(function)
858+
859+
def _kwargsName(function):
860+
"""
861+
Name of 'function's **kwargs-style catch-all parameter, or None - the field holding it is
862+
called 'varkw' on a getfullargspec() result and 'keywords' on a (Python 2) getargspec() one
863+
"""
864+
865+
spec = _argSpec(function)
866+
return getattr(spec, "varkw", None) or getattr(spec, "keywords", None)
867+
850868
def _setTamperingFunctions():
851869
"""
852870
Loads tampering functions from given script(s)
@@ -913,7 +931,7 @@ def _setTamperingFunctions():
913931
priority = PRIORITY.NORMAL
914932

915933
for name, function in inspect.getmembers(module, inspect.isfunction):
916-
if name == "tamper" and (hasattr(inspect, "signature") and all(_ in inspect.signature(function).parameters for _ in ("payload", "kwargs")) or inspect.getargspec(function).args and inspect.getargspec(function).keywords == "kwargs"):
934+
if name == "tamper" and (hasattr(inspect, "signature") and all(_ in inspect.signature(function).parameters for _ in ("payload", "kwargs")) or _argSpec(function).args and _kwargsName(function) == "kwargs"):
917935
found = True
918936
kb.tamperFunctions.append(function)
919937
function.__name__ = module.__name__
@@ -1023,7 +1041,7 @@ def _setPreprocessFunctions():
10231041

10241042
for name, function in inspect.getmembers(module, inspect.isfunction):
10251043
try:
1026-
if name == "preprocess" and inspect.getargspec(function).args and all(_ in inspect.getargspec(function).args for _ in ("req",)):
1044+
if name == "preprocess" and _argSpec(function).args and all(_ in _argSpec(function).args for _ in ("req",)):
10271045
found = True
10281046

10291047
kb.preprocessFunctions.append(function)
@@ -1106,7 +1124,7 @@ def _setPostprocessFunctions():
11061124
raise SqlmapSyntaxException("cannot import postprocess module '%s' (%s)" % (getUnicode(filename[:-3]), getSafeExString(ex)))
11071125

11081126
for name, function in inspect.getmembers(module, inspect.isfunction):
1109-
if name == "postprocess" and inspect.getargspec(function).args and all(_ in inspect.getargspec(function).args for _ in ("page", "headers", "code")):
1127+
if name == "postprocess" and _argSpec(function).args and all(_ in _argSpec(function).args for _ in ("page", "headers", "code")):
11101128
found = True
11111129

11121130
kb.postprocessFunctions.append(function)

‎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.9.28"
23+
VERSION = "1.10.9.29"
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/test_esperanto.py‎

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,15 @@
4949
u"\U00010348", u"Aé€\U00010348Z",
5050
]
5151

52+
# every connection _oracle()/_disguisedOracle() open outlives the helper call (the returned
53+
# closure keeps using it), so callers can't close it themselves - tracked here and closed once
54+
# in tearDownModule() instead of leaking to GC (ResourceWarning: unclosed database)
55+
_OPEN_CONNECTIONS = []
56+
5257

5358
def _oracle(value=None, is_null=False):
5459
con = sqlite3.connect(":memory:")
60+
_OPEN_CONNECTIONS.append(con)
5561
con.execute("CREATE TABLE t (v TEXT)")
5662
con.execute("INSERT INTO t VALUES (?)", (None if is_null else value,))
5763
con.commit()
@@ -84,6 +90,7 @@ def _disguisedOracle(blocked=None, rewrites=(), funcs=()):
8490
`funcs` registers extra SQL functions (e.g. a variadic CONCAT SQLite lacks). The
8591
engine never sees SQLite - it must adapt to whatever back-end the scenario emulates."""
8692
con = sqlite3.connect(":memory:")
93+
_OPEN_CONNECTIONS.append(con)
8794
for name, narg, fn in funcs:
8895
con.create_function(name, narg, fn)
8996
for stmt in _SEED:
@@ -221,6 +228,7 @@ def test_host_native_integrity_parity(self):
221228
# hostExtract must mirror the native EXACT/AMBIGUOUS verdict: in a mode with no byte-exact
222229
# witness (ordinal), both must be WHOLE_BUT_AMBIGUOUS, not native-ambiguous/host-exact.
223230
con = sqlite3.connect(":memory:")
231+
self.addCleanup(con.close)
224232
con.execute("CREATE TABLE t (v TEXT)")
225233
con.execute("INSERT INTO t VALUES ('Admin-42')")
226234
con.commit()
@@ -249,6 +257,7 @@ def test_framing_length_witness_catches_capped_hex(self):
249257
# must reject the shortened token -> fall back to cell-by-cell (which reads the true
250258
# length via substring), so a 400-char value is recovered whole, not silently as 300.
251259
con = sqlite3.connect(":memory:")
260+
self.addCleanup(con.close)
252261
con.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT)")
253262
big = "A" * 400
254263
con.execute("INSERT INTO t VALUES (1, ?)", (big,))
@@ -273,6 +282,7 @@ def test_integer_final_equality(self):
273282
# operands -> bisection converges off-by-one. The final `expr = recovered` check must
274283
# reject it (fail closed) rather than return a silently-wrong integer.
275284
con = sqlite3.connect(":memory:")
285+
self.addCleanup(con.close)
276286

277287
def ask(cond):
278288
c = cond
@@ -302,6 +312,7 @@ def test_host_length_code_final_equality(self):
302312
# (LENGTH(..)>n, UNICODE(..)>n) converges off-by-one; the '=' confirmation must fail closed
303313
# rather than hand back silently-wrong code-point text as exact.
304314
con = sqlite3.connect(":memory:")
315+
self.addCleanup(con.close)
305316
con.execute("CREATE TABLE t (v TEXT)")
306317
con.execute("INSERT INTO t VALUES ('Admin-42')")
307318
con.commit()
@@ -329,6 +340,7 @@ def test_hex_unknown_codec_non_ascii_not_exact(self):
329340
# (e.g. utf-8 bytes with an embedded NUL heuristically read as UTF-16). Bytes are faithful;
330341
# only a PROVEN codec makes the decoded text exact.
331342
con = sqlite3.connect(":memory:")
343+
self.addCleanup(con.close)
332344
con.execute("CREATE TABLE t (v TEXT)")
333345
con.execute("INSERT INTO t VALUES (?)", (u"é",)) # utf-8 C3 A9 -> non-ASCII bytes
334346
con.execute("CREATE TABLE a (v TEXT)")
@@ -356,6 +368,7 @@ def test_bytelen_witness_fails_closed(self):
356368
# Round 8 #5: once a byte-length witness is selected, an UNDECIDED reading must reject the
357369
# bytes (fail closed), never treat "couldn't verify" as "matched".
358370
con = sqlite3.connect(":memory:")
371+
self.addCleanup(con.close)
359372
con.execute("CREATE TABLE t (v TEXT)")
360373
con.execute("INSERT INTO t VALUES ('abc')")
361374
con.commit()
@@ -389,6 +402,7 @@ def test_lossy_cast_marked_inexact(self):
389402
# never ran on the very failure it defends against. The canary must run whenever a textcast
390403
# was applied, and an unproven cast must not certify source identity.
391404
con = sqlite3.connect(":memory:")
405+
self.addCleanup(con.close)
392406
con.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT)")
393407
con.execute(u"INSERT INTO t VALUES (1, 'café')")
394408
con.commit()
@@ -415,6 +429,7 @@ def test_repl_chars_escalate_to_hex(self):
415429
# the engine must escalate to the cast+hex path (which the framed dump proves works) and
416430
# recover the value exactly. Without hex, it stays honestly incomplete (no false-complete).
417431
con = sqlite3.connect(":memory:")
432+
self.addCleanup(con.close)
418433
con.execute("CREATE TABLE t (v TEXT)")
419434
con.execute(u"INSERT INTO t VALUES ('café-€')")
420435
con.commit()
@@ -466,6 +481,7 @@ def test_comparator_rejects_gte_rewrite(self):
466481
# False). the full truth table must reject 'gt' (and fall to BETWEEN) so counts/lengths
467482
# aren't read off-by-one. reproduced: extractInteger('5',max=10) used to return 6.
468483
con = sqlite3.connect(":memory:")
484+
self.addCleanup(con.close)
469485

470486
def ask(cond):
471487
try:
@@ -481,6 +497,7 @@ def test_exact_requires_byte_witness(self):
481497
# without a proven hex/binary witness (or code-codepoint), a value is WHOLE_BUT_AMBIGUOUS,
482498
# never EXACT - plain '=' is collation-dependent and can't certify byte-exactness.
483499
con = sqlite3.connect(":memory:")
500+
self.addCleanup(con.close)
484501
con.execute("CREATE TABLE t (v TEXT)")
485502
con.execute("INSERT INTO t VALUES ('Zz')")
486503
con.commit()
@@ -510,6 +527,7 @@ def test_extractresult_no_contradiction(self):
510527

511528
def test_host_maxlen_zero(self):
512529
con = sqlite3.connect(":memory:")
530+
self.addCleanup(con.close)
513531
con.execute("CREATE TABLE t (v TEXT)")
514532
con.execute("INSERT INTO t VALUES ('abcdef')")
515533
con.commit()
@@ -541,6 +559,7 @@ def test_integrity_semantics(self):
541559
# `complete` (walk finished) must be distinct from `exact` (bytes proven identical).
542560
# a case-insensitive collation recovers a WHOLE value that is NOT exact.
543561
con = sqlite3.connect(":memory:")
562+
self.addCleanup(con.close)
544563
con.execute("CREATE TABLE t (v TEXT)")
545564
con.execute("INSERT INTO t VALUES ('A')")
546565
con.commit()
@@ -568,6 +587,7 @@ def test_hostextract_structured_and_tristate(self):
568587
# hostExtract returns an ExtractResult: a bounded read is TRUNCATED (not a bare string),
569588
# and an undecided (None) host observation degrades to a FAILED result, never a fake bit.
570589
con = sqlite3.connect(":memory:")
590+
self.addCleanup(con.close)
571591
con.execute("CREATE TABLE t (v TEXT)")
572592
con.execute("INSERT INTO t VALUES ('abcdef')")
573593
con.commit()
@@ -602,6 +622,7 @@ def test_key_uniqueness_gate(self):
602622
# key's first column repeats -> `> prev` paging silently drops rows sharing it, so it
603623
# must be REJECTED (COUNT(*) != COUNT(DISTINCT col)); only a unique column is accepted.
604624
con = sqlite3.connect(":memory:")
625+
self.addCleanup(con.close)
605626
con.execute("CREATE TABLE k (a INT, b INT, u INT, nul INT)")
606627
con.executemany("INSERT INTO k VALUES (?,?,?,?)", [(1, 1, 10, 1), (1, 2, 20, None), (2, 1, 30, 3)])
607628
con.commit()
@@ -651,6 +672,7 @@ def test_coalesce(self):
651672

652673
def test_enumerate(self):
653674
con = sqlite3.connect(":memory:")
675+
self.addCleanup(con.close)
654676
con.execute("CREATE TABLE alpha (x)")
655677
con.execute("CREATE TABLE beta (y)")
656678
con.commit()
@@ -668,6 +690,7 @@ def ask(cond):
668690
def test_dump(self):
669691
# row DATA byte-exact, including commas / quotes / unicode (hex framing keeps intact)
670692
con = sqlite3.connect(":memory:")
693+
self.addCleanup(con.close)
671694
con.execute("CREATE TABLE users (id, uname, note)")
672695
truth = [(1, u"admin", u"all,good"), (2, u"o'brien", u"café,€"), (3, u"x", u"")]
673696
for row in truth:
@@ -692,6 +715,7 @@ def test_dump_scavenges_without_hex_or_concat(self):
692715
# extraction. Quotes/commas/NULLs in the data must survive (a single value has
693716
# no framing ambiguity). This is the scavenger's whole reason to exist.
694717
con = sqlite3.connect(":memory:")
718+
self.addCleanup(con.close)
695719
con.execute("CREATE TABLE users (id INTEGER, name TEXT, email TEXT)")
696720
for row in ((1, "luther", "a@b.c"), (2, "o'brien", "x,y@z"), (3, "wu", None)):
697721
con.execute("INSERT INTO users VALUES (?,?,?)", row)
@@ -723,6 +747,7 @@ def test_enumerate_without_count(self):
723747
# catalog detection, brute-force existence probing, and identifier quoting are
724748
# all COUNT-free (scalar-subquery existence), so tables/columns/dump still work.
725749
con = sqlite3.connect(":memory:")
750+
self.addCleanup(con.close)
726751
con.execute("CREATE TABLE users (id INTEGER, name TEXT)")
727752
con.execute("INSERT INTO users VALUES (1, 'admin'), (2, 'root')")
728753
con.commit()
@@ -747,6 +772,7 @@ def ask(cond):
747772
def test_quoting(self):
748773
# reserved-word / spaced column names must be quoted, not interpolated raw
749774
con = sqlite3.connect(":memory:")
775+
self.addCleanup(con.close)
750776
con.execute('CREATE TABLE q (id INTEGER, "order" TEXT, "group by" TEXT)')
751777
con.execute('INSERT INTO q VALUES (1, ?, ?)', ("a'b", "x,y"))
752778
con.commit()
@@ -767,6 +793,7 @@ def ask(cond):
767793
def test_strategy_handoff(self):
768794
# the frozen InferenceStrategy is a *sufficient* host interface, and immutable
769795
con = sqlite3.connect(":memory:")
796+
self.addCleanup(con.close)
770797
con.execute("CREATE TABLE k (v TEXT)")
771798
con.execute("INSERT INTO k VALUES ('Str4t3gy!')")
772799
con.commit()
@@ -788,6 +815,7 @@ def ask(cond):
788815
def test_pattern_match_fallback(self):
789816
# SUBSTR + LENGTH + hex + code fns ALL blacklisted -> pure GLOB/LIKE floor
790817
con = sqlite3.connect(":memory:")
818+
self.addCleanup(con.close)
791819
con.execute("CREATE TABLE flag (id INTEGER, v TEXT)")
792820
con.execute("INSERT INTO flag VALUES (1, 'FLAG{no_SUBSTR_%_needed}')")
793821
con.commit()
@@ -812,6 +840,7 @@ def test_like_floor_escapes_wildcard_chars(self):
812840
# like 'all_products' must escape them (\_ ESCAPE '\'), not treat them as
813841
# match-anything. GLOB is blocked here to force LIKE (where '_'/'%' are magic).
814842
con = sqlite3.connect(":memory:")
843+
self.addCleanup(con.close)
815844
con.execute("CREATE TABLE v (val TEXT)")
816845
con.execute("INSERT INTO v VALUES ('all_products')")
817846
for t in ("all_products", "wp_users", "sales%2024"): # '_'/'%' in identifiers
@@ -838,6 +867,7 @@ def ask(cond):
838867
def test_length_from_substring(self):
839868
# every length fn blacklisted but SUBSTR present -> length derived from the end
840869
con = sqlite3.connect(":memory:")
870+
self.addCleanup(con.close)
841871
con.execute("CREATE TABLE t6 (v TEXT)")
842872
con.execute(u"INSERT INTO t6 VALUES ('Admin-42€')")
843873
con.commit()
@@ -859,6 +889,7 @@ def test_enumeration_degrades_not_crashes(self):
859889
# a permission/charset wall mid-walk (oracle can't decide the keyset bound)
860890
# must STOP with partial results, never crash with OracleUndecided
861891
con = sqlite3.connect(":memory:")
892+
self.addCleanup(con.close)
862893
for t in ("alpha", "beta", "gamma"):
863894
con.execute("CREATE TABLE %s (x)" % t)
864895
con.commit()
@@ -884,6 +915,7 @@ def test_left_right_rung(self):
884915
# RIGHT( onto them (the same rewrite the disguised-dialect oracles use). Keeps the
885916
# test portable across SQLite builds while still exercising the real left_right rung.
886917
con = sqlite3.connect(":memory:")
918+
self.addCleanup(con.close)
887919
con.execute("CREATE TABLE t7 (v TEXT)")
888920
con.execute("INSERT INTO t7 VALUES ('Zagreb-42')")
889921
con.commit()
@@ -1029,6 +1061,7 @@ def test_disguise_bracket_quoting_reserved_words(self):
10291061
seed = ('CREATE TABLE q (id INTEGER, "order" TEXT, "group" TEXT)',
10301062
"INSERT INTO q VALUES (1, ?, ?)")
10311063
con = sqlite3.connect(":memory:")
1064+
self.addCleanup(con.close)
10321065
con.execute(seed[0]); con.execute(seed[1], ("a,b", "x'y")); con.commit()
10331066
blk = re.compile(r'"|`')
10341067

@@ -1050,6 +1083,7 @@ def test_disguise_lossy_charcode_escalates_to_hex(self):
10501083
# a first-byte charcode fn (MySQL-style ASCII) is lossy for non-ASCII; the
10511084
# engine must detect that and escalate to hex, recovering the bytes exactly
10521085
con = sqlite3.connect(":memory:")
1086+
self.addCleanup(con.close)
10531087
con.execute("CREATE TABLE t (v TEXT)")
10541088
con.execute(u"INSERT INTO t VALUES ('café-€')") # cafe-EUR
10551089
con.commit()
@@ -1071,6 +1105,7 @@ def test_disguise_unicode_through_dialect(self):
10711105
# non-ASCII data recovered byte-exact even while the dialect is disguised
10721106
# (SUBSTR/MID/CHAR_LENGTH/LENGTH blocked -> SUBSTRING/LEN mapped to SQLite)
10731107
con = sqlite3.connect(":memory:")
1108+
self.addCleanup(con.close)
10741109
con.execute("CREATE TABLE s (v TEXT)")
10751110
con.execute(u"INSERT INTO s VALUES ('Zagreb-župa-€42')") # Zagreb-zupa-EUR42
10761111
con.commit()
@@ -1091,5 +1126,13 @@ def ask(cond):
10911126
self.assertEqual(esp.extractText("(SELECT v FROM s)"), u"Zagreb-župa-€42")
10921127

10931128

1129+
def tearDownModule():
1130+
while _OPEN_CONNECTIONS:
1131+
try:
1132+
_OPEN_CONNECTIONS.pop().close()
1133+
except Exception:
1134+
pass
1135+
1136+
10941137
if __name__ == "__main__":
10951138
unittest.main(verbosity=2)

0 commit comments

Comments
 (0)