File size: 4,926 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
134
135
136
137
138
139
"""
Security Tests for list_tasks Tool

Validates security aspects of list_tasks tool:
- User_id scoping enforcement (cross-user isolation)
- Prevents access to other users' tasks
"""

import pytest

from src.tools.list_tasks import list_tasks_internal
from tests.utils.task_helpers import create_multiple_tasks


@pytest.mark.security
@pytest.mark.asyncio
async def test_list_tasks_enforces_user_id_scoping(mock_mcp_context, mock_mcp_context_user2, test_session):
    """
    Test: list_tasks enforces user_id scoping (cross-user isolation)

    Verifies that list_tasks only returns tasks for the authenticated user.
    """
    # Setup: Create tasks for both users
    create_multiple_tasks(test_session, mock_mcp_context.user_id, count=3, title_prefix="User1 Task")
    create_multiple_tasks(test_session, mock_mcp_context_user2.user_id, count=2, title_prefix="User2 Task")

    # Execute for user 1
    result1 = await list_tasks_internal(ctx=mock_mcp_context)

    # Assert user 1 sees only their tasks
    assert result1["total"] == 3
    assert len(result1["tasks"]) == 3

    for task in result1["tasks"]:
        assert "User1 Task" in task["title"]
        assert "User2 Task" not in task["title"]

    # Execute for user 2
    result2 = await list_tasks_internal(ctx=mock_mcp_context_user2)

    # Assert user 2 sees only their tasks
    assert result2["total"] == 2
    assert len(result2["tasks"]) == 2

    for task in result2["tasks"]:
        assert "User2 Task" in task["title"]
        assert "User1 Task" not in task["title"]


@pytest.mark.security
@pytest.mark.asyncio
async def test_list_tasks_with_user1_context_cannot_see_user2_tasks(mock_mcp_context, mock_mcp_context_user2, test_session):
    """
    Test: list_tasks with user1 context cannot see user2 tasks

    Verifies complete data isolation between users.
    """
    # Setup: Create tasks for user 2 only
    create_multiple_tasks(test_session, mock_mcp_context_user2.user_id, count=5, title_prefix="User2 Secret Task")

    # Execute with user 1 context
    result = await list_tasks_internal(ctx=mock_mcp_context)

    # Assert user 1 sees no tasks (zero data leakage)
    assert result["total"] == 0
    assert len(result["tasks"]) == 0
    assert result["completed_count"] == 0
    assert result["pending_count"] == 0

    # Verify no user 2 tasks are visible
    for task in result["tasks"]:
        assert "User2 Secret Task" not in task["title"]


@pytest.mark.security
@pytest.mark.asyncio
async def test_list_tasks_returns_only_authenticated_user_tasks(mock_mcp_context, mock_mcp_context_user2, test_session):
    """
    Test: list_tasks returns only authenticated user tasks

    Comprehensive test for data isolation across multiple users.
    """
    # Setup: Create tasks for both users with different counts
    user1_tasks = create_multiple_tasks(test_session, mock_mcp_context.user_id, count=4)
    user2_tasks = create_multiple_tasks(test_session, mock_mcp_context_user2.user_id, count=3)

    # Get task IDs for verification
    user1_task_ids = [t.id for t in user1_tasks]
    user2_task_ids = [t.id for t in user2_tasks]

    # Execute for user 1
    result1 = await list_tasks_internal(ctx=mock_mcp_context)

    # Verify user 1 sees only their task IDs
    result1_task_ids = [t["id"] for t in result1["tasks"]]
    assert set(result1_task_ids) == set(user1_task_ids)
    assert not any(tid in user2_task_ids for tid in result1_task_ids)

    # Execute for user 2
    result2 = await list_tasks_internal(ctx=mock_mcp_context_user2)

    # Verify user 2 sees only their task IDs
    result2_task_ids = [t["id"] for t in result2["tasks"]]
    assert set(result2_task_ids) == set(user2_task_ids)
    assert not any(tid in user1_task_ids for tid in result2_task_ids)


@pytest.mark.security
@pytest.mark.asyncio
async def test_list_tasks_with_large_dataset_maintains_isolation(mock_mcp_context, mock_mcp_context_user2, test_session):
    """
    Test: list_tasks with large dataset maintains isolation

    Verifies that data isolation is maintained even with many tasks.
    """
    # Setup: Create many tasks for both users
    create_multiple_tasks(test_session, mock_mcp_context.user_id, count=50, title_prefix="User1")
    create_multiple_tasks(test_session, mock_mcp_context_user2.user_id, count=50, title_prefix="User2")

    # Execute for user 1
    result1 = await list_tasks_internal(ctx=mock_mcp_context)

    # Assert user 1 sees exactly 50 tasks, all their own
    assert result1["total"] == 50
    assert len(result1["tasks"]) == 50

    for task in result1["tasks"]:
        assert "User1" in task["title"]

    # Execute for user 2
    result2 = await list_tasks_internal(ctx=mock_mcp_context_user2)

    # Assert user 2 sees exactly 50 tasks, all their own
    assert result2["total"] == 50
    assert len(result2["tasks"]) == 50

    for task in result2["tasks"]:
        assert "User2" in task["title"]