Skip to content

Commit

Permalink
fix: Avoid caching negative results for lookups of username by user id
Browse files Browse the repository at this point in the history
Sending requests that cause lookup of a user-provided user id (the endpoints changed in this commit) with soon-to-exist user ids could break things, which was exploitable to cause availability issues.
  • Loading branch information
charmander committed Nov 21, 2024
1 parent 2b97608 commit 8f84c81
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 11 deletions.
24 changes: 21 additions & 3 deletions weasyl/controllers/interaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,14 @@ def followuser_(request):
followuser.insert(request.userid, otherid)
elif form.action == "unfollow":
followuser.remove(request.userid, otherid)
else:
raise WeasylError("Unexpected")

target_username = define.try_get_display_name(otherid)
if target_username is None:
raise WeasylError("Unexpected")

raise HTTPSeeOther(location="/~%s" % (define.get_sysname(define.get_display_name(otherid))))
raise HTTPSeeOther(location="/~%s" % (define.get_sysname(target_username)))


@login_required
Expand Down Expand Up @@ -56,11 +62,17 @@ def frienduser_(request):
frienduser.remove_request(request.userid, otherid)
elif form.action == "unfriend":
frienduser.remove(request.userid, otherid)
else:
raise WeasylError("Unexpected")

target_username = define.try_get_display_name(otherid)
if target_username is None:
raise WeasylError("Unexpected")

if form.feature == "pending":
raise HTTPSeeOther(location="/manage/friends?feature=pending")
else: # typical value will be user
raise HTTPSeeOther(location="/~%s" % (define.get_sysname(define.get_display_name(otherid))))
raise HTTPSeeOther(location="/~%s" % (define.get_sysname(target_username)))


@login_required
Expand All @@ -87,8 +99,14 @@ def ignoreuser_(request):
ignoreuser.insert(request.userid, [otherid])
elif form.action == "unignore":
ignoreuser.remove(request.userid, [otherid])
else:
raise WeasylError("Unexpected")

target_username = define.try_get_display_name(otherid)
if target_username is None:
raise WeasylError("Unexpected")

raise HTTPSeeOther(location="/~%s" % (define.get_sysname(define.get_display_name(otherid))))
raise HTTPSeeOther(location="/~%s" % (define.get_sysname(target_username)))


# Private messaging functions
Expand Down
2 changes: 1 addition & 1 deletion weasyl/controllers/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ def vouch_(request):
target=targetid,
).first()

target_username = define.get_display_name(targetid)
target_username = define.try_get_display_name(targetid)

if updated is not None:
define._get_all_config.invalidate(targetid)
Expand Down
18 changes: 12 additions & 6 deletions weasyl/define.py
Original file line number Diff line number Diff line change
Expand Up @@ -340,20 +340,26 @@ def get_premium(userid):
return "d" in config


@region.cache_on_arguments()
@region.cache_on_arguments(should_cache_fn=bool)
@record_timing
def _get_display_name(userid):
def _get_display_name(userid: int) -> str | None:
"""
Return the display name assiciated with `userid`; if no such user exists,
return None.
"""
return engine.scalar("SELECT username FROM profile WHERE userid = %(user)s", user=userid)


def get_display_name(userid):
if not userid:
return None
return _get_display_name(userid)
def get_display_name(userid: int) -> str:
username = _get_display_name(userid)

if username is None:
raise WeasylError("Unexpected")

return username


try_get_display_name = _get_display_name


def get_int(target):
Expand Down
5 changes: 4 additions & 1 deletion weasyl/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ def edit_streaming_settings(my_userid, userid, profile, set_stream=None, stream_
welcome.stream_insert(userid, stream_status)

pr = d.meta.tables['profile']
d.engine.execute(
result = d.engine.execute(
pr.update()
.where(pr.c.userid == userid)
.values({
Expand All @@ -515,6 +515,9 @@ def edit_streaming_settings(my_userid, userid, profile, set_stream=None, stream_
})
)

if result.rowcount != 1:
raise WeasylError("Unexpected")

if my_userid != userid:
from weasyl import moderation
note_body = (
Expand Down

0 comments on commit 8f84c81

Please sign in to comment.