diff --git a/.travis.yml b/.travis.yml index 7bc61b3d..1a5f0f56 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,4 +5,5 @@ python: install: - pip install -r development.txt script: + - pep8 --ignore E501,E712 CTFd/ tests/ - nosetests diff --git a/CTFd/__init__.py b/CTFd/__init__.py index 22f35c9b..43a8858f 100644 --- a/CTFd/__init__.py +++ b/CTFd/__init__.py @@ -13,6 +13,7 @@ from CTFd import utils __version__ = '1.0.2' + class ThemeLoader(FileSystemLoader): def get_source(self, environment, template): if template.startswith('admin/'): @@ -34,21 +35,21 @@ def create_app(config='CTFd.config.Config'): if url.drivername == 'postgres': url.drivername = 'postgresql' - ## Creates database if the database database does not exist + # Creates database if the database database does not exist if not database_exists(url): create_database(url) - ## Register database + # Register database db.init_app(app) - ## Register Flask-Migrate + # Register Flask-Migrate migrate.init_app(app, db) - ## This creates tables instead of db.create_all() - ## Allows migrations to happen properly + # This creates tables instead of db.create_all() + # Allows migrations to happen properly migrate_upgrade() - ## Alembic sqlite support is lacking so we should just create_all anyway + # Alembic sqlite support is lacking so we should just create_all anyway if url.drivername.startswith('sqlite'): db.create_all() @@ -59,10 +60,10 @@ def create_app(config='CTFd.config.Config'): version = utils.get_config('ctf_version') - if not version: ## Upgrading from an unversioned CTFd + if not version: # Upgrading from an unversioned CTFd utils.set_config('ctf_version', __version__) - if version and (StrictVersion(version) < StrictVersion(__version__)): ## Upgrading from an older version of CTFd + if version and (StrictVersion(version) < StrictVersion(__version__)): # Upgrading from an older version of CTFd print("/*\\ CTFd has updated and must update the database! /*\\") print("/*\\ Please backup your database before proceeding! /*\\") print("/*\\ CTFd maintainers are not responsible for any data loss! /*\\") diff --git a/CTFd/admin/challenges.py b/CTFd/admin/challenges.py index cb8ca30f..d0f28492 100644 --- a/CTFd/admin/challenges.py +++ b/CTFd/admin/challenges.py @@ -142,12 +142,12 @@ def admin_hints(hintid): db.session.add(hint) db.session.commit() json_data = { - 'hint': hint.hint, - 'type': hint.type, - 'chal': hint.chal, - 'cost': hint.cost, - 'id': hint.id - } + 'hint': hint.hint, + 'type': hint.type, + 'chal': hint.chal, + 'cost': hint.cost, + 'id': hint.id + } db.session.close() return jsonify(json_data) @@ -192,7 +192,7 @@ def admin_get_values(chalid, prop): 'key': x.flag, 'type': x.key_type, 'type_name': get_key_class(x.key_type).name - }) + }) return jsonify(json_data) elif prop == 'tags': tags = Tags.query.filter_by(chal=chalid).all() @@ -202,19 +202,19 @@ def admin_get_values(chalid, prop): 'id': x.id, 'chal': x.chal, 'tag': x.tag - }) + }) return jsonify(json_data) elif prop == 'hints': hints = Hints.query.filter_by(chal=chalid) json_data = {'hints': []} for hint in hints: json_data['hints'].append({ - 'hint': hint.hint, - 'type': hint.type, - 'chal': hint.chal, - 'cost': hint.cost, - 'id': hint.id - }) + 'hint': hint.hint, + 'type': hint.type, + 'chal': hint.chal, + 'cost': hint.cost, + 'id': hint.id + }) return jsonify(json_data) diff --git a/CTFd/admin/containers.py b/CTFd/admin/containers.py index 2c8762ae..d139f9d4 100644 --- a/CTFd/admin/containers.py +++ b/CTFd/admin/containers.py @@ -6,6 +6,7 @@ from CTFd import utils admin_containers = Blueprint('admin_containers', __name__) + @admin_containers.route('/admin/containers', methods=['GET']) @admins_only def list_container(): @@ -63,4 +64,4 @@ def new_container(): files = request.files.getlist('files[]') utils.create_image(name=name, buildfile=buildfile, files=files) utils.run_image(name) - return redirect(url_for('admin_containers.list_container')) \ No newline at end of file + return redirect(url_for('admin_containers.list_container')) diff --git a/CTFd/admin/keys.py b/CTFd/admin/keys.py index e6901a72..111ff766 100644 --- a/CTFd/admin/keys.py +++ b/CTFd/admin/keys.py @@ -7,6 +7,7 @@ from CTFd import utils admin_keys = Blueprint('admin_keys', __name__) + @admin_keys.route('/admin/key_types', methods=['GET']) @admins_only def admin_key_types(): @@ -16,6 +17,7 @@ def admin_key_types(): return jsonify(data) + @admin_keys.route('/admin/keys', defaults={'keyid': None}, methods=['POST', 'GET']) @admin_keys.route('/admin/keys/', methods=['POST', 'GET']) @admins_only @@ -53,7 +55,6 @@ def admin_keys_view(keyid): return '1' - @admin_keys.route('/admin/keys//delete', methods=['POST']) @admins_only def admin_delete_keys(keyid): @@ -62,4 +63,4 @@ def admin_delete_keys(keyid): db.session.delete(key) db.session.commit() db.session.close() - return '1' \ No newline at end of file + return '1' diff --git a/CTFd/admin/pages.py b/CTFd/admin/pages.py index a397a58a..863f6dc9 100644 --- a/CTFd/admin/pages.py +++ b/CTFd/admin/pages.py @@ -6,6 +6,7 @@ from CTFd import utils admin_pages = Blueprint('admin_pages', __name__) + @admin_pages.route('/admin/css', methods=['GET', 'POST']) @admins_only def admin_css(): @@ -59,4 +60,4 @@ def delete_page(pageroute): db.session.delete(page) db.session.commit() db.session.close() - return '1' \ No newline at end of file + return '1' diff --git a/CTFd/admin/scoreboard.py b/CTFd/admin/scoreboard.py index 3a3491ed..1bc0cf26 100644 --- a/CTFd/admin/scoreboard.py +++ b/CTFd/admin/scoreboard.py @@ -7,6 +7,7 @@ from CTFd import utils admin_scoreboard = Blueprint('admin_scoreboard', __name__) + @admin_scoreboard.route('/admin/scoreboard') @admins_only def admin_scoreboard_view(): @@ -24,4 +25,4 @@ def admin_scores(): json_data = {'teams': []} for i, x in enumerate(teams): json_data['teams'].append({'place': i + 1, 'id': x.teamid, 'name': x.name, 'score': int(x.score)}) - return jsonify(json_data) \ No newline at end of file + return jsonify(json_data) diff --git a/CTFd/admin/statistics.py b/CTFd/admin/statistics.py index e46b1335..78174a3a 100644 --- a/CTFd/admin/statistics.py +++ b/CTFd/admin/statistics.py @@ -6,11 +6,13 @@ from CTFd import utils admin_statistics = Blueprint('admin_statistics', __name__) + @admin_statistics.route('/admin/graphs') @admins_only def admin_graphs(): return render_template('admin/graphs.html') + @admin_statistics.route('/admin/graphs/') @admins_only def admin_graph(graph_type): @@ -31,6 +33,7 @@ def admin_graph(graph_type): json_data[name] = count return jsonify(json_data) + @admin_statistics.route('/admin/statistics', methods=['GET']) @admins_only def admin_stats(): @@ -66,6 +69,7 @@ def admin_stats(): most_solved=most_solved, least_solved=least_solved) + @admin_statistics.route('/admin/wrong_keys', defaults={'page': '1'}, methods=['GET']) @admin_statistics.route('/admin/wrong_keys/', methods=['GET']) @admins_only @@ -77,11 +81,11 @@ def admin_wrong_key(page): wrong_keys = WrongKeys.query.add_columns(WrongKeys.id, WrongKeys.chalid, WrongKeys.flag, WrongKeys.teamid, WrongKeys.date, Challenges.name.label('chal_name'), Teams.name.label('team_name')) \ - .join(Challenges) \ - .join(Teams) \ - .order_by(WrongKeys.date.desc()) \ - .slice(page_start, page_end) \ - .all() + .join(Challenges) \ + .join(Teams) \ + .order_by(WrongKeys.date.desc()) \ + .slice(page_start, page_end) \ + .all() wrong_count = db.session.query(db.func.count(WrongKeys.id)).first()[0] pages = int(wrong_count / results_per_page) + (wrong_count % results_per_page > 0) @@ -100,13 +104,13 @@ def admin_correct_key(page): solves = Solves.query.add_columns(Solves.id, Solves.chalid, Solves.teamid, Solves.date, Solves.flag, Challenges.name.label('chal_name'), Teams.name.label('team_name')) \ - .join(Challenges) \ - .join(Teams) \ - .order_by(Solves.date.desc()) \ - .slice(page_start, page_end) \ - .all() + .join(Challenges) \ + .join(Teams) \ + .order_by(Solves.date.desc()) \ + .slice(page_start, page_end) \ + .all() solve_count = db.session.query(db.func.count(Solves.id)).first()[0] pages = int(solve_count / results_per_page) + (solve_count % results_per_page > 0) - return render_template('admin/correct_keys.html', solves=solves, pages=pages, curr_page=page) \ No newline at end of file + return render_template('admin/correct_keys.html', solves=solves, pages=pages, curr_page=page) diff --git a/CTFd/admin/teams.py b/CTFd/admin/teams.py index 4136267a..dca6fd10 100644 --- a/CTFd/admin/teams.py +++ b/CTFd/admin/teams.py @@ -8,6 +8,7 @@ from CTFd import utils admin_teams = Blueprint('admin_teams', __name__) + @admin_teams.route('/admin/teams', defaults={'page': '1'}) @admin_teams.route('/admin/teams/') @admins_only @@ -225,6 +226,7 @@ def delete_wrong_key(keyid): db.session.close() return '1' + @admin_teams.route('/admin/awards//delete', methods=['POST']) @admins_only def delete_award(award_id): @@ -234,6 +236,7 @@ def delete_award(award_id): db.session.close() return '1' + @admin_teams.route('/admin/teams//awards', methods=['GET']) @admins_only def admin_awards(teamid): @@ -270,4 +273,4 @@ def create_award(): return '1' except Exception as e: print(e) - return '0' \ No newline at end of file + return '0' diff --git a/CTFd/auth.py b/CTFd/auth.py index 0c5576b6..0d7fd8b3 100644 --- a/CTFd/auth.py +++ b/CTFd/auth.py @@ -19,7 +19,7 @@ auth = Blueprint('auth', __name__) def confirm_user(data=None): if not utils.get_config('verify_emails'): return redirect(url_for('challenges.challenges_view')) - if data and request.method == "GET": # User is confirming email account + if data and request.method == "GET": # User is confirming email account try: s = Signer(app.config['SECRET_KEY']) email = s.unsign(urllib.unquote_plus(data.decode('base64'))) @@ -36,7 +36,7 @@ def confirm_user(data=None): if utils.authed(): return redirect(url_for('challenges.challenges_view')) return redirect(url_for('auth.login')) - if not data and request.method == "GET": # User has been directed to the confirm page because his account is not verified + if not data and request.method == "GET": # User has been directed to the confirm page because his account is not verified if not utils.authed(): return redirect(url_for('auth.login')) team = Teams.query.filter_by(id=session['id']).first_or_404() @@ -130,15 +130,15 @@ def register(): session['admin'] = team.admin session['nonce'] = utils.sha512(os.urandom(10)) - if utils.can_send_mail() and utils.get_config('verify_emails'): # Confirming users is enabled and we can send email. + if utils.can_send_mail() and utils.get_config('verify_emails'): # Confirming users is enabled and we can send email. db.session.close() logger = logging.getLogger('regs') logger.warn("[{0}] {1} registered (UNCONFIRMED) with {2}".format(time.strftime("%m/%d/%Y %X"), request.form['name'].encode('utf-8'), request.form['email'].encode('utf-8'))) return redirect(url_for('auth.confirm_user')) - else: # Don't care about confirming users - if utils.can_send_mail(): # We want to notify the user that they have registered. + else: # Don't care about confirming users + if utils.can_send_mail(): # We want to notify the user that they have registered. utils.sendmail(request.form['email'], "You've successfully registered for {}".format(utils.get_config('ctf_name'))) db.session.close() @@ -159,9 +159,9 @@ def login(): if team: if team and bcrypt_sha256.verify(request.form['password'], team.password): try: - session.regenerate() # NO SESSION FIXATION FOR YOU + session.regenerate() # NO SESSION FIXATION FOR YOU except: - pass # TODO: Some session objects don't implement regenerate :( + pass # TODO: Some session objects don't implement regenerate :( session['username'] = team.name session['id'] = team.id session['admin'] = team.admin @@ -174,7 +174,7 @@ def login(): if request.args.get('next') and utils.is_safe_url(request.args.get('next')): return redirect(request.args.get('next')) return redirect(url_for('challenges.challenges_view')) - else: # This user exists but the password is wrong + else: # This user exists but the password is wrong errors.append("Your username or password is incorrect") db.session.close() return render_template('login.html', errors=errors) diff --git a/CTFd/challenges.py b/CTFd/challenges.py index 92c6199a..9a139f85 100644 --- a/CTFd/challenges.py +++ b/CTFd/challenges.py @@ -14,6 +14,7 @@ from CTFd import utils challenges = Blueprint('challenges', __name__) + @challenges.route('/hints/', methods=['GET', 'POST']) def hints_view(hintid): hint = Hints.query.filter_by(id=hintid).first_or_404() @@ -22,15 +23,15 @@ def hints_view(hintid): if request.method == 'GET': if unlock: return jsonify({ - 'hint': hint.hint, - 'chal': hint.chal, - 'cost': hint.cost - }) + 'hint': hint.hint, + 'chal': hint.chal, + 'cost': hint.cost + }) else: return jsonify({ - 'chal': hint.chal, - 'cost': hint.cost - }) + 'chal': hint.chal, + 'cost': hint.cost + }) elif request.method == 'POST': if not unlock: team = Teams.query.filter_by(id=session['id']).first() @@ -42,18 +43,18 @@ def hints_view(hintid): db.session.add(award) db.session.commit() json_data = { - 'hint': hint.hint, - 'chal': hint.chal, - 'cost': hint.cost - } + 'hint': hint.hint, + 'chal': hint.chal, + 'cost': hint.cost + } db.session.close() return jsonify(json_data) else: json_data = { - 'hint': hint.hint, - 'chal': hint.chal, - 'cost': hint.cost - } + 'hint': hint.hint, + 'chal': hint.chal, + 'cost': hint.cost + } db.session.close() return jsonify(json_data) @@ -63,10 +64,10 @@ def challenges_view(): errors = [] start = utils.get_config('start') or 0 end = utils.get_config('end') or 0 - if not utils.is_admin(): # User is not an admin + if not utils.is_admin(): # User is not an admin if not utils.ctftime(): # It is not CTF time - if utils.view_after_ctf(): # But we are allowed to view after the CTF ends + if utils.view_after_ctf(): # But we are allowed to view after the CTF ends pass else: # We are NOT allowed to view after the CTF ends if utils.get_config('start') and not utils.ctf_started(): @@ -74,9 +75,9 @@ def challenges_view(): if (utils.get_config('end') and utils.ctf_ended()) and not utils.view_after_ctf(): errors.append('{} has ended'.format(utils.ctf_name())) return render_template('chals.html', errors=errors, start=int(start), end=int(end)) - if utils.get_config('verify_emails') and not utils.is_verified(): # User is not confirmed + if utils.get_config('verify_emails') and not utils.is_verified(): # User is not confirmed return redirect(url_for('auth.confirm_user')) - if utils.user_can_view_challenges(): # Do we allow unauthenticated users? + if utils.user_can_view_challenges(): # Do we allow unauthenticated users? if utils.get_config('start') and not utils.ctf_started(): errors.append('{} has not started yet'.format(utils.ctf_name())) if (utils.get_config('end') and utils.ctf_ended()) and not utils.view_after_ctf(): @@ -104,9 +105,9 @@ def chals(): hints = [] for hint in Hints.query.filter_by(chal=x.id).all(): if hint.id in unlocked_hints: - hints.append({'id':hint.id, 'cost':hint.cost, 'hint':hint.hint}) + hints.append({'id': hint.id, 'cost': hint.cost, 'hint': hint.hint}) else: - hints.append({'id':hint.id, 'cost':hint.cost}) + hints.append({'id': hint.id, 'cost': hint.cost}) # hints = [{'id':hint.id, 'cost':hint.cost} for hint in Hints.query.filter_by(chal=x.id).all()] chal_type = get_chal_class(x.type) json['game'].append({ @@ -293,7 +294,7 @@ def chal(chalid): logger.info("[{0}] {1} submitted {2} with kpm {3} [WRONG]".format(*data)) # return '0' # key was wrong if max_tries: - attempts_left = max_tries - fails - 1 ## Off by one since fails has changed since it was gotten + attempts_left = max_tries - fails - 1 # Off by one since fails has changed since it was gotten tries_str = 'tries' if attempts_left == 1: tries_str = 'try' diff --git a/CTFd/config.py b/CTFd/config.py index 86b7194d..cbfbcb79 100644 --- a/CTFd/config.py +++ b/CTFd/config.py @@ -1,6 +1,6 @@ import os -##### GENERATE SECRET KEY ##### +''' GENERATE SECRET KEY ''' with open('.ctfd_secret_key', 'a+b') as secret: secret.seek(0) # Seek to beginning of file since a+ mode leaves you at the end and w+ deletes the file @@ -10,7 +10,8 @@ with open('.ctfd_secret_key', 'a+b') as secret: secret.write(key) secret.flush() -##### SERVER SETTINGS ##### +''' SERVER SETTINGS ''' + class Config(object): ''' @@ -25,7 +26,6 @@ class Config(object): ''' SECRET_KEY = os.environ.get('SECRET_KEY') or key - ''' SQLALCHEMY_DATABASE_URI is the URI that specifies the username, password, hostname, port, and database of the server used to hold the CTFd database. @@ -34,52 +34,44 @@ class Config(object): ''' SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or 'sqlite:///{}/ctfd.db'.format(os.path.dirname(os.path.abspath(__file__))) - ''' SQLALCHEMY_TRACK_MODIFICATIONS is automatically disabled to suppress warnings and save memory. You should only enable this if you need it. ''' SQLALCHEMY_TRACK_MODIFICATIONS = False - ''' SESSION_TYPE is a configuration value used for Flask-Session. It is currently unused in CTFd. http://pythonhosted.org/Flask-Session/#configuration ''' SESSION_TYPE = "filesystem" - ''' SESSION_FILE_DIR is a configuration value used for Flask-Session. It is currently unused in CTFd. http://pythonhosted.org/Flask-Session/#configuration ''' SESSION_FILE_DIR = "/tmp/flask_session" - ''' SESSION_COOKIE_HTTPONLY controls if cookies should be set with the HttpOnly flag. ''' SESSION_COOKIE_HTTPONLY = True - ''' PERMANENT_SESSION_LIFETIME is the lifetime of a session. ''' - PERMANENT_SESSION_LIFETIME = 604800 # 7 days in seconds - + PERMANENT_SESSION_LIFETIME = 604800 # 7 days in seconds ''' HOST specifies the hostname where the CTFd instance will exist. It is currently unused. ''' HOST = ".ctfd.io" - ''' MAILFROM_ADDR is the email address that emails are sent from if not overridden in the configuration panel. ''' MAILFROM_ADDR = "noreply@ctfd.io" - ''' UPLOAD_FOLDER is the location where files are uploaded. The default destination is the CTFd/uploads folder. If you need Amazon S3 files @@ -87,14 +79,12 @@ class Config(object): ''' UPLOAD_FOLDER = os.path.join(os.path.dirname(__file__), 'uploads') - ''' TEMPLATES_AUTO_RELOAD specifies whether Flask should check for modifications to templates and reload them automatically ''' TEMPLATES_AUTO_RELOAD = True - ''' TRUSTED_PROXIES defines a set of regular expressions used for finding a user's IP address if the CTFd instance is behind a proxy. If you are running a CTF and users are on the same network as you, you may choose to remove @@ -114,7 +104,6 @@ class Config(object): '^192\.168\.' ] - ''' CACHE_TYPE specifies how CTFd should cache configuration values. If CACHE_TYPE is set to 'redis', CTFd will make use of the REDIS_URL specified in environment variables. You can also choose to hardcode the REDIS_URL here. diff --git a/CTFd/plugins/challenges/__init__.py b/CTFd/plugins/challenges/__init__.py index 27bf2674..2f00a8d6 100644 --- a/CTFd/plugins/challenges/__init__.py +++ b/CTFd/plugins/challenges/__init__.py @@ -1,10 +1,12 @@ from CTFd.plugins.keys import get_key_class from CTFd.models import db, Keys + class BaseChallenge(object): id = None name = None + class CTFdStandardChallenge(BaseChallenge): id = 0 name = "standard" @@ -19,11 +21,12 @@ class CTFdStandardChallenge(BaseChallenge): CHALLENGE_CLASSES = { - 0 : CTFdStandardChallenge + 0: CTFdStandardChallenge } + def get_chal_class(class_id): cls = CHALLENGE_CLASSES.get(class_id) if cls is None: raise KeyError - return cls \ No newline at end of file + return cls diff --git a/CTFd/plugins/keys/__init__.py b/CTFd/plugins/keys/__init__.py index c71e2246..1f8e5d64 100644 --- a/CTFd/plugins/keys/__init__.py +++ b/CTFd/plugins/keys/__init__.py @@ -11,6 +11,7 @@ class BaseKey(object): def compare(self, saved, provided): return True + class CTFdStaticKey(BaseKey): id = 0 name = "static" @@ -36,8 +37,8 @@ class CTFdRegexKey(BaseKey): KEY_CLASSES = { - 0 : CTFdStaticKey, - 1 : CTFdRegexKey + 0: CTFdStaticKey, + 1: CTFdRegexKey } @@ -45,4 +46,4 @@ def get_key_class(class_id): cls = KEY_CLASSES.get(class_id) if cls is None: raise KeyError - return cls \ No newline at end of file + return cls diff --git a/CTFd/scoreboard.py b/CTFd/scoreboard.py index 28200ccc..444c7ded 100644 --- a/CTFd/scoreboard.py +++ b/CTFd/scoreboard.py @@ -10,16 +10,16 @@ scoreboard = Blueprint('scoreboard', __name__) def get_standings(admin=False, count=None): scores = db.session.query( - Solves.teamid.label('teamid'), - db.func.sum(Challenges.value).label('score'), - db.func.max(Solves.date).label('date') - ).join(Challenges).group_by(Solves.teamid) + Solves.teamid.label('teamid'), + db.func.sum(Challenges.value).label('score'), + db.func.max(Solves.date).label('date') + ).join(Challenges).group_by(Solves.teamid) awards = db.session.query( - Awards.teamid.label('teamid'), - db.func.sum(Awards.value).label('score'), - db.func.max(Awards.date).label('date') - ).group_by(Awards.teamid) + Awards.teamid.label('teamid'), + db.func.sum(Awards.value).label('score'), + db.func.max(Awards.date).label('date') + ).group_by(Awards.teamid) freeze = utils.get_config('freeze') if not admin and freeze: @@ -29,28 +29,28 @@ def get_standings(admin=False, count=None): results = union_all(scores, awards).alias('results') sumscores = db.session.query( - results.columns.teamid, - db.func.sum(results.columns.score).label('score'), - db.func.max(results.columns.date).label('date') - ).group_by(results.columns.teamid).subquery() + results.columns.teamid, + db.func.sum(results.columns.score).label('score'), + db.func.max(results.columns.date).label('date') + ).group_by(results.columns.teamid).subquery() if admin: standings_query = db.session.query( - Teams.id.label('teamid'), - Teams.name.label('name'), - Teams.banned, sumscores.columns.score - )\ - .join(sumscores, Teams.id == sumscores.columns.teamid) \ - .order_by(sumscores.columns.score.desc(), sumscores.columns.date) + Teams.id.label('teamid'), + Teams.name.label('name'), + Teams.banned, sumscores.columns.score + )\ + .join(sumscores, Teams.id == sumscores.columns.teamid) \ + .order_by(sumscores.columns.score.desc(), sumscores.columns.date) else: standings_query = db.session.query( - Teams.id.label('teamid'), - Teams.name.label('name'), - sumscores.columns.score - )\ - .join(sumscores, Teams.id == sumscores.columns.teamid) \ - .filter(Teams.banned == False) \ - .order_by(sumscores.columns.score.desc(), sumscores.columns.date) + Teams.id.label('teamid'), + Teams.name.label('name'), + sumscores.columns.score + )\ + .join(sumscores, Teams.id == sumscores.columns.teamid) \ + .filter(Teams.banned == False) \ + .order_by(sumscores.columns.score.desc(), sumscores.columns.date) if count is None: standings = standings_query.all() @@ -102,7 +102,6 @@ def topteams(count): solves = Solves.query.filter_by(teamid=team.teamid) awards = Awards.query.filter_by(teamid=team.teamid) - freeze = utils.get_config('freeze') if freeze: @@ -112,7 +111,6 @@ def topteams(count): solves = solves.all() awards = awards.all() - json['scores'][team.name] = [] for x in solves: json['scores'][team.name].append({ diff --git a/CTFd/utils.py b/CTFd/utils.py index 2ae9591f..99cc2471 100644 --- a/CTFd/utils.py +++ b/CTFd/utils.py @@ -226,7 +226,6 @@ def is_scoreboard_frozen(): return False - def ctftime(): """ Checks whether it's CTF time or not. """ @@ -308,7 +307,7 @@ def get_ip(): combined = "(" + ")|(".join(trusted_proxies) + ")" route = request.access_route + [request.remote_addr] for addr in reversed(route): - if not re.match(combined, addr): # IP is not trusted but we trust the proxies + if not re.match(combined, addr): # IP is not trusted but we trust the proxies remote_addr = addr break else: @@ -316,7 +315,7 @@ def get_ip(): return remote_addr -def get_kpm(teamid): # keys per minute +def get_kpm(teamid): # keys per minute one_min_ago = datetime.datetime.utcnow() + datetime.timedelta(minutes=-1) return len(db.session.query(WrongKeys).filter(WrongKeys.teamid == teamid, WrongKeys.date >= one_min_ago).all()) @@ -355,7 +354,7 @@ def upload_file(file, chalid): def delete_file(file_id): f = Files.query.filter_by(id=file_id).first_or_404() upload_folder = os.path.join(app.root_path, app.config['UPLOAD_FOLDER']) - if os.path.exists(os.path.join(upload_folder, f.location)): # Some kind of os.path.isfile issue on Windows... + if os.path.exists(os.path.join(upload_folder, f.location)): # Some kind of os.path.isfile issue on Windows... os.unlink(os.path.join(upload_folder, f.location)) db.session.delete(f) db.session.commit() @@ -669,7 +668,7 @@ def export_ctf(segments=None): ] } - ## Backup database + # Backup database backup = io.BytesIO() backup_zip = zipfile.ZipFile(backup, 'w') @@ -682,7 +681,7 @@ def export_ctf(segments=None): result_file.seek(0) backup_zip.writestr('db/{}.json'.format(item), result_file.read()) - ## Backup uploads + # Backup uploads upload_folder = os.path.join(os.path.normpath(app.root_path), get_config('UPLOAD_FOLDER')) for root, dirs, files in os.walk(upload_folder): for file in files: @@ -730,7 +729,7 @@ def import_ctf(backup, segments=None, erase=False): ] } - ## Need special handling of metadata + # Need special handling of metadata if 'metadata' in segments: meta = groups['metadata'] segments.remove('metadata') @@ -741,7 +740,7 @@ def import_ctf(backup, segments=None, erase=False): path = "db/{}.json".format(item) data = backup.open(path).read() - ## Some JSON files will be empty + # Some JSON files will be empty if data: if item == 'config': saved = json.loads(data) @@ -772,11 +771,10 @@ def import_ctf(backup, segments=None, erase=False): if container: container.buildfile = buildfile else: - container = Containers(name, buildfile) + container = Containers(name, buildfile) db.session.add(container) db.session.commit() - for segment in segments: group = groups[segment] for item in group: @@ -791,24 +789,24 @@ def import_ctf(backup, segments=None, erase=False): else: continue - ## Extracting files + # Extracting files files = [f for f in backup.namelist() if f.startswith('uploads/')] upload_folder = app.config.get('UPLOAD_FOLDER') for f in files: filename = f.split(os.sep, 1) - if len(filename) < 2: ## just an empty uploads directory (e.g. uploads/) + if len(filename) < 2: # just an empty uploads directory (e.g. uploads/) continue - filename = filename[1] ## Get the second entry in the list (the actual filename) + filename = filename[1] # Get the second entry in the list (the actual filename) full_path = os.path.join(upload_folder, filename) dirname = os.path.dirname(full_path) - ## Create any parent directories for the file + # Create any parent directories for the file if not os.path.exists(dirname): os.makedirs(dirname) source = backup.open(f) target = file(full_path, "wb") with source, target: - shutil.copyfileobj(source, target) \ No newline at end of file + shutil.copyfileobj(source, target) diff --git a/CTFd/views.py b/CTFd/views.py index e15c3531..de221ed6 100644 --- a/CTFd/views.py +++ b/CTFd/views.py @@ -45,7 +45,7 @@ def setup(): # Index page page = Pages('index', """
- +

A cool CTF platform from ctfd.io

Follow us on social media:

@@ -55,7 +55,7 @@ def setup():


- Click here to login and setup your CTF + Click here to login and setup your CTF

""".format(request.script_root)) diff --git a/development.txt b/development.txt index 6ecbe87c..39b4e543 100644 --- a/development.txt +++ b/development.txt @@ -2,4 +2,5 @@ coverage>=4.1 mock>=2.0.0 nose>=1.3.7 -rednose>=1.1.1 \ No newline at end of file +rednose>=1.1.1 +pep8==1.7.0 diff --git a/tests/helpers.py b/tests/helpers.py index f350f8ab..b17db88a 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -106,6 +106,7 @@ def gen_solve(db, chalid, teamid, ip='127.0.0.1', flag='rightkey'): db.session.commit() return solve + def gen_wrongkey(db, teamid, chalid, flag='wrongkey'): wrongkey = WrongKeys(teamid, chalid, flag) db.session.add(wrongkey) @@ -117,4 +118,4 @@ def gen_tracking(db, ip, team): tracking = Tracking(ip, team) db.session.add(tracking) db.session.commit() - return tracking \ No newline at end of file + return tracking diff --git a/tests/test_admin_facing.py b/tests/test_admin_facing.py index eba1d1af..5e57b63a 100644 --- a/tests/test_admin_facing.py +++ b/tests/test_admin_facing.py @@ -73,4 +73,4 @@ def test_admin_config(): with app.app_context(): client = login_as_user(app, name="admin", password="password") r = client.get('/admin/config') - assert r.status_code == 200 \ No newline at end of file + assert r.status_code == 200 diff --git a/tests/test_user_facing.py b/tests/test_user_facing.py index f4299253..8a489aeb 100644 --- a/tests/test_user_facing.py +++ b/tests/test_user_facing.py @@ -22,7 +22,7 @@ def test_register_user(): def test_register_duplicate_teamname(): - """A user shouldn't be able to use and already registered team name""" + """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="user1@ctfd.io", password="password") @@ -74,7 +74,7 @@ def test_user_isnt_admin(): def test_user_get_teams(): - """Can a registered user can load /teams""" + """Can a registered user load /teams""" app = create_ctfd() with app.app_context(): register_user(app) @@ -84,7 +84,7 @@ def test_user_get_teams(): def test_user_get_scoreboard(): - """Can a registered user can load /scoreboard""" + """Can a registered user load /scoreboard""" app = create_ctfd() with app.app_context(): register_user(app) @@ -94,7 +94,7 @@ def test_user_get_scoreboard(): def test_user_get_scores(): - """Can a registered user can load /scores""" + """Can a registered user load /scores""" app = create_ctfd() with app.app_context(): register_user(app) @@ -104,7 +104,7 @@ def test_user_get_scores(): def test_user_get_topteams(): - """Can a registered user can load /top/10""" + """Can a registered user load /top/10""" app = create_ctfd() with app.app_context(): register_user(app) @@ -114,7 +114,7 @@ def test_user_get_topteams(): def test_user_get_challenges(): - """Can a registered user can load /challenges""" + """Can a registered user load /challenges""" app = create_ctfd() with app.app_context(): register_user(app) @@ -124,7 +124,7 @@ def test_user_get_challenges(): def test_user_get_chals(): - """Can a registered user can load /chals""" + """Can a registered user load /chals""" app = create_ctfd() with app.app_context(): register_user(app) @@ -134,7 +134,7 @@ def test_user_get_chals(): def test_user_get_solves_per_chal(): - """Can a registered user can load /chals/solves""" + """Can a registered user load /chals/solves""" app = create_ctfd() with app.app_context(): register_user(app) @@ -144,7 +144,7 @@ def test_user_get_solves_per_chal(): def test_user_get_solves(): - """Can a registered user can load /solves""" + """Can a registered user load /solves""" app = create_ctfd() with app.app_context(): register_user(app) @@ -154,7 +154,7 @@ def test_user_get_solves(): def test_user_get_team_page(): - """Can a registered user can load their public profile (/team/2)""" + """Can a registered user load their public profile (/team/2)""" app = create_ctfd() with app.app_context(): register_user(app) @@ -164,7 +164,7 @@ def test_user_get_team_page(): def test_user_get_profile(): - """Can a registered user can load their private profile (/profile)""" + """Can a registered user load their private profile (/profile)""" app = create_ctfd() with app.app_context(): register_user(app)