sqlmap/tests/test_convert.py
2026-07-08 12:13:49 +02:00

140 lines
5.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
See the file 'LICENSE' for copying permission
Encoding / decoding / serialization round-trips and known vectors.
Covers: hex, base64 (std + url-safe), DBMS hex decode, byte<->text conversion,
JSON (de)serialization, restricted base64-pickle.
"""
import os
import random
import sys
import unittest
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from _testutils import bootstrap
bootstrap()
from lib.core.convert import (decodeHex, encodeHex, decodeBase64, encodeBase64,
getBytes, getText, getUnicode, getOrds,
jsonize, dejsonize, serializeValue, deserializeValue)
from lib.core.common import decodeDbmsHexValue
try:
unichr = unichr
except NameError:
unichr = chr
RND = random.Random(0xC0FFEE)
def _rand_bytes(maxlen=48):
return bytes(bytearray(RND.randint(0, 255) for _ in range(RND.randint(0, maxlen))))
class TestHex(unittest.TestCase):
def test_known_vectors(self):
self.assertEqual(decodeHex("31323334", binary=True), b"1234")
self.assertEqual(getText(encodeHex(b"1234", binary=False)), "31323334")
def test_roundtrip_property(self):
for _ in range(3000):
raw = _rand_bytes()
self.assertEqual(decodeHex(encodeHex(raw, binary=False), binary=True), raw)
class TestBase64(unittest.TestCase):
def test_known_vectors(self):
self.assertEqual(decodeBase64("MTIz", binary=True), b"123")
self.assertEqual(decodeBase64("MTIzNA", binary=True), b"1234") # missing padding
self.assertEqual(decodeBase64("MTIzNA==", binary=True), b"1234")
self.assertEqual(getText(encodeBase64(b"123", binary=False)), "MTIz")
# url-safe and standard alphabets must decode equivalently
self.assertEqual(decodeBase64("A-B_CDE", binary=True), decodeBase64("A+B/CDE", binary=True))
def test_roundtrip_property(self):
for _ in range(3000):
raw = _rand_bytes()
self.assertEqual(decodeBase64(encodeBase64(raw, binary=True), binary=True), raw)
self.assertEqual(decodeBase64(encodeBase64(raw, binary=True, safe=True), binary=True), raw)
self.assertEqual(decodeBase64(encodeBase64(raw, binary=True, padding=False), binary=True), raw)
class TestDecodeDbmsHexValue(unittest.TestCase):
# authoritative vectors taken from the function's own doctests
def test_known_vectors(self):
self.assertEqual(decodeDbmsHexValue("3132332031"), u"123 1")
self.assertEqual(decodeDbmsHexValue("31003200330020003100"), u"123 1") # utf-16-le shaped
self.assertEqual(decodeDbmsHexValue("00310032003300200031"), u"123 1") # utf-16-be shaped
self.assertEqual(decodeDbmsHexValue("0x31003200330020003100"), u"123 1")
self.assertEqual(decodeDbmsHexValue("313233203"), u"123 ?") # odd length
self.assertEqual(decodeDbmsHexValue(["0x31", "0x32"]), [u"1", u"2"]) # list input
def test_ascii_roundtrip_property(self):
for _ in range(1000):
s = "".join(chr(RND.randint(0x20, 0x7e)) for _ in range(RND.randint(1, 30)))
if len(s) % 2 == 0: # avoid the deliberate odd-length '?' behavior
self.assertEqual(decodeDbmsHexValue(getText(encodeHex(getBytes(s), binary=False))), s)
class TestByteTextConversion(unittest.TestCase):
def test_ascii_roundtrip(self):
for _ in range(1000):
s = u"".join(unichr(RND.randint(0x20, 0x7e)) for _ in range(RND.randint(0, 30)))
self.assertEqual(getUnicode(getBytes(s)), s)
def test_unicode_roundtrip(self):
samples = [u"café", u"你好", u"\U0001F600", u"ab™c"]
for s in samples:
self.assertEqual(getUnicode(getBytes(s)), s)
def test_getords(self):
self.assertEqual(getOrds(b"AB"), [65, 66])
class TestJson(unittest.TestCase):
def test_roundtrip(self):
for obj in [{"a": 1, "b": [1, 2, 3]}, [1, "x", None], {"nested": {"k": "v"}}, "str", 123]:
self.assertEqual(dejsonize(jsonize(obj)), obj)
def test_jsonize_produces_text_not_identity(self):
# anchor: jsonize must serialize to a JSON string, not pass the object through
out = jsonize({"a": 1})
self.assertIsInstance(out, str)
self.assertIn('"a"', out)
self.assertEqual(jsonize(123), "123") # int -> textual "123"
class TestSerialize(unittest.TestCase):
# Smoke coverage of the safe (JSON-based) session serializer; the exhaustive corner-case
# and security suite lives in tests/test_serialize.py.
def test_roundtrip_allowed_types(self):
for obj in [[1, 2, 3], {"a": 1}, (1, 2), "text", 42, 3.14, True, None, {"k": [1, {"n": "v"}]}]:
self.assertEqual(deserializeValue(serializeValue(obj)), obj)
def test_bytes_roundtrip(self):
for raw in [b"x", b"\x00\x01\xff", b"\xde\xad\xbe\xef"]:
self.assertEqual(deserializeValue(serializeValue(raw)), raw, msg="bytes round-trip %r" % raw)
def test_bytes_nested_in_container_roundtrip(self):
for obj in [{"a": b"bytes"}, [b"ab", "s", 1, None], ("t", b"\xde\xad")]:
self.assertEqual(deserializeValue(serializeValue(obj)), obj, msg="nested-bytes round-trip %r" % (obj,))
def test_output_is_plain_ascii_text_no_base64(self):
# the serialized form must be readable JSON text (not Base64 / binary) so it lands verbatim in
# the TEXT session column with zero wrapping overhead
out = serializeValue({"k": [1, (2, 3)]})
self.assertIsInstance(out, str)
self.assertTrue(out.startswith("{") and out.endswith("}"), out)
def test_deserialize_accepts_bytes(self):
# BigArray hands the serialized data back as bytes (off a compressed disk chunk)
self.assertEqual(deserializeValue(getBytes(serializeValue([1, (2, 3)]))), [1, (2, 3)])
if __name__ == "__main__":
unittest.main(verbosity=2)