Skip to content

UX: show complete URL path if website domain is same as instance domain#5

Open
ShashankFC wants to merge 1 commit into
url-handling-prefrom
url-handling-post
Open

UX: show complete URL path if website domain is same as instance domain#5
ShashankFC wants to merge 1 commit into
url-handling-prefrom
url-handling-post

Conversation

@ShashankFC

@ShashankFC ShashankFC commented Feb 13, 2026

Copy link
Copy Markdown

User description

Test 6

Summary by CodeRabbit

  • Refactor

    • Reorganized code architecture to improve maintainability and better separate concerns.
  • Tests

    • Enhanced test coverage for website domain name handling with new test cases covering different domain matching scenarios, including host name derivation and path normalization based on domain configuration relationships.

✏️ Tip: You can customize this high-level summary in your review settings.


Replicated from ai-code-review-evaluation/discourse-coderabbit#6


CodeAnt-AI Description

Show full website path on user profiles when website domain matches forum domain

What Changed

  • User serializer now returns a website_name that is the website host by default, but includes the full path when the website's domain equals or is a parent of the forum's domain (e.g., example.com/user shown as "example.com/user" when the forum is on example.com or forums.example.com).
  • Profile view now displays model.website_name instead of extracting the host client-side, so the displayed text matches the new server-side logic and shows the path when appropriate.
  • Tests updated to cover website path preservation and the new domain-matching cases.

Impact

✅ Clearer website links on user profiles
✅ Accurate display when forum and website domains match
✅ Fewer client-side inconsistencies for profile website text

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.

@ShashankFC

Copy link
Copy Markdown
Author

@CodeAnt-AI: review

@codeant-ai

codeant-ai Bot commented Feb 13, 2026

Copy link
Copy Markdown

CodeAnt AI is running the review.

@codeant-ai codeant-ai Bot added the size:M This PR changes 30-99 lines, ignoring generated files label Feb 13, 2026
@codeant-ai

codeant-ai Bot commented Feb 13, 2026

Copy link
Copy Markdown

Nitpicks 🔍

🔒 No security issues identified
⚡ Recommended areas for review

  • Untrusted attribute exposure
    :website is listed as an untrusted attribute (hidden for TL0/anonymous), but :website_name was added as a normal attribute and not included in untrusted_attributes. Combined with the include-method naming issue, this may unintentionally expose website_name to users who should not see it.

  • Include method naming
    The method added to control serialization is named include_website_name (no question mark). The serializer pattern in this file uses predicate methods with a trailing ? (for example include_card_image_badge_id?). Because of the missing ?, the serializer may not call this method to decide inclusion, producing inconsistent behavior.

  • URI parsing and edge cases
    The website_name implementation calls URI(website.to_s) multiple times and rescues to nil. Non-HTTP schemes (e.g. mailto:) will produce nil hosts and may lead to website_name being nil while website is present. Re-parsing the URI multiple times is less robust and slightly inefficient.

Comment on lines +138 to +154
website_host = URI(website.to_s).host rescue nil
discourse_host = Discourse.current_hostname
return if website_host.nil?
if website_host == discourse_host
# example.com == example.com
website_host + URI(website.to_s).path
elsif (website_host.split('.').length == discourse_host.split('.').length) && discourse_host.split('.').length > 2
# www.example.com == forum.example.com
website_host.split('.')[1..-1].join('.') == discourse_host.split('.')[1..-1].join('.') ? website_host + URI(website.to_s).path : website_host
else
# example.com == forum.example.com
discourse_host.ends_with?("." << website_host) ? website_host + URI(website.to_s).path : website_host
end
end

def include_website_name
website.present?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Suggestion: The website_name attribute is always considered for serialization, even when the user's website field is supposed to be hidden (e.g. TL0 users viewed anonymously) or blank, because the include hook is misnamed (include_website_name instead of include_website_name?) and does not respect scope.restrict_user_fields?, which can leak website information and produce inconsistent JSON; update the guard to check both website.present? and !scope.restrict_user_fields?(object) and use the correct include_... ? naming so the serializer honours it. [security]

Severity Level: Critical 🚨
- ❌ TL0 anonymous user website hidden but website_name exposed.
- ❌ /users/:username.json leaks restricted website URL data.
- ⚠️ HTML profile preloaded JSON also exposes website_name.
Suggested change
website_host = URI(website.to_s).host rescue nil
discourse_host = Discourse.current_hostname
return if website_host.nil?
if website_host == discourse_host
# example.com == example.com
website_host + URI(website.to_s).path
elsif (website_host.split('.').length == discourse_host.split('.').length) && discourse_host.split('.').length > 2
# www.example.com == forum.example.com
website_host.split('.')[1..-1].join('.') == discourse_host.split('.')[1..-1].join('.') ? website_host + URI(website.to_s).path : website_host
else
# example.com == forum.example.com
discourse_host.ends_with?("." << website_host) ? website_host + URI(website.to_s).path : website_host
end
end
def include_website_name
website.present?
return if website.blank? || scope.restrict_user_fields?(object)
website_host = URI(website.to_s).host rescue nil
discourse_host = Discourse.current_hostname
return if website_host.nil?
if website_host == discourse_host
# example.com == example.com
website_host + URI(website.to_s).path
elsif (website_host.split('.').length == discourse_host.split('.').length) && discourse_host.split('.').length > 2
# www.example.com == forum.example.com
website_host.split('.')[1..-1].join('.') == discourse_host.split('.')[1..-1].join('.') ? website_host + URI(website.to_s).path : website_host
else
# example.com == forum.example.com
discourse_host.ends_with?("." << website_host) ? website_host + URI(website.to_s).path : website_host
end
end
def include_website_name?
website.present? && !scope.restrict_user_fields?(object)
Steps of Reproduction ✅
1. In the Rails app, create or use a TL0 user with a non-blank website, matching the setup
from `spec/serializers/user_serializer_spec.rb:6-12` (user with `trust_level: 0` and a
`user_profile`), but also set `user.user_profile.website = 'http://example.com/user'` as
in the spec context at `spec/serializers/user_serializer_spec.rb:67-70`.

2. Serialize this user as anonymous using the same pattern as the specs: `serializer =
UserSerializer.new(user, scope: Guardian.new, root: false); json = serializer.as_json`
(see `spec/serializers/user_serializer_spec.rb:8-9` where `scope: Guardian.new` represents
an anonymous guardian).

3. Because the user is TL0 and the scope is anonymous, `Guardian#restrict_user_fields?` at
`lib/guardian/user_guardian.rb:58-60` returns true, so the dynamically defined
`include_website?` method (created by `untrusted_attributes` at
`app/serializers/user_serializer.rb:25-33,105-111`) evaluates `return false if
scope.restrict_user_fields?(object)` and suppresses the `website` attribute; this behavior
is asserted for `:website` in the TL0 spec at
`spec/serializers/user_serializer_spec.rb:11-15`.

4. Despite `website` being omitted, the `website_name` attribute is still serialized
because (a) it is listed in `attributes` at `app/serializers/user_serializer.rb:35-43`,
(b) its include hook is misnamed `include_website_name` without a `?` at
`app/serializers/user_serializer.rb:153-155`, so ActiveModel::Serializer does not treat it
as an `include_... ?` predicate, and (c) the `website_name` method at
`app/serializers/user_serializer.rb:137-151` has no `restrict_user_fields?` guard; as a
result, `json[:website]` is absent while `json[:website_name]` still contains
`'example.com/user'`, leaking a field that was intentionally hidden for TL0 anonymous
views.
Prompt for AI Agent 🤖
This is a comment left during a code review.

**Path:** app/serializers/user_serializer.rb
**Line:** 138:154
**Comment:**
	*Security: The `website_name` attribute is always considered for serialization, even when the user's website field is supposed to be hidden (e.g. TL0 users viewed anonymously) or blank, because the include hook is misnamed (`include_website_name` instead of `include_website_name?`) and does not respect `scope.restrict_user_fields?`, which can leak website information and produce inconsistent JSON; update the guard to check both `website.present?` and `!scope.restrict_user_fields?(object)` and use the correct `include_... ?` naming so the serializer honours it.

Validate the correctness of the flagged issue. If correct, How can I resolve this? If you propose a fix, implement it and please make it concise.
👍 | 👎

@codeant-ai

codeant-ai Bot commented Feb 13, 2026

Copy link
Copy Markdown

CodeAnt AI finished running the review.

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

Labels

size:M This PR changes 30-99 lines, ignoring generated files

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants