File size: 4,939 Bytes
310260a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
"""
Error Message Security Tests

Tests that error messages don't expose sensitive internal information.
"""

import pytest

from src.tools.create_task import create_task_internal
from src.tools.get_task import get_task_internal
from src.tools.update_task import update_task_internal
from tests.utils.task_helpers import create_test_task


@pytest.mark.security
@pytest.mark.asyncio
async def test_error_messages_do_not_expose_database_schema(mock_mcp_context):
    """
    Test: Error messages do not expose database schema

    Verifies that error messages don't reveal table names, column names, or schema details.
    """
    # Trigger various errors
    result1 = await create_task_internal(ctx=mock_mcp_context, title="A" * 201)
    result2 = await get_task_internal(ctx=mock_mcp_context, task_id=99999)
    result3 = await update_task_internal(ctx=mock_mcp_context, task_id=99999, title="Test")

    # Check all error messages
    for result in [result1, result2, result3]:
        if result["status"] == "error":
            error_msg = result["error"].lower()

            # Should not contain database-specific terms
            assert "table" not in error_msg
            assert "column" not in error_msg
            assert "schema" not in error_msg
            assert "constraint" not in error_msg
            assert "foreign key" not in error_msg
            assert "primary key" not in error_msg
            assert "index" not in error_msg


@pytest.mark.security
@pytest.mark.asyncio
async def test_error_messages_do_not_expose_internal_paths(mock_mcp_context):
    """
    Test: Error messages do not expose internal paths

    Verifies that error messages don't reveal file system paths or internal structure.
    """
    # Trigger errors
    result1 = await create_task_internal(ctx=mock_mcp_context, title="")
    result2 = await get_task_internal(ctx=mock_mcp_context, task_id=-1)

    # Check error messages
    for result in [result1, result2]:
        if result["status"] == "error":
            error_msg = result["error"]

            # Should not contain file paths
            assert "/" not in error_msg or "not found" in error_msg.lower()
            assert "\\" not in error_msg
            assert "src/" not in error_msg
            assert "backend/" not in error_msg
            assert ".py" not in error_msg


@pytest.mark.security
@pytest.mark.asyncio
async def test_error_messages_do_not_expose_stack_traces(mock_mcp_context):
    """
    Test: Error messages do not expose stack traces

    Verifies that error messages don't include stack traces or exception details.
    """
    # Trigger various errors
    result1 = await create_task_internal(ctx=mock_mcp_context, title=None)
    result2 = await update_task_internal(ctx=mock_mcp_context, task_id=99999, title="Test")

    # Check error messages
    for result in [result1, result2]:
        if result["status"] == "error":
            error_msg = result["error"].lower()

            # Should not contain stack trace elements
            assert "traceback" not in error_msg
            assert "file \"" not in error_msg
            assert "line " not in error_msg
            assert "exception" not in error_msg
            assert "error:" not in error_msg or error_msg.count("error:") == 1


@pytest.mark.security
@pytest.mark.asyncio
async def test_error_messages_are_user_friendly(mock_mcp_context):
    """
    Test: Error messages are user-friendly

    Verifies that error messages provide helpful information without technical details.
    """
    # Test validation errors
    result1 = await create_task_internal(ctx=mock_mcp_context, title="")
    assert result1["status"] == "error"
    assert len(result1["error"]) > 0
    assert "title" in result1["error"].lower() or "required" in result1["error"].lower()

    # Test not found errors
    result2 = await get_task_internal(ctx=mock_mcp_context, task_id=99999)
    assert result2["status"] == "error"
    assert "not found" in result2["error"].lower()


@pytest.mark.security
@pytest.mark.asyncio
async def test_error_messages_consistent_across_tools(mock_mcp_context, mock_mcp_context_user2, test_session):
    """
    Test: Error messages consistent across tools

    Verifies that similar errors produce consistent messages across different tools.
    """
    # Create task for user 2
    user2_task = create_test_task(test_session, mock_mcp_context_user2.user_id, title="User 2 Task")

    # User 1 tries to access user 2's task via different tools
    result1 = await get_task_internal(ctx=mock_mcp_context, task_id=user2_task.id)
    result2 = await update_task_internal(ctx=mock_mcp_context, task_id=user2_task.id, title="Test")

    # Both should return "not found" error (consistent messaging)
    assert result1["status"] == "error"
    assert result2["status"] == "error"
    assert "not found" in result1["error"].lower()
    assert "not found" in result2["error"].lower()