fix: Ballot return to url via url params rather than session#7788
fix: Ballot return to url via url params rather than session#7788rjsparks merged 9 commits intoietf-tools:mainfrom
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. |
jennifer-richards
left a comment
There was a problem hiding this comment.
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
|
|
||
| if send_mail: | ||
| qstr="" | ||
| query=dict() |
There was a problem hiding this comment.
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.
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.
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.
^^ working on this now
jennifer-richards
left a comment
There was a problem hiding this comment.
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.
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.
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.
Now assuming return_to_url.py is empty, make this a class in tests_ballot.py?
ietf/doc/views_ballot.py
Outdated
| 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.
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)
jennifer-richards
left a comment
There was a problem hiding this comment.
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_pointfrom session to a url param, with some additional safety checks to protect against phishing attacks.Tasks
[x] Playwright tests