sqlmap/tests/test_dialect.py
2026-06-15 09:50:47 +02:00

107 lines
4.1 KiB
Python

#!/usr/bin/env python
"""
Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org)
See the file 'LICENSE' for copying permission
Per-DBMS query building (the injection-engine "dialect" layer).
These pin the exact SQL that agent.* emits for each back-end. They are the
regression net for queries.xml edits and for dialect gates in agent.py - the
kind of change that silently mis-builds a payload for one DBMS while leaving
every other green.
Includes the SYBASE limitQuery fix: Sybase must now emit a TOP-based limited
query like MSSQL (previously it fell through and returned the query unchanged).
"""
import os
import sys
import unittest
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from _testutils import bootstrap, set_dbms
bootstrap()
from lib.core.agent import agent
from lib.core.enums import DBMS
class TestLimitQuery(unittest.TestCase):
"""agent.limitQuery(num, query, field) per dialect (probed, not guessed)."""
def test_mysql(self):
set_dbms(DBMS.MYSQL)
self.assertEqual(agent.limitQuery(0, "SELECT name FROM users", "name"),
"SELECT name FROM users LIMIT 0,1")
def test_pgsql(self):
set_dbms(DBMS.PGSQL)
self.assertEqual(agent.limitQuery(0, "SELECT name FROM users", "name"),
"SELECT name FROM users OFFSET 0 LIMIT 1")
def test_oracle(self):
set_dbms(DBMS.ORACLE)
self.assertEqual(agent.limitQuery(0, "SELECT name FROM users", "name"),
"SELECT name FROM (SELECT name,ROWNUM AS CAP FROM users) WHERE CAP=1")
def test_mssql_is_top_based(self):
set_dbms(DBMS.MSSQL)
q = agent.limitQuery(0, "SELECT name FROM users", "name")
self.assertTrue(q.startswith("SELECT TOP 1 name FROM users WHERE"), msg=q)
self.assertIn("ORDER BY 1", q)
def test_sybase_limit_fix_is_top_based(self):
# REGRESSION: the user's limitQuery fix. Sybase must now produce a TOP-based
# limited query (mirroring MSSQL), NOT the query returned unchanged.
set_dbms(DBMS.SYBASE)
q = agent.limitQuery(0, "SELECT name FROM users", "name")
self.assertTrue(q.startswith("SELECT TOP 1 name FROM users WHERE"), msg=q)
self.assertIn("ORDER BY 1", q)
self.assertNotEqual(q, "SELECT name FROM users") # the pre-fix (broken) behavior
# Sybase casts via CONVERT(VARCHAR(...)), distinguishing it from MSSQL's NVARCHAR
self.assertIn("CONVERT(VARCHAR", q)
class TestNullAndCastField(unittest.TestCase):
"""agent.nullAndCastField('col') differs per dialect - pin each."""
CASES = {
DBMS.MYSQL: "IFNULL(CAST(col AS NCHAR),' ')",
DBMS.MSSQL: "ISNULL(CAST(col AS NVARCHAR(4000)),' ')",
DBMS.SYBASE: "ISNULL(CONVERT(VARCHAR(4000),col),' ')",
DBMS.PGSQL: "COALESCE(CAST(col AS VARCHAR(10000))::text,' ')",
DBMS.ORACLE: "NVL(CAST(col AS VARCHAR(4000)),' ')",
}
def test_per_dbms(self):
for dbms, expected in self.CASES.items():
set_dbms(dbms)
self.assertEqual(agent.nullAndCastField("col"), expected, msg="nullAndCastField for %s" % dbms)
class TestHexConvertField(unittest.TestCase):
# hexConvertField differs per dialect; pin each (was a one-platform stub before)
CASES = {
DBMS.MYSQL: "HEX(name)",
DBMS.ORACLE: "RAWTOHEX(name)",
DBMS.PGSQL: "ENCODE(CONVERT_TO((name),'UTF8'),'HEX')",
DBMS.MSSQL: "master.dbo.fn_varbintohexstr(CAST(name AS VARBINARY(8000)))",
}
def test_per_dbms(self):
for dbms, expected in self.CASES.items():
set_dbms(dbms)
self.assertEqual(agent.hexConvertField("name"), expected, msg="hexConvertField for %s" % dbms)
class TestForgeUnionQuery(unittest.TestCase):
def test_position_and_count(self):
# count=3, position=1 -> the real column is slotted at index 1, NULLs elsewhere
set_dbms(DBMS.MYSQL)
q = agent.forgeUnionQuery("SELECT a FROM t", 1, 3, None, "", "", "NULL", None)
self.assertEqual(q, " UNION ALL SELECT NULL,a,NULL FROM t")
if __name__ == "__main__":
unittest.main(verbosity=2)