def test_admin_config(): """Does admin config return a 200 by default""" app = create_ctfd() with app.app_context(): client = login_as_user(app, name="admin", password="******") r = client.get('/admin/config') assert r.status_code == 200
def test_admin_panel(): """Does the admin panel return a 200 by default""" app = create_ctfd() with app.app_context(): client = login_as_user(app, name="admin", password="******") r = client.get('/admin') assert r.status_code == 302 r = client.get('/admin/graphs') assert r.status_code == 200
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_api_user_get_awards_after_freze_time(): """Can a user get /api/v1/users/<user_id>/awards 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"): gen_award(app.db, user_id=2) with freeze_time("2017-10-8"): gen_award(app.db, user_id=2) # There should now be two awards assigned to the same user. assert Awards.query.count() == 2 # User 2 should have 2 awards when seen by themselves client = login_as_user(app, name="user1") r = client.get("/api/v1/users/me/awards") data = r.get_json()["data"] assert len(data) == 2 # User 2 should have 1 award when seen by another user client = login_as_user(app, name="user2") r = client.get("/api/v1/users/2/awards") data = r.get_json()["data"] assert len(data) == 1 # Admins should see all awards for the user admin = login_as_user(app, name="admin") r = admin.get("/api/v1/users/2/awards") data = r.get_json()["data"] assert len(data) == 2 destroy_ctfd(app)
def test_api_token_delete(): """Can tokens be deleted by owners, and admins""" app = create_ctfd() with app.app_context(): # Can be deleted by the user user = gen_user(app.db) user_id = user.id username = user.name token = generate_user_token(user) token_id = token.id with login_as_user(app, username) as client: r = client.delete("/api/v1/tokens/" + str(token_id), json="") assert r.status_code == 200 assert Tokens.query.count() == 0 # Can be deleted by admins user = Users.query.filter_by(id=user_id).first() token = generate_user_token(user) token_id = token.id with login_as_user(app, "admin") as client: r = client.delete("/api/v1/tokens/" + str(token_id), json="") assert r.status_code == 200 assert Tokens.query.count() == 0 # First user first_user = Users.query.filter_by(id=user_id).first() token = generate_user_token(first_user) token_id = token.id # Second user second_user = gen_user(app.db, name="user2", email="*****@*****.**") username2 = second_user.name with login_as_user(app, username2) as client: r = client.delete("/api/v1/tokens/" + str(token_id), json="") assert r.status_code == 404 assert Tokens.query.count() == 1 destroy_ctfd(app)
def test_api_user_change_email(): """Test that users can change their email via the API""" app = create_ctfd() with app.app_context(): register_user(app) user = Users.query.filter_by(id=2).first() app.db.session.commit() with login_as_user(app) as client: # Test users can't submit null r = client.patch("/api/v1/users/me", json={"email": None, "confirm": "password"}) resp = r.get_json() print(resp) assert r.status_code == 400 assert resp["errors"]["email"] == ["Field may not be null."] # Test users can exercise the API r = client.patch("/api/v1/users/me", json={"email": "*****@*****.**", "confirm": "password"}) 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.email == "*****@*****.**" 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_api_users_post_admin_duplicate_information(): """Can an admin create a user with duplicate information""" app = create_ctfd() with app.app_context(): register_user(app) with login_as_user(app, "admin") as client: # Duplicate email r = client.post( "/api/v1/users", json={ "name": "user2", "email": "*****@*****.**", "password": "******" }, ) resp = r.get_json() assert r.status_code == 400 assert resp["errors"]["email"] assert resp["success"] is False assert Users.query.count() == 2 # Duplicate user r = client.post( "/api/v1/users", json={ "name": "user", "email": "*****@*****.**", "password": "******" }, ) resp = r.get_json() assert r.status_code == 400 assert resp["errors"]["name"] assert resp["success"] is False assert Users.query.count() == 2 destroy_ctfd(app)
def test_team_join_ratelimited(): """Test that team joins are ratelimited""" app = create_ctfd(user_mode="teams") with app.app_context(): gen_user(app.db, name="user") gen_team(app.db, name="team") with login_as_user(app) as client: r = client.get("/teams/join") assert r.status_code == 200 with client.session_transaction() as sess: data = { "name": "team", "password": "******", "nonce": sess.get("nonce"), } for _ in range(10): r = client.post("/teams/join", data=data) data["password"] = "******" for _ in range(10): r = client.post("/teams/join", data=data) assert r.status_code == 429 assert Users.query.filter_by(id=2).first().team_id is None destroy_ctfd(app)
def test_api_teams_post_admin(): """Can a user post /api/v1/teams if admin""" app = create_ctfd(user_mode="teams") with app.app_context(): with login_as_user(app, 'admin') as client: # Create team r = client.post('/api/v1/teams', json={ "website": "http://www.team.com", "name": "team", "country": "TW", "email": "*****@*****.**", "affiliation": "team", "password": "******" }) assert r.status_code == 200 # Make sure password was hashed properly team = Teams.query.filter_by(email='*****@*****.**').first() assert team assert verify_password('password', team.password) # Make sure team can actually be joined register_user(app) client = login_as_user(app) with client.session_transaction() as sess: data = { "name": "team", "password": "******", "nonce": sess.get('nonce') } r = client.post('/teams/join', data=data) user = Users.query.filter_by(id=2).first() assert user.team_id == 1 destroy_ctfd(app)
def test_ctf_ended(): """ Tests that the ctf_ended function returns the correct value """ app = create_ctfd() with app.app_context(): assert ctf_ended() is False 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-3"): assert ctf_ended() is False with freeze_time("2017-10-5"): assert ctf_ended() is False with freeze_time("2017-10-7"): assert ctf_ended() is True destroy_ctfd(app)
def test_api_users_post_admin(): """Can a user post /api/v1/users if admin""" app = create_ctfd() with app.app_context(): with login_as_user(app, 'admin') as client: # Create user r = client.post('/api/v1/users', json={ "name": "user", "email": "*****@*****.**", "password": "******" }) assert r.status_code == 200 # Make sure password was hashed properly user = Users.query.filter_by(email='*****@*****.**').first() assert user assert verify_password('password', user.password) # Make sure user can login with the creds client = login_as_user(app) r = client.get('/profile') assert r.status_code == 200 destroy_ctfd(app)
def test_api_tag_detail_get(): """Can a user get /api/v1/tokens/<token_id>""" app = create_ctfd() with app.app_context(): user = gen_user(app.db, name="user") generate_user_token(user) with login_as_user(app) as client: r = client.get("/api/v1/tokens/1", json="") assert r.status_code == 200 resp = r.get_json() assert sorted(resp["data"].keys()) == sorted(TokenSchema().views["user"]) with login_as_user(app, "admin") as client: r = client.get("/api/v1/tokens/1", json="") assert r.status_code == 200 resp = r.get_json() assert sorted(resp["data"].keys()) == sorted(TokenSchema().views["admin"]) gen_user(app.db, name="user2", email="*****@*****.**") with login_as_user(app, "user2") as client: r = client.get("/api/v1/tokens/1", json="") assert r.status_code == 404 destroy_ctfd(app)
def test_bypass_csrf_protection(): """ Test that the bypass_csrf_protection decorator functions properly """ app = create_ctfd() with app.app_context(): with app.test_client() as client: r = client.post('/login') output = r.get_data(as_text=True) assert r.status_code == 403 def bypass_csrf_protection_test_route(): return "Success", 200 # Hijack an existing route to avoid any kind of hacks to create a test route app.view_functions['auth.login'] = bypass_csrf_protection(bypass_csrf_protection_test_route) with app.test_client() as client: r = client.post('/login') output = r.get_data(as_text=True) assert r.status_code == 200 assert output == "Success" destroy_ctfd(app)
def test_api_team_get_awards_after_freze_time(): """Can a user get /api/v1/teams/<team_id>/awards 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"): gen_award(app.db, user_id=3) with freeze_time("2017-10-8"): gen_award(app.db, user_id=3) assert Awards.query.count() == 2 with login_as_user(app) as client: r = client.get("/api/v1/teams/1/awards") 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/awards") 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/awards") data = r.get_json()["data"] assert len(data) == 2 destroy_ctfd(app)
def test_api_challenge_get_verified_emails(): """Can a verified email load /api/v1/challenges/<challenge_id>""" app = create_ctfd() with app.app_context(), freeze_time("2017-10-5"): 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 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', json="") assert r.status_code == 403 r = registered_client.get('/api/v1/challenges/1') assert r.status_code == 200 destroy_ctfd(app)
def test_unlocking_hints_with_cost_during_ctf_without_points(): """Test that hints with a cost are not unlocked if you don't have the points""" 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, cost=10) client = login_as_user(app) r = client.get("/api/v1/hints/1") assert r.get_json()["data"].get("content") is None r = client.post("/api/v1/unlocks", json={"target": 1, "type": "hints"}) assert (r.get_json()["errors"]["score"] == "You do not have enough points to unlock this hint") r = client.get("/api/v1/hints/1") assert r.get_json()["data"].get("content") is None user = Users.query.filter_by(id=2).first() assert user.score == 0 destroy_ctfd(app)
def test_api_csrf_failure(): """Test that API requests require the CSRF-Token header""" app = create_ctfd() app.test_client_class = FlaskClient with app.app_context(): with login_as_user(app, "admin") as client: r = client.post( "/api/v1/challenges", json={ "name": "chal", "category": "cate", "description": "desc", "value": "100", "state": "hidden", "type": "standard", }, ) assert r.status_code == 403 with client.session_transaction() as sess: nonce = sess.get("nonce") r = client.post( "/api/v1/challenges", headers={"CSRF-Token": nonce}, json={ "name": "chal", "category": "cate", "description": "desc", "value": "100", "state": "hidden", "type": "standard", }, ) assert r.status_code == 200 destroy_ctfd(app)
def test_api_notifications_get(): app = create_ctfd() with app.app_context(): register_user(app) gen_notification(app.db) with login_as_user(app) as client: # test_api_notifications_get """Can the users get /api/v1/notifications""" r = client.get("/api/v1/notifications", json="") assert r.status_code == 200 assert len(r.get_json()["data"]) == 1 # test_api_get_notification_detail r = client.get("/api/v1/notifications/1", json="") assert r.status_code == 200 resp = r.get_json() assert resp["data"]["title"] == "title" assert resp["data"]["content"] == "content" # test_api_notifications_post_non_admin """Can the users post /api/v1/notifications if not admin""" r = client.post("/api/v1/notifications", json="") assert r.status_code == 403 destroy_ctfd(app)
def test_unlocking_hints_with_cost_during_ctf_without_points(): """Test that hints with a cost are not unlocked if you don't have the points""" 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, cost=10) client = login_as_user(app) r = client.get('/api/v1/hints/1') assert r.get_json()['data'].get('content') is None r = client.post('/api/v1/unlocks', json={'target': 1, 'type': 'hints'}) assert r.get_json()['errors'][ 'score'] == 'You do not have enough points to unlock this hint' r = client.get('/api/v1/hints/1') assert r.get_json()['data'].get('content') is None user = Users.query.filter_by(id=2).first() assert user.score == 0 destroy_ctfd(app)
def test_api_notifications_get(): app = create_ctfd() with app.app_context(): register_user(app) gen_notification(app.db) with login_as_user(app) as client: # test_api_notifications_get """Can the users get /api/v1/notifications""" r = client.get('/api/v1/notifications', json="") assert r.status_code == 200 assert len(r.get_json()['data']) == 1 # test_api_get_notification_detail r = client.get('/api/v1/notifications/1', json="") assert r.status_code == 200 resp = r.get_json() assert resp['data']['title'] == 'title' assert resp['data']['content'] == 'content' # test_api_notifications_post_non_admin """Can the users post /api/v1/notifications if not admin""" r = client.post('/api/v1/notifications', json="") assert r.status_code == 403 destroy_ctfd(app)
def test_create_new_challenge(): """Test that an admin can create a challenge properly""" app = create_ctfd() with app.app_context(): register_user(app) client = login_as_user(app, name="admin", password="******") challenge_data = { "name": "name", "category": "category", "description": "description", "value": 100, "state": "hidden", "type": "standard" } r = client.post('/api/v1/challenges', json=challenge_data) assert r.get_json().get('data')['id'] == 1 r = client.get('/admin/challenges/1') assert r.status_code == 200 r = client.get('/api/v1/challenges/1') assert r.get_json().get('data')['id'] == 1 destroy_ctfd(app)
def test_api_pages_post_admin(): """Can a user post /api/v1/pages if admin""" app = create_ctfd() with app.app_context(): gen_challenge(app.db) with login_as_user(app, name="admin") as client: with client.session_transaction() as sess: nonce = sess.get('nonce') r = client.post('/api/v1/pages', json={ "title": "testing_page_title", "route": "/route", "content": "testing_page_content", "nonce": nonce, "auth_required": False }) r = client.get('/') assert r.status_code == 200 assert "testing_page_title" in r.get_data(as_text=True) r = client.get('/route') assert r.status_code == 200 assert "testing_page_content" in r.get_data(as_text=True) destroy_ctfd(app)
def test_challenges_admin_only_as_user(): app = create_ctfd() with app.app_context(): set_config('challenge_visibility', 'admins') register_user(app) client = login_as_user(app) gen_challenge(app.db) gen_flag(app.db, challenge_id=1, content='flag') r = client.get('/challenges') assert r.status_code == 403 r = client.get('/api/v1/challenges', json='') assert r.status_code == 403 r = client.get('/api/v1/challenges/1', json='') assert r.status_code == 403 data = {"submission": 'flag', "challenge_id": 1} r = client.post('/api/v1/challenges/attempt', json=data) assert r.status_code == 403 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": "*****@*****.**", "confirm": "password" }, ) 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_teams_join_post(): """Can a user post /teams/join""" app = create_ctfd(user_mode="teams") with app.app_context(): gen_user(app.db, name="user") gen_team(app.db, name="team") with login_as_user(app) as client: r = client.get("/teams/join") assert r.status_code == 200 with client.session_transaction() as sess: data = { "name": "team", "password": "******", "nonce": sess.get("nonce"), } r = client.post("/teams/join", data=data) assert r.status_code == 302 # Cannot join a team with an incorrect password incorrect_data = data incorrect_data["password"] = "" r = client.post("/teams/join", data=incorrect_data) assert r.status_code == 403 destroy_ctfd(app)
def test_api_challenge_with_properties_delete_admin(): """Can a user delete /api/v1/challenges/<challenge_id> if the challenge has other properties""" app = create_ctfd() with app.app_context(): challenge = gen_challenge(app.db) gen_hint(app.db, challenge_id=challenge.id) gen_tag(app.db, challenge_id=challenge.id) gen_flag(app.db, challenge_id=challenge.id) challenge = Challenges.query.filter_by(id=1).first() assert len(challenge.hints) == 1 assert len(challenge.tags) == 1 assert len(challenge.flags) == 1 with login_as_user(app, "admin") as client: r = client.delete("/api/v1/challenges/1", json="") assert r.status_code == 200 assert r.get_json().get("data") is None assert Tags.query.count() == 0 assert Hints.query.count() == 0 assert Flags.query.count() == 0 destroy_ctfd(app)
def test_hidden_pages(): """Test that hidden pages aren't on the navbar but can be loaded""" app = create_ctfd() with app.app_context(): page = gen_page( app.db, title="HiddenPageTitle", route="this-is-a-hidden-route", content="This is some HTML", hidden=True, ) clear_pages() assert page not in get_pages() with app.test_client() as client: r = client.get("/") assert r.status_code == 200 assert "HiddenPageTitle" not in r.get_data(as_text=True) with app.test_client() as client: r = client.get("/this-is-a-hidden-route") assert r.status_code == 200 assert "This is some HTML" in r.get_data(as_text=True) 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_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_challenges_admin_only_as_user(): app = create_ctfd() with app.app_context(): set_config("challenge_visibility", "admins") register_user(app) admin = login_as_user(app, name="admin") gen_challenge(app.db) gen_flag(app.db, challenge_id=1, content="flag") r = admin.get("/challenges") assert r.status_code == 200 r = admin.get("/api/v1/challenges", json="") assert r.status_code == 200 r = admin.get("/api/v1/challenges/1", json="") assert r.status_code == 200 data = {"submission": "flag", "challenge_id": 1} r = admin.post("/api/v1/challenges/attempt", json=data) assert r.status_code == 200 destroy_ctfd(app)
def test_api_tag_list_post(): """Can a user create a token""" app = create_ctfd() with app.app_context(): user = gen_user(app.db, name="user") user_id = user.id with login_as_user(app) as client: r = client.post("/api/v1/tokens", json={}) assert r.status_code == 200 resp = r.get_json() value = resp["data"]["value"] token = Tokens.query.filter_by(value=value).first() assert token.user_id == user_id assert token.expiration > datetime.datetime.utcnow() data = {"expiration": "9999-12-30"} r = client.post("/api/v1/tokens", json=data) assert r.status_code == 200 resp = r.get_json() value = resp["data"]["value"] token = Tokens.query.filter_by(value=value).first() assert token.user_id == user_id assert token.expiration.year == 9999 destroy_ctfd(app)