#!/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"a’b™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)