diff --git a/molecule/testinfra/staging/app/test_apparmor.py b/molecule/testinfra/staging/app/test_apparmor.py index d3e236ed5d7..d6c91e65c65 100644 --- a/molecule/testinfra/staging/app/test_apparmor.py +++ b/molecule/testinfra/staging/app/test_apparmor.py @@ -105,8 +105,8 @@ def test_apparmor_total_profiles(host): """ Ensure number of total profiles is sum of enforced and complaining profiles """ with host.sudo(): - total_expected = str((len(sdvars.apparmor_enforce) - + len(sdvars.apparmor_complain))) + total_expected = str(len(sdvars.apparmor_enforce) + + len(sdvars.apparmor_complain)) # Trusty has ~10, Xenial about ~20 profiles, so let's expect # *at least* the sum. assert host.check_output("aa-status --profiled") >= total_expected diff --git a/securedrop/create-dev-data.py b/securedrop/create-dev-data.py index ab4e886710e..2e4d753c632 100755 --- a/securedrop/create-dev-data.py +++ b/securedrop/create-dev-data.py @@ -102,10 +102,9 @@ def create_source_and_submissions(num_submissions=2, num_replies=2): db.session.commit() - print(("Test source (codename: '{}', journalist designation '{}') " - "added with {} submissions and {} replies") - .format(codename, journalist_designation, num_submissions, - num_replies)) + print("Test source (codename: '{}', journalist designation '{}') " + "added with {} submissions and {} replies").format( + codename, journalist_designation, num_submissions, num_replies) if __name__ == "__main__": # pragma: no cover diff --git a/securedrop/dockerfiles/xenial/python2/Dockerfile b/securedrop/dockerfiles/xenial/python2/Dockerfile index 56ae80a0d0c..f17d7bab21b 100644 --- a/securedrop/dockerfiles/xenial/python2/Dockerfile +++ b/securedrop/dockerfiles/xenial/python2/Dockerfile @@ -1,5 +1,5 @@ -# ubuntu 16.04 image - 2019-01-22 -FROM ubuntu@sha256:e4a134999bea4abb4a27bc437e6118fdddfb172e1b9d683129b74d254af51675 +# ubuntu 16.04 image from 2019-03-12 +FROM ubuntu@sha256:58d0da8bc2f434983c6ca4713b08be00ff5586eb5cdff47bcde4b2e88fd40f88 ARG USER_NAME ENV USER_NAME ${USER_NAME:-root} ARG USER_ID diff --git a/securedrop/i18n_tool.py b/securedrop/i18n_tool.py index 1b1bc085b0a..8c982ebc3be 100755 --- a/securedrop/i18n_tool.py +++ b/securedrop/i18n_tool.py @@ -285,8 +285,8 @@ def add(p): def upstream_commit(self, args, code): self.require_git_email_name(args.root) authors = set() - diffs = str(git('--no-pager', '-C', args.root, - 'diff', '--name-only', '--cached').stdout) + diffs = six.text_type(git('--no-pager', '-C', args.root, + 'diff', '--name-only', '--cached').stdout) for path in diffs.strip().split('\n'): previous_message = str(git( '--no-pager', '-C', args.root, 'log', '-n', '1', path, diff --git a/securedrop/journalist_app/api.py b/securedrop/journalist_app/api.py index 8ffef0c704b..1db02d19589 100644 --- a/securedrop/journalist_app/api.py +++ b/securedrop/journalist_app/api.py @@ -232,7 +232,7 @@ def all_source_replies(source_uuid): user = get_user_object(request) - data = json.loads(request.data.decode('utf-8')) + data = request.json if not data['reply']: abort(400, 'reply should not be empty') diff --git a/securedrop/manage.py b/securedrop/manage.py index c5378fa1dff..97ccda2a709 100755 --- a/securedrop/manage.py +++ b/securedrop/manage.py @@ -120,7 +120,7 @@ def _get_username(): try: Journalist.check_username_acceptable(username) except InvalidUsernameException as e: - print(('Invalid username: ' + str(e))) + print('Invalid username: ' + str(e)) else: return username @@ -154,7 +154,7 @@ def _add_user(is_admin=False): print("Note: Passwords are now autogenerated.") password = _make_password() - print(("This user's password is: {}".format(password))) + print("This user's password is: {}".format(password)) is_hotp = _get_yubikey_usage() otp_secret = None @@ -166,9 +166,9 @@ def _add_user(is_admin=False): if otp_secret: tmp_str = otp_secret.replace(" ", "") if len(tmp_str) != 40: - print(("The length of the secret is not correct. " - "Expected 40 characters, but received {0}. " - "Try again.".format(len(tmp_str)))) + print("The length of the secret is not correct. " + "Expected 40 characters, but received {0}. " + "Try again.".format(len(tmp_str))) continue if otp_secret: break @@ -186,11 +186,11 @@ def _add_user(is_admin=False): print('ERROR: That username is already taken!') else: exc_type, exc_value, exc_traceback = sys.exc_info() - print((repr(traceback.format_exception(exc_type, exc_value, - exc_traceback)))) + print(repr(traceback.format_exception(exc_type, exc_value, + exc_traceback))) return 1 else: - print(('User "{}" successfully added'.format(username))) + print('User "{}" successfully added'.format(username)) if not otp_secret: # Print the QR code for FreeOTP print('\nScan the QR code below with FreeOTP:\n') @@ -199,13 +199,13 @@ def _add_user(is_admin=False): qr = qrcode.QRCode() qr.add_data(uri) qr.print_ascii(tty=sys.stdout.isatty()) - print(('\nIf the barcode does not render correctly, try ' - "changing your terminal's font (Monospace for Linux, " - 'Menlo for OS X). If you are using iTerm on Mac OS X, ' - 'you will need to change the "Non-ASCII Font", which ' - "is your profile\'s Text settings.\n\nCan't scan the " - 'barcode? Enter following shared secret manually:' - '\n{}\n'.format(user.formatted_otp_secret))) + print('\nIf the barcode does not render correctly, try ' + "changing your terminal's font (Monospace for Linux, " + 'Menlo for OS X). If you are using iTerm on Mac OS X, ' + 'you will need to change the "Non-ASCII Font", which ' + "is your profile\'s Text settings.\n\nCan't scan the " + 'barcode? Enter following shared secret manually:' + '\n{}\n'.format(user.formatted_otp_secret)) return 0 @@ -217,8 +217,8 @@ def _get_delete_confirmation(user): confirmation = obtain_input('Are you sure you want to delete user ' '"{}" (y/n)?'.format(user)) if confirmation.lower() != 'y': - print(('Confirmation not received: user "{}" was NOT ' - 'deleted'.format(user))) + print('Confirmation not received: user "{}" was NOT ' + 'deleted'.format(user)) return False return True @@ -253,7 +253,7 @@ def delete_user(args): else: raise e - print(('User "{}" successfully deleted'.format(username))) + print('User "{}" successfully deleted'.format(username)) return 0 diff --git a/securedrop/secure_tempfile.py b/securedrop/secure_tempfile.py index 959180a26d2..89890974d69 100644 --- a/securedrop/secure_tempfile.py +++ b/securedrop/secure_tempfile.py @@ -48,9 +48,8 @@ def __init__(self, store_dir): """ self.last_action = 'init' self.create_key() - self.temp_file_id = "" - data = base64.urlsafe_b64encode(os.urandom(32)) + data = base64.urlsafe_b64encode(os.urandom(32)) if not six.PY2: # For Python3 self.tmp_file_id = data.decode('utf-8').strip('=') else: @@ -69,8 +68,8 @@ def create_key(self): grsecurity-patched kernel it uses (for further details consult https://github.com/freedomofpress/securedrop/pull/477#issuecomment-168445450). """ - self.key = os.urandom(int(self.AES_key_size / 8)) - self.iv = os.urandom(int(self.AES_block_size / 8)) + self.key = os.urandom(self.AES_key_size // 8) + self.iv = os.urandom(self.AES_block_size // 8) self.initialize_cipher() def initialize_cipher(self): diff --git a/securedrop/tests/conftest.py b/securedrop/tests/conftest.py index 52559b99d42..1cb8dd3dd08 100644 --- a/securedrop/tests/conftest.py +++ b/securedrop/tests/conftest.py @@ -228,8 +228,7 @@ def journalist_api_token(journalist_app, test_journo): 'passphrase': test_journo['password'], 'one_time_code': valid_token}), headers=utils.api_helper.get_api_headers()) - observed_response = json.loads(response.data.decode('utf-8')) - return observed_response['token'] + return response.json['token'] def _start_test_rqworker(config): diff --git a/securedrop/tests/functional/journalist_navigation_steps.py b/securedrop/tests/functional/journalist_navigation_steps.py index 9aacf56fac4..f16dabb2ca6 100644 --- a/securedrop/tests/functional/journalist_navigation_steps.py +++ b/securedrop/tests/functional/journalist_navigation_steps.py @@ -232,7 +232,7 @@ def _add_user(self, username, is_admin=False, hotp=None): if hotp: hotp_checkbox = self.driver.find_element_by_css_selector( 'input[name="is_hotp"]') - print((str(hotp_checkbox.__dict__))) + print(str(hotp_checkbox.__dict__)) hotp_checkbox.click() hotp_secret = self.driver.find_element_by_css_selector( 'input[name="otp_secret"]') diff --git a/securedrop/tests/i18n/code.py b/securedrop/tests/i18n/code.py index 446c48a932c..90b456adc69 100644 --- a/securedrop/tests/i18n/code.py +++ b/securedrop/tests/i18n/code.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- from flask_babel import gettext -print((gettext('code hello i18n'))) +print(gettext('code hello i18n')) diff --git a/securedrop/tests/test_journalist_api.py b/securedrop/tests/test_journalist_api.py index ba90befbd4b..e8e47330f13 100644 --- a/securedrop/tests/test_journalist_api.py +++ b/securedrop/tests/test_journalist_api.py @@ -23,12 +23,11 @@ def test_unauthenticated_user_gets_all_endpoints(journalist_app): with journalist_app.test_client() as app: response = app.get(url_for('api.get_endpoints')) - observed_endpoints = json.loads(response.data.decode('utf-8')) expected_endpoints = ['current_user_url', 'submissions_url', 'sources_url', 'auth_token_url', 'replies_url'] expected_endpoints.sort() - sorted_observed_endpoints = list(observed_endpoints.keys()) + sorted_observed_endpoints = list(response.json.keys()) sorted_observed_endpoints.sort() assert expected_endpoints == sorted_observed_endpoints @@ -42,11 +41,10 @@ def test_valid_user_can_get_an_api_token(journalist_app, test_journo): 'passphrase': test_journo['password'], 'one_time_code': valid_token}), headers=get_api_headers()) - observed_response = json.loads(response.data.decode('utf-8')) - assert observed_response['journalist_uuid'] == test_journo['uuid'] + assert response.json['journalist_uuid'] == test_journo['uuid'] assert isinstance(Journalist.validate_api_token_and_get_user( - observed_response['token']), Journalist) is True + response.json['token']), Journalist) is True assert response.status_code == 200 @@ -60,10 +58,9 @@ def test_user_cannot_get_an_api_token_with_wrong_password(journalist_app, 'passphrase': 'wrong password', 'one_time_code': valid_token}), headers=get_api_headers()) - observed_response = json.loads(response.data.decode('utf-8')) assert response.status_code == 403 - assert observed_response['error'] == 'Forbidden' + assert response.json['error'] == 'Forbidden' def test_user_cannot_get_an_api_token_with_wrong_2fa_token(journalist_app, @@ -76,10 +73,9 @@ def test_user_cannot_get_an_api_token_with_wrong_2fa_token(journalist_app, 'passphrase': test_journo['password'], 'one_time_code': '123456'}), headers=get_api_headers()) - observed_response = json.loads(response.data.decode('utf-8')) assert response.status_code == 403 - assert observed_response['error'] == 'Forbidden' + assert response.json['error'] == 'Forbidden' def test_user_cannot_get_an_api_token_with_no_passphase_field(journalist_app, @@ -91,11 +87,10 @@ def test_user_cannot_get_an_api_token_with_no_passphase_field(journalist_app, {'username': test_journo['username'], 'one_time_code': valid_token}), headers=get_api_headers()) - observed_response = json.loads(response.data.decode('utf-8')) assert response.status_code == 400 - assert observed_response['error'] == 'Bad Request' - assert observed_response['message'] == 'passphrase field is missing' + assert response.json['error'] == 'Bad Request' + assert response.json['message'] == 'passphrase field is missing' def test_user_cannot_get_an_api_token_with_no_username_field(journalist_app, @@ -107,11 +102,10 @@ def test_user_cannot_get_an_api_token_with_no_username_field(journalist_app, {'passphrase': test_journo['password'], 'one_time_code': valid_token}), headers=get_api_headers()) - observed_response = json.loads(response.data.decode('utf-8')) assert response.status_code == 400 - assert observed_response['error'] == 'Bad Request' - assert observed_response['message'] == 'username field is missing' + assert response.json['error'] == 'Bad Request' + assert response.json['message'] == 'username field is missing' def test_user_cannot_get_an_api_token_with_no_otp_field(journalist_app, @@ -122,11 +116,10 @@ def test_user_cannot_get_an_api_token_with_no_otp_field(journalist_app, {'username': test_journo['username'], 'passphrase': test_journo['password']}), headers=get_api_headers()) - observed_response = json.loads(response.data.decode('utf-8')) assert response.status_code == 400 - assert observed_response['error'] == 'Bad Request' - assert observed_response['message'] == 'one_time_code field is missing' + assert response.json['error'] == 'Bad Request' + assert response.json['message'] == 'one_time_code field is missing' def test_authorized_user_gets_all_sources(journalist_app, test_submissions, @@ -135,13 +128,11 @@ def test_authorized_user_gets_all_sources(journalist_app, test_submissions, response = app.get(url_for('api.get_all_sources'), headers=get_api_headers(journalist_api_token)) - data = json.loads(response.data.decode('utf-8')) - assert response.status_code == 200 # We expect to see our test source in the response assert test_submissions['source'].journalist_designation == \ - data['sources'][0]['journalist_designation'] + response.json['sources'][0]['journalist_designation'] def test_user_without_token_cannot_get_protected_endpoints(journalist_app, @@ -260,10 +251,9 @@ def test_api_error_handler_404(journalist_app, journalist_api_token): with journalist_app.test_client() as app: response = app.get('/api/v1/invalidendpoint', headers=get_api_headers(journalist_api_token)) - json_response = json.loads(response.data.decode('utf-8')) assert response.status_code == 404 - assert json_response['error'] == 'Not Found' + assert response.json['error'] == 'Not Found' def test_trailing_slash_cleanly_404s(journalist_app, test_source, @@ -273,10 +263,9 @@ def test_trailing_slash_cleanly_404s(journalist_app, test_source, response = app.get(url_for('api.single_source', source_uuid=uuid) + '/', headers=get_api_headers(journalist_api_token)) - json_response = json.loads(response.data.decode('utf-8')) assert response.status_code == 404 - assert json_response['error'] == 'Not Found' + assert response.json['error'] == 'Not Found' def test_authorized_user_gets_single_source(journalist_app, test_source, @@ -288,9 +277,8 @@ def test_authorized_user_gets_single_source(journalist_app, test_source, assert response.status_code == 200 - data = json.loads(response.data.decode('utf-8')) - assert data['uuid'] == test_source['source'].uuid - assert 'BEGIN PGP PUBLIC KEY' in data['key']['public'] + assert response.json['uuid'] == test_source['source'].uuid + assert 'BEGIN PGP PUBLIC KEY' in response.json['key']['public'] def test_get_non_existant_source_404s(journalist_app, journalist_api_token): @@ -332,8 +320,7 @@ def test_authorized_user_can_star_a_source(journalist_app, test_source, # API should also report is_starred is true response = app.get(url_for('api.single_source', source_uuid=uuid), headers=get_api_headers(journalist_api_token)) - json_response = json.loads(response.data.decode('utf-8')) - assert json_response['is_starred'] is True + assert response.json['is_starred'] is True def test_authorized_user_can_unstar_a_source(journalist_app, test_source, @@ -356,8 +343,7 @@ def test_authorized_user_can_unstar_a_source(journalist_app, test_source, # API should also report is_starred is false response = app.get(url_for('api.single_source', source_uuid=uuid), headers=get_api_headers(journalist_api_token)) - json_response = json.loads(response.data.decode('utf-8')) - assert json_response['is_starred'] is False + assert response.json['is_starred'] is False def test_disallowed_methods_produces_405(journalist_app, test_source, @@ -366,10 +352,9 @@ def test_disallowed_methods_produces_405(journalist_app, test_source, uuid = test_source['source'].uuid response = app.delete(url_for('api.add_star', source_uuid=uuid), headers=get_api_headers(journalist_api_token)) - json_response = json.loads(response.data.decode('utf-8')) assert response.status_code == 405 - assert json_response['error'] == 'Method Not Allowed' + assert response.json['error'] == 'Method Not Allowed' def test_authorized_user_can_get_all_submissions(journalist_app, @@ -380,10 +365,8 @@ def test_authorized_user_can_get_all_submissions(journalist_app, headers=get_api_headers(journalist_api_token)) assert response.status_code == 200 - json_response = json.loads(response.data.decode('utf-8')) - observed_submissions = [submission['filename'] for - submission in json_response['submissions']] + submission in response.json['submissions']] expected_submissions = [submission.filename for submission in Submission.query.all()] @@ -400,10 +383,8 @@ def test_authorized_user_get_source_submissions(journalist_app, headers=get_api_headers(journalist_api_token)) assert response.status_code == 200 - json_response = json.loads(response.data.decode('utf-8')) - observed_submissions = [submission['filename'] for - submission in json_response['submissions']] + submission in response.json['submissions']] expected_submissions = [submission.filename for submission in test_submissions['source'].submissions] @@ -423,13 +404,11 @@ def test_authorized_user_can_get_single_submission(journalist_app, assert response.status_code == 200 - json_response = json.loads(response.data.decode('utf-8')) - - assert json_response['uuid'] == submission_uuid - assert json_response['is_read'] is False - assert json_response['filename'] == \ + assert response.json['uuid'] == submission_uuid + assert response.json['is_read'] is False + assert response.json['filename'] == \ test_submissions['source'].submissions[0].filename - assert json_response['size'] == \ + assert response.json['size'] == \ test_submissions['source'].submissions[0].size @@ -440,10 +419,8 @@ def test_authorized_user_can_get_all_replies(journalist_app, test_files, headers=get_api_headers(journalist_api_token)) assert response.status_code == 200 - json_response = json.loads(response.data.decode('utf-8')) - observed_replies = [reply['filename'] for - reply in json_response['replies']] + reply in response.json['replies']] expected_replies = [reply.filename for reply in Reply.query.all()] @@ -459,10 +436,8 @@ def test_authorized_user_get_source_replies(journalist_app, test_files, headers=get_api_headers(journalist_api_token)) assert response.status_code == 200 - json_response = json.loads(response.data.decode('utf-8')) - observed_replies = [reply['filename'] for - reply in json_response['replies']] + reply in response.json['replies']] expected_replies = [reply.filename for reply in test_files['source'].replies] @@ -481,19 +456,17 @@ def test_authorized_user_can_get_single_reply(journalist_app, test_files, assert response.status_code == 200 - json_response = json.loads(response.data.decode('utf-8')) - reply = Reply.query.filter(Reply.uuid == reply_uuid).one() - assert json_response['uuid'] == reply_uuid - assert json_response['journalist_username'] == \ + assert response.json['uuid'] == reply_uuid + assert response.json['journalist_username'] == \ reply.journalist.username - assert json_response['journalist_uuid'] == \ + assert response.json['journalist_uuid'] == \ reply.journalist.uuid - assert json_response['is_deleted_by_source'] is False - assert json_response['filename'] == \ + assert response.json['is_deleted_by_source'] is False + assert response.json['filename'] == \ test_files['source'].replies[0].filename - assert json_response['size'] == \ + assert response.json['size'] == \ test_files['source'].replies[0].size @@ -601,10 +574,9 @@ def test_authorized_user_can_get_current_user_endpoint(journalist_app, headers=get_api_headers(journalist_api_token)) assert response.status_code == 200 - json_response = json.loads(response.data.decode('utf-8')) - assert json_response['is_admin'] is False - assert json_response['username'] == test_journo['username'] - assert json_response['uuid'] == test_journo['journalist'].uuid + assert response.json['is_admin'] is False + assert response.json['username'] == test_journo['username'] + assert response.json['uuid'] == test_journo['journalist'].uuid def test_request_with_missing_auth_header_triggers_403(journalist_app): @@ -733,8 +705,7 @@ def test_reply_with_valid_curly_json_400(journalist_app, journalist_api_token, headers=get_api_headers(journalist_api_token)) assert response.status_code == 400 - json_response = json.loads(response.data.decode('utf-8')) - assert json_response['message'] == 'reply not found in request body' + assert response.json['message'] == 'reply not found in request body' def test_reply_with_valid_square_json_400(journalist_app, journalist_api_token, @@ -747,8 +718,7 @@ def test_reply_with_valid_square_json_400(journalist_app, journalist_api_token, headers=get_api_headers(journalist_api_token)) assert response.status_code == 400 - json_response = json.loads(response.data.decode('utf-8')) - assert json_response['message'] == 'reply not found in request body' + assert response.json['message'] == 'reply not found in request body' def test_malformed_json_400(journalist_app, journalist_api_token, test_journo, @@ -768,10 +738,9 @@ def test_malformed_json_400(journalist_app, journalist_api_token, test_journo, response = app.post(protected_route, data="{this is invalid {json!", headers=get_api_headers(journalist_api_token)) - observed_response = json.loads(response.data.decode('utf-8')) assert response.status_code == 400 - assert observed_response['error'] == 'Bad Request' + assert response.json['error'] == 'Bad Request' def test_empty_json_400(journalist_app, journalist_api_token, test_journo, @@ -789,10 +758,9 @@ def test_empty_json_400(journalist_app, journalist_api_token, test_journo, response = app.post(protected_route, data="", headers=get_api_headers(journalist_api_token)) - observed_response = json.loads(response.data.decode('utf-8')) assert response.status_code == 400 - assert observed_response['error'] == 'Bad Request' + assert response.json['error'] == 'Bad Request' def test_empty_json_20X(journalist_app, journalist_api_token, test_journo, diff --git a/securedrop/tests/test_source.py b/securedrop/tests/test_source.py index b029b673f96..4499054642f 100644 --- a/securedrop/tests/test_source.py +++ b/securedrop/tests/test_source.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- import gzip -import json import platform import re import subprocess @@ -532,10 +531,8 @@ def test_metadata_route(source_app): resp = app.get(url_for('api.metadata')) assert resp.status_code == 200 assert resp.headers.get('Content-Type') == 'application/json' - assert json.loads(resp.data.decode('utf-8')).get('sd_version') \ - == version.__version__ - assert json.loads(resp.data.decode('utf-8')).get('server_os') \ - == '16.04' + assert resp.json.get('sd_version') == version.__version__ + assert resp.json.get('server_os') == '16.04' def test_login_with_overly_long_codename(source_app):