import re

class SwitchValidateController:
    """
    Provides methods to validate switch for different tools.
    """
    allowed_chars = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_. :/@*,=")
    MAX_LENGTH = 80
    BLACKLIST_PATTERNS = [r';', r'&&', r'\|\|', r'`', r'\$\(.*\)', r'<', r'>']

    @staticmethod
    def validate_switch(switch: str, tool_name: str) -> bool:
        if len(switch) > SwitchValidateController.MAX_LENGTH:
            return False

        if not all(c in SwitchValidateController.allowed_chars for c in switch):
            return False

        for pattern in SwitchValidateController.BLACKLIST_PATTERNS:
            if re.search(pattern, switch):
                return False

        method_name = f'_validate_{tool_name.lower().replace("-", "_")}_switch'
        validator = getattr(SwitchValidateController, method_name, None)
        if validator:
            return validator(switch)
        return True

    @staticmethod
    def _validate_nmap_switch(switch: str) -> bool:
        nmap_allowed_switch = [
            '--append-output', '--badsum', '--data', '--data-length', '--data-string', '--datadir',
            '--dns-servers', '--exclude', '--exclude-ports', '--excludefile', '--host-timeout',
            '--iflist', '--ip-options', '--max-rate', '--max-retries', '--max-scan-delay',
            '--min-hostgroup', '--min-parallelism', '--min-rate', '--min-rtt-timeout', '--mtu',
            '--no-stylesheet', '--noninteractive', '--open', '--osscan-guess', '--osscan-limit',
            '--packet-trace', '--port-ratio', '--privileged', '--proxies', '--reason', '--resume',
            '--scan-delay', '--scanswitch', '--script', '--script-args', '--script-args-file',
            '--script-help', '--script-trace', '--script-updatedb', '--send-eth', '--send-ip',
            '--source-port', '--spoof-mac', '--stylesheet', '--system-dns', '--top-ports',
            '--traceroute', '--ttl', '--unprivileged', '--version-all', '--version-intensity',
            '--version-light', '--version-trace', '--webxml', '-2', '-5', '-6', '-A', '-D', '-E', '-F',
            '-O', '-P', '-R', '-S', '-T', '-V', '-b', '-c', '-d', '-e', '-f', '-g', '-h', '-i', '-l',
            '-n', '-o', '-p', '-r', '-s', '-t', '-v', '-sT', '-sU', '-sN', '-sF', '-sX', '-sC', '-sV',
            '-Pn', '-T0', '-T1', '-T2', '-T3', '-T4', '-T5', '-oN', '-oX', '-oG', '-oA', '-iL', '-iR'
        ]
        switch_list = re.findall(r'(?:[^\s,"]|"(?:\\.|[^"])*")+', switch)
        for arg in switch_list:
            if arg.startswith('-'):
                arg_base = arg.split('=')[0]
                if arg_base not in nmap_allowed_switch:
                    return False
        return True

    @staticmethod
    def _validate_bettercap_switch(switch: str) -> bool:
        bettercap_allowed_switch = [
            '--help', '-version', '--env', '-script', '-eval', '-silent', '-no-colors',
            '-autostart', '-caplet', '-caplets-path', '-cpu-profile', '-debug', '-env-file',
            '-gateway-override', '-iface', '-mem-profile', '-no-history', '-pcap-buf-size',
            '--sniffer-source', '--sniffer-destination', '--httpd', '--httpd-port', '--dns',
            '--dns-port', '--dns-ttl', '--dns-fake-ip', '--dns-fake', '--dns-server', '--dns-domain',
            '--dns-resolve', '--dns-spoof', '--dns-gateway', '--dns-dnscrypt', '--dns-dnscrypt-port',
            '--dns-dnscrypt-proxy', '--dns-dnscrypt-resolver', '--dns-dnscrypt-local', '--dns-dnscrypt-ec',
            '--dns-dnscrypt-version', '--dns-dnscrypt-curve', '--dns-dnscrypt-provider', '--dns-dnscrypt-key',
            '--dns-dnscrypt-dnssec', '--dns-dnscrypt-dnssec-valid', '--dns-dnscrypt-dnssec-non-valid'
        ]
        switch_list = switch.split()
        for arg in switch_list:
            if arg.startswith('-') and arg not in bettercap_allowed_switch:
                return False
        return True

    @staticmethod
    def _validate_sqlmap_switch(switch: str) -> bool:
        sqlmap_allowed_switch = [
            '-u', '--url', '--dbs', '--tables', '--columns', '--dump', '--dump-all', '--search',
            '--sql-query', '--sql-shell', '--sql-file', '--batch', '--threads', '--tamper',
            '--random-agent', '--proxy', '--tor', '--check-tor', '--tor-type', '--level', '--risk',
            '--string', '--not-string', '--regexp', '--code', '--text-only', '--titles', '--technique',
            '--time-sec', '--union-cols', '--union-char', '--union-from', '--dns-domain', '--second-order',
            '--delay', '--timeout', '--retries', '--randomize', '--scope', '--alert', '--safe-url',
            '--safe-freq', '--mobile', '--os', '--os-shell', '--os-pwn', '--os-smbrelay', '--os-bof',
            '--priv-esc', '--msf-path', '--tmp-path', '--wizard', '--answers', '--safe-url', '--auth-type',
            '--auth-cred', '--auth-cert', '--auth-cert-key', '--auth-verify', '--auth-pwd', '--auth-sso',
            '--for-mysql', '--for-mssql', '--for-postgres', '--for-oracle', '--for-sqlite', '--for-access',
            '--for-firebird', '--for-sybase', '--for-maxdb', '--for-db2', '--for-informix', '--batch',
            '--flush-session', '--forms', '--crawl', '--crawl-exclude', '--crawl-depth', '--crawl-spider',
            '--forms', '--csrf-token', '--csrf-url', '--csrf-value', '--csrf-cookie', '--force-ssl',
            '--force-ssl-verify', '--force-ssl-no-verify', '--force-ssl-ciphers', '--force-ssl-protocols',
            '--force-ssl-versions', '--force-ssl-cert', '--force-ssl-key', '--force-ssl-pwd', '--force-ssl-sni',
            '--force-ssl-dns', '--force-ssl-ca', '--force-ssl-ca-path', '--force-ssl-ignore-cts',
            '--force-ssl-no-cts', '--force-ssl-no-staple', '--force-ssl-no-hsts', '--force-ssl-ignore-timestamp',
            '--force-ssl-ignore-expiration', '--force-ssl-ignore-revocation', '--force-ssl-no-revocation',
            '--force-ssl-no-tls1', '--force-ssl-no-tls1_1', '--force-ssl-no-tls1_2', '--force-ssl-no-tls1_3'
        ]
        switch_list = switch.split()
        for arg in switch_list:
            if arg.startswith('--') and arg not in sqlmap_allowed_switch:
                return False
        return True

    @staticmethod
    def _validate_evil_winrm_switch(switch: str) -> bool:
        evilwinrm_allowed_switch = [
           '-S', '--ssl', '-c', '--pub-key', '-k', '--priv-key', '-r', '--realm', '-s',
           '--scripts', '--spn', '-e', '--executables', '-i', '--ip', '-U', '--url',
           '-u', '--user', '-p', '--password', '-H', '--hash', '-P', '--port', '-V',
           '--version', '-n', '--no-colors', '-N', '--no-rpath-completion', '-l', '--log',
           '-h', '--help'
        ]
        switch_list = switch.split()
        for arg in switch_list:
            if arg.startswith('-') and arg not in evilwinrm_allowed_switch:
                return False
        return True

    @staticmethod
    def _validate_wpscan_switch(switch: str) -> bool:
        wpscan_allowed_switch = [
            '--url', '-h', '--help', '-hh', '--version', '--ignore-main-redirect', '-v',
            '--verbose', '--banner', '--no-banner', '--max-scan-duration', '-o', '--output',
            '-f', '--format', '--detection-mode', '--scope', '--user-agent', '--ua',
            '--headers', '--vhost', '--random-user-agent', '--rua', '--user-agents-list',
            '--http-auth', '-t', '--max-threads', '--throttle', '--request-timeout',
            '--connect-timeout', '--disable-tls-checks', '--proxy', '--proxy-auth',
            '--cookie-string', '--cookie-jar', '--cache-ttl', '--clear-cache',
            '--cache-dir', '--server', '--force', '--update', '--no-update', 'api-token',
            '--wp-content-dir', '--wp-plugins-dir', '--interesting-findings-detection',
            '--wp-version-all', '--wp-version-detection', '--main-theme-detection',
            '-e', '--enumerate', '--exclude-content-based', '--plugins-list',
            '--plugins-detection', '--plugins-version-all', '--plugins-version-detection',
            '--plugins-threshold', '--themes-list', '--themes-detection',
            '--themes-version-all', '--themes-version-detection', '--themes-threshold',
            '--timthumbs-list', '--timthumbs-detection', '--config-backups-list',
            '--config-backups-detection', '--db-exports-list', '--db-exports-detection',
            '--medias-detection', '--users-list', '--users-detection',
            '--exclude-usernames', '-P', '--passwords', '-U', '--usernames',
            '--multicall-max-passwords', '--password-attack', '--login-uri', '--stealthy'
        ]
        switch_list = switch.split()
        for arg in switch_list:
            if arg.startswith('-') and arg not in wpscan_allowed_switch:
                return False
        return True

    @staticmethod
    def _validate_katana_switch(switch: str) -> bool:
        katana_allowed_switch = [
            '-u', '-list', '-resume', '-e', '-exclude', '-r', '-resolvers', '-d',
            '-depth', '-jc', '-js-crawl', '-jsl', '-jsluice', '-ct', '-crawl-duration',
            '-kf', '-known-files', '-mrs', '-max-response-size', '-timeout', '-aff',
            '-automatic-form-fill', '-fx', '-form-extraction', '-retry', '-proxy',
            '-td', '-tech-detect', '-H', '-headers', '-config', '-fc', '-form-config',
            '-flc', '-field-config', '-s', '-strategy', '-iqp', '-ignore-query-params',
            '-tlsi', '-tls-impersonate', '-dr', '-disable-redirects', '-hc',
            '-health-check', '-elog', '-error-log', '-pprof-server', '-hl',
            '-headless', '-sc', '-system-chrome', '-sb', '-show-browser', '-ho',
            '-headless-options', '-nos', '-no-sandbox', '-cdd', '-chrome-data-dir',
            '-scp', '-system-chrome-path', '-noi', '-no-incognito', '-cwu',
            '-chrome-ws-url', '-xhr', '-xhr-extraction', '-cs', '-crawl-scope',
            '-cos', '-crawl-out-scope', '-fs', '-field-scope', '-ns', '-no-scope',
            '-do', '-display-out-scope', '-mr', '-match-regex', '-fr', '-filter-regex',
            '-f', '-field', '-sf', '-store-field', '-em', '-extension-match', '-ef',
            '-extension-filter', '-mdc', '-match-condition', '-fdc', '-filter-condition',
            '-c', '-concurrency', '-p', '-parallelism', '-rd', '-delay', '-rl',
            '-rate-limit', '-rlm', '-rate-limit-minute', '-update', '-up', '-duc',
            '-disable-update-check', '-o', '-output', '-sr', '-store-response',
            '-srd', '-store-response-dir', '-ncb', '-no-clobber', '-sfd',
            '-store-field-dir', '-or', '-omit-raw', '-ob', '-omit-body', '-j',
            '-jsonl', '-nc', '-no-color', '-silent', '-v', '-verbose', '-debug',
            '-version'
        ]
        switch_list = switch.split()
        for arg in switch_list:
            if arg.startswith('-') and arg not in katana_allowed_switch:
                return False
        return True

    @staticmethod
    def _validate_subfinder_switch(switch: str) -> bool:
        subfinder_allowed_switch = [
            '-d', '-domain', '-s', '-sources', '-recursive', '-all', '-es', 'exclude-sources',
            '-m', '-match', '-f', '-filter', '-rl', '-rate-limit', '-rls', '-rate-limits',
            '-t', '-up', '-update', '-duc', '-disable-update-check', '-o', '-output',
            '-oJ', '-json', 'oD', '-output-dir', '-cs', '-collect-sources', 'oI', '-ip',
            '-config', '-pc', '-provider-config', '-r', '-rL', '-rlist', '-nW', '-active',
            '-proxy', '-ei', '-exclude-ip', '-silent', '-version', '-v', '-nc', '-no-color',
            'ls', '-list-sources', '-stats', '-timeout', '-max-time', '-dL', '-list',
            '-timeout', '-max-time',
    ]
        switch_list = switch.split()
        for arg in switch_list:
            if arg.startswith('-') and arg not in subfinder_allowed_switch:
                return False
        return True

    @staticmethod
    def _validate_tshark_switch(switch: str) -> bool:
        tshark_allowed_switch = [
            '-i', '-D', '-l', '-n', '-N', '-t', '-T', '-w', '-r', '-c', '-F',
            '-V', '-x', '-X', '-z', '-Y', '-R', '-e', '-E', '-f', '-s', '-B',
            '--help', '--version', '--list-interfaces', '--list-data-link-types',
            '--list-time-stamp-types', '--color', '--no-duplicate-keys',
            '--print', '--print-summary', '--enable-protocol',
            '--disable-protocol', '--disable-all-protocols',
            '--enable-heuristic', '--disable-heuristic', '--export-objects',
            '--export-tls-session-keys', '--capture-comment', '--temp-dir',
            '--log-level', '--log-file'
        ]

        switch_list = re.findall(r'(?:[^\s,"]|"(?:\\.|[^"])*")+', switch)

        for sw in switch_list:
            if sw.startswith('-'):
                sw_base = sw.split('=')[0]  # allow --option=value
                if sw_base not in tshark_allowed_switch:
                    return False
        return True

