def test_accessing_hidden_teams(): """Hidden teams should not give any data from /teams or /api/v1/teams""" app = create_ctfd(user_mode="teams") with app.app_context(): register_user(app) register_user(app, name="visible_user", email="*****@*****.**") with login_as_user(app, name="visible_user") as client: user = Users.query.filter_by(id=2).first() team = gen_team(app.db, name="visible_team", hidden=True) team.members.append(user) user.team_id = team.id app.db.session.commit() assert client.get("/teams/1").status_code == 404 assert client.get("/api/v1/teams/1").status_code == 404 assert client.get("/api/v1/teams/1/solves").status_code == 404 assert client.get("/api/v1/teams/1/fails").status_code == 404 assert client.get("/api/v1/teams/1/awards").status_code == 404 destroy_ctfd(app)
def test_api_user_patch_admin(): """Can a user patch /api/v1/users/<user_id> if admin""" app = create_ctfd() with app.app_context(): register_user(app) with login_as_user(app, "admin") as client: r = client.patch( "/api/v1/users/2", json={ "name": "user", "email": "*****@*****.**", "password": "******", "country": "US", "verified": True, }, ) assert r.status_code == 200 user_data = r.get_json()["data"] assert user_data["country"] == "US" assert user_data["verified"] is True destroy_ctfd(app)
def test_anonymous_users_view_public_challenges_without_team(): """Test that if challenges are public, users without team can still view them""" app = create_ctfd(user_mode="teams") with app.app_context(): register_user(app) gen_challenge(app.db) with app.test_client() as client: r = client.get("/challenges") assert r.status_code == 302 assert r.location.startswith("http://localhost/login") set_config("challenge_visibility", "public") with app.test_client() as client: r = client.get("/challenges") assert r.status_code == 200 with login_as_user(app) as client: r = client.get("/challenges") assert r.status_code == 302 assert r.location.startswith("http://localhost/team") destroy_ctfd(app)
def test_api_challenge_get_solves_verified_emails(): """Can a verified email get /api/v1/challenges/<challenge_id>/solves""" app = create_ctfd() with app.app_context(): set_config("verify_emails", True) gen_challenge(app.db) gen_user( app.db, name="user_name", email="*****@*****.**", password="******", verified=True, ) register_user(app) client = login_as_user(app) registered_client = login_as_user(app, "user_name", "password") r = client.get("/api/v1/challenges/1/solves", json="") assert r.status_code == 403 r = registered_client.get("/api/v1/challenges/1/solves") assert r.status_code == 200 destroy_ctfd(app)
def test_api_challenges_get_solves_score_visibility(): """Can a user get /api/v1/challenges/<challenge_id>/solves if score_visibility is public/private/admin""" app = create_ctfd() with app.app_context(): set_config("challenge_visibility", "public") set_config("score_visibility", "public") gen_challenge(app.db) with app.test_client() as client: r = client.get("/api/v1/challenges/1/solves") assert r.status_code == 200 set_config("challenge_visibility", "private") set_config("score_visibility", "private") register_user(app) private_client = login_as_user(app) r = private_client.get("/api/v1/challenges/1/solves") assert r.status_code == 200 set_config("score_visibility", "admin") admin = login_as_user(app, "admin", "password") r = admin.get("/api/v1/challenges/1/solves") assert r.status_code == 200 destroy_ctfd(app)
def test_user_get_scoreboard_components(): app = create_kmactf() with app.app_context(): register_user(app) client = login_as_user(app) # test_user_get_scoreboard """Can a registered user load scoreboard components""" r = client.get("/scoreboard") assert r.status_code == 200 # test_user_get_scores """Can a registered user load /api/v1/scoreboard""" r = client.get("/api/v1/scoreboard") assert r.status_code == 200 # test_user_get_topteams """Can a registered user load /api/v1/scoreboard/top/10""" r = client.get("/api/v1/scoreboard/top/10") assert r.status_code == 200 destroy_kmactf(app)
def test_register_duplicate_email(): """A user shouldn't be able to use an already registered email address""" app = create_kmactf() with app.app_context(): register_user( app, name="user1", email="*****@*****.**", password="******", raise_for_error=False, ) register_user( app, name="user2", email="*****@*****.**", password="******", raise_for_error=False, ) user_count = Users.query.count() assert user_count == 2 # There's the admin user and the first created user destroy_kmactf(app)
def test_submitting_invalid_regex_flag(): """Test that invalid regex flags are errored out to the user""" app = create_ctfd() with app.app_context(): register_user(app) client = login_as_user(app) chal = gen_challenge(app.db) gen_flag( app.db, challenge_id=chal.id, type="regex", content="**", data="case_insensitive", ) data = {"submission": "FLAG", "challenge_id": chal.id} r = client.post("/api/v1/challenges/attempt", json=data) assert r.status_code == 200 resp = r.get_json()["data"] assert resp.get("status") == "incorrect" assert resp.get("message") == "Regex parse error occured" destroy_ctfd(app)
def test_api_user_change_verify_email(): """Test that users are marked unconfirmed if they change their email and verify_emails is turned on""" app = create_ctfd() with app.app_context(): set_config('verify_emails', True) register_user(app) user = Users.query.filter_by(id=2).first() user.verified = True app.db.session.commit() with login_as_user(app) as client: r = client.patch('/api/v1/users/me', json={ "email": "*****@*****.**", }) assert r.status_code == 200 resp = r.get_json() assert resp['data']['email'] == "*****@*****.**" assert resp['success'] is True user = Users.query.filter_by(id=2).first() assert user.verified is False destroy_ctfd(app)
def test_submitting_correct_regex_case_insensitive_flag(): """Test that correct regex flags are correct if the regex flag is marked case_insensitive""" app = create_ctfd() with app.app_context(): register_user(app) client = login_as_user(app) chal = gen_challenge(app.db) gen_flag( app.db, challenge_id=chal.id, type="regex", content="flag", data="case_insensitive", ) data = {"submission": "FLAG", "challenge_id": chal.id} r = client.post("/api/v1/challenges/attempt", json=data) assert r.status_code == 200 resp = r.get_json()["data"] assert resp.get("status") == "correct" assert resp.get("message") == "Correct" destroy_ctfd(app)
def test_api_hint_unlocked(): """Can the users unlock /api/v1/hints/<hint_id> if they have enough points""" app = create_ctfd() with app.app_context(): chal = gen_challenge(app.db) gen_hint(app.db, chal.id, content="This is a hint", cost=1, type="standard") register_user(app) # Give user points with an award gen_award(app.db, 2) client = login_as_user(app) r = client.get("/api/v1/hints/1") assert r.status_code == 200 r = client.post("/api/v1/unlocks", json={"target": 1, "type": "hints"}) assert r.status_code == 200 r = client.get("/api/v1/hints/1") assert r.status_code == 200 destroy_ctfd(app)
def test_api_user_get_schema(): """Can a user get /api/v1/users/<user_id> doesn't return unnecessary data""" app = create_ctfd() with app.app_context(): register_user(app, name="user1", email="*****@*****.**") # ID 2 register_user(app, name="user2", email="*****@*****.**") # ID 3 with app.test_client() as client: r = client.get("/api/v1/users/3") data = r.get_json()["data"] assert sorted(data.keys()) == sorted( UserSchema.views["user"] + ["score", "place"] ) with login_as_user(app, name="user1") as client: r = client.get("/api/v1/users/3") data = r.get_json()["data"] assert sorted(data.keys()) == sorted( UserSchema.views["user"] + ["score", "place"] ) destroy_ctfd(app)
def test_draft_pages(): """Test that draft pages can't be seen""" app = create_ctfd() with app.app_context(): gen_page( app.db, title="Title", route="this-is-a-route", content="This is some HTML", draft=True, ) with app.test_client() as client: r = client.get("/this-is-a-route") assert r.status_code == 404 register_user(app) client = login_as_user(app) r = client.get("/this-is-a-route") assert r.status_code == 404 destroy_ctfd(app)
def test_ratelimit_on_auth(): """Test that ratelimiting function works properly""" app = create_ctfd() with app.app_context(): register_user(app) with app.test_client() as client: r = client.get('/login') with client.session_transaction() as sess: data = { "name": "user", "password": "******", "nonce": sess.get('nonce') } for x in range(10): r = client.post('/login', data=data) assert r.status_code == 200 for x in range(5): r = client.post('/login', data=data) assert r.status_code == 429 destroy_ctfd(app)
def test_unlocking_hints_with_cost_before_ctf(): """Test that hints are not unlocked if the CTF hasn't begun""" app = create_ctfd() with app.app_context(): register_user(app) chal = gen_challenge(app.db) chal_id = chal.id gen_hint(app.db, chal_id) gen_award(app.db, user_id=2) set_config('start', '1507089600' ) # Wednesday, October 4, 2017 12:00:00 AM GMT-04:00 DST set_config( 'end', '1507262400') # Friday, October 6, 2017 12:00:00 AM GMT-04:00 DST with freeze_time("2017-10-1"): client = login_as_user(app) r = client.get('/api/v1/hints/1') assert r.status_code == 403 assert r.get_json().get('data') is None r = client.post('/api/v1/unlocks', json={ 'target': 1, 'type': 'hints' }) assert r.status_code == 403 assert r.get_json().get('data') is None r = client.get('/api/v1/hints/1') assert r.get_json().get('data') is None assert r.status_code == 403 user = Users.query.filter_by(id=2).first() assert user.score == 100 assert Unlocks.query.count() == 0 destroy_ctfd(app)
def test_register_duplicate_username(): """A user shouldn't be able to use an already registered team name""" app = create_ctfd() with app.app_context(): register_user( app, name="user1", email="*****@*****.**", password="******", raise_for_error=False, ) register_user( app, name="user1", email="*****@*****.**", password="******", raise_for_error=False, ) register_user( app, name="admin ", email="*****@*****.**", password="******", raise_for_error=False, ) user_count = Users.query.count() assert user_count == 2 # There's the admin user and the first created user destroy_ctfd(app)
def test_accessing_hidden_users(): """Hidden users should not give any data from /users or /api/v1/users""" app = create_ctfd() with app.app_context(): register_user(app, name="visible_user", email="*****@*****.**") # ID 2 register_user(app, name="hidden_user", email="*****@*****.**") # ID 3 register_user(app, name="banned_user", email="*****@*****.**") # ID 4 user = Users.query.filter_by(name="hidden_user").first() user.hidden = True app.db.session.commit() user = Users.query.filter_by(name="banned_user").first() user.banned = True app.db.session.commit() with login_as_user(app, name="visible_user") as client: assert client.get("/users/3").status_code == 404 assert client.get("/api/v1/users/3").status_code == 404 assert client.get("/api/v1/users/3/solves").status_code == 404 assert client.get("/api/v1/users/3/fails").status_code == 404 assert client.get("/api/v1/users/3/awards").status_code == 404 assert client.get("/users/4").status_code == 404 assert client.get("/api/v1/users/4").status_code == 404 assert client.get("/api/v1/users/4/solves").status_code == 404 assert client.get("/api/v1/users/4/fails").status_code == 404 assert client.get("/api/v1/users/4/awards").status_code == 404 destroy_ctfd(app)
def test_user_can_unlock_hint(): """Test that a user can unlock a hint if they have enough points""" app = create_kmactf() with app.app_context(): with app.test_client(): register_user(app, name="user1", email="*****@*****.**") chal = gen_challenge(app.db, value=100) chal_id = chal.id gen_flag(app.db, challenge_id=chal.id, content="flag") hint = gen_hint(app.db, chal_id, cost=10) hint_id = hint.id gen_award(app.db, user_id=2, value=15) client = login_as_user(app, name="user1", password="******") user = Users.query.filter_by(name="user1").first() assert user.score == 15 with client.session_transaction(): r = client.get("/api/v1/hints/{}".format(hint_id)) resp = r.get_json() assert resp["data"].get("content") is None params = {"target": hint_id, "type": "hints"} r = client.post("/api/v1/unlocks", json=params) resp = r.get_json() assert resp["success"] is True r = client.get("/api/v1/hints/{}".format(hint_id)) resp = r.get_json() assert resp["data"].get("content") == "This is a hint" user = Users.query.filter_by(name="user1").first() assert user.score == 5 destroy_kmactf(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_api_accessing_banned_users(): """Banned users should not be visible to normal users, only to admins""" app = create_ctfd() with app.app_context(): register_user(app, name="visible_user", email="*****@*****.**") register_user(app, name="banned_user", email="*****@*****.**") # ID 3 user = Users.query.filter_by(name="banned_user").first() user.banned = True app.db.session.commit() with login_as_user(app, name="visible_user") as client: assert client.get("/api/v1/users/3").status_code == 404 assert client.get("/api/v1/users/3/solves").status_code == 404 assert client.get("/api/v1/users/3/fails").status_code == 404 assert client.get("/api/v1/users/3/awards").status_code == 404 with login_as_user(app, name="admin") as client: assert client.get("/api/v1/users/3").status_code == 200 assert client.get("/api/v1/users/3/solves").status_code == 200 assert client.get("/api/v1/users/3/fails").status_code == 200 assert client.get("/api/v1/users/3/awards").status_code == 200 destroy_ctfd(app)
def test_api_hint_admin_access(): """Can the users patch/delete /api/v1/hint/<hint_id> if not admin""" app = create_ctfd() with app.app_context(): chal = gen_challenge(app.db) gen_hint(app.db, chal.id, content="This is a hint", cost=1, type="standard") admin = login_as_user(app, "admin") register_user(app) client = login_as_user(app) r = client.patch("/api/v1/hints/1", json="") assert r.status_code == 403 r = client.delete("/api/v1/hints/1", json="") assert r.status_code == 403 r_admin = admin.patch("/api/v1/hints/1", json={"cost": 2}) assert r_admin.status_code == 200 r_admin = admin.delete("/api/v1/hints/1", json="") assert r_admin.status_code == 200 destroy_ctfd(app)
def test_page_requiring_auth(): """Test that pages properly require authentication""" app = create_ctfd() with app.app_context(): gen_page( app.db, title="Title", route="this-is-a-route", content="This is some HTML", auth_required=True, ) with app.test_client() as client: r = client.get("/this-is-a-route") assert r.status_code == 302 assert r.location == "http://localhost/login?next=%2Fthis-is-a-route%3F" register_user(app) client = login_as_user(app) r = client.get("/this-is-a-route") assert r.status_code == 200 destroy_ctfd(app)
def test_team_fields_required_on_creation(): app = create_ctfd(user_mode="teams") with app.app_context(): register_user(app) gen_field(app.db, type="team") with app.app_context(): with login_as_user(app) as client: assert Teams.query.count() == 0 r = client.get("/teams/new") resp = r.get_data(as_text=True) assert "CustomField" in resp assert "CustomFieldDescription" in resp with client.session_transaction() as sess: data = { "name": "team", "password": "******", "nonce": sess.get("nonce"), } r = client.post("/teams/new", data=data) assert "Please provide all required fields" in r.get_data(as_text=True) assert Teams.query.count() == 0 with client.session_transaction() as sess: data = { "name": "team", "password": "******", "fields[1]": "CustomFieldEntry", "nonce": sess.get("nonce"), } r = client.post("/teams/new", data=data) assert r.status_code == 302 assert Teams.query.count() == 1 entry = TeamFieldEntries.query.filter_by(id=1).first() assert entry.team_id == 1 assert entry.value == "CustomFieldEntry" destroy_ctfd(app)
def test_unlocking_hints_with_cost_during_ctf_with_points(): """Test that hints with a cost are unlocked if you have the points""" app = create_kmactf() with app.app_context(): register_user(app) chal = gen_challenge(app.db) chal_id = chal.id gen_hint(app.db, chal_id, cost=10) gen_award(app.db, user_id=2) client = login_as_user(app) r = client.get("/api/v1/hints/1") assert r.get_json()["data"].get("content") is None client.post("/api/v1/unlocks", json={"target": 1, "type": "hints"}) r = client.get("/api/v1/hints/1") assert r.get_json()["data"].get("content") == "This is a hint" user = Users.query.filter_by(id=2).first() assert user.score == 90 destroy_kmactf(app)
def test_api_hint_404(): """Are admin protected resources accessible by admins/non-admins""" app = create_ctfd() endpoints = ['/api/v1/configs/{}', '/api/v1/challenges/types', '/api/v1/statistics/teams', '/api/v1/flags/{}', '/api/v1/statistics/users/{}', '/api/v1/configs', '/api/v1/statistics/challenges/solves/percentages', '/api/v1/tags/{}', '/api/v1/pages', '/api/v1/files/{}', '/api/v1/challenges/{}/tags', '/api/v1/hints', '/api/v1/challenges/{}/files', '/api/v1/flags', '/api/v1/submissions/{}', '/api/v1/challenges/{}/flags', '/api/v1/awards/{}', '/api/v1/unlocks', '/api/v1/challenges/{}/hints', '/api/v1/statistics/submissions/{}', '/api/v1/flags/types/{}', '/api/v1/tags', '/api/v1/statistics/challenges/{}', '/api/v1/files', '/api/v1/flags/types', '/api/v1/submissions', '/api/v1/pages/{}'] with app.app_context(): register_user(app) client = login_as_user(app) for endpoint in endpoints: r = client.get(endpoint.format(1)) assert r.status_code == 302 assert r.location.startswith('http://localhost/login') destroy_ctfd(app)
def test_api_topics_non_admin(): """Can a user interact with /api/v1/topics if not admin""" app = create_ctfd() with app.app_context(): gen_challenge(app.db) gen_topic(app.db, challenge_id=1) with app.test_client() as client: r = client.get("/api/v1/topics", json="") assert r.status_code == 403 """Can a user post /api/v1/topics if not admin""" r = client.post("/api/v1/topics") assert r.status_code == 403 """Can a user delete /api/v1/topics if not admin""" r = client.delete("/api/v1/topics") assert r.status_code == 403 """Can a user get /api/v1/topics/<topic_id> if not admin""" r = client.get("/api/v1/topics/1", json="") assert r.status_code == 403 """Can a user delete /api/v1/topics/<topic_id> if not admin""" r = client.delete("/api/v1/topics/1", json="") assert r.status_code == 403 register_user(app) with login_as_user(app) as client: r = client.get("/api/v1/topics", json="") assert r.status_code == 403 """Can a user post /api/v1/topics if not admin""" r = client.post("/api/v1/topics") assert r.status_code == 403 """Can a user delete /api/v1/topics if not admin""" r = client.delete("/api/v1/topics") assert r.status_code == 403 """Can a user get /api/v1/topics/<topic_id> if not admin""" r = client.get("/api/v1/topics/1", json="") assert r.status_code == 403 """Can a user delete /api/v1/topics/<topic_id> if not admin""" r = client.delete("/api/v1/topics/1", json="") assert r.status_code == 403 destroy_ctfd(app)
def test_challenges_cannot_be_solved_while_paused(): """Test that challenges cannot be solved when the CTF is paused""" app = create_ctfd() with app.app_context(): set_config('paused', True) register_user(app) client = login_as_user(app) r = client.get('/challenges') assert r.status_code == 200 # Assert that there is a paused message data = r.get_data(as_text=True) assert 'paused' in data chal = gen_challenge(app.db) gen_flag(app.db, challenge_id=chal.id, content='flag') data = { "submission": 'flag', "challenge_id": chal.id } r = client.post('/api/v1/challenges/attempt', json=data) # Assert that the JSON message is correct resp = r.get_json()['data'] assert r.status_code == 403 assert resp['status'] == 'paused' assert resp['message'] == 'CTFd is paused' # There are no solves saved solves = Solves.query.count() assert solves == 0 # There are no wrong keys saved wrong_keys = Fails.query.count() assert wrong_keys == 0 destroy_ctfd(app)
def test_hidden_challenge_is_unreachable(): """Test that hidden challenges return 404 and do not insert a solve or wrong key""" app = create_ctfd() with app.app_context(): register_user(app) client = login_as_user(app) chal = gen_challenge(app.db, state='hidden') gen_flag(app.db, challenge_id=chal.id, content='flag') chal_id = chal.id assert Challenges.query.count() == 1 r = client.get('/api/v1/challenges', json='') data = r.get_json().get('data') assert data == [] r = client.get('/api/v1/challenges/1', json='') assert r.status_code == 404 data = r.get_json().get('data') assert data is None data = { "submission": 'flag', "challenge_id": chal_id } r = client.post('/api/v1/challenges/attempt', json=data) assert r.status_code == 404 r = client.post('/api/v1/challenges/attempt?preview=true', json=data) assert r.status_code == 404 assert r.get_json().get('data') is None solves = Solves.query.count() assert solves == 0 wrong_keys = Fails.query.count() assert wrong_keys == 0 destroy_ctfd(app)
def test_scoreboard_is_cached(): """Test that /api/v1/scoreboard is properly cached and cleared""" app = create_kmactf() 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") # Check scoreboard page assert app.cache.get("view/scoreboard.listing") is None client.get("/scoreboard") assert app.cache.get("view/scoreboard.listing") # 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 assert app.cache.get("view/scoreboard.listing") is None destroy_kmactf(app)
def test_api_user_change_email_under_whitelist(): """Test that users can only change emails to ones in the whitelist""" app = create_ctfd() with app.app_context(): register_user(app) set_config("domain_whitelist", "whitelisted.com, whitelisted.org, whitelisted.net") with login_as_user(app) as client: r = client.patch("/api/v1/users/me", json={"email": "*****@*****.**"}) assert r.status_code == 400 resp = r.get_json() assert resp["errors"]["email"] assert resp["success"] is False r = client.patch("/api/v1/users/me", json={"email": "*****@*****.**"}) assert r.status_code == 200 resp = r.get_json() assert resp["data"]["email"] == "*****@*****.**" assert resp["success"] is True destroy_ctfd(app)
def test_no_permissions_protected_get_data(self): """ Test protected api against user without permissions """ with self.client: # user registration resp_register = register_user(self, '*****@*****.**', '123456') # api call resp_get_data = self.client.get( '/api/protected_get_data', headers={ 'Authentication-Token': 'Bearer ' + json.loads( resp_register.get_data().decode() )['auth_token'] } ) self.assertEqual(resp_get_data.status_code, html_codes.HTTP_BAD_FORBIDDEN)