Skip to content
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

Add homepage url to user profile #5240

Open
wants to merge 39 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
626e53f
successfully adds homepage url
jacklynhma Nov 14, 2024
9454dc2
adds test
jacklynhma Nov 14, 2024
1f66d68
fix homepage styling url
jacklynhma Nov 14, 2024
8267593
adds profile to show page
jacklynhma Nov 14, 2024
1ee00e3
fixes validation of url
jacklynhma Nov 14, 2024
2854947
passing test
jacklynhma Nov 14, 2024
708956b
tests are failing
jacklynhma Nov 14, 2024
eb0b5b4
undo change
jacklynhma Nov 14, 2024
d5ad960
passing tests
jacklynhma Nov 15, 2024
d3e13e9
add no follow
jacklynhma Nov 15, 2024
d02cad8
fix styling of link
jacklynhma Nov 15, 2024
14ff9bf
update user test
jacklynhma Nov 15, 2024
691a236
udpatetest
jacklynhma Nov 15, 2024
2f815db
Add rel=nofollow
martinemde Nov 20, 2024
3de3c7f
fix rel=nofollow on public profile
martinemde Nov 20, 2024
89cc35c
new XXS tests for homepage url
jacklynhma Nov 24, 2024
ff0086b
extract httpURL validator into a lib file and update the tests
jacklynhma Nov 24, 2024
dd31f9a
remove duplication
jacklynhma Nov 24, 2024
3e3fb7b
remove unneeded tests
jacklynhma Nov 24, 2024
b051551
add rescue
jacklynhma Nov 24, 2024
70feba9
Fix indentation
jacklynhma Dec 3, 2024
64d4a77
truncate really long homepage urls
jacklynhma Dec 3, 2024
f970fa0
renames test file so it doesn't conflict locally and adds confirmatio…
jacklynhma Dec 3, 2024
0c51ea0
restore file change
jacklynhma Dec 14, 2024
7033aff
fix indentation
jacklynhma Dec 14, 2024
df80ec6
add html_escape and rename files to allow tests to run locally
jacklynhma Dec 14, 2024
77b32e0
add html_escape for dashboards
jacklynhma Dec 14, 2024
41e72a5
fix failing tests
jacklynhma Dec 14, 2024
35b799d
change confirmation dialog prior to redirecting user
jacklynhma Dec 14, 2024
050b0a8
fix rubocop linter error
jacklynhma Dec 14, 2024
b63d9b5
create url append method
jacklynhma Dec 15, 2024
643462f
add tests for url helper method
jacklynhma Dec 15, 2024
d4042fd
fix rubocop
jacklynhma Dec 15, 2024
6ce413e
rename method
jacklynhma Dec 15, 2024
e3316bb
fix syntax error
jacklynhma Dec 15, 2024
0def58e
rename url helper and enhance url to escape html
jacklynhma Dec 15, 2024
6e3f245
fix syntax
jacklynhma Dec 15, 2024
7f3d0d3
remove test update since it is covered in a different PR
jacklynhma Dec 28, 2024
43e2dc2
add back profile_test.rb
jacklynhma Dec 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/controllers/profiles_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def params_user
end
end

PERMITTED_PROFILE_PARAMS = %i[handle twitter_username unconfirmed_email public_email full_name].freeze
PERMITTED_PROFILE_PARAMS = %i[handle twitter_username unconfirmed_email homepage_url public_email full_name].freeze

def verify_password
password = params.expect(user: :password)[:password]
Expand Down
1 change: 1 addition & 0 deletions app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ def create
password
website
twitter_username
homepage_url
full_name
].freeze

Expand Down
7 changes: 7 additions & 0 deletions app/helpers/url_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module UrlHelper
def display_safe_url(url)
return "" if url.blank?
return h(url) if url.start_with?("https://", "http://")
"https://#{h(url)}"
end
end
4 changes: 3 additions & 1 deletion app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ class User < ApplicationRecord
message: "can only contain letters, numbers, and underscores"
}, allow_nil: true, length: { within: 0..20 }

validates :homepage_url, http_url: true, allow_blank: true

validates :password,
length: { minimum: 10 },
unpwn: true,
Expand Down Expand Up @@ -352,7 +354,7 @@ def clear_personal_attributes
handle: nil, email_confirmed: false,
unconfirmed_email: nil, blocked_email: nil,
api_key: nil, confirmation_token: nil, remember_token: nil,
twitter_username: nil, webauthn_id: nil, full_name: nil,
twitter_username: nil, webauthn_id: nil, full_name: nil, homepage_url: nil,
totp_seed: nil, mfa_hashed_recovery_codes: nil,
mfa_level: :disabled,
password: SecureRandom.hex(20).encode("UTF-8")
Expand Down
14 changes: 14 additions & 0 deletions app/views/dashboards/_subject.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@
</div>
<% end %>

<% if user.homepage_url.present? %>
<div class="flex items-center mb-4 text-b3 lg:text-b2">
<%= icon_tag("link", color: :primary, class: "w-6 text-orange mr-3") %>
<p class="text-neutral-800 dark:text-white"><%=
link_to(
truncate(display_safe_url(user.homepage_url), length: 20),
display_safe_url(user.homepage_url),
rel: "nofollow",
data: { confirm: "You are about to be redirected #{display_safe_url(user.homepage_url)}" }
)
%></p>
</div>
<% end %>

<% if user.twitter_username.present? %>
<div class="flex items-center mb-4 text-b3 lg:text-b2">
<%= icon_tag("x-twitter", color: :primary, class: "w-6 text-orange mr-3") %>
Expand Down
5 changes: 5 additions & 0 deletions app/views/profiles/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
<%= form.text_field :handle, :class => 'form__input' %>
</div>

<div class="text_field">
<%= form.label :homepage_url, class: 'form__label' %>
<%= form.text_field(:homepage_url, class: 'form__input') %>
</div>

<div class="text_field">
<%= form.label :twitter_username, class: 'form__label form__label__icon-container' do %>
<%=
Expand Down
13 changes: 13 additions & 0 deletions app/views/profiles/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,19 @@
)
%>
<% end %>
<% if @user.homepage_url.present? %>
<p id="homepage-url">
<%=
link_to(
truncate(display_safe_url(@user.homepage_url),length: 20),
display_safe_url(@user.homepage_url),
rel: "nofollow",
class: "profile__header__attribute t-link--black",
data: { confirm: "You are about to be redirected #{display_safe_url(@user.homepage_url)} " }
)
%>
</p>
<% end %>
<% end %>
</div>

Expand Down
5 changes: 5 additions & 0 deletions db/migrate/20241114211431_add_homepage_url_to_users.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddHomepageUrlToUsers < ActiveRecord::Migration[7.2]
def change
add_column :users, :homepage_url, :string
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.2].define(version: 2024_11_04_065953) do
ActiveRecord::Schema[7.2].define(version: 2024_11_14_211431) do
# These are extensions that must be enabled in order to support this database
enable_extension "hstore"
enable_extension "pgcrypto"
Expand Down Expand Up @@ -557,6 +557,7 @@
t.string "mfa_hashed_recovery_codes", default: [], array: true
t.boolean "public_email", default: false, null: false
t.datetime "deleted_at"
t.string "homepage_url"
t.index "lower((email)::text) varchar_pattern_ops", name: "index_users_on_lower_email"
t.index ["email"], name: "index_users_on_email"
t.index ["handle"], name: "index_users_on_handle"
Expand Down
8 changes: 8 additions & 0 deletions lib/http_url_validator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class HttpUrlValidator < ActiveModel::EachValidator
jacklynhma marked this conversation as resolved.
Show resolved Hide resolved
def validate_each(record, attribute, value)
uri = URI::DEFAULT_PARSER.parse(value)
record.errors.add attribute, "is not a valid URL" unless [URI::HTTP, URI::HTTPS].member?(uri.class)
rescue URI::InvalidURIError
record.errors.add attribute, "is not a valid URL"
end
end
15 changes: 15 additions & 0 deletions test/integration/profile_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,21 @@ def sign_out
assert page.has_link?("@nick1", href: "https://twitter.com/nick1")
end

test "adding homepage url" do
sign_in
visit profile_path("nick1")

click_link "Edit Profile"
fill_in "user_homepage_url", with: "https://nickisawesome.com"
fill_in "Password", with: PasswordHelpers::SECURE_TEST_PASSWORD
click_button "Update"

click_link "Sign out"
visit profile_path("nick1")

assert page.has_link?("https://nickisawe...")
end

test "deleting profile" do
sign_in
visit profile_path("nick1")
Expand Down
13 changes: 13 additions & 0 deletions test/models/user_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,19 @@ class UserTest < ActiveSupport::TestCase
should_not allow_value("012345678901234567890").for(:twitter_username)
end

context "homepage url" do
should allow_value("https://www.mywebsite.com").for(:homepage_url)
should allow_value("http://www.mywebsite.com").for(:homepage_url)
should_not allow_value("<html><body>hi</body><html>").for(:homepage_url)
should_not allow_value("javascript:alert('hello');").for(:homepage_url)
should_not allow_value("file:///etc/passwd").for(:homepage_url)
should_not allow_value("file://C:/Windows/System32/cmd.exe").for(:homepage_url)
should_not allow_value("data:text/html,<script>alert('XSS')</script>").for(:homepage_url)
should_not allow_value("data:text/html;base64,SGVsbG8sIFdvcmxkIQ==").for(:homepage_url)
should_not allow_value("data:text/html,<img src='x' onerror='alert(1)'>").for(:homepage_url)
should_not allow_value("data:text/html,<iframe src='javascript:alert(1)'></iframe>").for(:homepage_url)
end

context "password" do
should "be between 10 characters and 72 bytes" do
user = build(:user, password: "%5a&12ed/")
Expand Down
33 changes: 33 additions & 0 deletions test/unit/helpers/url_helper_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
require "test_helper"

class UrlHelperTest < ActionView::TestCase
include ERB::Util
context "display_safe_url" do
should "return url if it begins with https" do
assert_equal "https://www.awesomesite.com", display_safe_url("https://www.awesomesite.com")
end
should "return empty string if url is empty" do
assert_equal "", display_safe_url("")
end

should "display a url starting with http" do
assert_equal "http://www.awesomesite.com", display_safe_url("http://www.awesomesite.com")
end

should "return link with https if it does not begin with https" do
assert_equal "https://javascript:alert(&#39;hello&#39;);", display_safe_url("javascript:alert('hello');")
end

should "escape html" do
assert_equal "https://&lt;script&gt;alert(&#39;hello&#39;);&lt;/script&gt;https://www", display_safe_url("<script>alert('hello');</script>https://www")
end

should "prepend https if url does not begin with http or https" do
assert_equal "https://www.awesomesite.com/https://javascript:alert(&#39;hello&#39;);", display_safe_url("www.awesomesite.com/https://javascript:alert('hello');")
end

should "return empty string if url is nil" do
assert_equal "", display_safe_url(nil)
end
end
end
Loading