Skip to content

Conversation

sbk2k1
Copy link

@sbk2k1 sbk2k1 commented Oct 12, 2025

Closes #4058

Update Issue and Activity Controllers to Use POST Method

Current Implementation

In website/views/issue.py (GET methods)

  • like_issue(request, issue_pk) - modifies user upvotes
  • dislike_issue(request, issue_pk) - modifies user downvotes
  • flag_issue(request, issue_pk) - modifies issue flags
  • save_issue(request, issue_pk) - adds/removes saved issues
  • unsave_issue(request, issue_pk) - removes saved issues

In website/views/organization.py (POST methods)

  • like_activity(request, id) - modifies activity likes/approval
  • dislike_activity(request, id) - modifies activity dislikes
  • approve_activity(request, id) - changes approval status

Changes Made

In website/views/issue.py (now POST methods)

  • like_issue(request, issue_pk) - modifies user upvotes
  • dislike_issue(request, issue_pk) - modifies user downvotes
  • flag_issue(request, issue_pk) - modifies issue flags
  • save_issue(request, issue_pk) - adds/removes saved issues
  • unsave_issue(request, issue_pk) - removes saved issues

Frontend Update

  • Updated website/templates/includes/_like_dislike_share.html to call the required APIs using POST requests.

Testing

  • All updated methods tested successfully.
  • Screenshot of local app with registering like/dislike/flag attached.
image

Summary by CodeRabbit

  • New Features

    • Shows a clear “Some error occurred!” notification if like, dislike, flag, or bookmark actions fail.
  • Bug Fixes

    • Improved reliability of like/unlike, dislike, flag, and bookmark interactions.
    • Ensured event handlers remain correctly wired after updates.
  • Refactor

    • Standardized these interactions to use secure form-style submissions (POST + CSRF) for consistency.
  • Chores

    • Added server-side protections and error handling for interactive actions.

Copy link
Contributor

coderabbitai bot commented Oct 12, 2025

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning In addition to requiring POST, the PR alters API response payloads by removing vote counts, simplifies return values to generic “Success” or “ERROR,” and adds broad try/except wrappers and AJAX error callbacks, which extend beyond the linked issue’s scope of only changing request methods and CSRF protection. Revert or isolate the modifications to response structure and error handling into a separate change so that this PR remains focused on converting GET-based endpoints to POST with CSRF protection.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The title “GET to POST implementation and calling” is overly generic and does not clearly specify which endpoints or functionality were affected by this change, making it difficult for reviewers to quickly understand the primary update. Consider revising the title to explicitly reference the issue-related endpoints being converted from GET to POST, such as “Convert issue like/dislike/save/flag endpoints from GET to POST.”
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues Check ✅ Passed The pull request successfully enforces POST-only access for all five issue-related endpoints (like_issue, dislike_issue, save_issue, unsave_issue, flag_issue) and updates the frontend template to use POST with CSRF protection, aligning precisely with the objective of issue #4058 to remove GET-based state changes.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
website/views/issue.py (3)

1918-1936: Remove GET flag link in sitemap.html: in templates/sitemap.html (line 260), replace the <a href="{% url 'flag_issue' … %}"> anchor with a POST-based action (form button or AJAX) so that all calls to flag_issue use POST.


139-157: Convert the sitemap dislike link to a POST request
The <a href="{% url 'dislike_issue' 1 %}"> in website/templates/sitemap.html (around line 253) still issues a GET and will 405 under @require_POST. Wrap it in a <form method="post"> with {% csrf_token %} or convert it to AJAX POST so the endpoint receives a POST.


90-136: Update sitemap.html links to POST
The anchor tags in website/templates/sitemap.html (around lines 246–253) still call like_issue/dislike_issue via GET and will 405 under @require_POST. Convert these to POST (for example using forms or AJAX).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between fbd3912 and 5c69f73.

📒 Files selected for processing (2)
  • website/templates/includes/_like_dislike_share.html (1 hunks)
  • website/views/issue.py (6 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run Tests
  • GitHub Check: docker-test
🔇 Additional comments (3)
website/templates/includes/_like_dislike_share.html (1)

195-217: LGTM! Consistent POST implementation.

The bookmark handler correctly implements the POST method with CSRF token, consistent with the other handlers. The error handling was already in place.

website/views/issue.py (2)

1841-1848: LGTM! Correct POST enforcement.

The @require_POST decorator is correctly applied to this state-changing operation.


1851-1867: LGTM! Correct POST enforcement.

The @require_POST decorator is correctly applied to this state-changing operation that toggles the saved status.

}

function like_unlike_handler(e) {
} function like_unlike_handler(e) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix formatting issue.

The function definition is concatenated with the previous line's closing brace. This makes the code harder to read and maintain.

Apply this diff to fix the formatting:

-    }    function like_unlike_handler(e) {
+    }
+    
+    function like_unlike_handler(e) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} function like_unlike_handler(e) {
}
function like_unlike_handler(e) {
🤖 Prompt for AI Agents
In website/templates/includes/_like_dislike_share.html around line 128, the
closing brace and the subsequent function declaration are concatenated ("}   
function like_unlike_handler(e)"), making it a formatting issue; fix it by
placing the closing brace on its own line (or adding a newline) so the function
declaration starts on the next line and adjust indentation so the function
signature is aligned with other top-level functions.

Comment on lines +132 to 146
type: 'POST',
url: '/like_issue/' + issue_pk + '/',
data: {},
data: {
'csrfmiddlewaretoken': $('[name=csrfmiddlewaretoken]').val()
},
success: function (data) {
window.location.reload();
},
error: function () {
$.notify("Some error occurred!", {
style: "custom",
className: "danger"
});
}
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify CSRF token field availability.

The code correctly uses POST and includes the CSRF token. However, ensure that a CSRF token field (<input name="csrfmiddlewaretoken">) exists in the page, either in this template, an included template, or the parent template. Without it, all AJAX requests will fail with CSRF validation errors.

Run the following script to check if the CSRF token is present in related templates:


🏁 Script executed:

#!/bin/bash
# Description: Check for CSRF token in templates

# Search for csrf_token template tag in related templates
rg -n "{% csrf_token %}" --type=html

# Search for csrfmiddlewaretoken input field
rg -n 'name=["\']csrfmiddlewaretoken["\']' --type=html

Length of output: 160


The previous search errored due to unescaped characters. Please rerun with literal searches:


🏁 Script executed:

#!/bin/bash
# Search for CSRF token template tag
rg -nF "{% csrf_token %}" --glob "*.html"

# Search for CSRF token input field
rg -n "csrfmiddlewaretoken" --glob "*.html"

Length of output: 12593


🏁 Script executed:

#!/bin/bash
# Check for csrf_token tag in the include
rg -nF "{% csrf_token %}" website/templates/includes/_like_dislike_share.html

# Check for hidden input field for CSRF token in the include
rg -n '<input[^>]*name=["'\'']csrfmiddlewaretoken["'\'']' website/templates/includes/_like_dislike_share.html

Length of output: 0


Ensure a CSRF token input is rendered in _like_dislike_share.html.
Add {% csrf_token %} or a hidden field <input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}"> in website/templates/includes/_like_dislike_share.html so $(' [name=csrfmiddlewaretoken]').val() isn’t empty.

🤖 Prompt for AI Agents
In website/templates/includes/_like_dislike_share.html around lines 132 to 146,
the AJAX expects a csrfmiddlewaretoken selector but the template does not render
a CSRF input; add a CSRF token input (either include the Django template tag {%
csrf_token %} inside the form or add a hidden input with
name="csrfmiddlewaretoken" and value="{{ csrf_token }}") in this template so $('
[name=csrfmiddlewaretoken]').val() returns the token and the POST request is
accepted.

$('body').on('click', '#like_unlike', like_unlike_handler);

function dislike_handler(e){
$('body').on('click', '#like_unlike', like_unlike_handler); function dislike_handler(e){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix formatting issue.

The event binding is concatenated with the previous line's closing brace, followed by the function definition.

Apply this diff to fix the formatting:

-    $('body').on('click', '#like_unlike', like_unlike_handler);    function dislike_handler(e){
+    $('body').on('click', '#like_unlike', like_unlike_handler);
+    
+    function dislike_handler(e){
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$('body').on('click', '#like_unlike', like_unlike_handler); function dislike_handler(e){
$('body').on('click', '#like_unlike', like_unlike_handler);
function dislike_handler(e){
🤖 Prompt for AI Agents
In website/templates/includes/_like_dislike_share.html around line 149, the
event binding statement is concatenated with the previous function's closing
brace and the next function definition (the line reads "...like_unlike_handler);
function dislike_handler(e){"); fix this by inserting a newline (and optional
semicolon if missing) between the event binding call and the start of the
dislike_handler function so the binding stands alone on its own line and the
function definition begins on the next line.

</script>
<script>
function flag_unflag_handler(e){
<script> function flag_unflag_handler(e){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix formatting issue.

The script tag is concatenated with the previous line.

Apply this diff to fix the formatting:

-</script>
-<script>    function flag_unflag_handler(e){
+</script>
+<script>
+    function flag_unflag_handler(e){
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<script> function flag_unflag_handler(e){
</script>
<script>
function flag_unflag_handler(e){
🤖 Prompt for AI Agents
In website/templates/includes/_like_dislike_share.html around line 171, the
opening "<script>" tag was concatenated onto the previous line causing
formatting issues; move the "<script>" tag to its own line (insert a newline
before "<script>") so the tag starts at column 1 on line 171, preserving
existing indentation for the function that follows and ensuring proper HTML
formatting.

</script>
<script>
function bookmark_handler(e){
<script> function bookmark_handler(e){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix formatting issue.

The script tag is concatenated with the previous line.

Apply this diff to fix the formatting:

-</script>
-<script>    function bookmark_handler(e){
+</script>
+<script>
+    function bookmark_handler(e){
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<script> function bookmark_handler(e){
</script>
<script>
function bookmark_handler(e){
🤖 Prompt for AI Agents
In website/templates/includes/_like_dislike_share.html around line 195 the
opening "<script>" tag is concatenated with the previous line; separate the
script tag onto its own line by inserting a newline (or line break) before
"<script>    function bookmark_handler(e){" so the "<script>" starts on its own
line and the function body remains properly indented.

@github-project-automation github-project-automation bot moved this from Backlog to Ready in 📌 OWASP BLT Project Board Oct 12, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
website/views/issue.py (2)

90-137: Approve POST enforcement, but remove unused context variables.

The @require_POST decorator correctly prevents GET-based state changes. However, the context dictionary created at lines 133-136 is never used since the function returns a plain HttpResponse("Success") at line 137.

Apply this diff to remove the unused code:

-    total_votes = UserProfile.objects.filter(issue_upvoted=issue).count()
-    context["object"] = issue
-    context["likes"] = total_votes
-    context["isLiked"] = UserProfile.objects.filter(issue_upvoted=issue, user=request.user).exists()
     return HttpResponse("Success")

Alternatively, if you need to return this data to the frontend, consider returning a JSON response:

+    total_votes = UserProfile.objects.filter(issue_upvoted=issue).count()
+    is_liked = UserProfile.objects.filter(issue_upvoted=issue, user=request.user).exists()
-    return HttpResponse("Success")
+    return JsonResponse({
+        "status": "success",
+        "likes": total_votes,
+        "isLiked": is_liked
+    })

140-158: Remove unused context variables (same issue as in like_issue).

Similar to like_issue, the context dictionary at lines 154-157 is populated but never used.

Apply this diff:

-    total_votes = UserProfile.objects.filter(issue_downvoted=issue).count()
-    context["object"] = issue
-    context["dislikes"] = total_votes
-    context["isDisliked"] = UserProfile.objects.filter(issue_downvoted=issue, user=request.user).exists()
     return HttpResponse("Success")

Or return the data as JSON if needed by the frontend:

+    total_votes = UserProfile.objects.filter(issue_downvoted=issue).count()
+    is_disliked = UserProfile.objects.filter(issue_downvoted=issue, user=request.user).exists()
-    return HttpResponse("Success")
+    return JsonResponse({
+        "status": "success",
+        "dislikes": total_votes,
+        "isDisliked": is_disliked
+    })
🧹 Nitpick comments (3)
website/views/issue.py (3)

1842-1849: Consider adding error handling for database operations.

The function lacks error handling for potential database issues (e.g., issue not found, user profile not found).

Consider adding error handling:

 @require_POST
 @login_required(login_url="/accounts/login")
 def unsave_issue(request, issue_pk):
-    issue_pk = int(issue_pk)
-    issue = Issue.objects.get(pk=issue_pk)
-    userprof = UserProfile.objects.get(user=request.user)
-    userprof.issue_saved.remove(issue)
-    return HttpResponse("OK")
+    try:
+        issue_pk = int(issue_pk)
+        issue = get_object_or_404(Issue, pk=issue_pk)
+        userprof = UserProfile.objects.get(user=request.user)
+        userprof.issue_saved.remove(issue)
+        return JsonResponse({"status": "success"})
+    except (ValueError, UserProfile.DoesNotExist) as e:
+        return JsonResponse({"status": "error", "message": str(e)}, status=400)

1852-1867: Consider error handling and standardized JSON responses.

The function lacks error handling and uses inconsistent response formats ("REMOVED" vs "OK").

Consider adding error handling and returning standardized JSON:

 @require_POST
 @login_required(login_url="/accounts/login")
 def save_issue(request, issue_pk):
-    issue_pk = int(issue_pk)
-    issue = Issue.objects.get(pk=issue_pk)
-    userprof = UserProfile.objects.get(user=request.user)
-
-    already_saved = userprof.issue_saved.filter(pk=issue_pk).exists()
-
-    if already_saved:
-        userprof.issue_saved.remove(issue)
-        return HttpResponse("REMOVED")
-
-    else:
-        userprof.issue_saved.add(issue)
-        return HttpResponse("OK")
+    try:
+        issue_pk = int(issue_pk)
+        issue = get_object_or_404(Issue, pk=issue_pk)
+        userprof = UserProfile.objects.get(user=request.user)
+
+        already_saved = userprof.issue_saved.filter(pk=issue_pk).exists()
+
+        if already_saved:
+            userprof.issue_saved.remove(issue)
+            return JsonResponse({"status": "success", "action": "removed", "saved": False})
+        else:
+            userprof.issue_saved.add(issue)
+            return JsonResponse({"status": "success", "action": "added", "saved": True})
+    except (ValueError, UserProfile.DoesNotExist) as e:
+        return JsonResponse({"status": "error", "message": str(e)}, status=400)

1919-1936: POST enforcement looks good; consider adding error handling.

The @require_POST decorator correctly prevents GET-based state changes. The function returns an HTML fragment, which differs from the other functions but may be intentional if the frontend expects partial HTML updates.

Consider adding error handling for database operations:

 @require_POST
 @login_required(login_url="/accounts/login")
 def flag_issue(request, issue_pk):
-    context = {}
-    issue_pk = int(issue_pk)
-    issue = Issue.objects.get(pk=issue_pk)
-    userprof = UserProfile.objects.get(user=request.user)
-    if userprof in UserProfile.objects.filter(issue_flaged=issue):
-        userprof.issue_flaged.remove(issue)
-    else:
-        userprof.issue_flaged.add(issue)
-        issue_pk = issue.pk
-
-    userprof.save()
-    total_flag_votes = UserProfile.objects.filter(issue_flaged=issue).count()
-    context["object"] = issue
-    context["flags"] = total_flag_votes
-    return render(request, "includes/_flags.html", context)
+    try:
+        context = {}
+        issue_pk = int(issue_pk)
+        issue = get_object_or_404(Issue, pk=issue_pk)
+        userprof = UserProfile.objects.get(user=request.user)
+        if userprof in UserProfile.objects.filter(issue_flaged=issue):
+            userprof.issue_flaged.remove(issue)
+        else:
+            userprof.issue_flaged.add(issue)
+            issue_pk = issue.pk
+
+        userprof.save()
+        total_flag_votes = UserProfile.objects.filter(issue_flaged=issue).count()
+        context["object"] = issue
+        context["flags"] = total_flag_votes
+        return render(request, "includes/_flags.html", context)
+    except (ValueError, UserProfile.DoesNotExist):
+        return HttpResponse("Error processing flag", status=400)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 5c69f73 and 26a2fc3.

📒 Files selected for processing (1)
  • website/views/issue.py (5 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run Tests
  • GitHub Check: docker-test

@sbk2k1
Copy link
Author

sbk2k1 commented Oct 12, 2025

@DonnieBLT Could you please review the PR? Specifically, do you think I should address the changes suggested by coderabbit? Those changes modify the source in ways unrelated to the security updates.

@DonnieBLT
Copy link
Collaborator

Yes please update

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (5)
website/views/issue.py (5)

90-93: LGTM! POST requirement correctly enforced.

The @require_POST decorator properly prevents GET-based state changes. However, the context dictionary initialized on line 93 is never used and can be removed.

Apply this diff to remove the unused variable:

 @require_POST
 @login_required(login_url="/accounts/login")
 def like_issue(request, issue_pk):
-    context = {}
     issue_pk = int(issue_pk)
     issue = get_object_or_404(Issue, pk=issue_pk)

136-139: LGTM! POST requirement correctly enforced.

The @require_POST decorator properly prevents GET-based state changes. However, the context dictionary initialized on line 139 is never used and can be removed.

Apply this diff to remove the unused variable:

 @require_POST
 @login_required(login_url="/accounts/login")
 def dislike_issue(request, issue_pk):
-    context = {}
     issue_pk = int(issue_pk)
     issue = get_object_or_404(Issue, pk=issue_pk)

1837-1844: Improve exception handling specificity.

The try/except block catches all exceptions without logging or using the exception variable. This can hide bugs and make debugging difficult.

Consider one of these approaches:

Option 1: Log the exception for debugging

+import logging
+
+logger = logging.getLogger(__name__)
+
 @require_POST
 @login_required(login_url="/accounts/login")
 def unsave_issue(request, issue_pk):
     try:
         issue_pk = int(issue_pk)
         issue = Issue.objects.get(pk=issue_pk)
         userprof = UserProfile.objects.get(user=request.user)
         userprof.issue_saved.remove(issue)
         return HttpResponse("OK")
     except Exception as e:
+        logger.error(f"Error unsaving issue {issue_pk}: {e}")
         return HttpResponse("ERROR")

Option 2: Catch specific exceptions

 @require_POST
 @login_required(login_url="/accounts/login")
 def unsave_issue(request, issue_pk):
     try:
         issue_pk = int(issue_pk)
         issue = Issue.objects.get(pk=issue_pk)
         userprof = UserProfile.objects.get(user=request.user)
         userprof.issue_saved.remove(issue)
         return HttpResponse("OK")
-    except Exception as e:
+    except (Issue.DoesNotExist, UserProfile.DoesNotExist, ValueError) as e:
         return HttpResponse("ERROR")

1850-1865: Improve exception handling specificity.

The try/except block catches all exceptions without logging or using the exception variable. This can hide bugs and make debugging difficult.

Consider one of these approaches:

Option 1: Log the exception for debugging

+import logging
+
+logger = logging.getLogger(__name__)
+
 @require_POST
 @login_required(login_url="/accounts/login")
 def save_issue(request, issue_pk):
     try:
         issue_pk = int(issue_pk)
         issue = Issue.objects.get(pk=issue_pk)
         userprof = UserProfile.objects.get(user=request.user)
 
         already_saved = userprof.issue_saved.filter(pk=issue_pk).exists()
 
         if already_saved:
             userprof.issue_saved.remove(issue)
             return HttpResponse("REMOVED")
 
         else:
             userprof.issue_saved.add(issue)
             return HttpResponse("OK")
     except Exception as e:
+        logger.error(f"Error saving/unsaving issue {issue_pk}: {e}")
         return HttpResponse("ERROR")

Option 2: Catch specific exceptions

 @require_POST
 @login_required(login_url="/accounts/login")
 def save_issue(request, issue_pk):
     try:
         issue_pk = int(issue_pk)
         issue = Issue.objects.get(pk=issue_pk)
         userprof = UserProfile.objects.get(user=request.user)
 
         already_saved = userprof.issue_saved.filter(pk=issue_pk).exists()
 
         if already_saved:
             userprof.issue_saved.remove(issue)
             return HttpResponse("REMOVED")
 
         else:
             userprof.issue_saved.add(issue)
             return HttpResponse("OK")
-    except Exception as e:
+    except (Issue.DoesNotExist, UserProfile.DoesNotExist, ValueError) as e:
         return HttpResponse("ERROR")

1920-1937: Improve exception handling specificity.

The try/except block catches all exceptions without logging or using the exception variable. This makes debugging difficult and returns a generic error message to the user.

Consider one of these approaches:

Option 1: Log the exception for debugging

+import logging
+
+logger = logging.getLogger(__name__)
+
 @require_POST
 @login_required(login_url="/accounts/login")
 def flag_issue(request, issue_pk):
     try:
         context = {}
         issue_pk = int(issue_pk)
         issue = Issue.objects.get(pk=issue_pk)
         userprof = UserProfile.objects.get(user=request.user)
         if userprof in UserProfile.objects.filter(issue_flaged=issue):
             userprof.issue_flaged.remove(issue)
         else:
             userprof.issue_flaged.add(issue)
             issue_pk = issue.pk
 
         userprof.save()
         total_flag_votes = UserProfile.objects.filter(issue_flaged=issue).count()
         context["object"] = issue
         context["flags"] = total_flag_votes
         return render(request, "includes/_flags.html", context)
     except Exception as e:
+        logger.error(f"Error flagging issue {issue_pk}: {e}")
         return HttpResponse("Error processing flag", status=400)

Option 2: Catch specific exceptions

 @require_POST
 @login_required(login_url="/accounts/login")
 def flag_issue(request, issue_pk):
     try:
         context = {}
         issue_pk = int(issue_pk)
         issue = Issue.objects.get(pk=issue_pk)
         userprof = UserProfile.objects.get(user=request.user)
         if userprof in UserProfile.objects.filter(issue_flaged=issue):
             userprof.issue_flaged.remove(issue)
         else:
             userprof.issue_flaged.add(issue)
             issue_pk = issue.pk
 
         userprof.save()
         total_flag_votes = UserProfile.objects.filter(issue_flaged=issue).count()
         context["object"] = issue
         context["flags"] = total_flag_votes
         return render(request, "includes/_flags.html", context)
-    except Exception as e:
+    except (Issue.DoesNotExist, UserProfile.DoesNotExist, ValueError) as e:
         return HttpResponse("Error processing flag", status=400)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting

📥 Commits

Reviewing files that changed from the base of the PR and between 26a2fc3 and be85404.

📒 Files selected for processing (1)
  • website/views/issue.py (4 hunks)
🧰 Additional context used
🪛 Ruff (0.13.3)
website/views/issue.py

1843-1843: Do not catch blind exception: Exception

(BLE001)


1843-1843: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


1864-1864: Do not catch blind exception: Exception

(BLE001)


1864-1864: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)


1936-1936: Do not catch blind exception: Exception

(BLE001)


1936-1936: Local variable e is assigned to but never used

Remove assignment to unused variable e

(F841)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run Tests
  • GitHub Check: docker-test
🔇 Additional comments (1)
website/views/issue.py (1)

90-90: CSRF protection verified on AJAX POST endpoints

CsrfViewMiddleware is enabled, no @csrf_exempt on like/dislike/save/flag views, and the base templates (e.g. includes/header.html and issue.html) include a hidden csrfmiddlewaretoken input. All POST endpoints are CSRF-protected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Ready

Development

Successfully merging this pull request may close these issues.

Remove GET based actions that make server side changes.

2 participants