Minor update

This commit is contained in:
Miroslav Štampar
2026-08-11 23:07:22 +02:00
parent 53411f6fd8
commit 90b14df46c
5 changed files with 126 additions and 2 deletions
+1 -1
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.8.25"
VERSION = "1.10.8.26"
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)
+10
View File
@@ -55,6 +55,7 @@ from lib.core.settings import ODATA_KEY_CANDIDATES
from lib.core.settings import ODATA_MAX_KEY
from lib.core.settings import ODATA_MAX_LENGTH
from lib.core.settings import ODATA_MAX_RECORDS
from lib.core.settings import SQLMAP_ENVIRONMENT_PREFIX
from lib.core.settings import UPPER_RATIO_BOUND
from lib.request.connect import Connect as Request
from lib.utils.xrange import xrange
@@ -416,6 +417,9 @@ def _findKeyAndEntities(place, parameter, boundary, emptyPage, errorSurface=True
else:
misses += 1
if present:
if len(present) >= ODATA_MAX_RECORDS:
logger.warning("stopped at the %d-entity cap, so further entities are left undumped "
"(raise it with %s_ODATA_MAX_RECORDS)" % (ODATA_MAX_RECORDS, SQLMAP_ENVIRONMENT_PREFIX))
return candidate, present
return None, []
@@ -463,6 +467,12 @@ def _inferField(oracle, key, keyValue, field, maxLen=ODATA_MAX_LENGTH, useIn=Non
hi = mid - 1
length = lo
if length >= maxLen:
# the bisection is bounded, so a value at the bound is a FLOOR, not a measurement - saying
# nothing here hands back a prefix that reads exactly like a complete value
logger.warning("property '%s' is at least %d characters; the recovered value is truncated "
"at the cap (raise it with %s_ODATA_MAX_LENGTH)" % (field, maxLen, SQLMAP_ENVIRONMENT_PREFIX))
chars = []
for pos in xrange(length):
# A whole candidate set is asked about in ONE request, so a probe halves the space just as
+14 -1
View File
@@ -58,6 +58,7 @@ from lib.core.settings import SPARQL_ERROR_SIGNATURES
from lib.core.settings import SPARQL_MAX_LENGTH
from lib.core.settings import SPARQL_MAX_PREDICATES
from lib.core.settings import SPARQL_MAX_RECORDS
from lib.core.settings import SQLMAP_ENVIRONMENT_PREFIX
from lib.core.settings import UPPER_RATIO_BOUND
from lib.request.connect import Connect as Request
from lib.utils.xrange import xrange
@@ -397,6 +398,12 @@ def _inferString(truth, inner, maxLen=SPARQL_MAX_LENGTH):
hi = mid - 1
length = lo
if length >= maxLen:
# the bisection is bounded, so a value at the bound is a FLOOR, not a measurement - saying
# nothing here hands back a prefix that reads exactly like a complete IRI or literal
logger.warning("value is at least %d characters; it is truncated at the cap "
"(raise it with %s_SPARQL_MAX_LENGTH)" % (maxLen, SQLMAP_ENVIRONMENT_PREFIX))
chars = []
last = len(_CS_ORDS) - 1
for pos in xrange(1, length + 1):
@@ -445,7 +452,13 @@ def _dumpGraph(truth):
if predicates is None or triples is None:
logger.warning("dump aborted: the oracle could not resolve the graph size; detection stands")
return
logger.info("default graph holds %d distinct predicate(s) and %d triple(s)" % (predicates, triples))
# _inferCount bisects inside a bound, so a count sitting ON that bound is a floor, not a count
saturated = predicates >= SPARQL_MAX_PREDICATES
logger.info("default graph holds %s%d distinct predicate(s) and %d triple(s)"
% ("at least " if saturated else "", predicates, triples))
if saturated:
logger.warning("the predicate count hit the %d cap, so the list below is partial "
"(raise it with %s_SPARQL_MAX_PREDICATES)" % (SPARQL_MAX_PREDICATES, SQLMAP_ENVIRONMENT_PREFIX))
predRows = []
for offset in xrange(min(predicates, SPARQL_MAX_PREDICATES)):
+46
View File
@@ -340,6 +340,52 @@ class TestDetectionAndExtraction(unittest.TestCase):
self.assertIsNone(template)
class TestCapsAreDisclosed(unittest.TestCase):
"""Every bound here is a bisection bound, so a result sitting ON it is a FLOOR, not a measurement.
Handing back a capped prefix (or a capped entity list) with no word said reads exactly like a
complete answer - the one failure mode a blind dumper must never have."""
def setUp(self):
self.saved, self.savedParams = odata._send, odata.conf.parameters
odata._send = _mockSend
odata.conf.parameters = {odata.PLACE.GET: "name=luther"}
odata.SENTINEL = "zzsentinelzz"
self.savedWarning, self.warnings = odata.logger.warning, []
odata.logger.warning = lambda message, *args: self.warnings.append(message % args if args else message)
def tearDown(self):
odata._send, odata.conf.parameters = self.saved, self.savedParams
odata.logger.warning = self.savedWarning
def _oracle(self):
_t, _p, boundary = odata._detectBoolean(odata.PLACE.GET, "name")
oracle = odata._makeOracle(odata.PLACE.GET, "name", boundary, truePredicate="(Id eq 1)")
self.assertIsNotNone(oracle)
return oracle
def test_truncated_value_is_reported(self):
value = odata._inferField(self._oracle(), "Id", 1, "Secret", maxLen=4)
self.assertEqual(value, "S3CR")
self.assertTrue(any("at least 4 characters" in _ for _ in self.warnings), self.warnings)
def test_value_that_fits_is_silent(self):
value = odata._inferField(self._oracle(), "Id", 1, "Name", maxLen=64)
self.assertEqual(value, "luther")
self.assertEqual(self.warnings, [])
def test_entity_cap_is_reported(self):
savedCap, odata.ODATA_MAX_RECORDS = odata.ODATA_MAX_RECORDS, 2
try:
_t, _p, boundary = odata._detectBoolean(odata.PLACE.GET, "name")
key, keys = odata._findKeyAndEntities(odata.PLACE.GET, "name", boundary, _EMPTY)
finally:
odata.ODATA_MAX_RECORDS = savedCap
self.assertEqual(key, "Id")
self.assertEqual(keys, [1, 2]) # the third entity exists but is beyond the cap...
self.assertTrue(any("2-entity cap" in _ for _ in self.warnings), self.warnings)
_LITERAL = r"'(?:[^']|'')*'"
_IN_REGEX = re.compile(r"(?P<lhs>substring\(\w+,\d+,1\)|%s) in \((?P<items>%s(?:,%s)*)\)"
% (_LITERAL, _LITERAL, _LITERAL))
+55
View File
@@ -325,5 +325,60 @@ class TestDetectionAndExtraction(unittest.TestCase):
self.assertIsNone(template) # no true/false divergence -> not even boolean-detected
class _Dumper(object):
"""Stands in for conf.dumper, which the offline bootstrap does not build."""
def __init__(self):
self.output = []
def singleString(self, value):
self.output.append(value)
class TestCapsAreDisclosed(unittest.TestCase):
"""Both bounds here are bisection bounds, so a result sitting ON one is a FLOOR, not a measurement.
A capped IRI reads exactly like a whole one, and a capped predicate count reads like the size of
the graph - the reader has no way to tell unless it is said."""
def setUp(self):
self.saved, self.savedParams = sparql._send, sparql.conf.parameters
sparql._send = _mockSend
sparql.conf.parameters = {sparql.PLACE.GET: "q=luther"}
sparql.SENTINEL = "zzsentinelzz"
self.savedDumper, sparql.conf.dumper = sparql.conf.get("dumper"), _Dumper()
self.savedWarning, self.warnings = sparql.logger.warning, []
sparql.logger.warning = lambda message, *args: self.warnings.append(message % args if args else message)
def tearDown(self):
sparql._send, sparql.conf.parameters = self.saved, self.savedParams
sparql.conf.dumper = self.savedDumper
sparql.logger.warning = self.savedWarning
def _truth(self):
_t, _p, boundary = sparql._detectBoolean(sparql.PLACE.GET, "q")
truth = sparql._makeOracle(sparql.PLACE.GET, "q", boundary)
self.assertIsNotNone(truth)
return truth
def test_truncated_value_is_reported(self):
value = sparql._inferString(self._truth(), sparql._nthObject(0), maxLen=4)
self.assertEqual(value, _OBJECTS[0][:4])
self.assertTrue(any("at least 4 characters" in _ for _ in self.warnings), self.warnings)
def test_value_that_fits_is_silent(self):
value = sparql._inferString(self._truth(), sparql._nthObject(0), maxLen=64)
self.assertEqual(value, _OBJECTS[0])
self.assertEqual(self.warnings, [])
def test_saturated_predicate_count_is_reported_as_a_floor(self):
saved, sparql.SPARQL_MAX_PREDICATES = sparql.SPARQL_MAX_PREDICATES, 2
try:
sparql._dumpGraph(self._truth())
finally:
sparql.SPARQL_MAX_PREDICATES = saved
self.assertTrue(any("hit the 2 cap" in _ for _ in self.warnings), self.warnings)
if __name__ == "__main__":
unittest.main(verbosity=2)