diff --git a/CTFd/admin/__init__.py b/CTFd/admin/__init__.py index 8872fe49..89e64be2 100644 --- a/CTFd/admin/__init__.py +++ b/CTFd/admin/__init__.py @@ -125,8 +125,8 @@ def export_csv(): if model is None: abort(404) - output = six.StringIO() - writer = csv.writer(output) + temp = six.StringIO() + writer = csv.writer(temp) header = [column.name for column in model.__mapper__.columns] writer.writerow(header) @@ -138,7 +138,14 @@ def export_csv(): [getattr(curr, column.name) for column in model.__mapper__.columns] ) + temp.seek(0) + + # In Python 3 send_file requires bytes + output = six.BytesIO() + output.write(temp.getvalue().encode("utf-8")) output.seek(0) + temp.close() + return send_file( output, as_attachment=True, diff --git a/tests/admin/test_export_csv.py b/tests/admin/test_export_csv.py new file mode 100644 index 00000000..371eab93 --- /dev/null +++ b/tests/admin/test_export_csv.py @@ -0,0 +1,16 @@ +from tests.helpers import create_ctfd, destroy_ctfd, login_as_user, gen_challenge + + +def test_export_csv_works(): + """Test that CSV exports work properly""" + app = create_ctfd() + with app.app_context(): + gen_challenge(app.db) + client = login_as_user(app, name="admin", password="password") + + csv_data = client.get("/admin/export/csv?table=challenges").get_data( + as_text=True + ) + assert len(csv_data) > 0 + + destroy_ctfd(app)