Skip to content

Commit 2976629

Browse files
authored
Merge pull request #3555 from bdarnell/release-653
Accumulated changes for v6.5.3
2 parents f01a7b0 + 9c163ae commit 2976629

File tree

11 files changed

+167
-345
lines changed

11 files changed

+167
-345
lines changed

demos/README.rst

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,6 @@ This directory contains several example apps that illustrate the usage of
55
various Tornado features. If you're not sure where to start, try the ``chat``,
66
``blog``, or ``websocket`` demos.
77

8-
.. note::
9-
10-
These applications require features due to be introduced in Tornado 6.3
11-
which is not yet released. Unless you are testing the new release,
12-
use the GitHub branch selector to access the ``stable`` branch
13-
(or the ``branchX.y`` branch corresponding to the version of Tornado you
14-
are using) to get a suitable version of the demos.
15-
16-
TODO: remove this when 6.3 ships.
17-
188
Web Applications
199
~~~~~~~~~~~~~~~~
2010

@@ -24,7 +14,6 @@ Web Applications
2414
- ``websocket``: Similar to ``chat`` but with WebSockets instead of
2515
long polling.
2616
- ``helloworld``: The simplest possible Tornado web page.
27-
- ``s3server``: Implements a basic subset of the Amazon S3 API.
2817

2918
Feature demos
3019
~~~~~~~~~~~~~

demos/blog/blog.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,14 @@ async def prepare(self):
132132
async def any_author_exists(self):
133133
return bool(await self.query("SELECT * FROM authors LIMIT 1"))
134134

135+
def redirect_to_next(self):
136+
next = self.get_argument("next", "/")
137+
if next.startswith("//") or not next.startswith("/"):
138+
# Absolute URLs are not allowed because this would be an open redirect
139+
# vulnerability (https://cwe.mitre.org/data/definitions/601.html).
140+
raise tornado.web.HTTPError(400)
141+
self.redirect(next)
142+
135143

136144
class HomeHandler(BaseHandler):
137145
async def get(self):
@@ -243,7 +251,7 @@ async def post(self):
243251
tornado.escape.to_unicode(hashed_password),
244252
)
245253
self.set_signed_cookie("blogdemo_user", str(author.id))
246-
self.redirect(self.get_argument("next", "/"))
254+
self.redirect_to_next()
247255

248256

249257
class AuthLoginHandler(BaseHandler):
@@ -270,15 +278,15 @@ async def post(self):
270278
)
271279
if password_equal:
272280
self.set_signed_cookie("blogdemo_user", str(author.id))
273-
self.redirect(self.get_argument("next", "/"))
281+
self.redirect_to_next()
274282
else:
275283
self.render("login.html", error="incorrect password")
276284

277285

278286
class AuthLogoutHandler(BaseHandler):
279287
def get(self):
280288
self.clear_cookie("blogdemo_user")
281-
self.redirect(self.get_argument("next", "/"))
289+
self.redirect_to_next()
282290

283291

284292
class EntryModule(tornado.web.UIModule):

demos/blog/templates/base.html

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,31 @@
11
<!DOCTYPE html>
22
<html>
3-
<head>
4-
<meta charset="UTF-8">
5-
<title>{{ escape(handler.settings["blog_title"]) }}</title>
6-
<link rel="stylesheet" href="{{ static_url("blog.css") }}" type="text/css">
7-
<link rel="alternate" href="/feed" type="application/atom+xml" title="{{ escape(handler.settings["blog_title"]) }}">
8-
{% block head %}{% end %}
9-
</head>
10-
<body>
11-
<div id="body">
12-
<div id="header">
13-
<div style="float:right">
14-
{% if current_user %}
15-
<a href="/compose">{{ _("New post") }}</a> -
16-
<a href="/auth/logout?next={{ url_escape(request.uri) }}">{{ _("Sign out") }}</a>
17-
{% else %}
18-
{% raw _('<a href="%(url)s">Sign in</a> to compose/edit') % {"url": "/auth/login?next=" + url_escape(request.uri)} %}
19-
{% end %}
20-
</div>
21-
<h1><a href="/">{{ escape(handler.settings["blog_title"]) }}</a></h1>
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<title>{{ escape(handler.settings["blog_title"]) }}</title>
7+
<link rel="stylesheet" href="{{ static_url(" blog.css") }}" type="text/css">
8+
<link rel="alternate" href="/feed" type="application/atom+xml" title="{{ escape(handler.settings[" blog_title"]) }}">
9+
{% block head %}{% end %}
10+
</head>
11+
12+
<body>
13+
<div id="body">
14+
<div id="header">
15+
<div style="float:right">
16+
{% if current_user %}
17+
<a href="/compose">{{ _("New post") }}</a> -
18+
<a href="/auth/logout?next={{ url_escape(request.path) }}">{{ _("Sign out") }}</a>
19+
{% else %}
20+
{% raw _('<a href="%(url)s">Sign in</a> to compose/edit') % {"url": "/auth/login?next=" +
21+
url_escape(request.path)} %}
22+
{% end %}
2223
</div>
23-
<div id="content">{% block body %}{% end %}</div>
24+
<h1><a href="/">{{ escape(handler.settings["blog_title"]) }}</a></h1>
2425
</div>
25-
{% block bottom %}{% end %}
26-
</body>
27-
</html>
26+
<div id="content">{% block body %}{% end %}</div>
27+
</div>
28+
{% block bottom %}{% end %}
29+
</body>
30+
31+
</html>

demos/chat/chatdemo.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,12 @@ def post(self):
7171
message["html"] = tornado.escape.to_unicode(
7272
self.render_string("message.html", message=message)
7373
)
74-
if self.get_argument("next", None):
75-
self.redirect(self.get_argument("next"))
74+
if next := self.get_argument("next", None):
75+
if next.startswith("//") or not next.startswith("/"):
76+
# Absolute URLs are not allowed because this would be an open redirect
77+
# vulnerability (https://cwe.mitre.org/data/definitions/601.html).
78+
raise tornado.web.HTTPError(400)
79+
self.redirect(next)
7680
else:
7781
self.write(message)
7882
global_message_buffer.add_message(message)

demos/facebook/facebook.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -70,13 +70,7 @@ async def get(self):
7070

7171
class AuthLoginHandler(BaseHandler, tornado.auth.FacebookGraphMixin):
7272
async def get(self):
73-
my_url = (
74-
self.request.protocol
75-
+ "://"
76-
+ self.request.host
77-
+ "/auth/login?next="
78-
+ tornado.escape.url_escape(self.get_argument("next", "/"))
79-
)
73+
my_url = self.request.protocol + "://" + self.request.host + "/auth/login"
8074
if self.get_argument("code", False):
8175
user = await self.get_authenticated_user(
8276
redirect_uri=my_url,
@@ -85,7 +79,7 @@ async def get(self):
8579
code=self.get_argument("code"),
8680
)
8781
self.set_signed_cookie("fbdemo_user", tornado.escape.json_encode(user))
88-
self.redirect(self.get_argument("next", "/"))
82+
self.redirect("/")
8983
return
9084
self.authorize_redirect(
9185
redirect_uri=my_url,
@@ -97,7 +91,7 @@ async def get(self):
9791
class AuthLogoutHandler(BaseHandler, tornado.auth.FacebookGraphMixin):
9892
def get(self):
9993
self.clear_cookie("fbdemo_user")
100-
self.redirect(self.get_argument("next", "/"))
94+
self.redirect("/")
10195

10296

10397
class PostModule(tornado.web.UIModule):

0 commit comments

Comments
 (0)