mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2026-10-12 02:22:55 +00:00
Minor update
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)):
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user