mirror of
https://github.com/aljazceru/CTFd.git
synced 2025-12-17 22:14:25 +01:00
3.3.0 (#1833)
# 3.3.0 / UNRELEASED
**General**
- Don't require a team for viewing challenges if Challenge visibility is set to public
- Add a `THEME_FALLBACK` config to help develop themes. See **Themes** section for details.
**API**
- Implement a faster `/api/v1/scoreboard` endpoint in Teams Mode
- Add the `solves` item to both `/api/v1/challenges` and `/api/v1/challenges/[challenge_id]` to more easily determine how many solves a challenge has
- Add the `solved_by_me` item to both `/api/v1/challenges` and `/api/v1/challenges/[challenge_id]` to more easily determine if the current account has solved the challenge
- Prevent admins from deleting themselves through `DELETE /api/v1/users/[user_id]`
- Add length checking to some sensitive fields in the Pages and Challenges schemas
- Fix issue where `PATCH /api/v1/users[user_id]` returned a list instead of a dict
- Fix exception that occured on demoting admins through `PATCH /api/v1/users[user_id]`
- Add `team_id` to `GET /api/v1/users` to determine if a user is already in a team
**Themes**
- Add a `THEME_FALLBACK` config to help develop themes.
- `THEME_FALLBACK` will configure CTFd to try to find missing theme files in the default built-in `core` theme.
- This makes it easier to develop themes or use incomplete themes.
- Allow for one theme to reference and inherit from another theme through approaches like `{% extends "core/page.html" %}`
- Allow for the automatic date rendering format to be overridden by specifying a `data-time-format` attribute.
- Add styling for the `<blockquote>` element.
- Fix scoreboard table identifier to switch between User/Team depending on configured user mode
- Switch to using Bootstrap's scss in `core/main.scss` to allow using Bootstrap variables
- Consolidate Jinja error handlers into a single function and better handle issues where error templates can't be found
**Plugins**
- Set plugin migration version after successful migrations
- Fix issue where Page URLs injected into the navbar were relative instead of absolute
**Admin Panel**
- Add User standings as well as Teams standings to the admin scoreboard when in Teams Mode
- Add a UI for adding members to a team from the team's admin page
- Add ability for admins to disable public team creation
- Link directly to users who submitted something in the submissions page if the CTF is in Teams Mode
- Fix Challenge Requirements interface in Admin Panel to not allow empty/null requirements to be added
- Fixed an issue where config times (start, end, freeze times) could not be removed
- Fix an exception that occurred when demoting an Admin user
- Adds a temporary hack for re-enabling Javascript snippets in Flag editor templates. (See #1779)
**Deployment**
- Install `python3-dev` instead of `python-dev` in apt
- Bump lxml to 4.6.2
- Bump pip-compile to 5.4.0
**Miscellaneous**
- Cache Docker builds more by copying and installing Python dependencies before copying CTFd
- Change the default emails slightly and rework confirmation email page to make some recommendations clearer
- Use `examplectf.com` as testing/development domain instead of `ctfd.io`
- Fixes issue where user's name and email would not appear in logs properly
- Add more linting by also linting with `flake8-comprehensions` and `flake8-bugbear`
This commit is contained in:
@@ -154,6 +154,250 @@ def test_api_challenges_get_hidden_admin():
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_api_challenges_get_solve_status():
|
||||
"""Does the challenge list API show the current user's solve status?"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
chal_id = gen_challenge(app.db).id
|
||||
register_user(app)
|
||||
client = login_as_user(app)
|
||||
# First request - unsolved
|
||||
r = client.get("/api/v1/challenges")
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"].pop()
|
||||
assert chal_data["solved_by_me"] is False
|
||||
# Solve and re-request
|
||||
gen_solve(app.db, user_id=2, challenge_id=chal_id)
|
||||
r = client.get("/api/v1/challenges")
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"].pop()
|
||||
assert chal_data["solved_by_me"] is True
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_api_challenges_get_solve_count():
|
||||
"""Does the challenge list API show the solve count?"""
|
||||
# This is checked with public requests against the API after each generated
|
||||
# user makes a solve
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
set_config("challenge_visibility", "public")
|
||||
chal_id = gen_challenge(app.db).id
|
||||
with app.test_client() as client:
|
||||
_USER_BASE = 2 # First user we create will have this ID
|
||||
_MAX = 3 # arbitrarily selected
|
||||
for i in range(_MAX):
|
||||
# Confirm solve count against `i` first
|
||||
r = client.get("/api/v1/challenges")
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"].pop()
|
||||
assert chal_data["solves"] == i
|
||||
# Generate a new user and solve for the challenge
|
||||
uname = "user{}".format(i)
|
||||
uemail = uname + "@examplectf.com"
|
||||
register_user(app, name=uname, email=uemail)
|
||||
gen_solve(app.db, user_id=_USER_BASE + i, challenge_id=chal_id)
|
||||
# Confirm solve count one final time against `_MAX`
|
||||
r = client.get("/api/v1/challenges")
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"].pop()
|
||||
assert chal_data["solves"] == _MAX
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_api_challenges_get_solve_info_score_visibility():
|
||||
"""Does the challenge list API show solve info if scores are hidden?"""
|
||||
app = create_ctfd()
|
||||
with app.app_context(), app.test_client() as pub_client:
|
||||
set_config("challenge_visibility", "public")
|
||||
|
||||
# Generate a challenge, user and solve to test the API with
|
||||
chal_id = gen_challenge(app.db).id
|
||||
register_user(app)
|
||||
gen_solve(app.db, user_id=2, challenge_id=chal_id)
|
||||
|
||||
# With the public setting any unauthed user should see the solve
|
||||
set_config("score_visibility", "public")
|
||||
r = pub_client.get("/api/v1/challenges")
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"].pop()
|
||||
assert chal_data["solves"] == 1
|
||||
assert chal_data["solved_by_me"] == False
|
||||
|
||||
# With the private setting only an authed user should see the solve
|
||||
set_config("score_visibility", "private")
|
||||
# Test public user
|
||||
r = pub_client.get("/api/v1/challenges")
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"].pop()
|
||||
assert chal_data["solves"] is None
|
||||
assert chal_data["solved_by_me"] is False
|
||||
# Test authed user
|
||||
user_client = login_as_user(app)
|
||||
r = user_client.get("/api/v1/challenges")
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"].pop()
|
||||
assert chal_data["solves"] == 1
|
||||
assert chal_data["solved_by_me"] is True
|
||||
|
||||
# With the admins setting only admins should see the solve
|
||||
set_config("score_visibility", "admins")
|
||||
# Test authed user
|
||||
r = user_client.get("/api/v1/challenges")
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"].pop()
|
||||
assert chal_data["solves"] is None
|
||||
assert chal_data["solved_by_me"] is True
|
||||
# Test admin
|
||||
admin_client = login_as_user(app, "admin", "password")
|
||||
r = admin_client.get("/api/v1/challenges")
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"].pop()
|
||||
assert chal_data["solves"] == 1
|
||||
assert chal_data["solved_by_me"] is False
|
||||
|
||||
# With the hidden setting nobody should see the solve
|
||||
set_config("score_visibility", "hidden")
|
||||
r = admin_client.get("/api/v1/challenges")
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"].pop()
|
||||
assert chal_data["solves"] is None
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_api_challenges_get_solve_info_account_visibility():
|
||||
"""Does the challenge list API show solve info if accounts are hidden?"""
|
||||
app = create_ctfd()
|
||||
with app.app_context(), app.test_client() as pub_client:
|
||||
set_config("challenge_visibility", "public")
|
||||
|
||||
# Generate a challenge, user and solve to test the API with
|
||||
chal_id = gen_challenge(app.db).id
|
||||
register_user(app)
|
||||
gen_solve(app.db, user_id=2, challenge_id=chal_id)
|
||||
|
||||
# With the public setting any unauthed user should see the solve
|
||||
set_config("account_visibility", "public")
|
||||
r = pub_client.get("/api/v1/challenges")
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"].pop()
|
||||
assert chal_data["solves"] == 1
|
||||
assert chal_data["solved_by_me"] is False
|
||||
|
||||
# With the private setting only an authed user should see the solve
|
||||
set_config("account_visibility", "private")
|
||||
# Test public user
|
||||
r = pub_client.get("/api/v1/challenges")
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"].pop()
|
||||
assert chal_data["solves"] is None
|
||||
assert chal_data["solved_by_me"] is False
|
||||
# Test user
|
||||
user_client = login_as_user(app)
|
||||
r = user_client.get("/api/v1/challenges")
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"].pop()
|
||||
assert chal_data["solves"] == 1
|
||||
assert chal_data["solved_by_me"] is True
|
||||
|
||||
# With the admins setting only admins should see the solve
|
||||
set_config("account_visibility", "admins")
|
||||
# Test user
|
||||
r = user_client.get("/api/v1/challenges")
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"].pop()
|
||||
assert chal_data["solves"] is None
|
||||
assert chal_data["solved_by_me"] is True
|
||||
# Test admin user
|
||||
admin_client = login_as_user(app, "admin", "password")
|
||||
r = admin_client.get("/api/v1/challenges")
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"].pop()
|
||||
assert chal_data["solves"] == 1
|
||||
assert chal_data["solved_by_me"] is False
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_api_challenges_get_solve_count_frozen():
|
||||
"""Does the challenge list API count solves made during a freeze?"""
|
||||
app = create_ctfd()
|
||||
with app.app_context(), app.test_client() as client:
|
||||
set_config("challenge_visibility", "public")
|
||||
set_config("freeze", "1507262400")
|
||||
chal_id = gen_challenge(app.db).id
|
||||
|
||||
with freeze_time("2017-10-4"):
|
||||
# Create a user and generate a solve from before the freeze time
|
||||
register_user(app, name="user1", email="user1@examplectf.com")
|
||||
gen_solve(app.db, user_id=2, challenge_id=chal_id)
|
||||
|
||||
# Confirm solve count is now `1`
|
||||
r = client.get("/api/v1/challenges")
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"].pop()
|
||||
assert chal_data["solves"] == 1
|
||||
|
||||
with freeze_time("2017-10-8"):
|
||||
# Create a user and generate a solve from after the freeze time
|
||||
register_user(app, name="user2", email="user2@examplectf.com")
|
||||
gen_solve(app.db, user_id=3, challenge_id=chal_id)
|
||||
|
||||
# Confirm solve count is still `1` despite the new solve
|
||||
r = client.get("/api/v1/challenges")
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"].pop()
|
||||
assert chal_data["solves"] == 1
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_api_challenges_get_solve_count_hidden_user():
|
||||
"""Does the challenge list API show solve counts for hidden users?"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
set_config("challenge_visibility", "public")
|
||||
chal_id = gen_challenge(app.db).id
|
||||
# The admin is expected to be hidden by default
|
||||
gen_solve(app.db, user_id=1, challenge_id=chal_id)
|
||||
with app.test_client() as client:
|
||||
# Confirm solve count is `0` despite the hidden admin having solved
|
||||
r = client.get("/api/v1/challenges")
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"].pop()
|
||||
assert chal_data["solves"] == 0
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_api_challenges_get_solve_count_banned_user():
|
||||
"""Does the challenge list API show solve counts for banned users?"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
set_config("challenge_visibility", "public")
|
||||
chal_id = gen_challenge(app.db).id
|
||||
|
||||
# Create a banned user and generate a solve for the challenge
|
||||
register_user(app)
|
||||
gen_solve(app.db, user_id=2, challenge_id=chal_id)
|
||||
|
||||
# Confirm that the solve is there
|
||||
with app.test_client() as client:
|
||||
r = client.get("/api/v1/challenges")
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"].pop()
|
||||
assert chal_data["solves"] == 1
|
||||
|
||||
# Ban the user
|
||||
Users.query.get(2).banned = True
|
||||
app.db.session.commit()
|
||||
|
||||
with app.test_client() as client:
|
||||
# Confirm solve count is `0` despite the banned user having solved
|
||||
r = client.get("/api/v1/challenges")
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"].pop()
|
||||
assert chal_data["solves"] == 0
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_api_challenges_post_admin():
|
||||
"""Can a user post /api/v1/challenges if admin"""
|
||||
app = create_ctfd()
|
||||
@@ -325,6 +569,264 @@ def test_api_challenge_get_non_existing():
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_api_challenge_get_solve_status():
|
||||
"""Does the challenge detail API show the current user's solve status?"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
chal_id = gen_challenge(app.db).id
|
||||
chal_uri = "/api/v1/challenges/{}".format(chal_id)
|
||||
register_user(app)
|
||||
client = login_as_user(app)
|
||||
# First request - unsolved
|
||||
r = client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solved_by_me"] is False
|
||||
# Solve and re-request
|
||||
gen_solve(app.db, user_id=2, challenge_id=chal_id)
|
||||
r = client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solved_by_me"] is True
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_api_challenge_get_solve_info_score_visibility():
|
||||
"""Does the challenge detail API show solve info if scores are hidden?"""
|
||||
app = create_ctfd()
|
||||
with app.app_context(), app.test_client() as pub_client:
|
||||
set_config("challenge_visibility", "public")
|
||||
# Generate a challenge, user and solve to test the API with
|
||||
chal_id = gen_challenge(app.db).id
|
||||
chal_uri = "/api/v1/challenges/{}".format(chal_id)
|
||||
register_user(app)
|
||||
gen_solve(app.db, user_id=2, challenge_id=chal_id)
|
||||
|
||||
# With the public setting any unauthed user should see the solve
|
||||
set_config("score_visibility", "public")
|
||||
r = pub_client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solves"] == 1
|
||||
assert chal_data["solved_by_me"] is False
|
||||
|
||||
# With the private setting only an authed user should see the solve
|
||||
set_config("score_visibility", "private")
|
||||
# Test public user
|
||||
r = pub_client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solves"] is None
|
||||
assert chal_data["solved_by_me"] is False
|
||||
# Test user
|
||||
user_client = login_as_user(app)
|
||||
r = user_client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solves"] == 1
|
||||
assert chal_data["solved_by_me"] is True
|
||||
|
||||
# With the admins setting only admins should see the solve
|
||||
set_config("score_visibility", "admins")
|
||||
# Test user
|
||||
r = user_client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solves"] is None
|
||||
assert chal_data["solved_by_me"] is True
|
||||
# Test admin user
|
||||
admin_client = login_as_user(app, "admin", "password")
|
||||
r = admin_client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solves"] == 1
|
||||
assert chal_data["solved_by_me"] is False
|
||||
|
||||
# With the hidden setting nobody should see the solve
|
||||
set_config("score_visibility", "hidden")
|
||||
r = admin_client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solves"] is None
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_api_challenge_get_solve_info_account_visibility():
|
||||
"""Does the challenge detail API show solve info if accounts are hidden?"""
|
||||
app = create_ctfd()
|
||||
with app.app_context(), app.test_client() as pub_client:
|
||||
set_config("challenge_visibility", "public")
|
||||
# Generate a challenge, user and solve to test the API with
|
||||
chal_id = gen_challenge(app.db).id
|
||||
chal_uri = "/api/v1/challenges/{}".format(chal_id)
|
||||
register_user(app)
|
||||
gen_solve(app.db, user_id=2, challenge_id=chal_id)
|
||||
|
||||
# With the public setting any unauthed user should see the solve
|
||||
set_config("account_visibility", "public")
|
||||
r = pub_client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solves"] == 1
|
||||
assert chal_data["solved_by_me"] is False
|
||||
|
||||
# With the private setting only an authed user should see the solve
|
||||
set_config("account_visibility", "private")
|
||||
# Test public user
|
||||
r = pub_client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solves"] is None
|
||||
assert chal_data["solved_by_me"] is False
|
||||
# Test user
|
||||
user_client = login_as_user(app)
|
||||
r = user_client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solves"] == 1
|
||||
assert chal_data["solved_by_me"] is True
|
||||
|
||||
# With the admins setting only admins should see the solve
|
||||
set_config("account_visibility", "admins")
|
||||
# Test user
|
||||
r = user_client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solves"] is None
|
||||
assert chal_data["solved_by_me"] is True
|
||||
# Test admin user
|
||||
admin_client = login_as_user(app, "admin", "password")
|
||||
r = admin_client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solves"] == 1
|
||||
assert chal_data["solved_by_me"] is False
|
||||
|
||||
# With the hidden setting admins can still see the solve
|
||||
# because the challenge detail endpoint doesn't have an admin specific view
|
||||
set_config("account_visibility", "hidden")
|
||||
r = admin_client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solves"] == 1
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_api_challenge_get_solve_count():
|
||||
"""Does the challenge detail API show the solve count?"""
|
||||
# This is checked with public requests against the API after each generated
|
||||
# user makes a solve
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
set_config("challenge_visibility", "public")
|
||||
chal_id = gen_challenge(app.db).id
|
||||
chal_uri = "/api/v1/challenges/{}".format(chal_id)
|
||||
with app.test_client() as client:
|
||||
_USER_BASE = 2 # First user we create will have this ID
|
||||
_MAX = 3 # arbitrarily selected
|
||||
for i in range(_MAX):
|
||||
# Confirm solve count against `i` first
|
||||
r = client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solves"] == i
|
||||
# Generate a new user and solve for the challenge
|
||||
uname = "user{}".format(i)
|
||||
uemail = uname + "@examplectf.com"
|
||||
register_user(app, name=uname, email=uemail)
|
||||
gen_solve(app.db, user_id=_USER_BASE + i, challenge_id=chal_id)
|
||||
# Confirm solve count one final time against `_MAX`
|
||||
r = client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solves"] == _MAX
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_api_challenge_get_solve_count_frozen():
|
||||
"""Does the challenge detail API count solves made during a freeze?"""
|
||||
app = create_ctfd()
|
||||
with app.app_context(), app.test_client() as client:
|
||||
set_config("challenge_visibility", "public")
|
||||
# Friday, October 6, 2017 4:00:00 AM
|
||||
set_config("freeze", "1507262400")
|
||||
chal_id = gen_challenge(app.db).id
|
||||
chal_uri = "/api/v1/challenges/{}".format(chal_id)
|
||||
|
||||
with freeze_time("2017-10-4"):
|
||||
# Create a user and generate a solve from before the freeze time
|
||||
register_user(app, name="user1", email="user1@examplectf.com")
|
||||
gen_solve(app.db, user_id=2, challenge_id=chal_id)
|
||||
|
||||
# Confirm solve count is now `1`
|
||||
r = client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solves"] == 1
|
||||
|
||||
with freeze_time("2017-10-8"):
|
||||
# Create a user and generate a solve from after the freeze time
|
||||
register_user(app, name="user2", email="user2@examplectf.com")
|
||||
gen_solve(app.db, user_id=3, challenge_id=chal_id)
|
||||
|
||||
# Confirm solve count is still `1` despite the new solve
|
||||
r = client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solves"] == 1
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_api_challenge_get_solve_count_hidden_user():
|
||||
"""Does the challenge detail API show solve counts for hidden users?"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
set_config("challenge_visibility", "public")
|
||||
chal_id = gen_challenge(app.db).id
|
||||
chal_uri = "/api/v1/challenges/{}".format(chal_id)
|
||||
# The admin is expected to be hidden by default
|
||||
gen_solve(app.db, user_id=1, challenge_id=chal_id)
|
||||
with app.test_client() as client:
|
||||
# Confirm solve count is `0` despite the hidden admin having solved
|
||||
r = client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solves"] == 0
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_api_challenge_get_solve_count_banned_user():
|
||||
"""Does the challenge detail API show solve counts for banned users?"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
set_config("challenge_visibility", "public")
|
||||
chal_id = gen_challenge(app.db).id
|
||||
chal_uri = "/api/v1/challenges/{}".format(chal_id)
|
||||
|
||||
# Create a user and generate a solve for the challenge
|
||||
register_user(app)
|
||||
gen_solve(app.db, user_id=2, challenge_id=chal_id)
|
||||
|
||||
# Confirm that the solve is there
|
||||
with app.test_client() as client:
|
||||
r = client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solves"] == 1
|
||||
|
||||
# Ban the user
|
||||
Users.query.get(2).banned = True
|
||||
app.db.session.commit()
|
||||
|
||||
# Confirm solve count is `0` despite the banned user having solved
|
||||
with app.test_client() as client:
|
||||
r = client.get(chal_uri)
|
||||
assert r.status_code == 200
|
||||
chal_data = r.get_json()["data"]
|
||||
assert chal_data["solves"] == 0
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_api_challenge_patch_non_admin():
|
||||
"""Can a user patch /api/v1/challenges/<challenge_id> if not admin"""
|
||||
app = create_ctfd()
|
||||
@@ -439,7 +941,7 @@ def test_api_challenge_attempt_post_private():
|
||||
challenge_id = gen_challenge(app.db).id
|
||||
gen_flag(app.db, challenge_id)
|
||||
with login_as_user(app) as client:
|
||||
for i in range(10):
|
||||
for _ in range(10):
|
||||
gen_fail(app.db, user_id=2, challenge_id=challenge_id)
|
||||
r = client.post(
|
||||
"/api/v1/challenges/attempt",
|
||||
@@ -480,7 +982,7 @@ def test_api_challenge_attempt_post_private():
|
||||
challenge_id = gen_challenge(app.db).id
|
||||
gen_flag(app.db, challenge_id)
|
||||
with login_as_user(app) as client:
|
||||
for i in range(10):
|
||||
for _ in range(10):
|
||||
gen_fail(app.db, user_id=2, team_id=team_id, challenge_id=challenge_id)
|
||||
r = client.post(
|
||||
"/api/v1/challenges/attempt",
|
||||
|
||||
Reference in New Issue
Block a user