Adding support for better JSON comparison

This commit is contained in:
Miroslav Štampar 2026-06-16 10:02:44 +02:00
parent cc7f803d60
commit a0cbfba9bd
7 changed files with 256 additions and 32 deletions

View file

@ -1442,6 +1442,45 @@ def parseJson(content):
return retVal
def jsonMinimize(content):
"""
Returns an order-independent canonical "leaf-path" projection of a JSON document, used for
structure-aware response comparison (so key reordering / whitespace / number formatting do
not perturb the comparison ratio, while a changed value or array length does). Returns None
(and only None) when content is not parseable JSON, so callers can fall back to text comparison
>>> jsonMinimize('{"b": 2, "a": 1}') == jsonMinimize('{"a":1, "b":2}')
True
>>> jsonMinimize('{"a": {"b": 1}}') == '.a.b=1'
True
>>> jsonMinimize('not json') is None
True
>>> jsonMinimize('{}') == ''
True
"""
try:
data = json.loads(content)
except (ValueError, TypeError):
return None
lines = []
def _walk(obj, path):
if isinstance(obj, dict):
for key in sorted(obj): # sorted keys -> key-order/whitespace immune
_walk(obj[key], "%s.%s" % (path, key))
elif isinstance(obj, (list, tuple)):
lines.append("%s.__len__=%d" % (path, len(obj))) # length change always registers
for index in xrange(len(obj)): # index kept -> order-sensitive (correct for result sets)
_walk(obj[index], "%s[%d]" % (path, index))
else:
lines.append("%s=%s" % (path, obj)) # scalar values kept (boolean detection flips values)
_walk(data, "")
return "\n".join(sorted(lines))
def parsePasswordHash(password):
"""
In case of Microsoft SQL Server password hash value is expanded to its components

View file

@ -20,7 +20,7 @@ from lib.core.enums import OS
from thirdparty import six
# sqlmap version (<major>.<minor>.<month>.<monthly commit>)
VERSION = "1.10.6.118"
VERSION = "1.10.6.119"
TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable"
TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34}
VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE)

View file

@ -55,6 +55,7 @@ def vulnTest():
("--dummy", ("all tested parameters do not appear to be injectable", "does not seem to be injectable", "there is not at least one", "~might be injectable")),
("-u \"<url>&id2=1\" -p id2 -v 5 --flush-session --level=5 --text-only --test-filter=\"AND boolean-based blind - WHERE or HAVING clause (MySQL comment)\"", ("~1AND",)),
("--list-tampers", ("between", "MySQL", "xforwardedfor")),
("-u \"<url>&json=1\" -p id --flush-session --technique=B --banner", ("Type: boolean-based blind", "banner: '3.")), # JSON-response detection via the structure-aware oracle (no --string hint)
("-r <request> --flush-session -v 5 --test-skip=\"heavy\" --save=<config>", ("CloudFlare", "web application technology: Express", "possible DBMS: 'SQLite'", "User-Agent: foobar", "~Type: time-based blind", "saved command line options to the configuration file")),
("-c <config>", ("CloudFlare", "possible DBMS: 'SQLite'", "User-Agent: foobar", "~Type: time-based blind")),
("-l <log> --flush-session --keep-alive --skip-waf -vvvvv --technique=U --union-from=users --banner --parse-errors", ("banner: '3.", "ORDER BY term out of range", "~xp_cmdshell", "Connection: keep-alive")),