From 31b0ade00b9df24144223322e6006761a9b8317d Mon Sep 17 00:00:00 2001 From: redshiftzero Date: Fri, 31 May 2019 11:22:27 -0700 Subject: [PATCH 1/2] app: session expiration - redirect to index when session expires Previous logic when sessions expire on /generate: 1. On /generate page, codename is added to the session. 2. When expiration occurs, session is cleared. 3. A flashed message indicating that the session has expired would be added to the current session (this is done via passing `_flashes` on the session object [0]). 4. Execution enters the view function associated with `/create`. But `/create` expects codename to be in the session (which was cleared in step 2), thus a KeyError will occur. Logic now when sessions expire on /generate: 1. On /generate page, codename is added to the session 2. When expiration occurs, session is cleared and user is redirected to the index. [0] https://github.com/pallets/flask/blob/cd4023d9d2ab630ce4f95856f065072ef8badb2b/flask/helpers.py#L449 --- securedrop/source_app/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/securedrop/source_app/__init__.py b/securedrop/source_app/__init__.py index d2679b737c..a411843ba6 100644 --- a/securedrop/source_app/__init__.py +++ b/securedrop/source_app/__init__.py @@ -145,7 +145,9 @@ def setup_g(): # clear the session after we render the message so it's localized session.clear() + # Redirect to index with flashed message flash(Markup(msg), "important") + return redirect(url_for('main.index')) session['expires'] = datetime.utcnow() + \ timedelta(minutes=getattr(config, From 7fafd7c6098b02b58ae1856bc06596dcc0fd13fe Mon Sep 17 00:00:00 2001 From: redshiftzero Date: Fri, 31 May 2019 15:24:48 -0700 Subject: [PATCH 2/2] regression test: bug #4490 --- securedrop/tests/test_source.py | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/securedrop/tests/test_source.py b/securedrop/tests/test_source.py index 4d1cfb9ae5..a72548ce36 100644 --- a/securedrop/tests/test_source.py +++ b/securedrop/tests/test_source.py @@ -3,6 +3,7 @@ import re import subprocess import six +import time from io import BytesIO from flask import session, escape, current_app, url_for, g @@ -667,7 +668,33 @@ def test_source_session_expiration(config, source_app): # which is always present and 'csrf_token' which leaks no info) session.pop('expires', None) session.pop('csrf_token', None) - assert not session, session + assert not session + + text = resp.data.decode('utf-8') + assert 'Your session timed out due to inactivity' in text + + +def test_source_session_expiration_create(config, source_app): + with source_app.test_client() as app: + + seconds_session_expire = 1 + config.SESSION_EXPIRATION_MINUTES = seconds_session_expire / 60. + + # Make codename, and then wait for session to expire. + resp = app.get(url_for('main.generate')) + assert resp.status_code == 200 + + time.sleep(seconds_session_expire + 0.1) + + # Now when we click create, the session will have expired. + resp = app.post(url_for('main.create'), follow_redirects=True) + + # check that the session was cleared (apart from 'expires' + # which is always present and 'csrf_token' which leaks no info) + session.pop('expires', None) + session.pop('csrf_token', None) + assert not session + text = resp.data.decode('utf-8') assert 'Your session timed out due to inactivity' in text