|
| 1 | +#!/usr/bin/env python |
| 2 | + |
| 3 | +""" |
| 4 | +Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) |
| 5 | +See the file 'LICENSE' for copying permission |
| 6 | +
|
| 7 | +The last branch of main()'s unhandled-exception chain in sqlmap.py: when the |
| 8 | +experimental Python JIT is turned on, a crash gets the tier-2 advisory (Reference: |
| 9 | +'https://github.com/python/cpython/issues/156319') instead of the automatic |
| 10 | +issue-creation prompt - but the traceback is still printed, so a genuine sqlmap |
| 11 | +bug is not swallowed. |
| 12 | +
|
| 13 | +Driven through a subprocess because it is main()'s own except chain under test. |
| 14 | +""" |
| 15 | + |
| 16 | +import os |
| 17 | +import subprocess |
| 18 | +import sys |
| 19 | +import unittest |
| 20 | + |
| 21 | +ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
| 22 | + |
| 23 | +# forces a crash inside init(), i.e. the same place #6117 landed, without needing a target. |
| 24 | +# codeIsModified() is neutralized because its branch sits earlier in the chain and would |
| 25 | +# otherwise win on any working tree with uncommitted edits (or a stale txt/checksum.md5). |
| 26 | +DRIVER = """ |
| 27 | +import sys |
| 28 | +sys.path.insert(0, %r) |
| 29 | +sys.argv = ["sqlmap.py", "-u", "http://127.0.0.1/?id=1", "--batch"] |
| 30 | +import sqlmap |
| 31 | +import lib.core.option |
| 32 | +sqlmap.codeIsModified = lambda: False |
| 33 | +lib.core.option.loadPayloads = lambda: "a" < 128 |
| 34 | +sqlmap.main() |
| 35 | +""" % ROOT |
| 36 | + |
| 37 | +ADVISORY = "experimental Python JIT compiler is turned on" |
| 38 | +TRACEBACK = "TypeError" |
| 39 | +PROMPT = "automatically create a new (anonymized) issue" |
| 40 | + |
| 41 | + |
| 42 | +def _run(jit): |
| 43 | + env = dict(os.environ) |
| 44 | + env["PYTHON_JIT"] = jit |
| 45 | + proc = subprocess.Popen([sys.executable, "-c", DRIVER], cwd=ROOT, env=env, |
| 46 | + stdin=subprocess.PIPE, stdout=subprocess.PIPE, |
| 47 | + stderr=subprocess.STDOUT) |
| 48 | + out = proc.communicate(input=b"")[0] |
| 49 | + return out.decode("utf-8", "replace") |
| 50 | + |
| 51 | + |
| 52 | +def _jitEnabled(jit): |
| 53 | + """Whether the interpreter actually honours PYTHON_JIT=<jit> (build-dependent).""" |
| 54 | + code = "import sys, os; print(sys._jit.is_enabled() if hasattr(sys, '_jit') else os.environ.get('PYTHON_JIT') == '1')" |
| 55 | + env = dict(os.environ) |
| 56 | + env["PYTHON_JIT"] = jit |
| 57 | + return subprocess.check_output([sys.executable, "-c", code], env=env).strip() == b"True" |
| 58 | + |
| 59 | + |
| 60 | +class TestJitGuard(unittest.TestCase): |
| 61 | + def test_advisory_replaces_issue_prompt_but_keeps_traceback(self): |
| 62 | + if not _jitEnabled('1'): |
| 63 | + self.skipTest("interpreter does not report the JIT as enabled") |
| 64 | + |
| 65 | + out = _run('1') |
| 66 | + self.assertIn(ADVISORY, out) |
| 67 | + self.assertIn(TRACEBACK, out) # a real bug must still be visible in full |
| 68 | + self.assertNotIn(PROMPT, out) # ... but not auto-filed while the JIT is on |
| 69 | + |
| 70 | + def test_normal_path_is_untouched_without_jit(self): |
| 71 | + if _jitEnabled('0'): |
| 72 | + self.skipTest("JIT stays enabled with PYTHON_JIT=0 on this build") |
| 73 | + |
| 74 | + out = _run('0') |
| 75 | + self.assertNotIn(ADVISORY, out) |
| 76 | + self.assertIn(TRACEBACK, out) |
| 77 | + self.assertIn(PROMPT, out) |
| 78 | + |
| 79 | + |
| 80 | +if __name__ == "__main__": |
| 81 | + unittest.main() |
0 commit comments