Fix Critical Security Issue: Authorization bypass on shared tasks

Added comprehensive authorization checks to prevent users from:
1. Sharing tasks/tags they don't own
2. Modifying tasks/tags that are only shared with them (read-only access)

Changes:

tasks/serializers.py (TaskShareSerializer):
- Added validation in validate() method to verify task/tag ownership
- Users can only share their own tasks and tags
- Prevents malicious users from sharing other people's resources

tasks/permissions.py (NEW):
- Created IsOwnerOrReadOnlyIfShared permission class
- Owners: full access (read, update, delete)
- Shared users: read-only access
- Prevents privilege escalation via shared access

tasks/views.py:
- Applied IsOwnerOrReadOnlyIfShared to TaskDetailAPIView
- Applied IsOwnerOrReadOnlyIfShared to TagDetailAPIView
- Enforces object-level permissions on all update/delete operations

Security impact:
- Prevents users from modifying or deleting resources they don't own
- Prevents users from sharing resources they don't own
- Maintains proper separation between owner and shared access levels

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Keith Smith
2025-12-22 22:38:43 -07:00
co-authored by Claude Sonnet 4.5
parent deabe6ab3d
commit c50aaa2d81
3 changed files with 41 additions and 2 deletions
+10
View File
@@ -197,6 +197,16 @@ class TaskShareSerializer(serializers.ModelSerializer):
raise serializers.ValidationError("Must share either a task or a tag.")
if attrs.get('task') and attrs.get('tag'):
raise serializers.ValidationError("Cannot share both a task and a tag.")
# SECURITY: Verify that the task/tag belongs to the requesting user
user = self.context['request'].user
if attrs.get('task'):
if attrs['task'].user != user:
raise serializers.ValidationError("You can only share your own tasks.")
if attrs.get('tag'):
if attrs['tag'].user != user:
raise serializers.ValidationError("You can only share your own tags.")
return attrs
def create(self, validated_data):