Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 15, 2025

Addresses 13 critical bugs, security vulnerabilities, and template errors identified in PR #5284 code review.

Critical Bugs

  • timezone.datetime.mindatetime.min (core.py:2454) - Fixed AttributeError in management commands sort
  • SQLite Window function incompatibility - Removed Window(RowNumber()) partition, compute top-3 repos in Python
  • N+1 query - Changed template from org.repos.count to annotated org.repo_count field
  • N+1 view counts - Query only current page orgs instead of full queryset

Security

  • Hostname validation bypass - populate_github_org.py now uses parsed.hostname equality check instead of substring match to prevent github.com.evil.com
  • Reverse tabnabbing - Added rel="noopener noreferrer" to all target="_blank" links in repo_detail.js (issues/PRs/bounties)
  • Authorization bypass - refresh_organization_repos_api now requires staff/superuser/admin/manager role
  • Unsafe GitHub URL rendering - Added server-side is_valid_github_url validation with proper URL parsing
  • Empty href injection - Conditional link rendering only when URL exists

Template Fixes

  • TypeError on url_name - Added |default:'' filter to sidenav.html for nullable request.resolver_match.url_name
  • localStorage crash - Wrapped all localStorage access in try/catch for Safari private mode
  • Lost sort parameter - Pagination links now preserve ?sort= query param
  • NoReverseMatch - Breadcrumb checks both name and slug before URL reversal

Example: SQLite-compatible ranking

Before (breaks on SQLite):

top_repos_qs = Repo.objects.annotate(
    _rank=Window(
        expression=RowNumber(),
        partition_by=[F("organization_id")],
        order_by=[F("stars").desc()]
    )
).filter(_rank__lte=3)

After (database-agnostic):

# In get_context_data
for org in context["organizations"]:
    all_repos = list(org.repos.all())
    org.top_repos = sorted(all_repos, key=lambda r: (-r.stars, -r.id))[:3]
Original prompt

Make a new pr with these changes and address the feedback #5284


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 15, 2025

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Note

Free review on us!

CodeRabbit is offering free reviews until Wed Dec 17 2025 to showcase some of the refinements we've made.

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

Copilot AI changed the title [WIP] Address feedback from pull request 5284 Fix critical bugs and security issues from PR #5284 feedback Dec 15, 2025
Copilot AI requested a review from DonnieBLT December 15, 2025 07:22
@DonnieBLT DonnieBLT marked this pull request as ready for review December 15, 2025 07:24
Copilot AI review requested due to automatic review settings December 15, 2025 07:24
@github-actions github-actions bot added the files-changed: 8 PR changes 8 files label Dec 15, 2025
@DonnieBLT DonnieBLT merged commit e83cd26 into revert-5283-revert-5281-updates_dec14 Dec 15, 2025
13 checks passed
@DonnieBLT DonnieBLT deleted the copilot/address-feedback-pr-5284 branch December 15, 2025 07:24
@github-actions
Copy link
Contributor

❌ Pre-commit checks failed

The pre-commit hooks found issues that need to be fixed. Please run the following commands locally to fix them:

# Install pre-commit if you haven't already
pip install pre-commit

# Run pre-commit on all files
pre-commit run --all-files

# Or run pre-commit on staged files only
pre-commit run

After running these commands, the pre-commit hooks will automatically fix most issues.
Please review the changes, commit them, and push to your branch.

💡 Tip: You can set up pre-commit to run automatically on every commit by running:

pre-commit install
Pre-commit output
[INFO] Initializing environment for https://github.com/pre-commit/pre-commit-hooks.
[WARNING] repo `https://github.com/pre-commit/pre-commit-hooks` uses deprecated stage names (commit, push) which will be removed in a future version.  Hint: often `pre-commit autoupdate --repo https://github.com/pre-commit/pre-commit-hooks` will fix this.  if it does not -- consider reporting an issue to that repo.
[INFO] Initializing environment for https://github.com/pycqa/isort.
[WARNING] repo `https://github.com/pycqa/isort` uses deprecated stage names (commit, merge-commit, push) which will be removed in a future version.  Hint: often `pre-commit autoupdate --repo https://github.com/pycqa/isort` will fix this.  if it does not -- consider reporting an issue to that repo.
[INFO] Initializing environment for https://github.com/astral-sh/ruff-pre-commit.
[INFO] Initializing environment for https://github.com/djlint/djLint.
[INFO] Initializing environment for local.
[INFO] Installing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/pycqa/isort.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/astral-sh/ruff-pre-commit.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for https://github.com/djlint/djLint.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
[INFO] Installing environment for local.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
check python ast.........................................................Passed
check builtin type constructor use.......................................Passed
check yaml...............................................................Passed
fix python encoding pragma...............................................Passed
mixed line ending........................................................Passed
isort....................................................................Passed
ruff.....................................................................Failed
- hook id: ruff
- files were modified by this hook

Found 3 errors (3 fixed, 0 remaining).


For more information, see the pre-commit documentation.

@github-actions github-actions bot added the pre-commit: failed Pre-commit checks failed label Dec 15, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses 13 critical bugs, security vulnerabilities, and template errors identified in PR #5284 code review feedback. The changes focus on database compatibility, security hardening, and frontend stability improvements.

Key changes:

  • Fixed SQLite compatibility by replacing Window functions with Python-side sorting
  • Enhanced security through proper hostname validation, authorization checks, and reverse tabnabbing protection
  • Improved template robustness with null checks and error handling for localStorage

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated no comments.

Show a summary per file
File Description
website/views/organization.py Removed SQLite-incompatible Window functions, added authorization check to refresh_organization_repos_api, implemented server-side GitHub URL validation, fixed N+1 query by using current page orgs, and annotated repo_count field
website/views/core.py Fixed AttributeError by changing timezone.datetime.min to datetime.min for management command sorting
website/templates/repo/repo_list.html Added null check for breadcrumb to prevent NoReverseMatch when both organization name and slug exist
website/templates/organization/organization_list_mode.html Added conditional rendering to prevent empty href attributes on links with null/empty URL and email fields
website/templates/organization/organization_list.html Changed from org.repos.count to annotated org.repo_count to avoid N+1 queries, replaced unsafe template check with server-side is_valid_github_url validation, added sort parameter preservation in pagination links
website/templates/includes/sidenav.html Added default filter to prevent TypeError on nullable request.resolver_match.url_name, wrapped localStorage access in try-catch blocks for Safari private mode compatibility
website/static/js/repo_detail.js Added rel="noopener noreferrer" to all target="_blank" links (issues, PRs, bounties) to prevent reverse tabnabbing attacks
website/management/commands/populate_github_org.py Enhanced hostname validation to use parsed.hostname with exact equality check instead of substring match to prevent bypass attacks like github.com.evil.com
Comments suppressed due to low confidence (35)

website/views/organization.py:2705

  • This loop creates an N+1 query problem. For each domain in org.domain_set.all(), a separate database query is executed to fetch top_testers. Since domain_set is prefetched, iterating over it is efficient, but the User.objects.filter query inside will execute once per domain. Consider moving this logic outside the loop or using a more efficient aggregation approach that doesn't require a query for each domain.
            for domain in org.domain_set.all():
                domain.top_testers = (
                    User.objects.filter(issue__domain=domain)
                    .annotate(issue_count=Count("issue"))
                    .order_by("-issue_count")[:1]
                )

website/views/core.py:890

  • This comment appears to contain commented-out code.
# def chatbot_conversation(request):
#     try:
#         today = datetime.now(timezone.utc).date()
#         rate_limit_key = f"global_daily_requests_{today}"
#         request_count = cache.get(rate_limit_key, 0)

#         if request_count >= DAILY_REQUEST_LIMIT:
#             return Response(
#                 {"error": "Daily request limit exceeded."},
#                 status=status.HTTP_429_TOO_MANY_REQUESTS,

website/views/core.py:895

  • This comment appears to contain commented-out code.
#         if not question:
#             return Response({"error": "Invalid question"}, status=status.HTTP_400_BAD_REQUEST)

website/views/core.py:903

  • This comment appears to contain commented-out code.
#         if not check_api:
#             ChatBotLog.objects.create(question=question, answer="Error: Invalid API Key")
#             return Response({"error": "Invalid API Key"}, status=status.HTTP_400_BAD_REQUEST)

#         if not question or not isinstance(question, str):
#             ChatBotLog.objects.create(question=question, answer="Error: Invalid question")
#             return Response({"error": "Invalid question"}, status=status.HTTP_400_BAD_REQUEST)

website/views/core.py:911

  • This comment appears to contain commented-out code.
#         if not vector_store:
#             try:
#                 vector_store = load_vector_store()
#             except FileNotFoundError as e:
#                 ChatBotLog.objects.create(
#                     question=question, answer="Error: Vector store not found {e}"

website/views/core.py:921

  • This comment appears to contain commented-out code.
#             except Exception as e:
#                 ChatBotLog.objects.create(question=question, answer=f"Error: {str(e)}")
#                 return Response(
#                     {"error": "Error loading vector store"},
#                     status=status.HTTP_500_INTERNAL_SERVER_ERROR,

website/views/core.py:926

  • This comment appears to contain commented-out code.
#             finally:
#                 if not vector_store:
#                     ChatBotLog.objects.create(
#                         question=question, answer="Error: Vector store not loaded"

website/views/core.py:936

  • This comment appears to contain commented-out code.
#         if question.lower() == "exit":
#             if "buffer" in request.session:
#                 del request.session["buffer"]
#             return Response({"answer": "Conversation memory cleared."}, status=status.HTTP_200_OK)

website/views/core.py:948

  • This comment appears to contain commented-out code.
#         if "buffer" in request.session:
#             memory.buffer = request.session["buffer"]

#         try:
#             response = crc.invoke({"question": question})
#         except Exception as e:
#             ChatBotLog.objects.create(question=question, answer=f"Error: {str(e)}")
#             return Response(
#                 {"error": "An internal error has occurred."},
#                 status=status.HTTP_500_INTERNAL_SERVER_ERROR,

website/views/core.py:958

  • This comment appears to contain commented-out code.
#     except Exception as e:
#         ChatBotLog.objects.create(
#             question=request.data.get("question", ""), answer=f"Error: {str(e)}"

website/views/core.py:3420

  • This comment appears to contain commented-out code.
            # if request.user.is_authenticated:
            #     profile = request.user.userprofile
            #     profile.theme_preference = theme
            #     profile.save()

website/views/core.py:1048

  • Variable category_obj is not used.
            category_obj = ForumCategory.objects.get(pk=category)

website/views/core.py:1911

  • Variable months_data is not used.
        months_data = []

website/views/core.py:2801

  • Variable template_dirs is not used.
    template_dirs = []

website/views/organization.py:1807

  • Variable admin is not used.
                    admin = OrganizationAdmin.objects.create(

website/views/organization.py:3036

  • Variable error_stream is not used.
        def error_stream():

website/views/core.py:1451

  • This assignment to 'badges' is unnecessary as it is redefined before this value is used.
    badges = Badge.objects.all()

website/views/organization.py:1937

  • This assignment to 'paginator' is unnecessary as it is redefined before this value is used.
    paginator = Paginator(scoreboard, 10)

website/views/organization.py:23

  • Import of 'F' is not used.
    Import of 'Window' is not used.
from django.db.models import Count, F, Prefetch, Q, Sum, Window

website/views/organization.py:24

  • Import of 'RowNumber' is not used.
from django.db.models.functions import RowNumber

website/views/core.py:2878

  • This import of module json is redundant, as it was previously imported on line 3.
    import json

website/views/core.py:3408

  • This import of module json is redundant, as it was previously imported on line 3.
            import json

website/views/organization.py:4000

  • This import of module logging is redundant, as it was previously imported on line 3.
        import logging

website/views/organization.py:4205

  • This import of module logging is redundant, as it was previously imported on line 3.
        import logging

website/views/organization.py:4247

  • This import of module logging is redundant, as it was previously imported on line 3.
        import logging

website/views/organization.py:4248

  • This import of module re is redundant, as it was previously imported on line 5.
        import re

website/views/core.py:374

  • 'except' clause does nothing but pass and there is no explanatory comment.
                except (
                    psutil.NoSuchProcess,
                    psutil.AccessDenied,
                    psutil.ZombieProcess,
                ):

website/views/core.py:2386

  • 'except' clause does nothing but pass and there is no explanatory comment.
                except (ValueError, TypeError, KeyError):

website/views/core.py:2609

  • 'except' clause does nothing but pass and there is no explanatory comment.
                except Exception:

website/views/organization.py:310

  • 'except' clause does nothing but pass and there is no explanatory comment.
                except KeyError:

website/views/organization.py:1759

  • 'except' clause does nothing but pass and there is no explanatory comment.
                except KeyError:

website/views/organization.py:1775

  • 'except' clause does nothing but pass and there is no explanatory comment.
                        except KeyError:

website/views/organization.py:1820

  • 'except' clause does nothing but pass and there is no explanatory comment.
            except (Subscription.DoesNotExist, KeyError):

website/views/organization.py:1141

  • This assignment assigns a variable to itself.
            sort_by = sort_by

website/views/core.py:231

  • This statement is unreachable.
            bitcoin_rpc_user = os.getenv("BITCOIN_RPC_USER")

@github-actions
Copy link
Contributor

❌ Tests failed

The Django tests found issues that need to be fixed. Please review the test output below and fix the failing tests.

How to run tests locally

# Install dependencies
poetry install --with dev

# Run all tests
poetry run python manage.py test

# Run tests with verbose output
poetry run python manage.py test -v 3

# Run a specific test
poetry run python manage.py test app.tests.TestClass.test_method
Test output (last 100 lines)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/handlers/base.py", line 61, in load_middleware
    mw_instance = middleware(adapted_handler)
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/whitenoise/middleware.py", line 104, in __init__
    self.add_files(self.static_root, prefix=self.static_prefix)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/whitenoise/base.py", line 107, in add_files
    self.update_files_dictionary(root, prefix)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/whitenoise/base.py", line 114, in update_files_dictionary
    stat_cache = dict(scantree(root))
                 ^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/whitenoise/base.py", line 263, in scantree
    yield from scantree(entry.path)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/whitenoise/base.py", line 263, in scantree
    yield from scantree(entry.path)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/whitenoise/base.py", line 263, in scantree
    yield from scantree(entry.path)
  [Previous line repeated 3 more times]
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/whitenoise/base.py", line 265, in scantree
    yield entry.path, entry.stat()
                      ^^^^^^^^^^^^
FileNotFoundError: [Errno 2] No such file or directory: '/home/runner/work/BLT/BLT/blt/staticfiles/mdeditor/js/lib/codemirror/addon/mode/overlay.7abff7c501c4.js'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/multiprocessing/pool.py", line 125, in worker
    result = (True, func(*args, **kwds))
                    ^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/runner.py", line 466, in _run_subsuite
    result = runner.run(subsuite)
             ^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/runner.py", line 381, in run
    test(result)
  File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/unittest/suite.py", line 84, in __call__
    return self.run(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/unittest/suite.py", line 122, in run
    test(result)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/testcases.py", line 321, in __call__
    self._setup_and_call(result)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/testcases.py", line 376, in _setup_and_call
    super().__call__(result)
  File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/unittest/case.py", line 678, in __call__
    return self.run(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/unittest/case.py", line 622, in run
    with outcome.testPartExecutor(self):
  File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/contextlib.py", line 155, in __exit__
    self.gen.throw(typ, value, traceback)
  File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/unittest/case.py", line 74, in testPartExecutor
    _addError(self.result, test_case, exc_info)
  File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/unittest/case.py", line 99, in _addError
    result.addError(test, exc_info)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/runner.py", line 298, in addError
    self.check_picklable(test, err)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/runner.py", line 219, in check_picklable
    self._confirm_picklable(err)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/runner.py", line 189, in _confirm_picklable
    pickle.loads(pickle.dumps(obj))
                 ^^^^^^^^^^^^^^^^^
TypeError: cannot pickle 'traceback' object
"""

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/runner/work/BLT/BLT/manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
    utility.execute()
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/__init__.py", line 436, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/commands/test.py", line 24, in run_from_argv
    super().run_from_argv(argv)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/base.py", line 416, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/base.py", line 460, in execute
    output = self.handle(*args, **options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/core/management/commands/test.py", line 63, in handle
    failures = test_runner.run_tests(test_labels)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/runner.py", line 1099, in run_tests
    result = self.run_suite(suite)
             ^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/runner.py", line 1026, in run_suite
    return runner.run(suite)
           ^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/unittest/runner.py", line 217, in run
    test(result)
  File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/unittest/suite.py", line 84, in __call__
    return self.run(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/runner/.cache/pypoetry/virtualenvs/blt-yuw0N2NF-py3.11/lib/python3.11/site-packages/django/test/runner.py", line 553, in run
    subsuite_index, events = test_results.next(timeout=0.1)
                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/opt/hostedtoolcache/Python/3.11.2/x64/lib/python3.11/multiprocessing/pool.py", line 873, in next
    raise value
TypeError: cannot pickle 'traceback' object

For more information, see the Django testing documentation.

@github-actions github-actions bot added the tests: failed Django tests failed label Dec 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

files-changed: 8 PR changes 8 files pre-commit: failed Pre-commit checks failed tests: failed Django tests failed

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants