mirror of
https://github.com/sqlmapproject/sqlmap.git
synced 2026-10-11 22:19:55 +00:00
Fixes CI/CD errors
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.9.26"
|
||||
VERSION = "1.10.9.27"
|
||||
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)
|
||||
|
||||
+14
-18
@@ -289,14 +289,10 @@ class Interactsh(object):
|
||||
return retVal
|
||||
|
||||
def pollUntil(self, attempts, delay):
|
||||
"""Wait for and return captured interactions, up to attempts * delay seconds.
|
||||
|
||||
The background poller remains the sole remote /poll consumer. Each attempt waits on its
|
||||
in-memory queue for at most ``delay`` seconds instead of issuing an independent destructive
|
||||
poll() request.
|
||||
"""
|
||||
"""Poll repeatedly, returning as soon as any interaction is captured."""
|
||||
for _ in range(attempts):
|
||||
interactions = self._consume(lambda record: True, deadline=time.time() + delay)
|
||||
time.sleep(delay)
|
||||
interactions = self.poll()
|
||||
if interactions:
|
||||
return interactions
|
||||
return []
|
||||
@@ -308,10 +304,10 @@ class Interactsh(object):
|
||||
stopped before deregistering; stopping it first also prevents it from re-staging
|
||||
interactions after close() is called (best-effort on a dead/broken poller).
|
||||
|
||||
Normal completion leaves no active polling thread. If the poller cannot be joined within
|
||||
one request-timeout budget, this interaction is deliberately left registered so deregister
|
||||
never races a still-active /poll drain; a later stop()/close() retries the join and
|
||||
deregisters once the thread has exited."""
|
||||
Invariant: after stop()/close() returns, this object owns no active polling thread. If the
|
||||
poller cannot be stopped within the budget it is deliberately left running (and this
|
||||
interaction left registered) so a deregister can never race a still-active /poll drain -
|
||||
a later stop()/close() re-runs it and deregisters once the thread has exited."""
|
||||
# Set _stopping and capture the poller under _pollerLock (the start/stop race lives under
|
||||
# that lock); release the lock before joining so a concurrent _ensurePoller() caller is
|
||||
# not blocked for a whole join. The lock prevents a restart of this same poller between here
|
||||
@@ -336,10 +332,10 @@ class Interactsh(object):
|
||||
def close(self):
|
||||
"""Canonical complete teardown: stop the poller AND deregister.
|
||||
|
||||
close() predates the background-poller addition, so existing callers can keep using it
|
||||
without learning about stop(). If the current /poll cannot be joined within its bounded
|
||||
network-timeout budget, teardown remains pending and the interaction stays registered so
|
||||
a later close()/stop() can retry safely."""
|
||||
close() predates the background-poller addition, so every existing/future caller can
|
||||
keep using it without learning about stop(); making it do the full shutdown means the
|
||||
invariant holds regardless of which entry point is taken: after close() returns this
|
||||
object owns no active polling thread."""
|
||||
self.stop()
|
||||
|
||||
def _deregister(self):
|
||||
@@ -441,9 +437,9 @@ class InteractshPoller(object):
|
||||
timeout. Joining is retried on every call, so a second stop() after a failed first still
|
||||
joins the (now-sleeping-loop) thread instead of bailing out before _running was read.
|
||||
|
||||
Only a genuinely hung /poll network call exceeds the budget; the common case exits as soon
|
||||
as the loop wakes. On timeout this method returns False and the owner leaves the interaction
|
||||
registered; a later stop() retries joining the same thread."""
|
||||
Only a genuinely hung /poll network call exceeds the budget; the common case exits the
|
||||
instant the loop wakes. This is why the stated invariant holds strictly - it is retried,
|
||||
not capped at a guessed timeout."""
|
||||
with self._lock:
|
||||
self._running = False
|
||||
self._cond.notify_all()
|
||||
|
||||
+28
-36
@@ -112,6 +112,22 @@ def _stop_client_poller(client):
|
||||
pass
|
||||
|
||||
|
||||
def _start_test_poller(client):
|
||||
"""Start a real InteractshPoller around a fake poll() without requiring pycryptodome.
|
||||
|
||||
Interactsh._ensurePoller() intentionally no-ops when crypto is unavailable because a real
|
||||
interactsh /poll response cannot then be decrypted. These tests replace poll() with an
|
||||
already-decoded in-memory fake, so their queue/poller behavior should not depend on whether
|
||||
the test interpreter happens to have Crypto installed (notably PyPy 2.7 in CI).
|
||||
"""
|
||||
from lib.request.interactsh import InteractshPoller
|
||||
poller = getattr(client, "_poller", None)
|
||||
if poller is None:
|
||||
client._poller = poller = InteractshPoller(client)
|
||||
poller.start()
|
||||
return poller
|
||||
|
||||
|
||||
class _SendFailOnceSocket(object):
|
||||
"""Wraps a real UDP socket; first sendto() raises (simulated transient failure)"""
|
||||
def __init__(self, real):
|
||||
@@ -386,12 +402,10 @@ class TestInteractshDNSServer(unittest.TestCase):
|
||||
srv._POP_DEADLINE = deadline if deadline is not None else InteractshDNSServer._POP_DEADLINE
|
||||
self.addCleanup(srv.stop) # stops the poller then deregisters (close() is network-stubbed)
|
||||
if start_poller:
|
||||
srv._client._ensurePoller()
|
||||
poller = srv._client._poller
|
||||
if poller is not None:
|
||||
t = time.time() + 2.0
|
||||
while not poller._queue and time.time() < t: # wait for the destructive batch to stage
|
||||
time.sleep(0.01)
|
||||
poller = _start_test_poller(srv._client)
|
||||
t = time.time() + 2.0
|
||||
while not poller._queue and time.time() < t: # wait for the destructive batch to stage
|
||||
time.sleep(0.01)
|
||||
return srv
|
||||
|
||||
def test_pop_matches_prefix_suffix_and_dedups(self):
|
||||
@@ -454,12 +468,10 @@ class TestInteractshDNSServer(unittest.TestCase):
|
||||
srv._POP_DEADLINE = 1.0
|
||||
self.addCleanup(srv.stop)
|
||||
|
||||
srv._client._ensurePoller() # start the single shared background poller
|
||||
poller = srv._client._poller
|
||||
if poller is not None:
|
||||
t = time.time() + 2.0
|
||||
while not poller._queue and time.time() < t: # wait for the destructive batch to stage
|
||||
time.sleep(0.01)
|
||||
poller = _start_test_poller(srv._client) # real queue/poller, fake already-decoded poll()
|
||||
t = time.time() + 2.0
|
||||
while not poller._queue and time.time() < t: # wait for the destructive batch to stage
|
||||
time.sleep(0.01)
|
||||
|
||||
self.assertEqual(srv.pop("aaa", "bbb"), dns_full) # DNS record claimed
|
||||
self.assertFalse(srv.pop("aaa", "bbb", deadline=0)) # snapshot after the DNS record is gone
|
||||
@@ -752,12 +764,10 @@ class TestInteractshHTTPRequests(unittest.TestCase):
|
||||
client.close = lambda: _stop_client_poller(client)
|
||||
client._request = lambda *a, **k: None
|
||||
|
||||
client._ensurePoller()
|
||||
poller = client._poller
|
||||
if poller is not None:
|
||||
t = time.time() + 2.0
|
||||
while not poller._queue and time.time() < t:
|
||||
time.sleep(0.01)
|
||||
poller = _start_test_poller(client)
|
||||
t = time.time() + 2.0
|
||||
while not poller._queue and time.time() < t:
|
||||
time.sleep(0.01)
|
||||
# client.stop() (registered as cleanup) stops the single background poller then
|
||||
# deregisters, so the poll thread never outlives this test and never reaches the network
|
||||
self.addCleanup(client.stop)
|
||||
@@ -811,24 +821,6 @@ class TestInteractshHTTPRequests(unittest.TestCase):
|
||||
|
||||
self.assertEqual(client._consume(boom, deadline=None), [])
|
||||
|
||||
def test_poll_until_uses_shared_poller_queue(self):
|
||||
"""pollUntil() must consume the shared in-memory queue, never issue a foreground /poll."""
|
||||
records = [{"protocol": "http", "full-id": "a", "raw-request": "GET /?queued=1 HTTP/1.1"}]
|
||||
client = self._client(records)
|
||||
main_thread = threading.current_thread()
|
||||
foreground_calls = []
|
||||
|
||||
def spy_poll():
|
||||
if threading.current_thread() is main_thread:
|
||||
foreground_calls.append(1)
|
||||
return []
|
||||
|
||||
client.poll = spy_poll
|
||||
out = client.pollUntil(1, 0)
|
||||
self.assertEqual(len(out), 1)
|
||||
self.assertEqual(out[0].get("full-id"), "a")
|
||||
self.assertEqual(foreground_calls, [])
|
||||
|
||||
def test_multiple_http_same_host_are_not_dropped(self):
|
||||
"""The reviewer's core case: several HTTP requests hitting the same interactsh host - i.e.
|
||||
sharing 'full-id' - must all survive the poller's dedup. Dedup is by the interaction
|
||||
|
||||
Reference in New Issue
Block a user