General fixes and PEP8 enforcement (#258)

* Fixing index page links when you deploy on a subdirectory

* Updating travis for pep8

* autopep8 with just formatting changes
This commit is contained in:
Kevin Chung
2017-05-12 00:34:20 -04:00
committed by GitHub
parent b4d0d1ecab
commit e16d3a0b6e
21 changed files with 152 additions and 147 deletions

View File

@@ -5,4 +5,5 @@ python:
install:
- pip install -r development.txt
script:
- pep8 --ignore E501,E712 CTFd/ tests/
- nosetests

View File

@@ -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! /*\\")

View File

@@ -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)

View File

@@ -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():

View File

@@ -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/<int:keyid>', methods=['POST', 'GET'])
@admins_only
@@ -53,7 +55,6 @@ def admin_keys_view(keyid):
return '1'
@admin_keys.route('/admin/keys/<int:keyid>/delete', methods=['POST'])
@admins_only
def admin_delete_keys(keyid):

View File

@@ -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():

View File

@@ -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():

View File

@@ -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/<graph_type>')
@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/<int:page>', 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,11 +104,11 @@ 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)

View File

@@ -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/<int:page>')
@admins_only
@@ -225,6 +226,7 @@ def delete_wrong_key(keyid):
db.session.close()
return '1'
@admin_teams.route('/admin/awards/<int:award_id>/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/<int:teamid>/awards', methods=['GET'])
@admins_only
def admin_awards(teamid):

View File

@@ -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)

View File

@@ -14,6 +14,7 @@ from CTFd import utils
challenges = Blueprint('challenges', __name__)
@challenges.route('/hints/<int:hintid>', 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'

View File

@@ -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.

View File

@@ -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,9 +21,10 @@ 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:

View File

@@ -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
}

View File

@@ -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({

View File

@@ -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,20 +789,20 @@ 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)

View File

@@ -45,7 +45,7 @@ def setup():
# Index page
page = Pages('index', """<div class="container main-container">
<img class="logo" src="{0}/static/original/img/logo.png" />
<img class="logo" src="static/original/img/logo.png" />
<h3 class="text-center">
<p>A cool CTF platform from <a href="https://ctfd.io">ctfd.io</a></p>
<p>Follow us on social media:</p>
@@ -55,7 +55,7 @@ def setup():
</h3>
<br>
<h4 class="text-center">
<a href="{0}/admin">Click here</a> to login and setup your CTF
<a href="admin">Click here</a> to login and setup your CTF
</h4>
</div>""".format(request.script_root))

View File

@@ -3,3 +3,4 @@ coverage>=4.1
mock>=2.0.0
nose>=1.3.7
rednose>=1.1.1
pep8==1.7.0

View File

@@ -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)

View File

@@ -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)