def test_api_challenges_challenge_with_requirements_no_user(): """Does the challenge list API show gated challenges to the public?""" app = create_ctfd() with app.app_context(): set_config("challenge_visibility", "public") prereq_id = gen_challenge(app.db).id chal_obj = gen_challenge(app.db) chal_obj.requirements = {"prerequisites": [prereq_id]} # Create a new user which will solve the prerequisite register_user(app) # Confirm that only the prerequisite challenge is listed publicly with app.test_client() as client: r = client.get("/api/v1/challenges") assert r.status_code == 200 initial_data = r.get_json()["data"] (chal_data, ) = initial_data assert chal_data["id"] == prereq_id # Fix up the solve count for later comparison with `initial_data` chal_data["solves"] += 1 # Generate a solve and then confirm the response is unchanged gen_solve(app.db, user_id=2, challenge_id=prereq_id) with app.test_client() as client: r = client.get("/api/v1/challenges") assert r.status_code == 200 assert r.get_json()["data"] == initial_data destroy_ctfd(app)
def test_reset(): app = create_kmactf() with app.app_context(): base_user = "******" for x in range(10): chal = gen_challenge(app.db, name="chal_name{}".format(x)) gen_flag(app.db, challenge_id=chal.id, content="flag") for x in range(10): user = base_user + str(x) user_email = user + "@kmactf.io" user_obj = gen_user(app.db, name=user, email=user_email) gen_award(app.db, user_id=user_obj.id) gen_solve(app.db, user_id=user_obj.id, challenge_id=random.randint(1, 10)) gen_fail(app.db, user_id=user_obj.id, challenge_id=random.randint(1, 10)) gen_tracking(app.db, user_id=user_obj.id) assert Users.query.count() == 11 # 11 because of the first admin user assert Challenges.query.count() == 10 register_user(app) client = login_as_user(app, name="admin", password="******") with client.session_transaction() as sess: data = {"nonce": sess.get("nonce")} client.post("/admin/reset", data=data) assert Users.query.count() == 0 assert Challenges.query.count() == 10 assert Solves.query.count() == 0 assert Fails.query.count() == 0 assert Tracking.query.count() == 0 destroy_kmactf(app)
def test_scoreboard_is_cached(): """Test that /api/v1/scoreboard is properly cached and cleared""" app = create_ctfd() with app.app_context(): # create user1 register_user(app, name="user1", email="*****@*****.**") # create challenge chal = gen_challenge(app.db, value=100) gen_flag(app.db, challenge_id=chal.id, content="flag") chal_id = chal.id # create a solve for the challenge for user1. (the id is 2 because of the admin) gen_solve(app.db, user_id=2, challenge_id=chal_id) with login_as_user(app, "user1") as client: # No cached data assert app.cache.get("view/api.scoreboard_scoreboard_list") is None assert app.cache.get( "view/api.scoreboard_scoreboard_detail") is None # Load and check cached data client.get("/api/v1/scoreboard") assert app.cache.get("view/api.scoreboard_scoreboard_list") client.get("/api/v1/scoreboard/top/10") assert app.cache.get("view/api.scoreboard_scoreboard_detail") # Empty standings and check that the cached data is gone clear_standings() assert app.cache.get("view/api.scoreboard_scoreboard_list") is None assert app.cache.get( "view/api.scoreboard_scoreboard_detail") is None destroy_ctfd(app)
def test_api_challenges_challenge_with_requirements_hidden_user(): """Does the challenge list API show gated challenges to a hidden user?""" app = create_ctfd() with app.app_context(): prereq_id = gen_challenge(app.db).id chal_obj = gen_challenge(app.db) chal_obj.requirements = {"prerequisites": [prereq_id]} chal_id = chal_obj.id # Create a new user which will solve the prerequisite and hide them register_user(app) Users.query.get(2).hidden = True app.db.session.commit() # Confirm that only the prerequisite challenge is listed initially with login_as_user(app) as client: r = client.get("/api/v1/challenges") assert r.status_code == 200 (chal_data, ) = r.get_json()["data"] assert chal_data["id"] == prereq_id # Generate a solve and then confirm the second challenge is visible gen_solve(app.db, user_id=2, challenge_id=prereq_id) with login_as_user(app) as client: r = client.get("/api/v1/challenges") assert r.status_code == 200 data = r.get_json()["data"] assert len(data) == 2 chal_ids = {c["id"] for c in r.get_json()["data"]} assert chal_ids == {prereq_id, chal_id} destroy_ctfd(app)
def test_api_submissions_get_non_admin(): app = create_ctfd() with app.app_context(): gen_challenge(app.db) gen_solve(app.db, user_id=1) with app.test_client() as client: # test_api_submissions_get_non_admin """Can a user get /api/v1/submissions if not admin""" r = client.get("/api/v1/submissions", json="") assert r.status_code == 403 # test_api_submissions_post_non_admin """Can a user post /api/v1/submissions if not admin""" r = client.post("/api/v1/submissions") assert r.status_code == 403 # test_api_submission_get_non_admin """Can a user get /api/v1/submissions/<submission_id> if not admin""" r = client.get("/api/v1/submissions/1", json="") assert r.status_code == 403 # test_api_submission_delete_non_admin """Can a user delete /api/v1/submissions/<submission_id> if not admin""" r = client.delete("/api/v1/submissions/1", json="") assert r.status_code == 403 destroy_ctfd(app)
def test_api_submission_get_admin(): """Can a user get /api/v1/submissions/<submission_id> if admin""" app = create_ctfd() with app.app_context(): gen_solve(app.db, user_id=1) with login_as_user(app, "admin") as client: r = client.get("/api/v1/submissions/1", json="") assert r.status_code == 200 destroy_ctfd(app)
def test_api_submission_delete_admin(): """Can a user patch /api/v1/submissions/<submission_id> if admin""" app = create_ctfd() with app.app_context(): gen_solve(app.db, user_id=1) with login_as_user(app, 'admin') as client: r = client.delete('/api/v1/submissions/1', json="") assert r.status_code == 200 assert r.get_json().get('data') is None destroy_ctfd(app)
def test_reset_team_mode(): app = create_ctfd(user_mode="teams") with app.app_context(): base_user = '******' base_team = 'team' for x in range(10): chal = gen_challenge(app.db, name='chal_name{}'.format(x)) gen_flag(app.db, challenge_id=chal.id, content='flag') for x in range(10): user = base_user + str(x) user_email = user + "@ctfd.io" user_obj = gen_user(app.db, name=user, email=user_email) team_obj = gen_team(app.db, name=base_team + str(x), email=base_team + str(x) + '@ctfd.io') team_obj.members.append(user_obj) team_obj.captain_id = user_obj.id app.db.session.commit() gen_award(app.db, user_id=user_obj.id) gen_solve(app.db, user_id=user_obj.id, challenge_id=random.randint(1, 10)) gen_fail(app.db, user_id=user_obj.id, challenge_id=random.randint(1, 10)) gen_tracking(app.db, user_id=user_obj.id) assert Teams.query.count() == 10 assert Users.query.count( ) == 51 # 10 random users, 40 users (10 teams * 4), 1 admin user assert Challenges.query.count() == 10 register_user(app) client = login_as_user(app, name="admin", password="******") with client.session_transaction() as sess: data = {"nonce": sess.get('nonce')} client.post('/admin/reset', data=data) assert Teams.query.count() == 0 assert Users.query.count() == 0 assert Challenges.query.count() == 10 assert Solves.query.count() == 0 assert Fails.query.count() == 0 assert Tracking.query.count() == 0 destroy_ctfd(app)
def test_api_challenges_challenge_with_requirements_banned_user(): """Does the challenge list API show gated challenges to a banned user?""" app = create_ctfd() with app.app_context(): prereq_id = gen_challenge(app.db).id chal_obj = gen_challenge(app.db) chal_obj.requirements = {"prerequisites": [prereq_id]} # Create a new user which will solve the prerequisite and ban them register_user(app) Users.query.get(2).banned = True app.db.session.commit() # Generate a solve just in case and confirm the API 403s gen_solve(app.db, user_id=2, challenge_id=prereq_id) with login_as_user(app) as client: assert client.get("/api/v1/challenges").status_code == 403 destroy_ctfd(app)
def test_api_challenge_get_solves_ctf_frozen(): """Test users can only see challenge solves that happened before freeze time""" app = create_ctfd() with app.app_context(): register_user(app, name="user1", email="*****@*****.**") register_user(app, name="user2", email="*****@*****.**") # Friday, October 6, 2017 12:00:00 AM GMT-04:00 DST set_config("freeze", "1507262400") with freeze_time("2017-10-4"): chal = gen_challenge(app.db) chal_id = chal.id gen_solve(app.db, user_id=2, challenge_id=chal_id) chal2 = gen_challenge(app.db) chal2_id = chal2.id with freeze_time("2017-10-8"): chal2 = gen_solve(app.db, user_id=2, challenge_id=chal2_id) # There should now be two solves assigned to the same user. assert Solves.query.count() == 2 client = login_as_user(app, name="user2") # Challenge 1 should have one solve r = client.get("/api/v1/challenges/1/solves") data = r.get_json()["data"] assert len(data) == 1 # Challenge 2 should have a solve shouldn't be shown to the user r = client.get("/api/v1/challenges/2/solves") data = r.get_json()["data"] assert len(data) == 0 # Admins should see data as an admin with no modifications admin = login_as_user(app, name="admin") r = admin.get("/api/v1/challenges/2/solves") data = r.get_json()["data"] assert len(data) == 1 # But should see as a user if the preview param is passed r = admin.get("/api/v1/challenges/2/solves?preview=true") data = r.get_json()["data"] assert len(data) == 0 destroy_ctfd(app)
def test_api_team_captain_disbanding_only_inactive_teams(): """Test that only teams that haven't conducted any actions can be disbanded""" app = create_ctfd(user_mode="teams") with app.app_context(): user = gen_user(app.db, name="user") team = gen_team(app.db) team.members.append(user) user.team_id = team.id team.captain_id = 2 user2 = gen_user(app.db, name="user2", email="*****@*****.**") team.members.append(user2) app.db.session.commit() gen_challenge(app.db) gen_flag(app.db, 1) gen_solve(app.db, user_id=3, team_id=1, challenge_id=1) with login_as_user(app) as client: r = client.delete("/api/v1/teams/me", json="") assert r.status_code == 403 assert r.get_json() == { "success": False, "errors": { "": [ "You cannot disband your team as it has participated in the event. " "Please contact an admin to disband your team or remove a member." ] }, } user = gen_user(app.db, name="user3", email="*****@*****.**") team = gen_team(app.db, name="team2", email="*****@*****.**") print(user.id) team.members.append(user) user.team_id = team.id team.captain_id = user.id app.db.session.commit() with login_as_user(app, name="user3") as client: r = client.delete("/api/v1/teams/me", json="") print(r.get_json()) assert r.status_code == 200 assert r.get_json() == {"success": True} destroy_ctfd(app)
def test_api_user_get_solves_after_freze_time(): """Can a user get /api/v1/users/<user_id>/solves after freeze time""" app = create_ctfd(user_mode="users") with app.app_context(): register_user(app, name="user1", email="*****@*****.**") register_user(app, name="user2", email="*****@*****.**") # Friday, October 6, 2017 12:00:00 AM GMT-04:00 DST set_config("freeze", "1507262400") with freeze_time("2017-10-4"): chal = gen_challenge(app.db) chal_id = chal.id gen_solve(app.db, user_id=2, challenge_id=chal_id) chal2 = gen_challenge(app.db) chal2_id = chal2.id with freeze_time("2017-10-8"): chal2 = gen_solve(app.db, user_id=2, challenge_id=chal2_id) # There should now be two solves assigned to the same user. assert Solves.query.count() == 2 # User 2 should have 2 solves when seen by themselves client = login_as_user(app, name="user1") r = client.get("/api/v1/users/me/solves") data = r.get_json()["data"] assert len(data) == 2 # User 2 should have 1 solve when seen by another user client = login_as_user(app, name="user2") r = client.get("/api/v1/users/2/solves") data = r.get_json()["data"] assert len(data) == 1 # Admins should see all solves for the user admin = login_as_user(app, name="admin") r = admin.get("/api/v1/users/2/solves") data = r.get_json()["data"] assert len(data) == 2 destroy_ctfd(app)
def test_api_team_get_solves_after_freze_time(): """Can a user get /api/v1/teams/<team_id>/solves after freeze time""" app = create_ctfd(user_mode="teams") with app.app_context(): register_user(app) team = gen_team(app.db, name="team1", email="*****@*****.**", member_count=1) team_member = team.members[0] tm_name = team_member.name set_config("freeze", "1507262400") with freeze_time("2017-10-4"): chal = gen_challenge(app.db) chal_id = chal.id gen_solve(app.db, user_id=3, team_id=1, challenge_id=chal_id) chal2 = gen_challenge(app.db) chal2_id = chal2.id with freeze_time("2017-10-8"): gen_solve(app.db, user_id=3, team_id=1, challenge_id=chal2_id) assert Solves.query.count() == 2 with login_as_user(app) as client: r = client.get("/api/v1/teams/1/solves") data = r.get_json()["data"] assert len(data) == 1 with login_as_user(app, name=tm_name) as client: r = client.get("/api/v1/teams/me/solves") data = r.get_json()["data"] assert len(data) == 2 with login_as_user(app, name="admin") as client: r = client.get("/api/v1/teams/1/solves") data = r.get_json()["data"] assert len(data) == 2 destroy_ctfd(app)
def test_user_score_is_correct(): """Test that a user's score is correct""" app = create_ctfd() with app.app_context(): # create user1 register_user(app, name="user1", email="*****@*****.**") # create challenge chal = gen_challenge(app.db, value=100) gen_flag(app.db, challenge_id=chal.id, content="flag") chal_id = chal.id # create a solve for the challenge for user1. (the id is 2 because of the admin) gen_solve(app.db, user_id=2, challenge_id=chal_id) user1 = Users.query.filter_by(id=2).first() # assert that user1's score is 100 assert user1.score == 100 assert user1.place == "1st" # create user2 register_user(app, name="user2", email="*****@*****.**") # user2 solves the challenge gen_solve(app.db, 3, challenge_id=chal_id) # assert that user2's score is 100 but is in 2nd place user2 = Users.query.filter_by(id=3).first() assert user2.score == 100 assert user2.place == "2nd" # create an award for user2 gen_award(app.db, user_id=3, value=5) # assert that user2's score is now 105 and is in 1st place assert user2.score == 105 assert user2.place == "1st" destroy_ctfd(app)
def test_hidden_users_should_not_influence_scores(): app = create_ctfd() with app.app_context(): register_user(app, name="user1", email="*****@*****.**", password="******") register_user(app, name="user2", email="*****@*****.**", password="******") register_user(app, name="user3", email="*****@*****.**", password="******") user = Users.query.filter_by(name="user3").first() user.hidden = True app.db.session.commit() client1 = login_as_user(app, name="user1", password="******") # User 1 solves 1st challenge chal1 = gen_challenge(app.db) gen_solve(app.db, user_id=2, challenge_id=chal1.id) # User 2 solves 2nd challenge chal2 = gen_challenge(app.db) gen_solve(app.db, user_id=3, challenge_id=chal2.id) # User 3 solves both gen_solve(app.db, user_id=4, challenge_id=chal1.id) gen_solve(app.db, user_id=4, challenge_id=chal2.id) scores = get_scores(client1) for entry in scores: assert entry["name"] != "user3" user1 = Users.query.filter_by(name="user1").first() assert user1.place == "1st" user2 = Users.query.filter_by(name="user2").first() assert user2.place == "2nd" destroy_ctfd(app)
def test_api_challenge_solves_returns_correct_data(): """Test that /api/v1/<challenge_id>/solves returns expected data""" app = create_ctfd() with app.app_context(): register_user(app) client = login_as_user(app) chal = gen_challenge(app.db) gen_solve(app.db, user_id=2, challenge_id=chal.id) r = client.get("/api/v1/challenges/1/solves") resp = r.get_json()["data"] solve = resp[0] assert r.status_code == 200 assert solve.get("account_id") == 2 assert solve.get("name") == "user" assert solve.get("date") is not None assert solve.get("account_url") == "/users/2" destroy_ctfd(app) app = create_ctfd(user_mode="teams") with app.app_context(): register_user(app) client = login_as_user(app) team = gen_team(app.db) user = Users.query.filter_by(id=2).first() user.team_id = team.id app.db.session.commit() chal = gen_challenge(app.db) gen_solve(app.db, user_id=2, team_id=1, challenge_id=chal.id) r = client.get("/api/v1/challenges/1/solves") resp = r.get_json()["data"] solve = resp[0] assert r.status_code == 200 assert solve.get("account_id") == 1 assert solve.get("name") == "team_name" assert solve.get("date") is not None assert solve.get("account_url") == "/teams/1" destroy_ctfd(app) app = create_ctfd(application_root="/ctf") with app.app_context(): register_user(app) client = login_as_user(app) chal = gen_challenge(app.db) gen_solve(app.db, user_id=2, challenge_id=chal.id) r = client.get("/api/v1/challenges/1/solves") resp = r.get_json()["data"] solve = resp[0] assert r.status_code == 200 assert solve.get("account_id") == 2 assert solve.get("name") == "user" assert solve.get("date") is not None assert solve.get("account_url") == "/ctf/users/2" destroy_ctfd(app)
def test_reset(): app = create_ctfd() with app.app_context(): base_user = "******" for x in range(10): chal = gen_challenge(app.db, name="chal_name{}".format(x)) gen_flag(app.db, challenge_id=chal.id, content="flag") gen_hint(app.db, challenge_id=chal.id) gen_file( app.db, location="{name}/{name}.file".format(name=chal.name), challenge_id=chal.id, ) for x in range(10): user = base_user + str(x) user_email = user + "@ctfd.io" user_obj = gen_user(app.db, name=user, email=user_email) gen_award(app.db, user_id=user_obj.id) gen_solve(app.db, user_id=user_obj.id, challenge_id=random.randint(1, 10)) gen_fail(app.db, user_id=user_obj.id, challenge_id=random.randint(1, 10)) gen_tracking(app.db, user_id=user_obj.id) # Add PageFiles for x in range(5): gen_file( app.db, location="page_file{name}/page_file{name}.file".format(name=x), page_id=1, ) assert Users.query.count() == 11 # 11 because of the first admin user assert Challenges.query.count() == 10 assert ( Files.query.count() == 15 ) # This should be 11 because ChallengeFiles=10 and PageFiles=5 assert Flags.query.count() == 10 assert Hints.query.count() == 10 assert Submissions.query.count() == 20 assert Pages.query.count() == 1 assert Tracking.query.count() == 10 client = login_as_user(app, name="admin", password="******") with client.session_transaction() as sess: data = {"nonce": sess.get("nonce"), "pages": "on"} r = client.post("/admin/reset", data=data) assert r.location.endswith("/admin/statistics") assert Pages.query.count() == 0 assert Users.query.count() == 11 assert Challenges.query.count() == 10 assert Tracking.query.count() == 11 assert Files.query.count() == 10 with client.session_transaction() as sess: data = {"nonce": sess.get("nonce"), "notifications": "on"} r = client.post("/admin/reset", data=data) assert r.location.endswith("/admin/statistics") assert Notifications.query.count() == 0 assert Users.query.count() == 11 assert Challenges.query.count() == 10 assert Tracking.query.count() == 11 with client.session_transaction() as sess: data = {"nonce": sess.get("nonce"), "challenges": "on"} r = client.post("/admin/reset", data=data) assert r.location.endswith("/admin/statistics") assert Challenges.query.count() == 0 assert Flags.query.count() == 0 assert Hints.query.count() == 0 assert Files.query.count() == 0 assert Tags.query.count() == 0 assert Users.query.count() == 11 assert Tracking.query.count() == 11 with client.session_transaction() as sess: data = {"nonce": sess.get("nonce"), "submissions": "on"} r = client.post("/admin/reset", data=data) assert r.location.endswith("/admin/statistics") assert Submissions.query.count() == 0 assert Solves.query.count() == 0 assert Fails.query.count() == 0 assert Awards.query.count() == 0 assert Unlocks.query.count() == 0 assert Users.query.count() == 11 assert Challenges.query.count() == 0 assert Flags.query.count() == 0 assert Tracking.query.count() == 0 with client.session_transaction() as sess: data = {"nonce": sess.get("nonce"), "accounts": "on"} r = client.post("/admin/reset", data=data) assert r.location.endswith("/setup") assert Users.query.count() == 0 assert Solves.query.count() == 0 assert Fails.query.count() == 0 assert Tracking.query.count() == 0 destroy_ctfd(app)
def test_top_10(): """Make sure top10 returns correct information""" app = create_ctfd() with app.app_context(): register_user(app, name="user1", email="*****@*****.**") register_user(app, name="user2", email="*****@*****.**") register_user(app) chal1 = gen_challenge(app.db) gen_flag(app.db, challenge_id=chal1.id, content='flag') chal1_id = chal1.id chal2 = gen_challenge(app.db) gen_flag(app.db, challenge_id=chal2.id, content='flag') chal2_id = chal2.id # Generates solve for user1 with freeze_time("2017-10-3 03:21:34"): gen_solve(app.db, user_id=2, challenge_id=chal1_id) with freeze_time("2017-10-4 03:25:45"): gen_solve(app.db, user_id=2, challenge_id=chal2_id) # Generate solve for user2 with freeze_time("2017-10-3 03:21:34"): gen_solve(app.db, user_id=3, challenge_id=chal1_id) client = login_as_user(app) r = client.get('/api/v1/scoreboard/top/10') response = r.get_json()['data'] saved = { '1': { 'id': 2, 'solves': [{ 'date': '2017-10-03T03:21:34Z', 'challenge_id': 1, 'account_id': 2, 'user_id': 2, 'team_id': None, 'value': 100 }, { 'date': '2017-10-04T03:25:45Z', 'challenge_id': 2, 'account_id': 2, 'user_id': 2, 'team_id': None, 'value': 100 }], 'name': 'user1' }, '2': { 'id': 3, 'solves': [{ 'date': '2017-10-03T03:21:34Z', 'challenge_id': 1, 'account_id': 3, 'user_id': 3, 'team_id': None, 'value': 100 }], 'name': 'user2' } } assert saved == response destroy_ctfd(app)
def test_top_10(): """Make sure top10 returns correct information""" app = create_ctfd() with app.app_context(): register_user(app, name="user1", email="*****@*****.**") register_user(app, name="user2", email="*****@*****.**") register_user(app) chal1 = gen_challenge(app.db) gen_flag(app.db, challenge_id=chal1.id, content="flag") chal1_id = chal1.id chal2 = gen_challenge(app.db) gen_flag(app.db, challenge_id=chal2.id, content="flag") chal2_id = chal2.id # Generates solve for user1 with freeze_time("2017-10-3 03:21:34"): gen_solve(app.db, user_id=2, challenge_id=chal1_id) with freeze_time("2017-10-4 03:25:45"): gen_solve(app.db, user_id=2, challenge_id=chal2_id) # Generate solve for user2 with freeze_time("2017-10-3 03:21:34"): gen_solve(app.db, user_id=3, challenge_id=chal1_id) client = login_as_user(app) r = client.get("/api/v1/scoreboard/top/10") response = r.get_json()["data"] saved = { "1": { "id": 2, "solves": [ { "date": "2017-10-03T03:21:34Z", "challenge_id": 1, "account_id": 2, "user_id": 2, "team_id": None, "value": 100, }, { "date": "2017-10-04T03:25:45Z", "challenge_id": 2, "account_id": 2, "user_id": 2, "team_id": None, "value": 100, }, ], "name": "user1", }, "2": { "id": 3, "solves": [{ "date": "2017-10-03T03:21:34Z", "challenge_id": 1, "account_id": 3, "user_id": 3, "team_id": None, "value": 100, }], "name": "user2", }, } assert saved == response destroy_ctfd(app)