mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2026-06-20 14:40:36 +00:00
129 lines
4.4 KiB
Python
129 lines
4.4 KiB
Python
#!/usr/bin/env python
|
|
|
|
"""
|
|
Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
|
|
See the file 'LICENSE' for copying permission
|
|
|
|
Session storage layer (lib/utils/hashdb.py) - the on-disk SQLite cache that
|
|
makes --flush-session / resume work.
|
|
|
|
Exercised against a REAL temporary SQLite file (no network, no DBMS): scalar
|
|
write/retrieve, serialized round-trip for every container type sqlmap stores,
|
|
overwrite semantics, missing-key -> None, and key-hash determinism.
|
|
|
|
This is also the end-to-end regression for the base64-pickle bytes fix: a
|
|
serialized value containing raw `bytes` must survive a write/flush/retrieve
|
|
cycle on both Python 2 and 3 (it silently failed on py3 before the patch.py fix).
|
|
"""
|
|
|
|
import os
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
from _testutils import bootstrap
|
|
bootstrap()
|
|
|
|
from lib.utils.hashdb import HashDB
|
|
from lib.core.datatype import AttribDict
|
|
from lib.core.bigarray import BigArray
|
|
|
|
|
|
class _HashDBCase(unittest.TestCase):
|
|
def setUp(self):
|
|
fd, self.path = tempfile.mkstemp(suffix=".sqlite")
|
|
os.close(fd)
|
|
os.remove(self.path) # HashDB creates it lazily
|
|
self.db = HashDB(self.path)
|
|
|
|
def tearDown(self):
|
|
try:
|
|
self.db.closeAll()
|
|
except Exception:
|
|
pass
|
|
if os.path.exists(self.path):
|
|
os.remove(self.path)
|
|
|
|
|
|
class TestScalar(_HashDBCase):
|
|
def test_string_roundtrip(self):
|
|
self.db.write("greeting", "hello")
|
|
self.db.flush()
|
|
self.assertEqual(self.db.retrieve("greeting"), "hello")
|
|
|
|
def test_non_serialized_number_comes_back_as_text(self):
|
|
# non-serialized writes are stored via getUnicode()
|
|
self.db.write("num", 5)
|
|
self.db.flush()
|
|
self.assertEqual(self.db.retrieve("num"), "5")
|
|
|
|
def test_missing_key_is_none(self):
|
|
self.assertIsNone(self.db.retrieve("never-written"))
|
|
|
|
def test_overwrite_last_wins(self):
|
|
self.db.write("k", "v1")
|
|
self.db.write("k", "v2")
|
|
self.db.flush()
|
|
self.assertEqual(self.db.retrieve("k"), "v2")
|
|
|
|
def test_keys_are_independent(self):
|
|
self.db.write("a", "1")
|
|
self.db.write("b", "2")
|
|
self.db.flush()
|
|
self.assertEqual(self.db.retrieve("a"), "1")
|
|
self.assertEqual(self.db.retrieve("b"), "2")
|
|
|
|
|
|
class TestSerialized(_HashDBCase):
|
|
def test_list_dict_tuple_set(self):
|
|
cases = {
|
|
"list": [1, 2, 3, "x"],
|
|
"dict": {"k": [1, {"n": "v"}]},
|
|
"tuple": (1, "a", None),
|
|
"set": set([1, 2, 3]),
|
|
}
|
|
for key, val in cases.items():
|
|
self.db.write(key, val, True)
|
|
self.db.flush()
|
|
for key, val in cases.items():
|
|
self.assertEqual(self.db.retrieve(key, True), val, msg="serialized round-trip for %s" % key)
|
|
|
|
def test_attribdict_roundtrip(self):
|
|
ad = AttribDict()
|
|
ad.x = 1
|
|
ad.y = [1, 2]
|
|
self.db.write("ad", ad, True)
|
|
self.db.flush()
|
|
got = self.db.retrieve("ad", True)
|
|
self.assertIsInstance(got, AttribDict)
|
|
self.assertEqual(got.x, 1)
|
|
self.assertEqual(got.y, [1, 2])
|
|
|
|
def test_bigarray_roundtrip(self):
|
|
self.db.write("ba", BigArray([1, 2, 3]), True)
|
|
self.db.flush()
|
|
got = self.db.retrieve("ba", True)
|
|
self.assertIsInstance(got, BigArray)
|
|
self.assertEqual(list(got), [1, 2, 3])
|
|
|
|
def test_bytes_containing_value_survives(self):
|
|
# REGRESSION (base64-pickle bytes fix): silently failed to restore on py3 before the fix.
|
|
value = {"raw": b"\x00\x01\xff", "items": [b"ab", "s", 1]}
|
|
self.db.write("bytesval", value, True)
|
|
self.db.flush()
|
|
self.assertEqual(self.db.retrieve("bytesval", True), value)
|
|
|
|
|
|
class TestKeyHashing(_HashDBCase):
|
|
def test_distinct_keys_distinct_hashes(self):
|
|
# a broken hashKey that keys only on (say) length or the last char would collide; require
|
|
# 200 distinct keys to map to 200 distinct hashes. (Determinism is implied: the retrieve
|
|
# round-trips in TestScalar already depend on hashKey being stable.)
|
|
keys = ["key_%d_%s" % (i, "abcdefgh"[i % 8]) for i in range(200)]
|
|
hashes = set(HashDB.hashKey(k) for k in keys)
|
|
self.assertEqual(len(hashes), len(keys), msg="hashKey produced collisions across distinct keys")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main(verbosity=2)
|