-
-
Notifications
You must be signed in to change notification settings - Fork 313
feat: Add newsletter page with new releases (#3725) #5130
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
👋 Hi @dRaniwal! This pull request needs a peer review before it can be merged. Please request a review from a team member who is not:
Once a valid peer review is submitted, this check will pass automatically. Thank you! |
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughA comprehensive newsletter system is introduced, enabling users to subscribe to periodic newsletters featuring bug reports, leaderboard updates, and project news. The system includes models for newsletters and subscriptions, views for browsing and managing subscriptions, admin interfaces for newsletter creation and subscriber management, email templates, a management command to dispatch newsletters, and frontend integration with subscription banners and preference controls. Changes
Sequence DiagramssequenceDiagram
participant User
participant Browser
participant Django as Django Views
participant Email as Email Service
participant DB as Database
User->>Browser: Click "Subscribe to Newsletter"
Browser->>Django: GET /newsletter/subscribe/
Django->>Browser: Render subscription form
User->>Browser: Enter email & name, submit
Browser->>Django: POST email, name (rate limited by IP)
Django->>Django: Validate input & check rate limits
Django->>DB: Create/update NewsletterSubscriber
DB-->>Django: Return subscriber with token
Django->>Django: Generate confirmation URL with token
Django->>Email: Send confirmation email
Email-->>Django: Email queued/sent
Django->>Browser: Show success message
Browser-->>User: Display "Check your email"
par User receives email
Email-->>User: Confirmation email arrives
User->>Browser: Click confirmation link
Browser->>Django: GET /newsletter/confirm/<token>/
Django->>DB: Validate token & update confirmed=True
DB-->>Django: Updated
Django->>Browser: Redirect to newsletter home
Browser-->>User: Confirm subscription successful
end
sequenceDiagram
participant Admin
participant Command as Management Command
participant DB as Database
participant Django as Django Templates
participant Email as Email Service
participant Subscriber as Subscriber
Admin->>Command: Run send_newsletter management command
Command->>DB: Query published newsletters not yet emailed
DB-->>Command: Return newsletter(s)
loop For each newsletter
Command->>DB: Get active, confirmed subscribers
DB-->>Command: Return subscriber list
loop For each subscriber
Command->>Django: Render newsletter HTML with context
Django-->>Command: Return formatted email HTML
Command->>Django: Render plain text version
Django-->>Command: Return plain text
Command->>Email: Send via EmailMultiAlternatives
Email-->>Subscriber: Email delivered
Email-->>Command: Delivery status
end
alt At least one send succeeded
Command->>DB: Mark newsletter email_sent=True, set email_sent_at
DB-->>Command: Updated
end
end
Command-->>Admin: Report: X newsletters sent to Y subscribers
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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. Comment |
❌ Pre-commit checks failedThe 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 runAfter running these commands, the pre-commit hooks will automatically fix most issues. 💡 Tip: You can set up pre-commit to run automatically on every commit by running: pre-commit installPre-commit outputFor more information, see the pre-commit documentation. |
| Q(repo__repo_url__startswith="https://github.com/OWASP-BLT/") | ||
| | Q(repo__repo_url__startswith="https://github.com/owasp-blt/") | ||
| ) | ||
| .exclude(contributor__name__icontains="copilot") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: PR leaderboard at newsletter.py:96 incorrectly includes bot accounts due to incomplete exclusion.
Severity: CRITICAL | Confidence: High
🔍 Detailed Analysis
The newsletter page's PR contributors leaderboard at website/views/newsletter.py line 96 will display bot accounts like dependabot[bot], github-actions[bot], and renovate-bot. This occurs because the exclusion filter .exclude(contributor__name__icontains="copilot") is incomplete and does not cover all known bot names, violating critical bot exclusion requirements. This leads to inaccurate leaderboards and misrepresentation of actual human contributions.
💡 Suggested Fix
Update the bot exclusion logic at website/views/newsletter.py line 96 to include all known bot names, such as dependabot[bot], github-actions[bot], and renovate-bot, matching the exclusion logic used in website/tests/test_hackathon_leaderboard.py.
🤖 Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: website/views/newsletter.py#L96
Potential issue: The newsletter page's PR contributors leaderboard at
`website/views/newsletter.py` line 96 will display bot accounts like `dependabot[bot]`,
`github-actions[bot]`, and `renovate-bot`. This occurs because the exclusion filter
`.exclude(contributor__name__icontains="copilot")` is incomplete and does not cover all
known bot names, violating critical bot exclusion requirements. This leads to inaccurate
leaderboards and misrepresentation of actual human contributions.
Did we get this right? 👍 / 👎 to inform future reviews.
Reference ID: 4353869
| Q(pull_request__repo__repo_url__startswith="https://github.com/OWASP-BLT/") | ||
| | Q(pull_request__repo__repo_url__startswith="https://github.com/owasp-blt/") | ||
| ) | ||
| .exclude(reviewer_contributor__name__icontains="copilot") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Code review leaderboard at newsletter.py:119 incorrectly includes bot accounts due to incomplete exclusion.
Severity: CRITICAL | Confidence: High
🔍 Detailed Analysis
The newsletter page's code review leaderboard at website/views/newsletter.py line 119 will display bot accounts like dependabot[bot], github-actions[bot], and renovate-bot. This occurs because the exclusion filter .exclude(reviewer_contributor__name__icontains="copilot") is incomplete and does not cover all known bot names, violating critical bot exclusion requirements. This leads to inaccurate leaderboards and misrepresentation of actual human contributions.
💡 Suggested Fix
Update the bot exclusion logic at website/views/newsletter.py line 119 to include all known bot names, such as dependabot[bot], github-actions[bot], and renovate-bot, matching the exclusion logic used in website/tests/test_hackathon_leaderboard.py.
🤖 Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.
Location: website/views/newsletter.py#L119
Potential issue: The newsletter page's code review leaderboard at
`website/views/newsletter.py` line 119 will display bot accounts like `dependabot[bot]`,
`github-actions[bot]`, and `renovate-bot`. This occurs because the exclusion filter
`.exclude(reviewer_contributor__name__icontains="copilot")` is incomplete and does not
cover all known bot names, violating critical bot exclusion requirements. This leads to
inaccurate leaderboards and misrepresentation of actual human contributions.
Did we get this right? 👍 / 👎 to inform future reviews.
Reference ID: 4353869
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (9)
website/templates/newsletter.html (2)
35-57: Open/closed bug stats are computed but not surfaced here.The view prepares
open_bugsandclosed_bugsin the context, but the “Platform Statistics” section only shows total bugs, bugs this month, users, and active hunts.If surfacing open vs. closed issues is useful for the newsletter audience, you might want to add two more cards here; otherwise, consider dropping those context keys to avoid dead code.
105-154: Leaderboard avatar/email usage assumesUserProfileexists for each user.This block accesses
leader.userprofile.avatarand falls back to{% gravatar_url leader.email 40 %}for avatars. This is fine if every leaderboard user is guaranteed to have aUserProfileandIf this view becomes performance‑sensitive, you could:
- Add
select_related("userprofile")to the leaderboard query, and/or- Add a defensive check in the template (e.g., guard for missing
userprofile) to avoid surprises.Not blocking, but worth keeping in mind.
blt/urls.py (1)
625-625: Newsletter route is correctly exposed; considerpath()for simplicity (optional).The new
re_path(r"^newsletter/$", NewsletterView.as_view(), name="newsletter")correctly exposes/newsletter/and fits the surrounding style.Given there’s no regex in this pattern, you could optionally simplify to:
- re_path(r"^newsletter/$", NewsletterView.as_view(), name="newsletter"), + path("newsletter/", NewsletterView.as_view(), name="newsletter"),to match Django’s recommended style for simple, non‑regex routes.
website/views/newsletter.py (6)
47-56: You computeopen_bugsandclosed_bugsbut they’re not currently used.
open_bugsandclosed_bugsare added to the context but don’t appear innewsletter.html. If they’re not needed for the newsletter (or elsewhere via inclusion), consider removing them to avoid unnecessary queries; otherwise, surface them in the UI like additional stat cards.
75-82:monthly_leaderboardis unused in the template.You build
monthly_leaderboard(top contributors over the last 30 days), but the template currently has only a “Top Contributors” section and no separate “This Month” leaderboard.Either:
- Add a “Top Contributors This Month” section to the template that uses
monthly_leaderboard, or- Drop this context key until it’s needed to keep the view focused.
Right now it adds query cost without user‑visible benefit.
85-107: Minor cleanup:select_relatedis redundant before.values()in PR leaderboard.In the PR leaderboard query you call
.select_related("contributor", "user_profile__user")and then immediately switch to.values(...). Once you use.values(), the result is a dict‑like row andselect_relatedno longer provides benefit.You can safely drop
select_related(...)here to avoid confusion and a bit of overhead:- pr_leaderboard = ( - GitHubIssue.objects.filter( + pr_leaderboard = ( + GitHubIssue.objects.filter( type="pull_request", is_merged=True, contributor__isnull=False, merged_at__gte=last_6_months, ) .filter( Q(repo__repo_url__startswith="https://github.com/OWASP-BLT/") | Q(repo__repo_url__startswith="https://github.com/owasp-blt/") ) .exclude(contributor__name__icontains="copilot") - .select_related("contributor", "user_profile__user") .values(Functionality remains the same.
155-157:recent_projectscontext is currently unused.You populate
recent_projects = Project.objects.order_by("-created")[:5]and attach it to the context, but the template doesn’t referencerecent_projects.If you don’t plan to show a “Recent Projects” section in the newsletter, consider removing this query to keep the view lean; otherwise, add the corresponding block in the template.
159-169: Bug category distribution ignores hidden issues; confirm if that’s intentional.The bug category aggregation:
bug_categories = ( Issue.objects.filter(created__gte=last_30_days) .values("label") .annotate(count=Count("id")) )doesn’t filter
is_hidden=False, unlikerecent_bugs. That means hidden issues still contribute to the label counts.If hidden issues are meant to be completely excluded from user‑facing stats, add
.filter(is_hidden=False)here; if they’re just “non‑listed” but can still influence aggregate stats, current behavior is fine.
43-45: “This Month” uses last 30 days; consider calendar‑month semantics (optional).For “Bugs This Month” and “Bug Categories This Month” you use
last_30_days = now - timedelta(days=30). That’s perfectly valid, but slightly different from a calendar month (e.g., May 1–31).If you want strict calendar‑month semantics, you could compute the start as
now.replace(day=1)(possibly adjusted by timezone). If “last 30 days” is acceptable for the product language, no change is needed—just be aware of the nuance.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting
📒 Files selected for processing (3)
blt/urls.py(2 hunks)website/templates/newsletter.html(1 hunks)website/views/newsletter.py(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
blt/urls.py (1)
website/views/newsletter.py (1)
NewsletterView(31-171)
website/views/newsletter.py (1)
website/models.py (10)
Contributor(1297-1309)Domain(386-484)GitHubIssue(2034-2260)GitHubReview(2272-2316)Hunt(540-573)Issue(590-739)Organization(181-289)Project(1312-1381)Repo(1849-1926)UserProfile(896-1088)
⏰ 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)
blt/urls.py (1)
213-213: NewsletterView import wiring looks correct.Importing
NewsletterViewhere matches the newwebsite/views/newsletter.pymodule and keeps all main site views centralized in this urls file. No issues.website/views/newsletter.py (2)
39-56: Overall context building is cohesive and aligns with the template.The use of
timezone.now(), explicit 30‑day and 6‑month windows, and clear grouping of “Summary”, “Leaderboards”, “Hunts”, and “Releases” makesget_context_datastraightforward to follow. Counts and basic stats (bugs, users, domains, organizations, active hunts) map cleanly to the newsletter UI.No blocking issues here.
109-129: I encountered a repository cloning issue, but I can still analyze the provided code snippet and draw conclusions from the information presented.Looking at the code snippet provided in the review comment, I can see that line 129 clearly shows
context["code_review_leaderboard"] = code_review_leaderboard, which means the computed data is being added to the template context.The original review claims the data "isn't used in
newsletter.html," but the snippet itself shows the context assignment is present. Without access to the actual template file to verify whether it consumes this context variable, I cannot definitively confirm whether:
- The template actually uses this variable (supporting the review's concern), or
- The template ignores it despite receiving it (confirming unused data), or
- The template successfully displays it (making the review incorrect)
The code shown suggests the developer did intend to pass this data to the template. The review comment's concern would only be valid if:
- The
newsletter.htmltemplate doesn't reference this variable, OR- The context assignment doesn't actually execute/complete
The evidence is inconclusive because the provided snippet demonstrates the variable is being passed in the context, but the template file content is needed to verify actual usage.
Code_review_leaderboard context is passed but template usage unverified. The query builds top reviewers over 6 months from BLT repos (excluding Copilot), and line 129 confirms the data is added to context. Verify that
newsletter.htmlactually renders this leaderboard; if not, consider removing the unused computation.
❌ Pre-commit checks failedThe 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 runAfter running these commands, the pre-commit hooks will automatically fix most issues. 💡 Tip: You can set up pre-commit to run automatically on every commit by running: pre-commit installPre-commit outputFor more information, see the pre-commit documentation. |
There was a problem hiding this 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
♻️ Duplicate comments (1)
website/views/newsletter.py (1)
71-117: Bot filtering in PR & review leaderboards is mostly fixed; consider covering non‑[bot]names and de‑duplicating logicThe updated filters excluding
[bot],copilot, anddependaboton bothcontributor__nameandreviewer_contributor__nameshould now correctly hide common[bot]accounts likedependabot[bot]andgithub-actions[bot], addressing the bulk of the earlier concern. If your requirements also treat accounts likerenovate-bot(no[bot]suffix) as bots, they will still appear; in that case you can extend the filters, for example:- .exclude(contributor__name__icontains="dependabot") + .exclude(contributor__name__icontains="dependabot") + .exclude(contributor__name__icontains="renovate-bot")and mirror that change in the
reviewer_contributor__nameexcludes. To avoid future drift with other leaderboards/tests (e.g., hackathon leaderboard), it would be cleaner to centralize the list of bot substrings in a shared helper or constant and reuse it in both querysets instead of duplicating the exclusion chains.
🧹 Nitpick comments (1)
website/views/newsletter.py (1)
27-69: Context assembly and core leaderboards look solid; minor “this month” naming nitThe overall
TemplateViewsetup, timezone-aware window computation, and use ofannotate(Sum)for all‑time and 30‑day leaderboards look correct and idiomatic for Django. One small polish point:bugs_this_monthis actually “last 30 days”, not “calendar month”, so if the template copy says “This month” you may want to either (a) rename the context key tobugs_last_30_daysor (b) switch to a first‑of‑month boundary for stricter accuracy.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting
📒 Files selected for processing (2)
website/templates/newsletter.html(1 hunks)website/views/newsletter.py(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- website/templates/newsletter.html
⏰ 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 (2)
website/views/newsletter.py (2)
119-136: Streakers, releases, hunts, and recent projects queries are straightforward and appropriateSelecting top streakers by
current_streak, latest releases byrelease_datetime, active hunts byis_publishedandend_on__gte=now, and the most recent projects bycreatedis a clear and reasonable way to surface engagement and activity on the newsletter page. The limits ([:5],[:10]) also keep the page lightweight.
137-147: Bug‑category aggregation and label mapping look robustAggregating issues from the last 30 days by
labeland then mapping withlabel_map = dict(Issue.labels)with a safe"Unknown"fallback is a sensible way to build the “bug categories this month” summary while remaining resilient to unexpected/legacy labels.
❌ Tests failedThe 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_methodTest output (last 100 lines)For more information, see the Django testing documentation. |
|
@coderabbitai Failed Test Root Cause Why This Isn't Related to My PR urls.py - Added newsletter route Suggested Fix (for a separate PR) |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 81 🏁 Script executed: Length of output: 11408 🏁 Script executed: Length of output: 2089 🏁 Script executed: Length of output: 1532 🏁 Script executed: Length of output: 2016 🏁 Script executed: Length of output: 764 🏁 Script executed: Length of output: 2408 @dRaniwal Your analysis is spot-on! I've verified your findings and can confirm this test failure is not related to your newsletter changes. Verification SummaryFiles modified in your PR:
The failing code: ip_record.save(update_fields=["agent", "count"])Why This Isn't Your PR's Fault
Root CauseThis appears to be a race condition or transaction state issue in the middleware that surfaces intermittently during integration tests. The test Recommended Next Steps
Your newsletter implementation looks solid and isn't causing this failure. 👍 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolve the issues(sentry bot ones) please.
i have resolved the sentry bot issues: Fix applied: Added multiple exclusion filters to catch common bot patterns: This is applied to both the PR leaderboard (lines 85-87) and the code review leaderboard (lines 107-109) in newsletter.py. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Can you re-run the pre-commit hook locally.
- I think you can improve the bot filtering.
- You can also enhance performance with caching.
Thank you for the suggestions. The tests are passing now after running the precommit properly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 21 out of 22 changed files in this pull request and generated 15 comments.
| path("newsletter/<slug:slug>/", newsletter_detail, name="newsletter_detail"), | ||
| path("bounty_payout/", bounty_payout, name="bounty_payout"), |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The URL pattern for newsletter_detail at line 1229 (/newsletter/<slug:slug>/) must come after all other /newsletter/* patterns, otherwise it will match URLs like /newsletter/subscribe/, /newsletter/archive/, etc. and try to find a newsletter with slug "subscribe" or "archive". Move this pattern to the end of the newsletter URL group (after line 1228) to prevent it from capturing other newsletter URLs.
| path("newsletter/<slug:slug>/", newsletter_detail, name="newsletter_detail"), | |
| path("bounty_payout/", bounty_payout, name="bounty_payout"), | |
| path("bounty_payout/", bounty_payout, name="bounty_payout"), | |
| path("newsletter/<slug:slug>/", newsletter_detail, name="newsletter_detail"), |
| """Increment the view count for this newsletter""" | ||
| self.view_count += 1 | ||
| self.save(update_fields=["view_count"]) |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The increment_view_count() method uses save(update_fields=['view_count']) which is not atomic and can lead to race conditions if multiple users view the newsletter simultaneously. The view count will be incorrect. Use F() expression with update() instead: Newsletter.objects.filter(pk=self.pk).update(view_count=F('view_count') + 1) to ensure atomic increments.
| """Increment the view count for this newsletter""" | |
| self.view_count += 1 | |
| self.save(update_fields=["view_count"]) | |
| """Increment the view count for this newsletter atomically""" | |
| Newsletter.objects.filter(pk=self.pk).update(view_count=F('view_count') + 1) |
| } | ||
|
|
||
| # Convert Markdown content to HTML | ||
| html_content = markdown.markdown(self.content, extensions=["extra", "codehilite"]) |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The format_for_email() method uses codehilite extension for markdown, which requires Pygments. However, bleach and markdown are imported inline within the method instead of at the module level. Consider moving these imports to the top of the file for consistency with Django best practices, or document why they need to be imported inline.
| if name: | ||
| name = re.sub(r'[<>"\']', "", name) | ||
| name = name[:100] |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The name sanitization on lines 1783-1785 uses a regex to remove potentially dangerous characters, but this approach is fragile. Consider using Django's built-in escape() function or bleach.clean() for consistency with the rest of the codebase, especially since bleach is already imported and used elsewhere in the project.
| try: | ||
| with transaction.atomic(): | ||
| # Check if we're in a broken transaction | ||
| if transaction.get_rollback(): | ||
| logger.warning("Skipping block count increment - transaction marked for rollback") | ||
| return | ||
|
|
||
| # Use atomic QuerySet.update() with F() instead of save() | ||
| if ip: | ||
| Blocked.objects.filter(address=ip).update(count=models.F("count") + 1) | ||
| elif network: | ||
| Blocked.objects.filter(ip_network=network).update(count=models.F("count") + 1) | ||
| elif user_agent: | ||
| # Find matching user agents and update them | ||
| matching_agents = [ | ||
| agent | ||
| for agent in Blocked.objects.values_list("user_agent_string", flat=True) | ||
| if agent is not None and user_agent is not None and agent.lower() in user_agent.lower() | ||
| ] | ||
| if matching_agents: | ||
| Blocked.objects.filter(user_agent_string__in=matching_agents).update( | ||
| count=models.F("count") + 1 | ||
| ) | ||
| except Exception as e: | ||
| logger.error(f"Error incrementing block count: {str(e)}", exc_info=True) |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The changes to increment_block_count method appear to be unrelated to the newsletter feature. These middleware changes should be in a separate PR focused on fixing the IP restriction functionality. Mixing unrelated changes makes it harder to review and can complicate rollbacks if issues arise.
| except ConnectionError: | ||
| logger.exception("Connection error sending confirmation email") | ||
| messages.error(request, "We couldn't send a confirmation email right now. Please try again later.") |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The exception handling for ConnectionError at line 1850 will never be triggered because send_confirmation_email() raises RuntimeError (lines 1906, 1909) instead of ConnectionError. The error handling should catch RuntimeError or the send_confirmation_email() function should be updated to raise ConnectionError directly.
| <div class="relative mt-9"> | ||
| <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6"> | ||
| <div class="lg:flex lg:gap-8"> | ||
| <!-- Main content area --> |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The SVG icons throughout this template (and others) are missing aria-hidden="true" attributes. Decorative icons should be hidden from screen readers to avoid confusion. Add aria-hidden="true" to all decorative SVG elements, or provide meaningful aria-label attributes if the icons convey important information not available in surrounding text.
| user = models.ForeignKey( | ||
| User, on_delete=models.SET_NULL, null=True, blank=True, related_name="newsletter_subscriptions" | ||
| ) | ||
| email = models.EmailField(unique=True) |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The email field in the NewsletterSubscriber model has unique=True constraint. However, the subscription flow in newsletter_subscribe view (lines 1801-1820 in user.py) uses get_or_create() which can raise an IntegrityError if there's a race condition between the initial deactivation query and the get_or_create call. This can happen when multiple subscription requests for the same email arrive simultaneously. Consider using update_or_create() instead or wrapping in proper exception handling for IntegrityError.
| for subscriber in subscribers: | ||
| try: | ||
| self.send_to_subscriber(subscriber.email, newsletter, subscriber=subscriber) | ||
| successful_sends += 1 | ||
| except Exception as e: | ||
| logger.error(f"Failed to send newsletter to {subscriber.email}: {str(e)}") |
Copilot
AI
Dec 3, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The newsletter sending implementation sends emails sequentially in a loop (lines 63-68). This can be very slow for a large subscriber base and may timeout. Consider using bulk email sending with Django's send_mass_mail() or implementing a celery task queue to handle email sending asynchronously in batches.
|
@dRaniwal can you please resolve co-pilot comments, maybe you can drop a message if you resolved it, or maybe if it's not needed |
|
📅 PR Updated This pull request was last updated on: Sat, 06 Dec 2025 06:34:46 GMT This comment is automatically updated when the PR is synchronized with the base branch. |
blt/middleware/ip_restrict.py
Outdated
| # Delete duplicate records within the same atomic block | ||
| ip_records.exclude(pk=ip_record.pk).delete() | ||
| else: | ||
| # If no record exists, create a new one |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR already touches 22 files, so adding these changes or unrelated architectural updates would be out of scope. Could we keep the requested improvements limited to what is directly relevant to this PR?
|
@coderabbitai how many and what changes have been made to this PR since my approval message? |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 8687 @Pritz395 Since your approval on December 3, 2025 at 2:10 PM UTC, there have been 11 commits made to this PR. Here's the breakdown: 📊 Summary
🔧 Functional Changes After Your Approval1. Exception Handling Improvements (14:12 UTC)
2. NewsletterSubscriber Lookup Fix (14:30 UTC)
3. Import Error Fix (14:42 UTC)
4. Security Enhancement (14:51 UTC)
5. Migration Conflict Resolution (17:07 UTC)
6. Newsletter Model Fix (17:28 UTC)
7. IP Middleware Transaction Fix (18:48 UTC)
🔄 Merge Commits (4 total)Merges at 14:25, 16:09, 16:47, and 17:16 UTC to stay synced with main branch. Status: All changes are bug fixes and improvements addressing review feedback (from both you and automated reviews). No new features or scope changes have been introduced since your approval. |
|
@coderabbitai there are still unresolved errors |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 6815 🏁 Script executed: Length of output: 34024 @Pritz395 I've completed a comprehensive analysis. Here are the unresolved errors blocking this PR: 🔴 CRITICAL ISSUES (Must Fix)1. Incomplete Bot Exclusion - Sentry Bot (CRITICAL)Location: Problem: Bot accounts like Fix needed: @dRaniwal, update both locations to exclude all bot patterns: # Line 69 (PR leaderboard) - Add these exclusions:
.exclude(contributor__name__icontains="[bot]")
.exclude(contributor__name__icontains="copilot")
.exclude(contributor__name__icontains="dependabot")
.exclude(contributor__name__icontains="github-actions")
.exclude(contributor__name__icontains="renovate")
# Line 93 (Code review leaderboard) - Same exclusions:
.exclude(reviewer_contributor__name__icontains="[bot]")
.exclude(reviewer_contributor__name__icontains="copilot")
.exclude(reviewer_contributor__name__icontains="dependabot")
.exclude(reviewer_contributor__name__icontains="github-actions")
.exclude(reviewer_contributor__name__icontains="renovate")2. ReDoS Security Vulnerability - CodeQL + CodeRabbit + Copilot (CRITICAL)Location: Problem: Email validation regex Fix needed: Replace with Django's built-in validator: # Add at top of file:
from django.core.validators import validate_email
# Line 1789 - Replace:
if not re.match(r"[^@]+@[^@]+\.[^@]+", email):
messages.error(request, "Please enter a valid email address.")
return redirect("newsletter_subscribe")
# With:
try:
validate_email(email)
except ValidationError:
messages.error(request, "Please enter a valid email address.")
return redirect("newsletter_subscribe")
# Line 2075 - Similar fix for newsletter_resend_confirmation
if not email:
return JsonResponse({"success": False, "error": "Valid email address is required"})
try:
validate_email(email)
except ValidationError:
return JsonResponse({"success": False, "error": "Valid email address is required"})🟡 MAJOR ISSUES (Should Fix)3. Error Messages Expose Technical Details - CopilotLocations: Multiple in Problem: Error messages show error IDs and exception details to users, violating project guidelines. Fix needed: Use generic messages: # Don't expose error IDs to users
messages.error(request, "There was an error processing your request. Please try again later.")
# NOT: messages.error(request, f"An error occurred. Error ID: {error_id}")4. JavaScript Embedded in HTML Template - CopilotLocation: Problem: Violates project guideline: "❌ Do NOT embed JavaScript in HTML templates" Fix needed: Move JavaScript to 5. django-gravatar2 Version Downgrade - CopilotLocation: Problem: Changed from Fix needed: Revert to ⚪ MINOR ISSUES (Nice to Fix)6. Redundant Import & Unnecessary Pass - CopilotLocation:
7. Code Organization - CopilotLocation:
⏳ PENDING STATUS
Summary for @dRaniwal: The 2 critical security/correctness issues (#1 bot exclusion, #2 ReDoS) must be fixed immediately. Then address the major issues (#3-5) before requesting re-review. The PR cannot be merged until:
Would you like me to provide the complete code changes for all fixes? 🔧 |
There was a problem hiding this 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
♻️ Duplicate comments (1)
website/views/core.py (1)
3378-3414: Consider caching newsletter data in the context processorBecause
newsletter_context_processorruns on every template render, the per-request subscriber lookup andlatest_newsletterquery can add measurable overhead. Consider:
- Caching the per-user subscription status (e.g.,
newsletter_subscription_{request.user.id}) with a short TTL.- Caching the latest published newsletter (e.g., key like
"latest_newsletter") for a few minutes.- Optionally moving this function to a dedicated
context_processorsmodule for clearer separation from view logic.This would address the earlier performance concerns raised on this context processor without changing behavior.
🧹 Nitpick comments (1)
website/views/core.py (1)
3386-3414: Avoid duplicatecount()queries and improve exception logging innewsletter_context_processorYou can reduce DB hits and align with Ruff’s logging hints by reusing the count, ordering once, and switching to
logger.exceptionwith%sformatting plus an explicit fallback forlatest_newsletter:- if request.user.is_authenticated: - try: - # Use filter() instead of get() and order by most recent - subscribers = NewsletterSubscriber.objects.filter(user=request.user) - - # Log how many subscribers were found for debugging - if subscribers.count() > 1: - logger.warning( - f"Multiple newsletter subscriptions found for user {request.user.username} (ID: {request.user.id}). " - f"Count: {subscribers.count()}" - ) - - subscriber = subscribers.order_by("-subscribed_at").first() + if request.user.is_authenticated: + try: + # Use filter() instead of get() and order by most recent + subscribers = NewsletterSubscriber.objects.filter(user=request.user).order_by("-subscribed_at") + subscriber_count = subscribers.count() + + # Log how many subscribers were found for debugging + if subscriber_count > 1: + logger.warning( + "Multiple newsletter subscriptions found for user %s (ID: %s). Count: %s", + request.user.username, + request.user.id, + subscriber_count, + ) + + subscriber = subscribers.first() if subscriber: context["newsletter_subscription"] = { "subscribed": True, "confirmed": subscriber.confirmed, "is_active": subscriber.is_active, } else: context["newsletter_subscription"] = {"subscribed": False} - except Exception as e: - logger.error(f"Error in newsletter context processor for user {request.user.id}: {str(e)}") + except Exception: + logger.exception("Error in newsletter context processor for user %s", request.user.id) context["newsletter_subscription"] = {"subscribed": False} - try: - context["latest_newsletter"] = Newsletter.objects.filter(status="published").order_by("-published_at").first() - except Exception as e: - logger.error(f"Error fetching latest newsletter: {str(e)}") - - return context + try: + context["latest_newsletter"] = ( + Newsletter.objects.filter(status="published").order_by("-published_at").first() + ) + except Exception: + logger.exception("Error fetching latest newsletter") + context["latest_newsletter"] = None + + return contextThis keeps behavior the same while cutting one
COUNT(*)per request and giving you stack traces on failures.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Knowledge base: Disabled due to Reviews -> Disable Knowledge Base setting
📒 Files selected for processing (1)
website/views/core.py(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
website/views/core.py (1)
website/models.py (3)
Newsletter(3516-3677)NewsletterSubscriber(3680-3767)is_active(3328-3333)
🪛 Ruff (0.14.7)
website/views/core.py
3405-3405: Do not catch blind exception: Exception
(BLE001)
3406-3406: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
3406-3406: Use explicit conversion flag
Replace with conversion flag
(RUF010)
3411-3411: Do not catch blind exception: Exception
(BLE001)
3412-3412: Use logging.exception instead of logging.error
Replace with exception
(TRY400)
3412-3412: Use explicit conversion flag
Replace with conversion flag
(RUF010)
⏰ 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/core.py (1)
63-64: Newsletter imports wired correctlyImporting
NewsletterandNewsletterSubscriberhere matches their use innewsletter_context_processor; no changes needed.
feat: Add newsletter page with new releases" --body "## Summary
This PR implements a new `/newsletter/` page for the BLT project, addressing issue #3725.
Features Added
Technical Details
Screenshots
Checklist
Closes #3725" --base main --head dRaniwal:fix/ui-ux-inconsistencies-4693 --repo OWASP-BLT/BLT
Summary by CodeRabbit
Release Notes
✏️ Tip: You can customize this high-level summary in your review settings.