def test_submit_empty_message(self): with self.client as client: new_codename(client, session) resp = client.post('/submit', data=dict( msg="", fh=(StringIO(''), ''), ), follow_redirects=True) self.assertIn("You must enter a message or choose a file to " "submit.", resp.data)
def test_submit_file(self): with self.client as client: new_codename(client, session) self._dummy_submission(client) resp = client.post('/submit', data=dict( msg="", fh=(StringIO('This is a test'), 'test.txt'), ), follow_redirects=True) self.assertEqual(resp.status_code, 200) self.assertIn('Thanks! We received your document', resp.data)
def test_submit_empty_message(self): with self.client as client: new_codename(client, session) resp = client.post('/submit', data=dict( msg="", fh=(StringIO(''), ''), ), follow_redirects=True) self.assertIn("You must enter a message or choose a file to " "submit.", resp.data)
def test_submit_file(self): with self.client as client: new_codename(client, session) self._dummy_submission(client) resp = client.post('/submit', data=dict( msg="", fh=(StringIO('This is a test'), 'test.txt'), ), follow_redirects=True) self.assertEqual(resp.status_code, 200) self.assertIn('Thanks! We received your document', resp.data)
def test_generate_already_logged_in(self): with self.client as client: new_codename(client, session) # Make sure it redirects to /lookup when logged in resp = client.get('/generate') self.assertEqual(resp.status_code, 302) # Make sure it flashes the message on the lookup page resp = client.get('/generate', follow_redirects=True) # Should redirect to /lookup self.assertEqual(resp.status_code, 200) self.assertIn("because you are already logged in.", resp.data)
def test_generate_already_logged_in(self): with self.client as client: new_codename(client, session) # Make sure it redirects to /lookup when logged in resp = client.get('/generate') self.assertEqual(resp.status_code, 302) # Make sure it flashes the message on the lookup page resp = client.get('/generate', follow_redirects=True) # Should redirect to /lookup self.assertEqual(resp.status_code, 200) self.assertIn("because you are already logged in.", resp.data)
def test_initial_submission_notification(self): """ Regardless of the type of submission (message, file, or both), the first submission is always greeted with a notification reminding sources to check back later for replies. """ with self.client as client: new_codename(client, session) resp = self._dummy_submission(client) self.assertEqual(resp.status_code, 200) self.assertIn("Thank you for sending this information to us.", resp.data)
def test_submit_message_with_enough_entropy(self, get_entropy_estimate, async_genkey): get_entropy_estimate.return_value = 2400 with self.client as client: new_codename(client, session) self._dummy_submission(client) resp = client.post('/submit', data=dict( msg="This is a test.", fh=(StringIO(''), ''), ), follow_redirects=True) self.assertEqual(resp.status_code, 200) self.assertTrue(async_genkey.called)
def test_initial_submission_notification(self): """ Regardless of the type of submission (message, file, or both), the first submission is always greeted with a notification reminding sources to check back later for replies. """ with self.client as client: new_codename(client, session) resp = self._dummy_submission(client) self.assertEqual(resp.status_code, 200) self.assertIn( "Thank you for sending this information to us.", resp.data)
def test_submit_sanitizes_filename(self, gzipfile): """Test that upload file name is sanitized""" insecure_filename = '../../bin/gpg' sanitized_filename = 'bin_gpg' with self.client as client: new_codename(client, session) client.post('/submit', data=dict( msg="", fh=(StringIO('This is a test'), insecure_filename), ), follow_redirects=True) gzipfile.assert_called_with(filename=sanitized_filename, mode=ANY, fileobj=ANY)
def test_submit_sanitizes_filename(self, gzipfile): """Test that upload file name is sanitized""" insecure_filename = '../../bin/gpg' sanitized_filename = 'bin_gpg' with self.client as client: new_codename(client, session) client.post('/submit', data=dict( msg="", fh=(StringIO('This is a test'), insecure_filename), ), follow_redirects=True) gzipfile.assert_called_with(filename=sanitized_filename, mode=ANY, fileobj=ANY)
def test_submit_big_message(self): ''' When the message is larger than 512KB it's written to disk instead of just residing in memory. Make sure the different return type of SecureTemporaryFile is handled as well as BytesIO. ''' with self.client as client: new_codename(client, session) self._dummy_submission(client) resp = client.post('/submit', data=dict( msg="AA" * (1024 * 512), fh=(StringIO(''), ''), ), follow_redirects=True) self.assertEqual(resp.status_code, 200) self.assertIn("Thanks! We received your message", resp.data)
def test_submit_big_message(self): ''' When the message is larger than 512KB it's written to disk instead of just residing in memory. Make sure the different return type of SecureTemporaryFile is handled as well as BytesIO. ''' with self.client as client: new_codename(client, session) self._dummy_submission(client) resp = client.post('/submit', data=dict( msg="AA" * (1024 * 512), fh=(StringIO(''), ''), ), follow_redirects=True) self.assertEqual(resp.status_code, 200) self.assertIn("Thanks! We received your message", resp.data)
def test_submit_message_with_enough_entropy(self, get_entropy_estimate, async_genkey): get_entropy_estimate.return_value = 2400 with self.client as client: new_codename(client, session) self._dummy_submission(client) resp = client.post('/submit', data=dict( msg="This is a test.", fh=(StringIO(''), ''), ), follow_redirects=True) self.assertEqual(resp.status_code, 200) self.assertTrue(async_genkey.called)
def _test_source_session_expiration(self): try: old_expiration = config.SESSION_EXPIRATION_MINUTES has_session_expiration = True except AttributeError: has_session_expiration = False try: with self.client as client: codename = new_codename(client, session) # set the expiration to ensure we trigger an expiration config.SESSION_EXPIRATION_MINUTES = -1 resp = client.post('/login', data=dict(codename=codename), follow_redirects=True) assert resp.status_code == 200 resp = client.get('/lookup', 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, session assert ('You have been logged out due to inactivity' in resp.data.decode('utf-8')) finally: if has_session_expiration: config.SESSION_EXPIRATION_MINUTES = old_expiration else: del config.SESSION_EXPIRATION_MINUTES
def test_source_is_deleted_while_logged_in(self, logger): """If a source is deleted by a journalist when they are logged in, a NoResultFound will occur. The source should be redirected to the index when this happens, and a warning logged.""" with self.client as client: codename = new_codename(client, session) resp = client.post('login', data=dict(codename=codename), follow_redirects=True) # Now the journalist deletes the source filesystem_id = crypto_util.hash_codename(codename) crypto_util.delete_reply_keypair(filesystem_id) source = Source.query.filter_by(filesystem_id=filesystem_id).one() db_session.delete(source) db_session.commit() # Source attempts to continue to navigate resp = client.post('/lookup', follow_redirects=True) self.assertEqual(resp.status_code, 200) self.assertIn('Submit documents for the first time', resp.data) self.assertNotIn('logged_in', session.keys()) self.assertNotIn('codename', session.keys()) logger.assert_called_once_with( "Found no Sources when one was expected: " "No row was found for one()")
def test_source_is_deleted_while_logged_in(self, logger): """If a source is deleted by a journalist when they are logged in, a NoResultFound will occur. The source should be redirected to the index when this happens, and a warning logged.""" with self.client as client: codename = new_codename(client, session) resp = client.post('login', data=dict(codename=codename), follow_redirects=True) # Now the journalist deletes the source filesystem_id = crypto_util.hash_codename(codename) crypto_util.delete_reply_keypair(filesystem_id) source = Source.query.filter_by(filesystem_id=filesystem_id).one() db_session.delete(source) db_session.commit() # Source attempts to continue to navigate resp = client.post('/lookup', follow_redirects=True) self.assertEqual(resp.status_code, 200) self.assertIn('Submit documents for the first time', resp.data) self.assertNotIn('logged_in', session.keys()) self.assertNotIn('codename', session.keys()) logger.assert_called_once_with( "Found no Sources when one was expected: " "No row was found for one()")
def _test_source_session_expiration(self): try: old_expiration = config.SESSION_EXPIRATION_MINUTES has_session_expiration = True except AttributeError: has_session_expiration = False try: with self.client as client: codename = new_codename(client, session) # set the expiration to ensure we trigger an expiration config.SESSION_EXPIRATION_MINUTES = -1 resp = client.post('/login', data=dict(codename=codename), follow_redirects=True) assert resp.status_code == 200 resp = client.get('/lookup', 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, session assert ('You have been logged out due to inactivity' in resp.data.decode('utf-8')) finally: if has_session_expiration: config.SESSION_EXPIRATION_MINUTES = old_expiration else: del config.SESSION_EXPIRATION_MINUTES
def test_failed_normalize_timestamps_logs_warning(self, call, logger): """If a normalize timestamps event fails, the subprocess that calls touch will fail and exit 1. When this happens, the submission should still occur, but a warning should be logged (this will trigger an OSSEC alert).""" with self.client as client: new_codename(client, session) self._dummy_submission(client) resp = client.post('/submit', data=dict( msg="This is a test.", fh=(StringIO(''), ''), ), follow_redirects=True) self.assertEqual(resp.status_code, 200) self.assertIn("Thanks! We received your message", resp.data) logger.assert_called_once_with( "Couldn't normalize submission " "timestamps (touch exited with 1)" )
def test_lookup(self): """Test various elements on the /lookup page.""" with self.client as client: codename = new_codename(client, session) resp = client.post('login', data=dict(codename=codename), follow_redirects=True) # redirects to /lookup self.assertIn("public key", resp.data) # download the public key resp = client.get('journalist-key') self.assertIn("BEGIN PGP PUBLIC KEY BLOCK", resp.data)
def test_lookup(self): """Test various elements on the /lookup page.""" with self.client as client: codename = new_codename(client, session) resp = client.post('login', data=dict(codename=codename), follow_redirects=True) # redirects to /lookup self.assertIn("public key", resp.data) # download the public key resp = client.get('journalist-key') self.assertIn("BEGIN PGP PUBLIC KEY BLOCK", resp.data)
def test_failed_normalize_timestamps_logs_warning(self, call, logger): """If a normalize timestamps event fails, the subprocess that calls touch will fail and exit 1. When this happens, the submission should still occur, but a warning should be logged (this will trigger an OSSEC alert).""" with self.client as client: new_codename(client, session) self._dummy_submission(client) resp = client.post('/submit', data=dict( msg="This is a test.", fh=(StringIO(''), ''), ), follow_redirects=True) self.assertEqual(resp.status_code, 200) self.assertIn("Thanks! We received your message", resp.data) logger.assert_called_once_with("Couldn't normalize submission " "timestamps (touch exited with 1)")
def test_login_with_whitespace(self): """ Test that codenames with leading or trailing whitespace still work""" with self.client as client: def login_test(codename): resp = client.get('/login') self.assertEqual(resp.status_code, 200) self.assertIn("Enter Codename", resp.data) resp = client.post('/login', data=dict(codename=codename), follow_redirects=True) self.assertEqual(resp.status_code, 200) self.assertIn("Submit Materials", resp.data) self.assertTrue(session['logged_in']) resp = client.get('/logout', follow_redirects=True) codename = new_codename(client, session) login_test(codename + ' ') login_test(' ' + codename + ' ') login_test(' ' + codename)
def test_login_with_whitespace(self): """ Test that codenames with leading or trailing whitespace still work""" with self.client as client: def login_test(codename): resp = client.get('/login') self.assertEqual(resp.status_code, 200) self.assertIn("Enter Codename", resp.data) resp = client.post('/login', data=dict(codename=codename), follow_redirects=True) self.assertEqual(resp.status_code, 200) self.assertIn("Submit Materials", resp.data) self.assertTrue(session['logged_in']) resp = client.get('/logout', follow_redirects=True) codename = new_codename(client, session) login_test(codename + ' ') login_test(' ' + codename + ' ') login_test(' ' + codename)
def test_login_and_logout(self): resp = self.client.get('/login') self.assertEqual(resp.status_code, 200) self.assertIn("Enter Codename", resp.data) with self.client as client: codename = new_codename(client, session) resp = client.post('/login', data=dict(codename=codename), follow_redirects=True) self.assertEqual(resp.status_code, 200) self.assertIn("Submit Materials", resp.data) self.assertTrue(session['logged_in']) resp = client.get('/logout', follow_redirects=True) with self.client as c: resp = c.post('/login', data=dict(codename='invalid'), follow_redirects=True) self.assertEqual(resp.status_code, 200) self.assertIn('Sorry, that is not a recognized codename.', resp.data) self.assertNotIn('logged_in', session) with self.client as c: resp = c.post('/login', data=dict(codename=codename), follow_redirects=True) self.assertEqual(resp.status_code, 200) self.assertTrue(session['logged_in']) resp = c.get('/logout', follow_redirects=True) # sessions always have 'expires', so pop it for the next check session.pop('expires', None) self.assertNotIn('logged_in', session) self.assertNotIn('codename', session) self.assertIn('Thank you for exiting your session!', resp.data)
def test_login_and_logout(self): resp = self.client.get('/login') self.assertEqual(resp.status_code, 200) self.assertIn("Enter Codename", resp.data) with self.client as client: codename = new_codename(client, session) resp = client.post('/login', data=dict(codename=codename), follow_redirects=True) self.assertEqual(resp.status_code, 200) self.assertIn("Submit Materials", resp.data) self.assertTrue(session['logged_in']) resp = client.get('/logout', follow_redirects=True) with self.client as c: resp = c.post('/login', data=dict(codename='invalid'), follow_redirects=True) self.assertEqual(resp.status_code, 200) self.assertIn('Sorry, that is not a recognized codename.', resp.data) self.assertNotIn('logged_in', session) with self.client as c: resp = c.post('/login', data=dict(codename=codename), follow_redirects=True) self.assertEqual(resp.status_code, 200) self.assertTrue(session['logged_in']) resp = c.get('/logout', follow_redirects=True) # sessions always have 'expires', so pop it for the next check session.pop('expires', None) self.assertNotIn('logged_in', session) self.assertNotIn('codename', session) self.assertIn('Thank you for exiting your session!', resp.data)