-
Notifications
You must be signed in to change notification settings - Fork 378
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
fix: Ballot return to url via url params rather than session #7788
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #7788 +/- ##
========================================
Coverage 88.78% 88.79%
========================================
Files 296 303 +7
Lines 41320 41429 +109
========================================
+ Hits 36687 36787 +100
- Misses 4633 4642 +9 ☔ View full report in Codecov by Sentry. |
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.
A trivial style nit, plus a worry about the impact of moving the redirect into the URL. You've guarded against phishing, but I wonder if we need a more aggressive guard there. One thought: we only have a few places to return to; would it be tractable to put parameter less general than the URL in the query parameter so that it can't be exploited?
Also, I think my inline comment was wrong and you are putting an absolute URL in the parameter - if that could be made into a relative one, that'd be somewhat safer.
Finally, I think the playwright tests are failing. Is that something to fix in the code or is that github struggling?
ietf/doc/views_ballot.py
Outdated
@@ -209,9 +211,14 @@ def edit_position(request, name, ballot_id): | |||
save_position(form, doc, ballot, balloter, login, send_mail) | |||
|
|||
if send_mail: | |||
qstr="" | |||
query=dict() |
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.
syntax nit: query = dict()
(or query = {}
is perhaps trivially more efficient); either way, style is to have spaces around the =
ietf/doc/views_ballot.py
Outdated
# offsite links could be phishing attempts so let's reject them all, and require valid Datatracker | ||
# routes | ||
try: | ||
urlresolve(return_to_url) |
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.
I wonder if we should instead (or maybe in addition) check that the URL is not absolute? I think that'd prevent the phishing issue.
I wonder if it'd be worth putting an even tighter check here and only redirect to the view that we want. E.g., someone could make a nuisance link that'd cause logout (at least, until we disable log out by GET). I worry that there might be more harmful games possible with this.
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.
check that the URL is not absolute?
Hmm... I thought about checking whether it was a relative vs absolute URL through checking if it started with a /
but then there's that protocol-relative URL syntax of //example.com/phish
(now considered an antipattern) so I'd have to use a real URL parser to safely detect relative links, and then if I were checking for valid routes that's a subset of relative URLs so it didn't feel necessary.
only redirect to the view that we want
there are currently 5 views that assign request.session['ballot_edit_return_point']
so I'm guessing there are 5 ways into the ballot editing and 5 ways we should redirect out. We could have an allow list of those 5 route patterns.
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.
^^ working on this now
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.
Done. Thoughts?
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.
I like the approach. I think it is likely to be useful outside this spot so I have some slight reorg suggestions inline.
ietf/doc/return_to_url.py
Outdated
@@ -0,0 +1,34 @@ | |||
from django.urls import reverse as urlreverse, resolve as urlresolve, Resolver404 | |||
|
|||
def _parse_return_to_path(return_to_path, default_return_to_path, allowed_path_handlers): |
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 seems like a generally useful method. Maybe rename to something starting with validate_
or similar and move to ietf.utils.http
?
ietf/doc/return_to_url.py
Outdated
|
||
return return_to_path | ||
|
||
def parse_ballot_edit_return_point(ballot_edit_return_point: str, doc_name: str, ballot_id: str): |
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.
Probably goes in views_ballot.py
if you accept the suggestion to relocate the helper method. (I'm not opposed to moving away from mammoth files toward more, smaller files, but I think we should develop a strategy for organizing that)
ietf/doc/test_return_to_url.py
Outdated
from .return_to_url import parse_ballot_edit_return_point | ||
|
||
|
||
class ReturnToUrlTests(TestCase): |
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.
Now assuming return_to_url.py
is empty, make this a class in tests_ballot.py
?
ietf/doc/views_ballot.py
Outdated
from django.views.decorators.csrf import csrf_exempt | ||
from django.utils.html import escape | ||
from urllib.parse import urlencode as urllib_urlencode | ||
|
||
from .return_to_url import parse_ballot_edit_return_point |
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.
Minor, but organizationally we generally split imports into "basic packages", "django stuff", and "datatracker stuff" (i.e., ietf.*
), roughly alphabetically in each section. This should then move down into the ietf.*
section.
We're also slowly adopting the relative import syntax, so great to use it. If it's in a module that's not already using it, I've usually just followed whatever style is used there, but we should probably convert existing imports from ietf.thisapp.whatever
to .whatever
when doing so to keep it consistent.
(If you accept my reorg suggestions then that'll be moot here, though)
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.
I probably should have caught this on the last round, but the ValueErrors in the validate method will bubble up and, because they aren't handled, cause 500 errors. They should be caught in the views and lead to a 4xx response instead (probably a 400 unless there's a more specific appropriate one).
I'd be inclined to keep the response message simple and just say that the url is not allowed, rather than throwing url path names at the user. If you want to preserve the details you could log them, but I don't know if that's needed.
See #7287
This migrates the
ballot_edit_return_point
from session to a url param, with some additional safety checks to protect against phishing attacks.Tasks
[x] Playwright tests