Bump Dependencies (#2332)

* Bump dependencies
* Closes #2300 
* Closes #2331
This commit is contained in:
Kevin Chung
2023-07-02 17:33:58 -04:00
committed by GitHub
parent 7b68babee6
commit deae9e1941
43 changed files with 205 additions and 166 deletions

View File

@@ -23,7 +23,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install -r development.txt
python -m pip install -r linting.txt
sudo yarn install --non-interactive
sudo yarn global add prettier@1.17.0

View File

@@ -12,7 +12,6 @@ from flask_migrate import upgrade
from jinja2 import FileSystemLoader
from jinja2.sandbox import SandboxedEnvironment
from werkzeug.middleware.proxy_fix import ProxyFix
from werkzeug.utils import cached_property
import CTFd.utils.config
from CTFd import utils
@@ -36,16 +35,14 @@ __channel__ = "oss"
class CTFdRequest(Request):
@cached_property
def path(self):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
"""
Hijack the original Flask request path because it does not account for subdirectory deployments in an intuitive
manner. We append script_root so that the path always points to the full path as seen in the browser.
e.g. /subdirectory/path/route vs /path/route
:return: string
"""
return self.script_root + super(CTFdRequest, self).path
self.path = self.script_root + self.path
class CTFdFlask(Flask):
@@ -86,24 +83,30 @@ class SandboxedBaseEnvironment(SandboxedEnvironment):
theme = str(utils.get_config("ctf_theme"))
cache_name = theme + "/" + name
# Rest of this code is copied from Jinja
# https://github.com/pallets/jinja/blob/master/src/jinja2/environment.py#L802-L815
# Rest of this code roughly copied from Jinja
# https://github.com/pallets/jinja/blob/b08cd4bc64bb980df86ed2876978ae5735572280/src/jinja2/environment.py#L956-L973
cache_key = (weakref.ref(self.loader), cache_name)
if self.cache is not None:
template = self.cache.get(cache_key)
if template is not None and (
not self.auto_reload or template.is_up_to_date
):
# template.globals is a ChainMap, modifying it will only
# affect the template, not the environment globals.
if globals:
template.globals.update(globals)
return template
template = self.loader.load(self, name, globals)
template = self.loader.load(self, name, self.make_globals(globals))
if self.cache is not None:
self.cache[cache_key] = template
return template
class ThemeLoader(FileSystemLoader):
"""Custom FileSystemLoader that is aware of theme structure and config.
"""
"""Custom FileSystemLoader that is aware of theme structure and config."""
DEFAULT_THEMES_PATH = os.path.join(os.path.dirname(__file__), "themes")
_ADMIN_THEME_PREFIX = ADMIN_THEME + "/"

View File

@@ -120,7 +120,7 @@ def export_ctf():
backup = export_ctf_util()
ctf_name = ctf_config.ctf_name()
day = datetime.datetime.now().strftime("%Y-%m-%d_%T")
full_name = u"{}.{}.zip".format(ctf_name, day)
full_name = "{}.{}.zip".format(ctf_name, day)
return send_file(
backup, cache_timeout=-1, as_attachment=True, attachment_filename=full_name
)

View File

@@ -31,7 +31,5 @@ def sqlalchemy_to_pydantic(
for name, python_type in include.items():
default = None
fields[name] = (python_type, default)
pydantic_model = create_model(
db_model.__name__, **fields # type: ignore
)
pydantic_model = create_model(db_model.__name__, **fields) # type: ignore
return pydantic_model

View File

@@ -51,7 +51,10 @@ class TopicList(Resource):
{
"value": (str, None),
"q": (str, None),
"field": (RawEnum("TopicFields", {"value": "value"}), None,),
"field": (
RawEnum("TopicFields", {"value": "value"}),
None,
),
},
location="query",
)
@@ -122,7 +125,8 @@ class TopicList(Resource):
responses={200: ("Success", "APISimpleSuccessResponse")},
)
@validate_args(
{"type": (str, None), "target_id": (int, 0)}, location="query",
{"type": (str, None), "target_id": (int, 0)},
location="query",
)
def delete(self, query_args):
topic_type = query_args.get("type")

View File

@@ -208,9 +208,11 @@ def register():
registration_code = str(request.form.get("registration_code", ""))
name_len = len(name) == 0
names = Users.query.add_columns("name", "id").filter_by(name=name).first()
names = (
Users.query.add_columns(Users.name, Users.id).filter_by(name=name).first()
)
emails = (
Users.query.add_columns("email", "id")
Users.query.add_columns(Users.email, Users.id)
.filter_by(email=email_address)
.first()
)

View File

@@ -12,7 +12,8 @@ def render_error(error):
try:
return (
render_template(
"errors/{}.html".format(error.code), error=error.description,
"errors/{}.html".format(error.code),
error=error.description,
),
error.code,
)

View File

@@ -49,7 +49,8 @@ class AccountSettingsForm(BaseForm):
description="Max number of teams (Teams mode only)",
)
num_users = IntegerField(
widget=NumberInput(min=0), description="Max number of users",
widget=NumberInput(min=0),
description="Max number of users",
)
verify_emails = SelectField(
"Verify Emails",
@@ -101,13 +102,15 @@ class LegalSettingsForm(BaseForm):
description="External URL to a Terms of Service document hosted elsewhere",
)
tos_text = TextAreaField(
"Terms of Service", description="Text shown on the Terms of Service page",
"Terms of Service",
description="Text shown on the Terms of Service page",
)
privacy_url = URLField(
"Privacy Policy URL",
description="External URL to a Privacy Policy document hosted elsewhere",
)
privacy_text = TextAreaField(
"Privacy Policy", description="Text shown on the Privacy Policy page",
"Privacy Policy",
description="Text shown on the Privacy Policy page",
)
submit = SubmitField("Update")

View File

@@ -15,12 +15,13 @@ ma = Marshmallow()
def get_class_by_tablename(tablename):
"""Return class reference mapped to table.
https://stackoverflow.com/a/23754464
https://stackoverflow.com/a/66666783
:param tablename: String with name of table.
:return: Class reference or None.
"""
for c in db.Model._decl_class_registry.values():
for m in db.Model.registry.mappers:
c = m.class_
if hasattr(c, "__tablename__") and c.__tablename__ == tablename:
return c
return None

View File

@@ -25,11 +25,13 @@ class DynamicChallenge(Challenges):
class DynamicValueChallenge(BaseChallenge):
id = "dynamic" # Unique identifier used to register challenges
name = "dynamic" # Name of a challenge type
templates = { # Handlebars templates used for each aspect of challenge editing & viewing
templates = (
{ # Handlebars templates used for each aspect of challenge editing & viewing
"create": "/plugins/dynamic_challenges/assets/create.html",
"update": "/plugins/dynamic_challenges/assets/update.html",
"view": "/plugins/dynamic_challenges/assets/view.html",
}
)
scripts = { # Scripts that are loaded when a template is loaded
"create": "/plugins/dynamic_challenges/assets/create.js",
"update": "/plugins/dynamic_challenges/assets/update.js",

View File

@@ -68,5 +68,7 @@ class ChallengeSchema(ma.ModelSchema):
)
requirements = field_for(
Challenges, "requirements", validate=[ChallengeRequirementsValidator()],
Challenges,
"requirements",
validate=[ChallengeRequirementsValidator()],
)

View File

@@ -16,7 +16,9 @@ class PageSchema(ma.ModelSchema):
"title",
validate=[
validate.Length(
min=0, max=80, error="Page could not be saved. Your title is too long.",
min=0,
max=80,
error="Page could not be saved. Your title is too long.",
)
],
)

View File

@@ -111,10 +111,14 @@ def get_solve_counts_for_challenges(challenge_id=None, admin=False):
else:
freeze_cond = true()
exclude_solves_cond = and_(
AccountModel.banned == false(), AccountModel.hidden == false(),
AccountModel.banned == false(),
AccountModel.hidden == false(),
)
solves_q = (
db.session.query(Solves.challenge_id, sa_func.count(Solves.challenge_id),)
db.session.query(
Solves.challenge_id,
sa_func.count(Solves.challenge_id),
)
.join(AccountModel)
.filter(*challenge_id_filter, freeze_cond, exclude_solves_cond)
.group_by(Solves.challenge_id)

View File

@@ -368,21 +368,31 @@ def load_challenges_csv(dict_reader):
if flags:
flags = [flag.strip() for flag in flags.split(",")]
for flag in flags:
f = Flags(type="static", challenge_id=challenge.id, content=flag,)
f = Flags(
type="static",
challenge_id=challenge.id,
content=flag,
)
db.session.add(f)
db.session.commit()
if tags:
tags = [tag.strip() for tag in tags.split(",")]
for tag in tags:
t = Tags(challenge_id=challenge.id, value=tag,)
t = Tags(
challenge_id=challenge.id,
value=tag,
)
db.session.add(t)
db.session.commit()
if hints:
hints = [hint.strip() for hint in hints.split(",")]
for hint in hints:
h = Hints(challenge_id=challenge.id, content=hint,)
h = Hints(
challenge_id=challenge.id,
content=hint,
)
db.session.add(h)
db.session.commit()
if errors:

View File

@@ -84,6 +84,7 @@ def export_ctf():
backup_zip.close()
backup.seek(0)
db.close()
return backup

View File

@@ -17,10 +17,10 @@ migrations = Migrate()
def create_database():
url = make_url(app.config["SQLALCHEMY_DATABASE_URI"])
if url.drivername == "postgres":
url.drivername = "postgresql"
url = url.set(drivername="postgresql")
if url.drivername.startswith("mysql"):
url.query["charset"] = "utf8mb4"
url = url.update_query_dict({"charset": "utf8mb4"})
# Creates database if the database database does not exist
if not database_exists_util(url):
@@ -34,7 +34,7 @@ def create_database():
def drop_database():
url = make_url(app.config["SQLALCHEMY_DATABASE_URI"])
if url.drivername == "postgres":
url.drivername = "postgresql"
url = url.set(drivername="postgresql")
drop_database_util(url)

View File

@@ -124,9 +124,15 @@ def setup():
password = request.form["password"]
name_len = len(name) == 0
names = Users.query.add_columns("name", "id").filter_by(name=name).first()
names = (
Users.query.add_columns(Users.name, Users.id)
.filter_by(name=name)
.first()
)
emails = (
Users.query.add_columns("email", "id").filter_by(email=email).first()
Users.query.add_columns(Users.email, Users.id)
.filter_by(email=email)
.first()
)
pass_short = len(password) == 0
pass_long = len(password) > 128

View File

@@ -3,17 +3,14 @@ pip-tools==5.4.0
pytest==7.3.1
pytest-randomly==3.12.0
coverage==7.2.3
ruff==0.0.260
psycopg2-binary==2.9.6
moto==1.3.16
moto==4.1.11
bandit==1.6.2
flask_profiler==1.8.1
pytest-xdist==3.2.1
pytest-cov==4.0.0
sphinx_rtd_theme==0.4.3
flask-debugtoolbar==0.11.0
isort==4.3.21
Faker==4.1.0
pipdeptree==2.2.0
black==19.10b0
pytest-sugar==0.9.7

3
linting.txt Normal file
View File

@@ -0,0 +1,3 @@
black==22.3.0
isort==4.3.21
ruff==0.0.260

View File

@@ -17,7 +17,7 @@ if url.drivername.startswith("sqlite"):
# Null out the database so raw_connection doesnt error if it doesnt exist
# CTFd will create the database if it doesnt exist
url.database = None
url = url.set(database=None)
# Wait for the database server to be available
engine = create_engine(url)

View File

@@ -1,24 +1,22 @@
Flask==1.1.2
Werkzeug==1.0.1
Jinja2==2.11.3
Flask-SQLAlchemy==2.4.3
Flask-Caching==1.8.0
Flask==2.0.3
Werkzeug==2.1.2
Flask-SQLAlchemy==2.5.1
Flask-Caching==2.0.2
Flask-Migrate==2.5.3
Flask-Script==2.0.6
SQLAlchemy==1.3.17
SQLAlchemy-Utils==0.41.0
SQLAlchemy==1.4.48
SQLAlchemy-Utils==0.41.1
passlib==1.7.4
bcrypt==4.0.1
itsdangerous==1.1.0
requests==2.28.1
PyMySQL[rsa]==0.9.3
gunicorn==20.1.0
dataset==1.3.1
dataset==1.5.2
cmarkgfm==2022.10.27
redis==4.4.4
redis==4.5.5
gevent==22.10.2
python-dotenv==0.13.0
flask-restx==0.5.1
flask-restx==1.1.0
flask-marshmallow==0.10.1
marshmallow-sqlalchemy==0.17.0
boto3==1.13.9

View File

@@ -1,6 +1,6 @@
#
# This file is autogenerated by pip-compile with python 3.9
# To update, run:
# This file is autogenerated by pip-compile with Python 3.9
# by the following command:
#
# ./scripts/pip-compile.sh
#
@@ -16,6 +16,8 @@ attrs==20.3.0
# via jsonschema
babel==2.12.1
# via flask-babel
banal==1.0.6
# via dataset
bcrypt==4.0.1
# via -r requirements.in
boto3==1.13.9
@@ -24,6 +26,8 @@ botocore==1.16.26
# via
# boto3
# s3transfer
cachelib==0.9.0
# via flask-caching
certifi==2022.12.7
# via requests
cffi==1.15.0
@@ -39,11 +43,11 @@ cmarkgfm==2022.10.27
# via -r requirements.in
cryptography==40.0.2
# via pymysql
dataset==1.3.1
dataset==1.5.2
# via -r requirements.in
docutils==0.15.2
# via botocore
flask==1.1.2
flask==2.0.3
# via
# -r requirements.in
# flask-babel
@@ -55,17 +59,17 @@ flask==1.1.2
# flask-sqlalchemy
flask-babel==2.0.0
# via -r requirements.in
flask-caching==1.8.0
flask-caching==2.0.2
# via -r requirements.in
flask-marshmallow==0.10.1
# via -r requirements.in
flask-migrate==2.5.3
# via -r requirements.in
flask-restx==0.5.1
flask-restx==1.1.0
# via -r requirements.in
flask-script==2.0.6
# via -r requirements.in
flask-sqlalchemy==2.4.3
flask-sqlalchemy==2.5.1
# via
# -r requirements.in
# flask-migrate
@@ -74,18 +78,17 @@ freezegun==1.2.2
gevent==22.10.2
# via -r requirements.in
greenlet==2.0.1
# via gevent
# via
# gevent
# sqlalchemy
gunicorn==20.1.0
# via -r requirements.in
idna==2.10
# via requests
itsdangerous==1.1.0
itsdangerous==2.1.2
# via flask
jinja2==3.1.2
# via
# -r requirements.in
# flask
jinja2==2.11.3
# via
# -r requirements.in
# flask
# flask-babel
jmespath==0.10.0
@@ -96,7 +99,7 @@ jsonschema==3.2.0
# via flask-restx
mako==1.1.3
# via alembic
markupsafe==1.1.1
markupsafe==2.1.3
# via
# jinja2
# mako
@@ -139,7 +142,7 @@ pytz==2020.4
# via
# flask-babel
# flask-restx
redis==3.5.2
redis==4.5.5
# via -r requirements.in
requests==2.28.1
# via -r requirements.in
@@ -148,11 +151,10 @@ s3transfer==0.3.3
six==1.15.0
# via
# flask-marshmallow
# flask-restx
# jsonschema
# python-dateutil
# tenacity
sqlalchemy==1.3.17
sqlalchemy==1.4.48
# via
# -r requirements.in
# alembic
@@ -160,7 +162,7 @@ sqlalchemy==1.3.17
# flask-sqlalchemy
# marshmallow-sqlalchemy
# sqlalchemy-utils
sqlalchemy-utils==0.41.0
sqlalchemy-utils==0.41.1
# via -r requirements.in
tenacity==6.2.0
# via -r requirements.in
@@ -168,7 +170,7 @@ urllib3==1.25.11
# via
# botocore
# requests
werkzeug==1.0.1
werkzeug==2.1.2
# via
# -r requirements.in
# flask

View File

@@ -7,4 +7,4 @@ docker run \
-v $ROOTDIR:/mnt/CTFd \
-e CUSTOM_COMPILE_COMMAND='./scripts/pip-compile.sh' \
-it python:3.9-slim-buster \
-c 'cd /mnt/CTFd && pip install pip-tools==6.6.0 && pip-compile'
-c 'cd /mnt/CTFd && pip install pip-tools==6.13.0 && pip-compile'

View File

@@ -59,7 +59,7 @@ def test_admin_access():
for route in routes:
r = client.get(route)
assert r.status_code == 302
assert r.location.startswith("http://localhost/login")
assert r.location.startswith("/login")
admin = login_as_user(app, name="admin")
routes.remove("/admin")
@@ -78,5 +78,5 @@ def test_get_admin_as_user():
client = login_as_user(app)
r = client.get("/admin")
assert r.status_code == 302
assert r.location.startswith("http://localhost/login")
assert r.location.startswith("/login")
destroy_ctfd(app)

View File

@@ -132,7 +132,8 @@ def test_config_value_types():
# Test regular length strings
r = admin.patch(
"/api/v1/configs", json={"ctf_footer": "// regular length string"},
"/api/v1/configs",
json={"ctf_footer": "// regular length string"},
)
assert r.status_code == 200
assert get_config("ctf_footer") == "// regular length string"

View File

@@ -44,5 +44,5 @@ def test_api_hint_404():
for endpoint in endpoints:
r = client.get(endpoint.format(1))
assert r.status_code == 302
assert r.location.startswith("http://localhost/login")
assert r.location.startswith("/login")
destroy_ctfd(app)

View File

@@ -138,7 +138,7 @@ def create_ctfd(
config.APPLICATION_ROOT = application_root
url = make_url(config.SQLALCHEMY_DATABASE_URI)
if url.database:
url.database = str(uuid.uuid4())
url = url.set(database=str(uuid.uuid4()))
config.SQLALCHEMY_DATABASE_URI = str(url)
app = create_app(config)

View File

@@ -18,7 +18,7 @@ def test_oauth_not_configured():
with app.app_context():
with app.test_client() as client:
r = client.get("/oauth", follow_redirects=False)
assert r.location == "http://localhost/login"
assert r.location == "/login"
r = client.get(r.location)
resp = r.get_data(as_text=True)
assert "OAuth Settings not configured" in resp

View File

@@ -51,7 +51,7 @@ def test_anonymous_users_view_public_challenges_without_team():
with app.test_client() as client:
r = client.get("/challenges")
assert r.status_code == 302
assert r.location.startswith("http://localhost/login")
assert r.location.startswith("/login")
set_config("challenge_visibility", "public")
with app.test_client() as client:
@@ -61,5 +61,5 @@ def test_anonymous_users_view_public_challenges_without_team():
with login_as_user(app) as client:
r = client.get("/challenges")
assert r.status_code == 302
assert r.location.startswith("http://localhost/team")
assert r.location.startswith("/team")
destroy_ctfd(app)

View File

@@ -90,7 +90,7 @@ def test_that_ctfd_can_be_deployed_in_subdir():
with app.test_client() as client:
r = client.get("/")
assert r.status_code == 302
assert r.location == "http://localhost/ctf/setup"
assert r.location == "/ctf/setup"
r = client.get("/setup")
with client.session_transaction() as sess:
@@ -105,13 +105,13 @@ def test_that_ctfd_can_be_deployed_in_subdir():
}
r = client.post("/setup", data=data)
assert r.status_code == 302
assert r.location == "http://localhost/ctf/"
assert r.location == "/ctf/"
c = Client(app)
app_iter, status, headers = c.get("/")
headers = dict(headers)
assert status == "302 FOUND"
assert headers["Location"] == "http://localhost/ctf/"
response = c.get("/")
headers = dict(response.headers)
assert response.status == "302 FOUND"
assert headers["Location"] == "/ctf/?"
r = client.get("/challenges")
assert r.status_code == 200

View File

@@ -83,7 +83,7 @@ def test_page_requiring_auth():
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"
assert r.location == "/login?next=%2Fthis-is-a-route%3F"
register_user(app)
client = login_as_user(app)
@@ -388,6 +388,13 @@ def test_user_can_access_files_with_auth_token():
# Friday, October 6, 2017 12:00:00 AM GMT-04:00 DST
set_config("end", "1507262400")
set_config("view_after_ctf", True)
# Get file_url under current time
with login_as_user(app) as user:
req = user.get("/api/v1/challenges/1")
data = req.get_json()
file_url = data["data"]["files"][0]
for v in ("public", "private"):
set_config("challenge_visibility", v)
@@ -424,12 +431,13 @@ def test_user_can_access_files_if_view_after_ctf():
register_user(app)
with login_as_user(app) as client:
# After ctf end
# Get file_url during freeze time
with freeze_time("2017-10-7"):
req = client.get("/api/v1/challenges/1")
data = req.get_json()
file_url = data["data"]["files"][0]
# After ctf end
with freeze_time("2017-10-7"):
# Friday, October 6, 2017 12:00:00 AM GMT-04:00 DST
set_config("end", "1507262400")
@@ -443,8 +451,8 @@ def test_user_can_access_files_if_view_after_ctf():
assert r.get_data(as_text=True) == "testing file load"
# Unauthed users should be able to download if view_after_ctf
client = app.test_client()
r = client.get(file_url)
unauth_client = app.test_client()
r = unauth_client.get(file_url)
assert r.status_code == 200
assert r.get_data(as_text=True) == "testing file load"
finally:

View File

@@ -119,9 +119,7 @@ def test_user_bad_login():
with client.session_transaction() as sess:
assert sess.get("id") is None
r = client.get("/profile")
assert r.location.startswith(
"http://localhost/login"
) # We got redirected to login
assert r.location.startswith("/login") # We got redirected to login
destroy_ctfd(app)
@@ -132,9 +130,7 @@ def test_user_login():
register_user(app)
client = login_as_user(app)
r = client.get("/profile")
assert (
r.location != "http://localhost/login"
) # We didn't get redirected to login
assert r.location is None # We didn't get redirected to login
assert r.status_code == 200
destroy_ctfd(app)
@@ -146,9 +142,7 @@ def test_user_login_with_email():
register_user(app)
client = login_as_user(app, name="user@examplectf.com", password="password")
r = client.get("/profile")
assert (
r.location != "http://localhost/login"
) # We didn't get redirected to login
assert r.location is None # We didn't get redirected to login
assert r.status_code == 200
destroy_ctfd(app)
@@ -161,7 +155,7 @@ def test_user_get_logout():
client = login_as_user(app)
client.get("/logout", follow_redirects=True)
r = client.get("/challenges")
assert r.location == "http://localhost/login?next=%2Fchallenges%3F"
assert r.location == "/login?next=%2Fchallenges%3F"
assert r.status_code == 302
destroy_ctfd(app)
@@ -182,7 +176,7 @@ def test_user_isnt_admin():
"config",
]:
r = client.get("/admin/{}".format(page))
assert r.location.startswith("http://localhost/login?next=")
assert r.location.startswith("/login?next=")
assert r.status_code == 302
destroy_ctfd(app)
@@ -302,7 +296,7 @@ def test_user_can_confirm_email(mock_smtp):
client = login_as_user(app, name="user1", password="password")
r = client.get("http://localhost/confirm")
r = client.get("/confirm")
assert "We've sent a confirmation email" in r.get_data(as_text=True)
# smtp send message function was called
@@ -310,23 +304,21 @@ def test_user_can_confirm_email(mock_smtp):
with client.session_transaction() as sess:
data = {"nonce": sess.get("nonce")}
r = client.post("http://localhost/confirm", data=data)
r = client.post("/confirm", data=data)
assert "Confirmation email sent to" in r.get_data(as_text=True)
r = client.get("/challenges")
assert (
r.location == "http://localhost/confirm"
) # We got redirected to /confirm
assert r.location == "/confirm" # We got redirected to /confirm
r = client.get("http://localhost/confirm/" + serialize("user@user.com"))
assert r.location == "http://localhost/challenges"
r = client.get("/confirm/" + serialize("user@user.com"))
assert r.location == "/challenges"
# The team is now verified
user = Users.query.filter_by(email="user@user.com").first()
assert user.verified is True
r = client.get("http://localhost/confirm")
assert r.location == "http://localhost/settings"
r = client.get("/confirm")
assert r.location == "/settings"
destroy_ctfd(app)
@@ -462,7 +454,7 @@ def test_registration_code_required():
data["registration_code"] = "secret-sauce"
r = client.post("/register", data=data)
assert r.status_code == 302
assert r.location.startswith("http://localhost/challenges")
assert r.location.startswith("/challenges")
destroy_ctfd(app)
@@ -492,5 +484,5 @@ def test_registration_code_allows_numeric():
data["registration_code"] = "1234567890"
r = client.post("/register", data=data)
assert r.status_code == 302
assert r.location.startswith("http://localhost/challenges")
assert r.location.startswith("/challenges")
destroy_ctfd(app)

View File

@@ -228,7 +228,7 @@ def test_submitting_unicode_flag():
register_user(app)
client = login_as_user(app)
chal = gen_challenge(app.db)
gen_flag(app.db, challenge_id=chal.id, content=u"你好")
gen_flag(app.db, challenge_id=chal.id, content="你好")
with client.session_transaction():
data = {"submission": "你好", "challenge_id": chal.id}
r = client.post("/api/v1/challenges/attempt", json=data)
@@ -251,7 +251,7 @@ def test_challenges_with_max_attempts():
chal.max_attempts = 3
app.db.session.commit()
gen_flag(app.db, challenge_id=chal.id, content=u"flag")
gen_flag(app.db, challenge_id=chal.id, content="flag")
for _ in range(3):
data = {"submission": "notflag", "challenge_id": chal_id}
r = client.post("/api/v1/challenges/attempt", json=data)
@@ -281,7 +281,7 @@ def test_challenge_kpm_limit():
chal = gen_challenge(app.db)
chal_id = chal.id
gen_flag(app.db, challenge_id=chal.id, content=u"flag")
gen_flag(app.db, challenge_id=chal.id, content="flag")
for _ in range(11):
with client.session_transaction():
data = {"submission": "notflag", "challenge_id": chal_id}

View File

@@ -259,7 +259,7 @@ def test_user_needs_all_required_fields():
r = client.get("/challenges")
assert r.status_code == 302
assert r.location.startswith("http://localhost/settings")
assert r.location.startswith("/settings")
# Populate the non-required fields
r = client.patch(
@@ -277,7 +277,7 @@ def test_user_needs_all_required_fields():
# I should still be restricted from seeing challenges
r = client.get("/challenges")
assert r.status_code == 302
assert r.location.startswith("http://localhost/settings")
assert r.location.startswith("/settings")
# I should still see all fields b/c I don't have a complete profile
r = client.get("/settings")

View File

@@ -11,7 +11,7 @@ def test_ctfd_setup_redirect():
with app.test_client() as client:
r = client.get("/users")
assert r.status_code == 302
assert r.location == "http://localhost/setup"
assert r.location == "/setup"
# Files in /themes load properly
r = client.get("/themes/core/static/css/main.dev.css")
@@ -52,5 +52,5 @@ def test_ctfd_setup_verification():
data["email"] = "admin@examplectf.com"
r = client.post("/setup", data=data)
assert r.status_code == 302
assert r.location == "http://localhost/"
assert r.location == "/"
destroy_ctfd(app)

View File

@@ -21,7 +21,7 @@ def test_ctftime_prevents_accessing_challenges_before_ctf():
register_user(app)
chal = gen_challenge(app.db)
chal_id = chal.id
gen_flag(app.db, challenge_id=chal.id, content=u"flag")
gen_flag(app.db, challenge_id=chal.id, content="flag")
with ctftime.not_started():
client = login_as_user(app)
@@ -48,7 +48,7 @@ def test_ctftime_redirects_to_teams_page_in_teams_mode_before_ctf():
with ctftime.init():
register_user(app)
chal = gen_challenge(app.db)
gen_flag(app.db, challenge_id=chal.id, content=u"flag")
gen_flag(app.db, challenge_id=chal.id, content="flag")
with ctftime.not_started():
client = login_as_user(app)
@@ -83,7 +83,7 @@ def test_ctftime_allows_accessing_challenges_during_ctf():
register_user(app)
chal = gen_challenge(app.db)
chal_id = chal.id
gen_flag(app.db, challenge_id=chal.id, content=u"flag")
gen_flag(app.db, challenge_id=chal.id, content="flag")
with ctftime.started():
client = login_as_user(app)
@@ -112,7 +112,7 @@ def test_ctftime_prevents_accessing_challenges_after_ctf():
register_user(app)
chal = gen_challenge(app.db)
chal_id = chal.id
gen_flag(app.db, challenge_id=chal.id, content=u"flag")
gen_flag(app.db, challenge_id=chal.id, content="flag")
with ctftime.ended():
client = login_as_user(app)

View File

@@ -104,7 +104,7 @@ def test_sendmail_with_mailgun_from_config_file(fake_post_request):
args, kwargs = fake_post_request.call_args
assert args[0] == "https://api.mailgun.net/v3/file.faked.com/messages"
assert kwargs["auth"] == ("api", u"key-1234567890-file-config")
assert kwargs["auth"] == ("api", "key-1234567890-file-config")
assert kwargs["timeout"] == 1.0
assert kwargs["data"] == {
"to": ["user@user.com"],
@@ -144,7 +144,7 @@ def test_sendmail_with_mailgun_from_db_config(fake_post_request):
args, kwargs = fake_post_request.call_args
assert args[0] == "https://api.mailgun.net/v3/db.faked.com/messages"
assert kwargs["auth"] == ("api", u"key-1234567890-db-config")
assert kwargs["auth"] == ("api", "key-1234567890-db-config")
assert kwargs["timeout"] == 1.0
assert kwargs["data"] == {
"to": ["user@user.com"],

View File

@@ -1,7 +1,6 @@
from unittest.mock import Mock, patch
from uuid import UUID
from mock import Mock, patch
from tests.helpers import create_ctfd, destroy_ctfd, login_as_user, register_user
@@ -40,7 +39,7 @@ def test_session_invalidation_on_admin_password_change():
r = user.get("/settings")
# User's password was changed
# They should be logged out
assert r.location.startswith("http://localhost/login")
assert r.location.startswith("/login")
assert r.status_code == 302
destroy_ctfd(app)

View File

@@ -69,14 +69,14 @@ def test_update_check_ignores_downgrades(fake_post_request):
fake_response = Mock()
fake_post_request.return_value = fake_response
fake_response.json = lambda: {
u"resource": {
u"html_url": u"https://github.com/CTFd/CTFd/releases/tag/0.0.1",
u"download_url": u"https://api.github.com/repos/CTFd/CTFd/zipball/0.0.1",
u"published_at": u"Wed, 25 Oct 2017 19:39:42 -0000",
u"tag": u"0.0.1",
u"prerelease": False,
u"id": 6,
u"latest": True,
"resource": {
"html_url": "https://github.com/CTFd/CTFd/releases/tag/0.0.1",
"download_url": "https://api.github.com/repos/CTFd/CTFd/zipball/0.0.1",
"published_at": "Wed, 25 Oct 2017 19:39:42 -0000",
"tag": "0.0.1",
"prerelease": False,
"id": 6,
"latest": True,
}
}
update_check()
@@ -85,18 +85,18 @@ def test_update_check_ignores_downgrades(fake_post_request):
fake_response = Mock()
fake_post_request.return_value = fake_response
fake_response.json = lambda: {
u"resource": {
u"html_url": u"https://github.com/CTFd/CTFd/releases/tag/{}".format(
"resource": {
"html_url": "https://github.com/CTFd/CTFd/releases/tag/{}".format(
app.VERSION
),
u"download_url": u"https://api.github.com/repos/CTFd/CTFd/zipball/{}".format(
"download_url": "https://api.github.com/repos/CTFd/CTFd/zipball/{}".format(
app.VERSION
),
u"published_at": u"Wed, 25 Oct 2017 19:39:42 -0000",
u"tag": u"{}".format(app.VERSION),
u"prerelease": False,
u"id": 6,
u"latest": True,
"published_at": "Wed, 25 Oct 2017 19:39:42 -0000",
"tag": "{}".format(app.VERSION),
"prerelease": False,
"id": 6,
"latest": True,
}
}
update_check()