Summary
IssueVotePublicViewSet.create() allows users to cast votes even when voting is administratively disabled on the public board (is_votes_enabled = False).
Root Cause
Every other create() method in the public board views guards against its feature flag before writing:
# IssueCommentPublicViewSet.create()
if not project_deploy_board.is_comments_enabled:
return Response({"error": "Comments are not enabled"}, status=400)
# IssueReactionPublicViewSet.create()
if not project_deploy_board.is_reactions_enabled:
return Response({"error": "Reactions are not enabled"}, status=400)
IssueVotePublicViewSet.create() has no equivalent check. Note: get_queryset() does check is_votes_enabled, so vote listing is correctly gated. Only the write path is missing the gate.
Impact
- Security impact: None — functional/administrative enforcement gap only.
- Functional impact: Board administrators who disable voting cannot prevent authenticated users from casting votes via the API.
Recommended Fix
def create(self, request, anchor, issue_id):
project_deploy_board = DeployBoard.objects.get(anchor=anchor, entity_name="project")
if not project_deploy_board.is_votes_enabled:
return Response(
{"error": "Votes are not enabled for this project board"},
status=status.HTTP_400_BAD_REQUEST,
)
...
Affected File
apps/api/plane/space/views/issue.py — IssueVotePublicViewSet.create()
Related
Identified during security audit of PR #9498. Pre-existing issue, not introduced by that PR.
Summary
IssueVotePublicViewSet.create()allows users to cast votes even when voting is administratively disabled on the public board (is_votes_enabled = False).Root Cause
Every other
create()method in the public board views guards against its feature flag before writing:IssueVotePublicViewSet.create()has no equivalent check. Note:get_queryset()does checkis_votes_enabled, so vote listing is correctly gated. Only the write path is missing the gate.Impact
Recommended Fix
Affected File
apps/api/plane/space/views/issue.py—IssueVotePublicViewSet.create()Related
Identified during security audit of PR #9498. Pre-existing issue, not introduced by that PR.