"""Tests for MCP-003: Confused Deputy detection rule.

NOTE: This test file contains intentionally vulnerable code STRINGS
that are passed to the scanner for detection testing. These are not
executed - they are static analysis targets.
"""

import ast

from mcpaudit.models import Severity
from mcpaudit.rules.confused_deputy import ConfusedDeputyRule


class TestConfusedDeputy:
    """Tests for the Confused Deputy detection rule."""

    def setup_method(self):
        self.rule = ConfusedDeputyRule()

    def _check(self, code: str):
        tree = ast.parse(code)
        return self.rule.check(tree, code)

    def test_rule_metadata(self):
        assert self.rule.rule_id == "MCP-003"
        assert self.rule.severity == Severity.CRITICAL
        assert self.rule.cwe == "CWE-441"
        assert "Confused Deputy" in self.rule.name

    def test_tool_handler_open_with_param(self):
        code = '''
def handle_tool_read(path):
    return open(path).read()
'''
        findings = self._check(code)
        assert len(findings) >= 1
        assert findings[0].rule_id == "MCP-003"
        assert "file operation" in findings[0].title

    def test_decorated_tool_open_with_param(self):
        code = '''
@server.tool
def read_file(path):
    return open(path).read()
'''
        findings = self._check(code)
        assert len(findings) >= 1

    def test_tool_with_subprocess_param(self):
        # Detection target: tool handler passing user input to subprocess
        code = (
            "def handle_tool_run(cmd):\n"
            "    import subprocess\n"
            "    return subprocess.run(cmd)\n"
        )
        findings = self._check(code)
        assert len(findings) >= 1
        assert "command execution" in findings[0].title

    def test_tool_with_network_request_param(self):
        code = '''
def handle_tool_fetch(url):
    return requests.get(url)
'''
        findings = self._check(code)
        assert len(findings) >= 1
        assert "network request" in findings[0].title

    def test_tool_with_auth_check_passes(self):
        code = '''
def handle_tool_read(path):
    check_permission(current_user, path)
    return open(path).read()
'''
        findings = self._check(code)
        assert len(findings) == 0

    def test_tool_with_authorize_passes(self):
        code = '''
def handle_tool_delete(path):
    authorize(user, "delete", path)
    os.remove(path)
'''
        findings = self._check(code)
        assert len(findings) == 0

    def test_non_tool_function_ignored(self):
        code = '''
def process_data(path):
    return open(path).read()
'''
        findings = self._check(code)
        assert len(findings) == 0

    def test_tool_with_static_open_not_flagged(self):
        code = '''
def handle_tool_config():
    return open("config.json").read()
'''
        findings = self._check(code)
        assert len(findings) == 0

    def test_async_tool_handler(self):
        code = '''
async def handle_tool_read(path):
    return open(path).read()
'''
        findings = self._check(code)
        assert len(findings) >= 1

    def test_finding_has_remediation(self):
        code = '''
def handle_tool_read(path):
    return open(path).read()
'''
        findings = self._check(code)
        assert len(findings) >= 1
        assert findings[0].remediation != ""
        assert "authorization" in findings[0].remediation.lower()

    def test_finding_has_cwe(self):
        code = '''
def handle_tool_read(path):
    return open(path).read()
'''
        findings = self._check(code)
        assert findings[0].cwe == "CWE-441"

    def test_multiple_privileged_calls(self):
        code = '''
def handle_tool_copy(src, dst):
    data = open(src).read()
    open(dst, 'w').write(data)
'''
        findings = self._check(code)
        assert len(findings) >= 2

    def test_detailed_description(self):
        desc = self.rule.detailed_description
        assert "confused deputy" in desc.lower()
        assert "How to fix" in desc

    def test_kwarg_param_detected(self):
        code = '''
def handle_tool_write(path, content):
    with open(file=path, mode='w') as f:
        f.write(content)
'''
        findings = self._check(code)
        assert len(findings) >= 1
