#!/usr/bin/env python """ Copyright (c) 2006-2026 sqlmap developers (https://sqlmap.org) See the file 'LICENSE' for copying permission Additional coverage for option setup / normalization helpers in lib/core/option.py, targeting functions and branches NOT already exercised by tests/test_option_setup.py: * _setTamperingFunctions (loads real tamper modules into kb.tamperFunctions) * _setPreprocessFunctions (loads a preprocess(req) script into kb.preprocessFunctions) * _setPostprocessFunctions (loads a postprocess(page, headers, code) script) * _setSafeVisit (parses a safe request file into kb.safeReq) * _cleanupOptions (additional normalization branches: delay cast, csvDel/paramDel escape, col/binaryFields split, torType upper, abortCode, getAll, dummy->batch) * _basicOptionValidation (additional illegal option combinations / branches) * _normalizeOptions (string + boolean option coercion) * setVerbosity (eta clamp + high verbose) As in test_option_setup.py, option.py mutates the global conf/kb singletons aggressively, so every test saves and restores the conf/kb fields it touches via the _preserve() context manager so the shared state stays pristine for the rest of the suite. """ import contextlib import logging import os import sys import unittest sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from _testutils import bootstrap bootstrap() from lib.core.data import conf, kb, logger, paths from lib.core.exception import SqlmapSyntaxException from lib.core.exception import SqlmapSystemException from lib.core.exception import SqlmapGenericException from lib.core.exception import SqlmapFilePathException from lib.core.exception import SqlmapValueException from lib.core.settings import MAX_CONNECT_RETRIES import lib.core.option as option _SENTINEL = object() # scratchpad for the preprocess/postprocess/safe-req fixture files _SCRATCH = os.environ.get("CLAUDE_SCRATCH") or os.path.join(os.path.dirname(os.path.abspath(__file__)), "_option_more_tmp") def tearDownModule(): """Remove the scratch fixture directory so it never lingers on disk (and so a stray __init__.py there can't shadow imports in a subsequent run).""" import shutil if os.path.isdir(_SCRATCH): shutil.rmtree(_SCRATCH, ignore_errors=True) @contextlib.contextmanager def _preserve(target, *keys): """Save the given keys of an AttribDict (conf/kb), then restore on exit. Missing keys are restored to absent so a test can't leak a brand-new field. """ saved = {} for key in keys: saved[key] = target[key] if key in target else _SENTINEL try: yield finally: for key in keys: if saved[key] is _SENTINEL: try: del target[key] except KeyError: pass else: target[key] = saved[key] class _ImportSandboxMixin(object): """Loaders in option.py (tamper/preprocess/postprocess) permanently `sys.path.insert(0,