mirror of https://github.com/JohnHammond/CTFd.git
1.2.0 (#627)
* Changing to a new plugin oriented challenge type plugin and fixing extra width on admin chal description * Add window.challenge.submit, renderSubmissionResponse, and csrf_nonce * Update admin side renderer calls * Updating to Flask 1.0 and adding files for flask run * Adding a preliminary case-insensitive key * Adding case insensitive keys * Adding CTF Logo * Reducing the amount of team information shown on the main page * Add better base64 helpers * Switch from button to badge * Rudimentary solve checking from admin panel * Refine admin chals solves view & fix PEP8 * Compare base64 encoded data with bytestring * Removing need to urlencode/urldecode in base64 wrappers * Adding decorator documentation * Randomly order tests & add test for case_insensitive flags * Add regex flag case_insensitive test * Add tests for /admin/chal/1/solves and ctf_logoselenium-screenshot-testing
parent
9c812ad52e
commit
36c83b59bc
|
@ -15,6 +15,6 @@ before_script:
|
|||
- psql -c 'create database ctfd;' -U postgres
|
||||
script:
|
||||
- pep8 --ignore E501,E712 CTFd/ tests/
|
||||
- nosetests -d
|
||||
- nosetests -v -d --with-randomly
|
||||
after_success:
|
||||
- codecov
|
||||
|
|
|
@ -147,6 +147,13 @@ def admin_config():
|
|||
utils.set_config("mail_username", None)
|
||||
utils.set_config("mail_password", None)
|
||||
|
||||
if request.files.get('ctf_logo', None):
|
||||
ctf_logo = request.files['ctf_logo']
|
||||
file_id, file_loc = utils.upload_file(ctf_logo, None)
|
||||
utils.set_config("ctf_logo", file_loc)
|
||||
elif request.form.get('ctf_logo') == '':
|
||||
utils.set_config("ctf_logo", None)
|
||||
|
||||
utils.set_config("ctf_name", request.form.get('ctf_name', None))
|
||||
utils.set_config("ctf_theme", request.form.get('ctf_theme', None))
|
||||
utils.set_config('css', request.form.get('css', None))
|
||||
|
@ -176,6 +183,7 @@ def admin_config():
|
|||
cache.clear()
|
||||
|
||||
ctf_name = utils.get_config('ctf_name')
|
||||
ctf_logo = utils.get_config('ctf_logo')
|
||||
ctf_theme = utils.get_config('ctf_theme')
|
||||
hide_scores = utils.get_config('hide_scores')
|
||||
css = utils.get_config('css')
|
||||
|
@ -216,6 +224,7 @@ def admin_config():
|
|||
return render_template(
|
||||
'admin/config.html',
|
||||
ctf_name=ctf_name,
|
||||
ctf_logo=ctf_logo,
|
||||
ctf_theme_config=ctf_theme,
|
||||
css=css,
|
||||
start=start,
|
||||
|
|
|
@ -100,6 +100,19 @@ def admin_chal_detail(chalid):
|
|||
return jsonify(data)
|
||||
|
||||
|
||||
@admin_challenges.route('/admin/chal/<int:chalid>/solves', methods=['GET'])
|
||||
@admins_only
|
||||
def admin_chal_solves(chalid):
|
||||
response = {'teams': []}
|
||||
if utils.hide_scores():
|
||||
return jsonify(response)
|
||||
solves = Solves.query.join(Teams, Solves.teamid == Teams.id).filter(Solves.chalid == chalid).order_by(
|
||||
Solves.date.asc())
|
||||
for solve in solves:
|
||||
response['teams'].append({'id': solve.team.id, 'name': solve.team.name, 'date': solve.date})
|
||||
return jsonify(response)
|
||||
|
||||
|
||||
@admin_challenges.route('/admin/tags/<int:chalid>', methods=['GET', 'POST'])
|
||||
@admins_only
|
||||
def admin_tags(chalid):
|
||||
|
|
|
@ -57,6 +57,10 @@ def admin_create_team():
|
|||
affiliation = request.form.get('affiliation', None)
|
||||
country = request.form.get('country', None)
|
||||
|
||||
admin_user = True if request.form.get('admin', None) == 'on' else False
|
||||
verified = True if request.form.get('verified', None) == 'on' else False
|
||||
hidden = True if request.form.get('hidden', None) == 'on' else False
|
||||
|
||||
errors = []
|
||||
|
||||
if not name:
|
||||
|
@ -92,6 +96,10 @@ def admin_create_team():
|
|||
team.affiliation = affiliation
|
||||
team.country = country
|
||||
|
||||
team.admin = admin_user
|
||||
team.verified = verified
|
||||
team.hidden = hidden
|
||||
|
||||
db.session.add(team)
|
||||
db.session.commit()
|
||||
db.session.close()
|
||||
|
@ -119,24 +127,6 @@ def admin_team(teamid):
|
|||
return render_template('admin/team.html', solves=solves, team=user, addrs=addrs, score=score, missing=missing,
|
||||
place=place, wrong_keys=wrong_keys, awards=awards)
|
||||
elif request.method == 'POST':
|
||||
admin_user = request.form.get('admin', None)
|
||||
if admin_user:
|
||||
admin_user = True if admin_user == 'true' else False
|
||||
user.admin = admin_user
|
||||
# Set user.banned to hide admins from scoreboard
|
||||
user.banned = admin_user
|
||||
db.session.commit()
|
||||
db.session.close()
|
||||
return jsonify({'data': ['success']})
|
||||
|
||||
verified = request.form.get('verified', None)
|
||||
if verified:
|
||||
verified = True if verified == 'true' else False
|
||||
user.verified = verified
|
||||
db.session.commit()
|
||||
db.session.close()
|
||||
return jsonify({'data': ['success']})
|
||||
|
||||
name = request.form.get('name', None)
|
||||
password = request.form.get('password', None)
|
||||
email = request.form.get('email', None)
|
||||
|
@ -144,6 +134,10 @@ def admin_team(teamid):
|
|||
affiliation = request.form.get('affiliation', None)
|
||||
country = request.form.get('country', None)
|
||||
|
||||
admin_user = True if request.form.get('admin', None) == 'on' else False
|
||||
verified = True if request.form.get('verified', None) == 'on' else False
|
||||
hidden = True if request.form.get('hidden', None) == 'on' else False
|
||||
|
||||
errors = []
|
||||
|
||||
if email:
|
||||
|
@ -177,6 +171,9 @@ def admin_team(teamid):
|
|||
user.website = website
|
||||
user.affiliation = affiliation
|
||||
user.country = country
|
||||
user.admin = admin_user
|
||||
user.verified = verified
|
||||
user.banned = hidden
|
||||
db.session.commit()
|
||||
db.session.close()
|
||||
return jsonify({'data': ['success']})
|
||||
|
|
|
@ -29,7 +29,7 @@ def confirm_user(data=None):
|
|||
if data and request.method == "GET":
|
||||
try:
|
||||
s = TimedSerializer(app.config['SECRET_KEY'])
|
||||
email = s.loads(utils.base64decode(data, urldecode=True), max_age=1800)
|
||||
email = s.loads(utils.base64decode(data), max_age=1800)
|
||||
except BadTimeSignature:
|
||||
return render_template('confirm.html', errors=['Your confirmation link has expired'])
|
||||
except (BadSignature, TypeError, base64.binascii.Error):
|
||||
|
@ -86,7 +86,7 @@ def reset_password(data=None):
|
|||
if data is not None:
|
||||
try:
|
||||
s = TimedSerializer(app.config['SECRET_KEY'])
|
||||
name = s.loads(utils.base64decode(data, urldecode=True), max_age=1800)
|
||||
name = s.loads(utils.base64decode(data), max_age=1800)
|
||||
except BadTimeSignature:
|
||||
return render_template('reset_password.html', errors=['Your link has expired'])
|
||||
except (BadSignature, TypeError, base64.binascii.Error):
|
||||
|
|
|
@ -146,7 +146,7 @@ class CTFdStandardChallenge(BaseChallenge):
|
|||
provided_key = request.form['key'].strip()
|
||||
chal_keys = Keys.query.filter_by(chal=chal.id).all()
|
||||
for chal_key in chal_keys:
|
||||
if get_key_class(chal_key.type).compare(chal_key.flag, provided_key):
|
||||
if get_key_class(chal_key.type).compare(chal_key, provided_key):
|
||||
return True, 'Correct'
|
||||
return False, 'Incorrect'
|
||||
|
||||
|
|
|
@ -1,28 +1,22 @@
|
|||
// Markdown Preview
|
||||
$('#desc-edit').on('shown.bs.tab', function (event) {
|
||||
var md = window.markdownit({
|
||||
html: true,
|
||||
});
|
||||
if (event.target.hash == '#desc-preview'){
|
||||
if (event.target.hash == '#desc-preview') {
|
||||
var editor_value = $('#desc-editor').val();
|
||||
$(event.target.hash).html(
|
||||
md.render(editor_value)
|
||||
window.challenge.render(editor_value)
|
||||
);
|
||||
}
|
||||
});
|
||||
$('#new-desc-edit').on('shown.bs.tab', function (event) {
|
||||
var md = window.markdownit({
|
||||
html: true,
|
||||
});
|
||||
if (event.target.hash == '#new-desc-preview'){
|
||||
if (event.target.hash == '#new-desc-preview') {
|
||||
var editor_value = $('#new-desc-editor').val();
|
||||
$(event.target.hash).html(
|
||||
md.render(editor_value)
|
||||
window.challenge.render(editor_value)
|
||||
);
|
||||
}
|
||||
});
|
||||
$("#solve-attempts-checkbox").change(function() {
|
||||
if(this.checked) {
|
||||
$("#solve-attempts-checkbox").change(function () {
|
||||
if (this.checked) {
|
||||
$('#solve-attempts-input').show();
|
||||
} else {
|
||||
$('#solve-attempts-input').hide();
|
||||
|
@ -30,6 +24,6 @@ $("#solve-attempts-checkbox").change(function() {
|
|||
}
|
||||
});
|
||||
|
||||
$(document).ready(function(){
|
||||
$(document).ready(function () {
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
});
|
||||
|
|
|
@ -1,25 +1,35 @@
|
|||
$('#submit-key').unbind('click');
|
||||
$('#submit-key').click(function (e) {
|
||||
e.preventDefault();
|
||||
submitkey($('#chal-id').val(), $('#answer-input').val(), $('#nonce').val())
|
||||
window.challenge.renderer = new markdownit({
|
||||
html: true,
|
||||
});
|
||||
|
||||
$("#answer-input").keyup(function(event){
|
||||
if(event.keyCode == 13){
|
||||
$("#submit-key").click();
|
||||
}
|
||||
});
|
||||
window.challenge.preRender = function(){
|
||||
|
||||
$(".input-field").bind({
|
||||
focus: function() {
|
||||
$(this).parent().addClass('input--filled' );
|
||||
$label = $(this).siblings(".input-label");
|
||||
},
|
||||
blur: function() {
|
||||
if ($(this).val() === '') {
|
||||
$(this).parent().removeClass('input--filled' );
|
||||
$label = $(this).siblings(".input-label");
|
||||
$label.removeClass('input--hide' );
|
||||
}
|
||||
};
|
||||
|
||||
window.challenge.render = function(markdown){
|
||||
return window.challenge.renderer.render(markdown);
|
||||
};
|
||||
|
||||
|
||||
window.challenge.postRender = function(){
|
||||
|
||||
};
|
||||
|
||||
|
||||
window.challenge.submit = function(cb, preview){
|
||||
var chal_id = $('#chal-id').val();
|
||||
var answer = $('#answer-input').val();
|
||||
var nonce = $('#nonce').val();
|
||||
|
||||
var url = "/chal/";
|
||||
if (preview) {
|
||||
url = "/admin/chal/";
|
||||
}
|
||||
});
|
||||
|
||||
$.post(script_root + url + chal_id, {
|
||||
key: answer,
|
||||
nonce: nonce
|
||||
}, function (data) {
|
||||
cb(data);
|
||||
});
|
||||
};
|
|
@ -18,24 +18,18 @@ $('#limit_max_attempts').change(function() {
|
|||
|
||||
// Markdown Preview
|
||||
$('#desc-edit').on('shown.bs.tab', function (event) {
|
||||
var md = window.markdownit({
|
||||
html: true,
|
||||
});
|
||||
if (event.target.hash == '#desc-preview'){
|
||||
if (event.target.hash == '#desc-preview') {
|
||||
var editor_value = $('#desc-editor').val();
|
||||
$(event.target.hash).html(
|
||||
md.render(editor_value)
|
||||
window.challenge.render(editor_value)
|
||||
);
|
||||
}
|
||||
});
|
||||
$('#new-desc-edit').on('shown.bs.tab', function (event) {
|
||||
var md = window.markdownit({
|
||||
html: true,
|
||||
});
|
||||
if (event.target.hash == '#new-desc-preview'){
|
||||
if (event.target.hash == '#new-desc-preview') {
|
||||
var editor_value = $('#new-desc-editor').val();
|
||||
$(event.target.hash).html(
|
||||
md.render(editor_value)
|
||||
window.challenge.render(editor_value)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -24,12 +24,20 @@ class CTFdStaticKey(BaseKey):
|
|||
}
|
||||
|
||||
@staticmethod
|
||||
def compare(saved, provided):
|
||||
def compare(chal_key_obj, provided):
|
||||
saved = chal_key_obj.flag
|
||||
data = chal_key_obj.data
|
||||
|
||||
if len(saved) != len(provided):
|
||||
return False
|
||||
result = 0
|
||||
for x, y in zip(saved, provided):
|
||||
result |= ord(x) ^ ord(y)
|
||||
|
||||
if data == "case_insensitive":
|
||||
for x, y in zip(saved.lower(), provided.lower()):
|
||||
result |= ord(x) ^ ord(y)
|
||||
else:
|
||||
for x, y in zip(saved, provided):
|
||||
result |= ord(x) ^ ord(y)
|
||||
return result == 0
|
||||
|
||||
|
||||
|
@ -42,8 +50,15 @@ class CTFdRegexKey(BaseKey):
|
|||
}
|
||||
|
||||
@staticmethod
|
||||
def compare(saved, provided):
|
||||
res = re.match(saved, provided, re.IGNORECASE)
|
||||
def compare(chal_key_obj, provided):
|
||||
saved = chal_key_obj.flag
|
||||
data = chal_key_obj.data
|
||||
|
||||
if data == "case_insensitive":
|
||||
res = re.match(saved, provided, re.IGNORECASE)
|
||||
else:
|
||||
res = re.match(saved, provided)
|
||||
|
||||
return res and res.group() == provided
|
||||
|
||||
|
||||
|
|
|
@ -1,2 +1,6 @@
|
|||
<label for="create-key-regex" class="control-label">Enter Regex Key Data</label>
|
||||
<input type="text" id="create-key-regex" class="form-control" name="key" value="{{key}}" placeholder="Enter regex key data">
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" id="keydata" name="keydata" value="case_insensitive">
|
||||
<label class="form-check-label" for="keydata">Case Insensitive</label>
|
||||
</div>
|
|
@ -1,28 +1,33 @@
|
|||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header text-center">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h3 class="text-center">Regex Key</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form method="POST" action="{{ script_root }}/admin/keys/{{id}}">
|
||||
<input type="text" id="key-data" class="form-control" name="key" value="{{key}}" placeholder="Enter regex key data">
|
||||
<input type="hidden" id="key-type" name="key_type" value="regex">
|
||||
<input type="hidden" id="key-id">
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<input type="hidden" value="{{ nonce }}" name="nonce" id="nonce">
|
||||
<button id="submit-keys" class="btn btn-success float-right">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header text-center">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h3 class="text-center">Regex Key</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form method="POST" action="{{ script_root }}/admin/keys/{{id}}">
|
||||
<input type="text" id="key-data" class="form-control" name="key" value="{{key}}" placeholder="Enter regex key data">
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" id="keydata" name="keydata" value="case_insensitive"
|
||||
{% if data %}checked{% endif %}>
|
||||
<label class="form-check-label" for="keydata">Case Insensitive</label>
|
||||
</div>
|
||||
<input type="hidden" id="key-type" name="key_type" value="regex">
|
||||
<input type="hidden" id="key-id">
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<input type="hidden" value="{{ nonce }}" name="nonce" id="nonce">
|
||||
<button id="submit-keys" class="btn btn-success float-right">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,2 +1,6 @@
|
|||
<label for="create-key-static" class="control-label">Enter Static Key Data</label>
|
||||
<input type="text" id="create-key-static" class="form-control" name="key" value="{{key}}" placeholder="Enter static key data">
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" id="keydata" name="keydata" value="case_insensitive">
|
||||
<label class="form-check-label" for="keydata">Case Insensitive</label>
|
||||
</div>
|
|
@ -1,28 +1,34 @@
|
|||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header text-center">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h3 class="text-center">Static Key</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form method="POST" action="{{ script_root }}/admin/keys/{{id}}">
|
||||
<input type="text" id="key-data" class="form-control" name="key" value="{{key}}" placeholder="Enter static key data">
|
||||
<input type="hidden" id="key-type" name="key_type" value="static">
|
||||
<input type="hidden" id="key-id">
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<input type="hidden" value="{{ nonce }}" name="nonce" id="nonce">
|
||||
<button id="submit-keys" class="btn btn-success float-right">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-content">
|
||||
<div class="modal-header text-center">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h3 class="text-center">Static Key</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form method="POST" action="{{ script_root }}/admin/keys/{{ id }}">
|
||||
<input type="text" id="key-data" class="form-control" name="key" value="{{ key }}"
|
||||
placeholder="Enter static key data">
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" id="keydata" name="keydata" value="case_insensitive"
|
||||
{% if data %}checked{% endif %}>
|
||||
<label class="form-check-label" for="keydata">Case Insensitive</label>
|
||||
</div>
|
||||
<input type="hidden" id="key-type" name="key_type" value="static">
|
||||
<input type="hidden" id="key-id">
|
||||
<hr>
|
||||
<div class="form-group">
|
||||
<input type="hidden" value="{{ nonce }}" name="nonce" id="nonce">
|
||||
<button id="submit-keys" class="btn btn-success float-right">Update</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -4,8 +4,6 @@ pre {
|
|||
}
|
||||
|
||||
.chal-desc {
|
||||
padding-left: 30px;
|
||||
padding-right: 30px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
$.ajaxSetup({ cache: false });
|
||||
|
||||
window.challenge = new Object();
|
||||
|
||||
function load_chal_template(challenge){
|
||||
$.get(script_root + challenge.templates.create, function(template_data){
|
||||
var template = nunjucks.compile(template_data);
|
||||
$("#create-chal-entry-div").html(template.render({'nonce':nonce, 'script_root':script_root}));
|
||||
$.getScript(script_root + challenge.scripts.create, function(){
|
||||
console.log('loaded');
|
||||
$.getScript(script_root + challenge.scripts.modal, function () {
|
||||
console.log('loaded renderer');
|
||||
$.get(script_root + challenge.templates.create, function (template_data) {
|
||||
var template = nunjucks.compile(template_data);
|
||||
$("#create-chal-entry-div").html(template.render({'nonce': nonce, 'script_root': script_root}));
|
||||
$.getScript(script_root + challenge.scripts.create, function () {
|
||||
console.log('loaded');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
var challenges = {};
|
||||
|
||||
window.challenge = new Object();
|
||||
|
||||
function load_chal_template(id, success_cb){
|
||||
var obj = $.grep(challenges['game'], function (e) {
|
||||
return e.id == id;
|
||||
})[0];
|
||||
$.get(script_root + "/admin/chal/" + id, function (challenge_data) {
|
||||
$.get(script_root + obj.type_data.templates.update, function (template_data) {
|
||||
var template = nunjucks.compile(template_data);
|
||||
$.get(script_root + "/admin/chal/" + id, function (obj) {
|
||||
$.getScript(script_root + obj.type_data.scripts.modal, function () {
|
||||
console.log('loaded renderer');
|
||||
$.get(script_root + obj.type_data.templates.update, function (template_data) {
|
||||
var template = nunjucks.compile(template_data);
|
||||
|
||||
challenge_data['nonce'] = $('#nonce').val();
|
||||
challenge_data['script_root'] = script_root;
|
||||
obj['nonce'] = $('#nonce').val();
|
||||
obj['script_root'] = script_root;
|
||||
|
||||
$("#update-modals-entry-div").html(template.render(challenge_data));
|
||||
$("#update-modals-entry-div").html(template.render(obj));
|
||||
|
||||
$.ajax({
|
||||
url: script_root + obj.type_data.scripts.update,
|
||||
dataType: "script",
|
||||
success: success_cb,
|
||||
cache: false,
|
||||
$.ajax({
|
||||
url: script_root + obj.type_data.scripts.update,
|
||||
dataType: "script",
|
||||
success: success_cb,
|
||||
cache: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -30,55 +30,63 @@ function load_challenge_preview(id){
|
|||
|
||||
function render_challenge_preview(chal_id){
|
||||
var preview_window = $('#challenge-preview');
|
||||
var md = window.markdownit({
|
||||
html: true,
|
||||
});
|
||||
$.get(script_root + "/admin/chal/" + chal_id, function(challenge_data){
|
||||
$.get(script_root + challenge_data.type_data.templates.modal, function (template_data) {
|
||||
preview_window.empty();
|
||||
var template = nunjucks.compile(template_data);
|
||||
$.get(script_root + "/admin/chal/" + chal_id, function(obj){
|
||||
$.getScript(script_root + obj.type_data.scripts.modal, function () {
|
||||
console.log('loaded renderer');
|
||||
|
||||
challenge_data['description'] = md.render(challenge_data['description']);
|
||||
challenge_data['script_root'] = script_root;
|
||||
$.get(script_root + obj.type_data.templates.modal, function (template_data) {
|
||||
var template = nunjucks.compile(template_data);
|
||||
|
||||
var challenge = template.render(challenge_data);
|
||||
window.challenge.preRender()
|
||||
|
||||
preview_window.append(challenge);
|
||||
obj['description'] = window.challenge.render(obj['description']);
|
||||
obj['script_root'] = script_root;
|
||||
|
||||
var challenge = template.render(obj);
|
||||
|
||||
preview_window.html(challenge);
|
||||
|
||||
$('#submit-key').click(function (e) {
|
||||
e.preventDefault();
|
||||
$('#submit-key').addClass("disabled-button");
|
||||
$('#submit-key').prop('disabled', true);
|
||||
|
||||
window.challenge.submit(function (data) {
|
||||
renderSubmissionResponse(data)
|
||||
}, preview=true);
|
||||
});
|
||||
|
||||
$("#answer-input").keyup(function (event) {
|
||||
if (event.keyCode == 13) {
|
||||
$("#submit-key").click();
|
||||
}
|
||||
});
|
||||
|
||||
window.challenge.postRender()
|
||||
|
||||
$.getScript(script_root + challenge_data.type_data.scripts.modal, function () {
|
||||
preview_window.modal();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function loadchals(cb){
|
||||
$.post(script_root + "/admin/chals", {
|
||||
'nonce': $('#nonce').val()
|
||||
}, function (data) {
|
||||
var categories = [];
|
||||
challenges = $.parseJSON(JSON.stringify(data));
|
||||
|
||||
for (var i = challenges['game'].length - 1; i >= 0; i--) {
|
||||
if ($.inArray(challenges['game'][i].category, categories) == -1) {
|
||||
categories.push(challenges['game'][i].category)
|
||||
}
|
||||
}
|
||||
|
||||
if (cb) {
|
||||
cb();
|
||||
function loadsolves(id) {
|
||||
$.get(script_root + '/admin/chal/' + id + '/solves', function (data) {
|
||||
var teams = data['teams'];
|
||||
var box = $('#challenge-solves-body');
|
||||
var modal = $('#challenge-solves-modal')
|
||||
box.empty();
|
||||
for (var i = 0; i < teams.length; i++) {
|
||||
var id = teams[i].id;
|
||||
var name = teams[i].name;
|
||||
var date = moment(teams[i].date).local().format('MMMM Do, h:mm:ss A');
|
||||
box.append('<tr><td><a href="team/{0}">{1}</td><td><small>{2}</small></td></tr>'.format(id, htmlentities(name), date));
|
||||
}
|
||||
modal.modal();
|
||||
});
|
||||
}
|
||||
|
||||
loadchals(function(){
|
||||
$('.edit-challenge').click(function (e) {
|
||||
var id = $(this).attr('chal-id');
|
||||
load_chal_template(id, function () {
|
||||
openchal(id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function loadhint(hintid) {
|
||||
var md = window.markdownit({
|
||||
|
@ -107,64 +115,62 @@ function loadhint(hintid) {
|
|||
});
|
||||
}
|
||||
|
||||
function submitkey(chal, key, nonce){
|
||||
$.post(script_root + "/admin/chal/" + chal, {
|
||||
key: key,
|
||||
nonce: nonce
|
||||
}, function (data) {
|
||||
console.log(data);
|
||||
var result = $.parseJSON(JSON.stringify(data));
|
||||
function renderSubmissionResponse(data, cb) {
|
||||
var result = $.parseJSON(JSON.stringify(data));
|
||||
|
||||
var result_message = $('#result-message');
|
||||
var result_notification = $('#result-notification');
|
||||
var answer_input = $("#answer-input");
|
||||
result_notification.removeClass();
|
||||
result_message.text(result.message);
|
||||
var result_message = $('#result-message');
|
||||
var result_notification = $('#result-notification');
|
||||
var answer_input = $("#answer-input");
|
||||
result_notification.removeClass();
|
||||
result_message.text(result.message);
|
||||
|
||||
if (result.status == -1) {
|
||||
window.location = script_root + "/login?next=" + script_root + window.location.pathname + window.location.hash
|
||||
return
|
||||
}
|
||||
else if (result.status == 0) { // Incorrect key
|
||||
result_notification.addClass('alert alert-danger alert-dismissable text-center');
|
||||
result_notification.slideDown();
|
||||
|
||||
answer_input.removeClass("correct");
|
||||
answer_input.addClass("wrong");
|
||||
setTimeout(function () {
|
||||
answer_input.removeClass("wrong");
|
||||
}, 3000);
|
||||
}
|
||||
else if (result.status == 1) { // Challenge Solved
|
||||
result_notification.addClass('alert alert-success alert-dismissable text-center');
|
||||
result_notification.slideDown();
|
||||
|
||||
answer_input.val("");
|
||||
answer_input.removeClass("wrong");
|
||||
answer_input.addClass("correct");
|
||||
}
|
||||
else if (result.status == 2) { // Challenge already solved
|
||||
result_notification.addClass('alert alert-info alert-dismissable text-center');
|
||||
result_notification.slideDown();
|
||||
|
||||
answer_input.addClass("correct");
|
||||
}
|
||||
else if (result.status == 3) { // Keys per minute too high
|
||||
result_notification.addClass('alert alert-warning alert-dismissable text-center');
|
||||
result_notification.slideDown();
|
||||
|
||||
answer_input.addClass("too-fast");
|
||||
setTimeout(function () {
|
||||
answer_input.removeClass("too-fast");
|
||||
}, 3000);
|
||||
}
|
||||
if (result.status == -1) {
|
||||
window.location = script_root + "/login?next=" + script_root + window.location.pathname + window.location.hash
|
||||
return
|
||||
}
|
||||
else if (result.status == 0) { // Incorrect key
|
||||
result_notification.addClass('alert alert-danger alert-dismissable text-center');
|
||||
result_notification.slideDown();
|
||||
|
||||
answer_input.removeClass("correct");
|
||||
answer_input.addClass("wrong");
|
||||
setTimeout(function () {
|
||||
$('.alert').slideUp();
|
||||
$('#submit-key').removeClass("disabled-button");
|
||||
$('#submit-key').prop('disabled', false);
|
||||
answer_input.removeClass("wrong");
|
||||
}, 3000);
|
||||
});
|
||||
}
|
||||
else if (result.status == 1) { // Challenge Solved
|
||||
result_notification.addClass('alert alert-success alert-dismissable text-center');
|
||||
result_notification.slideDown();
|
||||
|
||||
answer_input.val("");
|
||||
answer_input.removeClass("wrong");
|
||||
answer_input.addClass("correct");
|
||||
}
|
||||
else if (result.status == 2) { // Challenge already solved
|
||||
result_notification.addClass('alert alert-info alert-dismissable text-center');
|
||||
result_notification.slideDown();
|
||||
|
||||
answer_input.addClass("correct");
|
||||
}
|
||||
else if (result.status == 3) { // Keys per minute too high
|
||||
result_notification.addClass('alert alert-warning alert-dismissable text-center');
|
||||
result_notification.slideDown();
|
||||
|
||||
answer_input.addClass("too-fast");
|
||||
setTimeout(function () {
|
||||
answer_input.removeClass("too-fast");
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
$('.alert').slideUp();
|
||||
$('#submit-key').removeClass("disabled-button");
|
||||
$('#submit-key').prop('disabled', false);
|
||||
}, 3000);
|
||||
|
||||
if (cb) {
|
||||
cb(result);
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
|
@ -194,9 +200,24 @@ $(document).ready(function () {
|
|||
});
|
||||
});
|
||||
|
||||
$('.edit-challenge').click(function (e) {
|
||||
var id = $(this).attr('chal-id');
|
||||
load_chal_template(id, function () {
|
||||
openchal(id);
|
||||
});
|
||||
});
|
||||
|
||||
$('.preview-challenge').click(function (e) {
|
||||
var chal_id = $(this).attr('chal-id');
|
||||
|
||||
load_challenge_preview(chal_id);
|
||||
});
|
||||
|
||||
$('.stats-challenge').click(function (e) {
|
||||
var chal_id = $(this).attr('chal-id');
|
||||
var title = $(this).attr('title') || $(this).attr('data-original-title');
|
||||
$('#challenge-solves-title').text(title);
|
||||
|
||||
loadsolves(chal_id);
|
||||
});
|
||||
});
|
|
@ -17,13 +17,9 @@ function load_edit_key_modal(key_id, key_type_name) {
|
|||
}
|
||||
|
||||
|
||||
function create_key(chal, key, key_type) {
|
||||
$.post(script_root + "/admin/keys", {
|
||||
chal: chal,
|
||||
key: key,
|
||||
key_type: key_type,
|
||||
nonce: $('#nonce').val()
|
||||
}, function (data) {
|
||||
function create_key(chal, chal_data) {
|
||||
chal_data.push({name: 'nonce', value: $('#nonce').val()});
|
||||
$.post(script_root + "/admin/keys", chal_data, function (data) {
|
||||
if (data == "1"){
|
||||
loadkeys(chal);
|
||||
$("#create-keys").modal('toggle');
|
||||
|
@ -73,17 +69,12 @@ function deletekey(key_id){
|
|||
}
|
||||
|
||||
function updatekey(){
|
||||
var edit_key_modal = $('#edit-keys form').serializeArray();
|
||||
|
||||
var key_id = $('#key-id').val();
|
||||
var chal = $("#update-keys").attr('chal-id');
|
||||
var key_data = $('#key-data').val();
|
||||
var key_type = $('#key-type').val();
|
||||
var nonce = $('#nonce').val();
|
||||
$.post(script_root + '/admin/keys/'+key_id, {
|
||||
'chal':chal,
|
||||
'key':key_data,
|
||||
'key_type': key_type,
|
||||
'nonce': nonce
|
||||
}, function(data){
|
||||
|
||||
$.post(script_root + '/admin/keys/'+key_id, edit_key_modal, function(data){
|
||||
if (data == "1") {
|
||||
loadkeys(chal);
|
||||
$('#edit-keys').modal('toggle');
|
||||
|
@ -132,9 +123,13 @@ $(document).ready(function () {
|
|||
|
||||
$('#create-keys-submit').click(function (e) {
|
||||
e.preventDefault();
|
||||
var chal_data = $('#create-keys-entry-div :input').serializeArray();
|
||||
|
||||
var chalid = $("#update-keys").attr('chal-id');
|
||||
var key_data = $('#create-keys').find('input[name=key]').val();
|
||||
chal_data.push({name: 'chal', value: chalid});
|
||||
|
||||
var key_type = $('#create-keys-select').val();
|
||||
create_key(chalid, key_data, key_type);
|
||||
chal_data.push({name: 'key_type', value: key_type});
|
||||
create_key(chalid, chal_data);
|
||||
});
|
||||
});
|
|
@ -19,6 +19,7 @@
|
|||
<script src="{{ request.script_root }}/themes/admin/static/js/vendor/nunjucks.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
var script_root = "{{ request.script_root }}";
|
||||
var csrf_nonce = "{{ nonce }}";
|
||||
</script>
|
||||
{% block stylesheets %} {% endblock %}
|
||||
</head>
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
{% block content %}
|
||||
{% include "admin/modals/challenges/challenges.html" %}
|
||||
{% include "admin/modals/challenges/challenges-solves.html" %}
|
||||
|
||||
{% include "admin/modals/tags/tags.html" %}
|
||||
{% include "admin/modals/files/files.html" %}
|
||||
|
@ -57,9 +58,9 @@
|
|||
<td class="d-none d-md-table-cell d-lg-table-cell">{{ challenge.type }}</td>
|
||||
<td class="d-none d-md-table-cell d-lg-table-cell text-center">
|
||||
{% if challenge.hidden %}
|
||||
<button class="btn-sm btn-danger" type="submit" disabled>Hidden</button>
|
||||
<span class="badge badge-danger">hidden</span>
|
||||
{% else %}
|
||||
<button class="btn-sm btn-success" type="submit" disabled>Visible</button>
|
||||
<span class="badge badge-success">visible</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
|
@ -75,6 +76,13 @@
|
|||
<i class="btn-fa fas fa-file-alt" aria-hidden="true" chal-id="{{ challenge.id }}"></i>
|
||||
</span>
|
||||
|
||||
<span class="stats-challenge" data-toggle="tooltip"
|
||||
data-placement="top" chal-id="{{ challenge.id }}"
|
||||
title="{{ challenge.name }} solves">
|
||||
<i class="btn-fa fas fa-chart-bar" aria-hidden="true"
|
||||
chal-id="{{ challenge.id }}"></i>
|
||||
</span>
|
||||
|
||||
<span> </span>
|
||||
|
||||
<span class="edit-keys" data-toggle="tooltip" data-placement="top"
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
</ul>
|
||||
</div>
|
||||
<div class="col-md-9">
|
||||
<form method="POST" autocomplete="off" class="w-100">
|
||||
<form method="POST" enctype="multipart/form-data" autocomplete="off" class="w-100">
|
||||
{% for error in errors %}
|
||||
<div class="alert alert-danger alert-dismissable" role="alert">
|
||||
<span class="sr-only">Error:</span>
|
||||
|
@ -43,7 +43,31 @@
|
|||
<div role="tabpanel" class="tab-pane active" id="appearance-section">
|
||||
<div class="form-group">
|
||||
<label for="ctf_name">CTF Name:</label>
|
||||
<input class="form-control" id='ctf_name' name='ctf_name' type='text' placeholder="CTF Name" {% if ctf_name is defined and ctf_name != None %}value="{{ ctf_name }}"{% endif %}>
|
||||
<input class="form-control" id='ctf_name' name='ctf_name' type='text' placeholder="CTF Name"
|
||||
{% if ctf_name is defined and ctf_name != None %}value="{{ ctf_name }}"{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="ctf_logo">CTF Logo:</label>
|
||||
|
||||
{% if ctf_logo %}
|
||||
<div class="d-block py-3">
|
||||
<img id="ctf_logo_preview" class="img-responsive ctf_logo" src="{{ request.script_root }}/files/{{ ctf_logo }}"
|
||||
height="25">
|
||||
<button type="button" class="btn-sm btn-danger float-right" onclick="
|
||||
$('#ctf_logo').attr('type', 'text');
|
||||
$('#ctf_logo').attr('placeholder', 'Logo will be removed on next update');
|
||||
$('#ctf_logo').attr('value', '');
|
||||
$('#ctf_logo').attr('readonly', 'true');
|
||||
$('#ctf_logo_preview').css('visibility', 'hidden');
|
||||
">
|
||||
Remove Logo
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<input class="form-control" id='ctf_logo' name='ctf_logo' type='file' placeholder="CTF Logo"
|
||||
{% if ctf_logo is defined and ctf_logo != None %}value="{{ ctf_logo }}"{% endif %}>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
<div id="challenge-solves-modal" class="modal fade" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header text-center">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h3 id="challenge-solves-title" class="text-center">Solves</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<table class="table table-striped text-center">
|
||||
<thead>
|
||||
<tr>
|
||||
<td><b>Name</b>
|
||||
</td>
|
||||
<td><b>Date</b>
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="challenge-solves-body">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -1,2 +1,3 @@
|
|||
|
||||
<div id="challenge-preview" class="modal fade" tabindex="-1">
|
||||
</div>
|
|
@ -82,6 +82,20 @@
|
|||
<input type="text" class="form-control" name="country" id="country"
|
||||
placeholder="Enter country">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="admin" id="admin-checkbox">
|
||||
<label class="form-check-label" for="admin-checkbox">Admin</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="verified" id="verified-checkbox">
|
||||
<label class="form-check-label" for="verified-checkbox">Verified</label>
|
||||
</div>
|
||||
<div class="form-check form-check-inline">
|
||||
<input class="form-check-input" type="checkbox" name="hidden" id="hidden-checkbox">
|
||||
<label class="form-check-label" for="hidden-checkbox">Hidden</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="results">
|
||||
|
||||
</div>
|
||||
|
@ -133,16 +147,18 @@
|
|||
</td>
|
||||
<td class="d-none d-md-table-cell d-lg-table-cell text-center"><b>Email</b>
|
||||
</td>
|
||||
<td class="d-none d-md-table-cell d-lg-table-cell text-center"><b>Website</b>
|
||||
<td class="d-none"><b>Website</b>
|
||||
</td>
|
||||
<td class="d-none d-md-table-cell d-lg-table-cell text-center"><b>Affiliation</b>
|
||||
<td class="d-none"><b>Affiliation</b>
|
||||
</td>
|
||||
<td class="d-none d-md-table-cell d-lg-table-cell text-center"><b>Country</b>
|
||||
<td class="d-none"><b>Country</b>
|
||||
</td>
|
||||
<td class="text-center"><b>Admin</b>
|
||||
</td>
|
||||
<td class="text-center"><b>Verified</b>
|
||||
</td>
|
||||
<td class="text-center"><b>Hidden</b>
|
||||
</td>
|
||||
<td class="text-center"><b>Settings</b>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -154,7 +170,7 @@
|
|||
<td class="team-name" value="{{ team.name }}"><a href="{{ request.script_root }}/admin/team/{{ team.id }}">{{ team.name | truncate(32) }}</a>
|
||||
</td>
|
||||
<td class="team-email d-none d-md-table-cell d-lg-table-cell" value="{{ team.email }}">{{ team.email | truncate(32) }}</td>
|
||||
<td class="team-website d-none d-md-table-cell d-lg-table-cell text-center">
|
||||
<td class="team-website d-none text-center">
|
||||
{% if team.website %}
|
||||
<a href="{{ team.website }}" target="_blank">
|
||||
<i class="btn-fa fas fa-external-link-alt" data-toggle="tooltip" data-placement="top"
|
||||
|
@ -162,21 +178,26 @@
|
|||
</a>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="team-affiliation d-none d-md-table-cell d-lg-table-cell" value="{{ team.affiliation if team.affiliation is not none }}">
|
||||
<td class="team-affiliation d-none" value="{{ team.affiliation if team.affiliation is not none }}">
|
||||
<span>{% if team.affiliation %}{{ team.affiliation | truncate(20) }}{% endif %}</span>
|
||||
</td>
|
||||
<td class="team-country d-none d-md-table-cell d-lg-table-cell" value="{{ team.country if team.country is not none }}">
|
||||
<td class="team-country d-none" value="{{ team.country if team.country is not none }}">
|
||||
<span>{% if team.country %}{{ team.country }}{% endif %}</span>
|
||||
</td>
|
||||
<td class="team-admin">
|
||||
<div class="center-block checkbox text-center">
|
||||
<input type="checkbox" {% if team.admin %}checked{% endif %}>
|
||||
</div>
|
||||
<td class="team-admin d-none d-md-table-cell d-lg-table-cell text-center" value="{{ team.admin }}">
|
||||
{% if team.admin %}
|
||||
<span class="badge badge-primary">admin</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="team-verified">
|
||||
<div class="center-block checkbox text-center">
|
||||
<input type="checkbox" {% if team.verified %}checked{% endif %}>
|
||||
</div>
|
||||
<td class="team-verified d-none d-md-table-cell d-lg-table-cell text-center" value="{{ team.verified }}">
|
||||
{% if team.verified %}
|
||||
<span class="badge badge-success">verified</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="team-hidden d-none d-md-table-cell d-lg-table-cell text-center" value="{{ team.banned }}">
|
||||
{% if team.banned %}
|
||||
<span class="badge badge-danger">hidden</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-center"><span>
|
||||
<span class="edit-team" data-toggle="tooltip" data-placement="top"
|
||||
|
@ -222,7 +243,7 @@
|
|||
<script>
|
||||
var nonce = "{{ nonce }}";
|
||||
|
||||
function load_update_modal(id, name, email, website, affiliation, country){
|
||||
function load_update_modal(id, name, email, website, affiliation, country, admin, verified, hidden){
|
||||
var modal_form = $('#update-user-modal form');
|
||||
|
||||
modal_form.find('input[name=name]').val(name);
|
||||
|
@ -233,6 +254,10 @@ function load_update_modal(id, name, email, website, affiliation, country){
|
|||
modal_form.find('input[name=country]').val(country);
|
||||
modal_form.find('input[name=password]').val('');
|
||||
|
||||
modal_form.find('input[name=admin]').prop('checked', admin);
|
||||
modal_form.find('input[name=verified]').prop('checked', verified);
|
||||
modal_form.find('input[name=hidden]').prop('checked', hidden);
|
||||
|
||||
if (id == 'new'){
|
||||
$('#update-user-modal .modal-action').text('Create Team');
|
||||
} else {
|
||||
|
@ -280,24 +305,6 @@ $(document).ready(function () {
|
|||
})
|
||||
});
|
||||
|
||||
$('.team-admin input').on('change', function () {
|
||||
var elem = $(this).parent().parent().parent();
|
||||
var id = elem.find('.team-id').text().trim();
|
||||
var admin = $(this).prop('checked');
|
||||
console.log(admin);
|
||||
|
||||
$.post('{{ request.script_root }}/admin/team/' + id, {'admin': admin, 'nonce': nonce});
|
||||
});
|
||||
|
||||
$('.team-verified input').on('change', function () {
|
||||
var elem = $(this).parent().parent().parent();
|
||||
var id = elem.find('.team-id').text().trim();
|
||||
var verified = $(this).prop('checked');
|
||||
console.log(verified);
|
||||
|
||||
$.post('{{ request.script_root }}/admin/team/' + id, {'verified': verified, 'nonce': nonce});
|
||||
});
|
||||
|
||||
$('#send-user-email').click(function (e) {
|
||||
e.preventDefault();
|
||||
var id = $('#email-user input[name="id"]').val();
|
||||
|
@ -329,11 +336,15 @@ $(document).ready(function () {
|
|||
var affiliation = elem.find('.team-affiliation').attr('value') || '';
|
||||
var country = elem.find('.team-country').attr('value') || '';
|
||||
|
||||
load_update_modal(id, name, email, website, affiliation, country);
|
||||
var admin = elem.find('.team-admin').attr('value') == 'True' || false;
|
||||
var verified = elem.find('.team-verified').attr('value') == 'True' || false;
|
||||
var hidden = elem.find('.team-hidden').attr('value') == 'True' || false;
|
||||
|
||||
load_update_modal(id, name, email, website, affiliation, country, admin, verified, hidden);
|
||||
});
|
||||
|
||||
$('.create-team').click(function () {
|
||||
load_update_modal('new', '', '', '', '', '');
|
||||
load_update_modal('new', '', '', '', '', '', false, false, false);
|
||||
});
|
||||
|
||||
$('.delete-team').click(function () {
|
||||
|
|
|
@ -2,6 +2,8 @@ var challenges;
|
|||
var user_solves = [];
|
||||
var templates = {};
|
||||
|
||||
window.challenge = new Object();
|
||||
|
||||
function loadchal(id) {
|
||||
var obj = $.grep(challenges['game'], function (e) {
|
||||
return e.id == id;
|
||||
|
@ -20,51 +22,77 @@ function loadchalbyname(chalname) {
|
|||
|
||||
function updateChalWindow(obj) {
|
||||
$.get(script_root + "/chals/" + obj.id, function(challenge_data){
|
||||
$.get(script_root + obj.template, function (template_data) {
|
||||
$('#chal-window').empty();
|
||||
var template = nunjucks.compile(template_data);
|
||||
var solves = obj.solves == 1 ? " Solve" : " Solves";
|
||||
var solves = obj.solves + solves;
|
||||
$.getScript(script_root + obj.script, function(){
|
||||
$.get(script_root + obj.template, function (template_data) {
|
||||
$('#chal-window').empty();
|
||||
|
||||
var nonce = $('#nonce').val();
|
||||
var template = nunjucks.compile(template_data);
|
||||
|
||||
var md = window.markdownit({
|
||||
html: true,
|
||||
});
|
||||
var solves = obj.solves == 1 ? " Solve" : " Solves";
|
||||
var solves = obj.solves + solves;
|
||||
|
||||
challenge_data['description'] = md.render(challenge_data['description']);
|
||||
challenge_data['script_root'] = script_root;
|
||||
challenge_data['solves'] = solves;
|
||||
var nonce = $('#nonce').val();
|
||||
|
||||
$('#chal-window').append(template.render(challenge_data));
|
||||
$.getScript(script_root + obj.script,
|
||||
function () {
|
||||
// Handle Solves tab
|
||||
$('.chal-solves').click(function (e) {
|
||||
getsolves($('#chal-id').val())
|
||||
});
|
||||
$('.nav-tabs a').click(function (e) {
|
||||
e.preventDefault();
|
||||
$(this).tab('show')
|
||||
});
|
||||
window.challenge.preRender();
|
||||
|
||||
// Handle modal toggling
|
||||
$('#chal-window').on('hide.bs.modal', function (event) {
|
||||
$("#answer-input").removeClass("wrong");
|
||||
$("#answer-input").removeClass("correct");
|
||||
$("#incorrect-key").slideUp();
|
||||
$("#correct-key").slideUp();
|
||||
$("#already-solved").slideUp();
|
||||
$("#too-fast").slideUp();
|
||||
});
|
||||
challenge_data['description'] = window.challenge.render(challenge_data['description']);
|
||||
challenge_data['script_root'] = script_root;
|
||||
challenge_data['solves'] = solves;
|
||||
|
||||
// $('pre code').each(function(i, block) {
|
||||
// hljs.highlightBlock(block);
|
||||
// });
|
||||
$('#chal-window').append(template.render(challenge_data));
|
||||
|
||||
window.location.replace(window.location.href.split('#')[0] + '#' + obj.name);
|
||||
$('#chal-window').modal();
|
||||
$('.chal-solves').click(function (e) {
|
||||
getsolves($('#chal-id').val())
|
||||
});
|
||||
$('.nav-tabs a').click(function (e) {
|
||||
e.preventDefault();
|
||||
$(this).tab('show')
|
||||
});
|
||||
|
||||
// Handle modal toggling
|
||||
$('#chal-window').on('hide.bs.modal', function (event) {
|
||||
$("#answer-input").removeClass("wrong");
|
||||
$("#answer-input").removeClass("correct");
|
||||
$("#incorrect-key").slideUp();
|
||||
$("#correct-key").slideUp();
|
||||
$("#already-solved").slideUp();
|
||||
$("#too-fast").slideUp();
|
||||
});
|
||||
|
||||
$('#submit-key').click(function (e) {
|
||||
e.preventDefault();
|
||||
$('#submit-key').addClass("disabled-button");
|
||||
$('#submit-key').prop('disabled', true);
|
||||
window.challenge.submit(function (data) {
|
||||
renderSubmissionResponse(data)
|
||||
});
|
||||
});
|
||||
|
||||
$("#answer-input").keyup(function (event) {
|
||||
if (event.keyCode == 13) {
|
||||
$("#submit-key").click();
|
||||
}
|
||||
});
|
||||
|
||||
$(".input-field").bind({
|
||||
focus: function () {
|
||||
$(this).parent().addClass('input--filled');
|
||||
$label = $(this).siblings(".input-label");
|
||||
},
|
||||
blur: function () {
|
||||
if ($(this).val() === '') {
|
||||
$(this).parent().removeClass('input--filled');
|
||||
$label = $(this).siblings(".input-label");
|
||||
$label.removeClass('input--hide');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window.challenge.postRender();
|
||||
|
||||
window.location.replace(window.location.href.split('#')[0] + '#' + obj.name);
|
||||
$('#chal-window').modal();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -76,72 +104,65 @@ $("#answer-input").keyup(function(event){
|
|||
});
|
||||
|
||||
|
||||
function submitkey(chal, key, nonce, cb) {
|
||||
$('#submit-key').addClass("disabled-button");
|
||||
$('#submit-key').prop('disabled', true);
|
||||
$.post(script_root + "/chal/" + chal, {
|
||||
key: key,
|
||||
nonce: nonce
|
||||
}, function (data) {
|
||||
var result = $.parseJSON(JSON.stringify(data));
|
||||
function renderSubmissionResponse(data, cb){
|
||||
var result = $.parseJSON(JSON.stringify(data));
|
||||
|
||||
var result_message = $('#result-message');
|
||||
var result_notification = $('#result-notification');
|
||||
var answer_input = $("#answer-input");
|
||||
result_notification.removeClass();
|
||||
result_message.text(result.message);
|
||||
var result_message = $('#result-message');
|
||||
var result_notification = $('#result-notification');
|
||||
var answer_input = $("#answer-input");
|
||||
result_notification.removeClass();
|
||||
result_message.text(result.message);
|
||||
|
||||
if (result.status == -1){
|
||||
window.location = script_root + "/login?next=" + script_root + window.location.pathname + window.location.hash
|
||||
return
|
||||
}
|
||||
else if (result.status == 0){ // Incorrect key
|
||||
result_notification.addClass('alert alert-danger alert-dismissable text-center');
|
||||
result_notification.slideDown();
|
||||
if (result.status == -1) {
|
||||
window.location = script_root + "/login?next=" + script_root + window.location.pathname + window.location.hash
|
||||
return
|
||||
}
|
||||
else if (result.status == 0) { // Incorrect key
|
||||
result_notification.addClass('alert alert-danger alert-dismissable text-center');
|
||||
result_notification.slideDown();
|
||||
|
||||
answer_input.removeClass("correct");
|
||||
answer_input.addClass("wrong");
|
||||
setTimeout(function () {
|
||||
answer_input.removeClass("wrong");
|
||||
}, 3000);
|
||||
}
|
||||
else if (result.status == 1){ // Challenge Solved
|
||||
result_notification.addClass('alert alert-success alert-dismissable text-center');
|
||||
result_notification.slideDown();
|
||||
|
||||
$('.chal-solves').text((parseInt($('.chal-solves').text().split(" ")[0]) + 1 + " Solves") );
|
||||
|
||||
answer_input.val("");
|
||||
answer_input.removeClass("correct");
|
||||
answer_input.addClass("wrong");
|
||||
setTimeout(function () {
|
||||
answer_input.removeClass("wrong");
|
||||
answer_input.addClass("correct");
|
||||
}
|
||||
else if (result.status == 2){ // Challenge already solved
|
||||
result_notification.addClass('alert alert-info alert-dismissable text-center');
|
||||
result_notification.slideDown();
|
||||
|
||||
answer_input.addClass("correct");
|
||||
}
|
||||
else if (result.status == 3){ // Keys per minute too high
|
||||
result_notification.addClass('alert alert-warning alert-dismissable text-center');
|
||||
result_notification.slideDown();
|
||||
|
||||
answer_input.addClass("too-fast");
|
||||
setTimeout(function() {
|
||||
answer_input.removeClass("too-fast");
|
||||
}, 3000);
|
||||
}
|
||||
marksolves();
|
||||
updatesolves();
|
||||
setTimeout(function(){
|
||||
$('.alert').slideUp();
|
||||
$('#submit-key').removeClass("disabled-button");
|
||||
$('#submit-key').prop('disabled', false);
|
||||
}, 3000);
|
||||
}
|
||||
else if (result.status == 1) { // Challenge Solved
|
||||
result_notification.addClass('alert alert-success alert-dismissable text-center');
|
||||
result_notification.slideDown();
|
||||
|
||||
if (cb) {
|
||||
cb(result);
|
||||
}
|
||||
})
|
||||
$('.chal-solves').text((parseInt($('.chal-solves').text().split(" ")[0]) + 1 + " Solves"));
|
||||
|
||||
answer_input.val("");
|
||||
answer_input.removeClass("wrong");
|
||||
answer_input.addClass("correct");
|
||||
}
|
||||
else if (result.status == 2) { // Challenge already solved
|
||||
result_notification.addClass('alert alert-info alert-dismissable text-center');
|
||||
result_notification.slideDown();
|
||||
|
||||
answer_input.addClass("correct");
|
||||
}
|
||||
else if (result.status == 3) { // Keys per minute too high
|
||||
result_notification.addClass('alert alert-warning alert-dismissable text-center');
|
||||
result_notification.slideDown();
|
||||
|
||||
answer_input.addClass("too-fast");
|
||||
setTimeout(function () {
|
||||
answer_input.removeClass("too-fast");
|
||||
}, 3000);
|
||||
}
|
||||
marksolves();
|
||||
updatesolves();
|
||||
setTimeout(function () {
|
||||
$('.alert').slideUp();
|
||||
$('#submit-key').removeClass("disabled-button");
|
||||
$('#submit-key').prop('disabled', false);
|
||||
}, 3000);
|
||||
|
||||
if (cb) {
|
||||
cb(result);
|
||||
}
|
||||
}
|
||||
|
||||
function marksolves(cb) {
|
||||
|
|
|
@ -28,12 +28,19 @@
|
|||
<script src="{{ request.script_root }}/themes/{{ ctf_theme() }}/static/js/vendor/nunjucks.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
var script_root = "{{ request.script_root }}";
|
||||
var csrf_nonce = "{{ nonce }}"
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
|
||||
<div class="container">
|
||||
<a href="{{ request.script_root }}/" class="navbar-brand">{{ ctf_name() }}</a>
|
||||
<a href="{{ request.script_root }}/" class="navbar-brand">
|
||||
{% if ctf_logo() %}
|
||||
<img class="img-responsive ctf_logo" src="{{ request.script_root }}/files/{{ ctf_logo() }}" height="25" alt="{{ ctf_name() }}">
|
||||
{% else %}
|
||||
{{ ctf_name() }}
|
||||
{% endif %}
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#base-navbars"
|
||||
aria-controls="base-navbars" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
|
|
|
@ -150,6 +150,7 @@ def init_utils(app):
|
|||
app.jinja_env.globals.update(can_register=can_register)
|
||||
app.jinja_env.globals.update(can_send_mail=can_send_mail)
|
||||
app.jinja_env.globals.update(ctf_name=ctf_name)
|
||||
app.jinja_env.globals.update(ctf_logo=ctf_logo)
|
||||
app.jinja_env.globals.update(ctf_theme=ctf_theme)
|
||||
app.jinja_env.globals.update(get_configurable_plugins=get_configurable_plugins)
|
||||
app.jinja_env.globals.update(get_registered_scripts=get_registered_scripts)
|
||||
|
@ -211,6 +212,11 @@ def ctf_name():
|
|||
return name if name else 'CTFd'
|
||||
|
||||
|
||||
@cache.memoize()
|
||||
def ctf_logo():
|
||||
return get_config('ctf_logo')
|
||||
|
||||
|
||||
@cache.memoize()
|
||||
def ctf_theme():
|
||||
theme = get_config('ctf_theme')
|
||||
|
@ -661,7 +667,7 @@ def verify_email(addr):
|
|||
text = """Please click the following link to confirm your email address for {ctf_name}: {url}/{token}""".format(
|
||||
ctf_name=get_config('ctf_name'),
|
||||
url=url_for('auth.confirm_user', _external=True),
|
||||
token=base64encode(token, urlencode=True)
|
||||
token=base64encode(token)
|
||||
)
|
||||
sendmail(addr, text)
|
||||
|
||||
|
@ -673,7 +679,7 @@ def forgot_password(email, team_name):
|
|||
|
||||
{0}/{1}
|
||||
|
||||
""".format(url_for('auth.reset_password', _external=True), base64encode(token, urlencode=True))
|
||||
""".format(url_for('auth.reset_password', _external=True), base64encode(token))
|
||||
|
||||
sendmail(email, text)
|
||||
|
||||
|
@ -700,35 +706,30 @@ def sha512(string):
|
|||
return hashlib.sha512(string).hexdigest()
|
||||
|
||||
|
||||
def base64encode(s, urlencode=False):
|
||||
def base64encode(s):
|
||||
if six.PY3 and isinstance(s, six.string_types):
|
||||
s = s.encode('utf-8')
|
||||
else:
|
||||
# Python 2 support because the base64 module doesnt like unicode
|
||||
s = str(s)
|
||||
|
||||
encoded = base64.urlsafe_b64encode(s)
|
||||
encoded = base64.urlsafe_b64encode(s).rstrip(b'\n=')
|
||||
if six.PY3:
|
||||
try:
|
||||
encoded = encoded.decode('utf-8')
|
||||
except UnicodeDecodeError:
|
||||
pass
|
||||
if urlencode:
|
||||
encoded = quote(encoded)
|
||||
return encoded
|
||||
|
||||
|
||||
def base64decode(s, urldecode=False):
|
||||
if urldecode:
|
||||
s = unquote(s)
|
||||
|
||||
def base64decode(s):
|
||||
if six.PY3 and isinstance(s, six.string_types):
|
||||
s = s.encode('utf-8')
|
||||
else:
|
||||
# Python 2 support because the base64 module doesnt like unicode
|
||||
s = str(s)
|
||||
|
||||
decoded = base64.urlsafe_b64decode(s)
|
||||
decoded = base64.urlsafe_b64decode(s.ljust(len(s) + len(s) % 4, b'='))
|
||||
if six.PY3:
|
||||
try:
|
||||
decoded = decoded.decode('utf-8')
|
||||
|
|
|
@ -4,11 +4,11 @@ import functools
|
|||
|
||||
|
||||
def during_ctf_time_only(f):
|
||||
'''
|
||||
"""
|
||||
Decorator to restrict an endpoint to only be seen during a CTF
|
||||
:param f:
|
||||
:return:
|
||||
'''
|
||||
"""
|
||||
@functools.wraps(f)
|
||||
def during_ctf_time_only_wrapper(*args, **kwargs):
|
||||
if utils.ctftime() or utils.is_admin():
|
||||
|
@ -28,6 +28,11 @@ def during_ctf_time_only(f):
|
|||
|
||||
|
||||
def require_verified_emails(f):
|
||||
"""
|
||||
Decorator to restrict an endpoint to users with confirmed active email addresses
|
||||
:param f:
|
||||
:return:
|
||||
"""
|
||||
@functools.wraps(f)
|
||||
def require_verified_emails_wrapper(*args, **kwargs):
|
||||
if utils.get_config('verify_emails'):
|
||||
|
@ -39,6 +44,11 @@ def require_verified_emails(f):
|
|||
|
||||
|
||||
def viewable_without_authentication(status_code=None):
|
||||
"""
|
||||
Decorator that allows users to view the specified endpoint if viewing challenges without authentication is enabled
|
||||
:param status_code:
|
||||
:return:
|
||||
"""
|
||||
def viewable_without_authentication_decorator(f):
|
||||
@functools.wraps(f)
|
||||
def viewable_without_authentication_wrapper(*args, **kwargs):
|
||||
|
@ -55,6 +65,11 @@ def viewable_without_authentication(status_code=None):
|
|||
|
||||
|
||||
def authed_only(f):
|
||||
"""
|
||||
Decorator that requires the user to be authenticated
|
||||
:param f:
|
||||
:return:
|
||||
"""
|
||||
@functools.wraps(f)
|
||||
def authed_only_wrapper(*args, **kwargs):
|
||||
if session.get('id'):
|
||||
|
@ -66,6 +81,11 @@ def authed_only(f):
|
|||
|
||||
|
||||
def admins_only(f):
|
||||
"""
|
||||
Decorator that requires the user to be authenticated and an admin
|
||||
:param f:
|
||||
:return:
|
||||
"""
|
||||
@functools.wraps(f)
|
||||
def admins_only_wrapper(*args, **kwargs):
|
||||
if session.get('admin'):
|
||||
|
|
|
@ -6,4 +6,6 @@ rednose>=1.1.1
|
|||
pep8>=1.7.0
|
||||
freezegun>=0.3.9
|
||||
psycopg2>=2.7.3.1
|
||||
psycopg2-binary>=2.7.3.1
|
||||
codecov>=2.0.9
|
||||
nose-randomly>=1.2.5
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Flask==0.12.2
|
||||
Flask==1.0.0
|
||||
Flask-SQLAlchemy==2.3.2
|
||||
Flask-Session==0.3.1
|
||||
Flask-Caching==1.3.3
|
||||
|
@ -19,3 +19,4 @@ netaddr==0.7.19
|
|||
redis==2.10.6
|
||||
datafreeze==0.1.0
|
||||
gevent==1.2.2
|
||||
python-dotenv==0.8.2
|
|
@ -16,6 +16,7 @@ def test_admin_post_config_values():
|
|||
data = {
|
||||
'nonce': sess.get('nonce'),
|
||||
'ctf_name': 'CTFd',
|
||||
'ctf_logo': '',
|
||||
'ctf_theme': 'core',
|
||||
'workshop_mode': 'on',
|
||||
'paused': 'on',
|
||||
|
@ -59,6 +60,7 @@ def test_admin_post_config_values():
|
|||
|
||||
result = {
|
||||
'ctf_name': 'CTFd',
|
||||
'ctf_logo': None,
|
||||
'ctf_theme': 'core',
|
||||
'workshop_mode': True,
|
||||
'paused': True,
|
||||
|
|
|
@ -208,7 +208,7 @@ def test_admins_can_delete_challenges():
|
|||
|
||||
|
||||
def test_admins_can_delete_challenges_with_extras():
|
||||
""""Test that admins can delete challenges that have a hint"""
|
||||
"""Test that admins can delete challenges that have a hint"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
client = login_as_user(app, name="admin", password="password")
|
||||
|
@ -274,6 +274,24 @@ def test_admin_chal_detail_returns_proper_data():
|
|||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_admin_load_chal_solves():
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
client = login_as_user(app, name="admin", password="password")
|
||||
|
||||
chal1 = gen_challenge(app.db)
|
||||
flag1 = gen_flag(app.db, chal=chal1.id, flag='flag')
|
||||
chal1_id = chal1.id
|
||||
|
||||
gen_solve(app.db, teamid=1, chalid=chal1_id)
|
||||
|
||||
r = client.get('/admin/chal/1/solves')
|
||||
data = r.get_data(as_text=True)
|
||||
assert json.loads(data)
|
||||
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_admins_can_create_teams():
|
||||
'''Test that admins can create new teams'''
|
||||
app = create_ctfd()
|
||||
|
|
|
@ -117,8 +117,10 @@ def gen_file(db, chal, location):
|
|||
return f
|
||||
|
||||
|
||||
def gen_flag(db, chal, flag='flag', key_type='static'):
|
||||
def gen_flag(db, chal, flag='flag', key_type='static', data=None):
|
||||
key = Keys(chal, flag, key_type)
|
||||
if data:
|
||||
key.data = data
|
||||
db.session.add(key)
|
||||
db.session.commit()
|
||||
return key
|
||||
|
|
|
@ -63,19 +63,14 @@ def test_base64encode():
|
|||
if six.PY2:
|
||||
assert base64encode('abc123') == 'YWJjMTIz'
|
||||
assert base64encode(unicode('abc123')) == 'YWJjMTIz'
|
||||
assert base64encode(unicode('"test@mailinator.com".DGxeoA.lCssU3M2QuBfohO-FtdgDQLKbU4'), urlencode=True) == 'InRlc3RAbWFpbGluYXRvci5jb20iLkRHeGVvQS5sQ3NzVTNNMlF1QmZvaE8tRnRkZ0RRTEtiVTQ%3D'
|
||||
assert base64encode('user+user@ctfd.io') == 'dXNlcit1c2VyQGN0ZmQuaW8='
|
||||
assert base64encode('user+user@ctfd.io', urlencode=True) == 'dXNlcit1c2VyQGN0ZmQuaW8%3D'
|
||||
assert base64encode('😆') == '8J-Yhg=='
|
||||
assert base64encode('😆', urlencode=True) == '8J-Yhg%3D%3D'
|
||||
assert base64encode(unicode('"test@mailinator.com".DGxeoA.lCssU3M2QuBfohO-FtdgDQLKbU4')) == 'InRlc3RAbWFpbGluYXRvci5jb20iLkRHeGVvQS5sQ3NzVTNNMlF1QmZvaE8tRnRkZ0RRTEtiVTQ'
|
||||
assert base64encode('user+user@ctfd.io') == 'dXNlcit1c2VyQGN0ZmQuaW8'
|
||||
assert base64encode('😆') == '8J-Yhg'
|
||||
else:
|
||||
assert base64encode('abc123') == 'YWJjMTIz'
|
||||
assert base64encode('abc123') == 'YWJjMTIz'
|
||||
assert base64encode('"test@mailinator.com".DGxeoA.lCssU3M2QuBfohO-FtdgDQLKbU4', urlencode=True) == 'InRlc3RAbWFpbGluYXRvci5jb20iLkRHeGVvQS5sQ3NzVTNNMlF1QmZvaE8tRnRkZ0RRTEtiVTQ%3D'
|
||||
assert base64encode('user+user@ctfd.io') == 'dXNlcit1c2VyQGN0ZmQuaW8='
|
||||
assert base64encode('user+user@ctfd.io', urlencode=True) == 'dXNlcit1c2VyQGN0ZmQuaW8%3D'
|
||||
assert base64encode('😆') == '8J-Yhg=='
|
||||
assert base64encode('😆', urlencode=True) == '8J-Yhg%3D%3D'
|
||||
assert base64encode('"test@mailinator.com".DGxeoA.lCssU3M2QuBfohO-FtdgDQLKbU4') == 'InRlc3RAbWFpbGluYXRvci5jb20iLkRHeGVvQS5sQ3NzVTNNMlF1QmZvaE8tRnRkZ0RRTEtiVTQ'
|
||||
assert base64encode('user+user@ctfd.io') == 'dXNlcit1c2VyQGN0ZmQuaW8'
|
||||
assert base64encode('😆') == '8J-Yhg'
|
||||
|
||||
|
||||
def test_base64decode():
|
||||
|
@ -83,17 +78,13 @@ def test_base64decode():
|
|||
if six.PY2:
|
||||
assert base64decode('YWJjMTIz') == 'abc123'
|
||||
assert base64decode(unicode('YWJjMTIz')) == 'abc123'
|
||||
assert base64decode(unicode('InRlc3RAbWFpbGluYXRvci5jb20iLkRHeGVvQS5sQ3NzVTNNMlF1QmZvaE8tRnRkZ0RRTEtiVTQ%3D'), urldecode=True) == '"test@mailinator.com".DGxeoA.lCssU3M2QuBfohO-FtdgDQLKbU4'
|
||||
assert base64decode('8J-Yhg==') == '😆'
|
||||
assert base64decode('8J-Yhg%3D%3D', urldecode=True) == '😆'
|
||||
assert base64decode(unicode('InRlc3RAbWFpbGluYXRvci5jb20iLkRHeGVvQS5sQ3NzVTNNMlF1QmZvaE8tRnRkZ0RRTEtiVTQ')) == '"test@mailinator.com".DGxeoA.lCssU3M2QuBfohO-FtdgDQLKbU4'
|
||||
assert base64decode('8J-Yhg') == '😆'
|
||||
else:
|
||||
assert base64decode('YWJjMTIz') == 'abc123'
|
||||
assert base64decode('YWJjMTIz') == 'abc123'
|
||||
assert base64decode('InRlc3RAbWFpbGluYXRvci5jb20iLkRHeGVvQS5sQ3NzVTNNMlF1QmZvaE8tRnRkZ0RRTEtiVTQ%3D', urldecode=True) == '"test@mailinator.com".DGxeoA.lCssU3M2QuBfohO-FtdgDQLKbU4'
|
||||
assert base64decode('dXNlcit1c2VyQGN0ZmQuaW8=') == 'user+user@ctfd.io'
|
||||
assert base64decode('dXNlcit1c2VyQGN0ZmQuaW8%3D', urldecode=True) == 'user+user@ctfd.io'
|
||||
assert base64decode('8J-Yhg==') == '😆'
|
||||
assert base64decode('8J-Yhg%3D%3D', urldecode=True) == '😆'
|
||||
assert base64decode('InRlc3RAbWFpbGluYXRvci5jb20iLkRHeGVvQS5sQ3NzVTNNMlF1QmZvaE8tRnRkZ0RRTEtiVTQ') == '"test@mailinator.com".DGxeoA.lCssU3M2QuBfohO-FtdgDQLKbU4'
|
||||
assert base64decode('dXNlcit1c2VyQGN0ZmQuaW8') == 'user+user@ctfd.io'
|
||||
assert base64decode('8J-Yhg') == '😆'
|
||||
|
||||
|
||||
def test_override_template():
|
||||
|
@ -251,7 +242,7 @@ def test_verify_email(mock_smtp):
|
|||
# This is currently not actually validated
|
||||
msg = ("Please click the following link to confirm"
|
||||
" your email address for CTFd:"
|
||||
" http://localhost/confirm/InVzZXJAdXNlci5jb20iLkFmS0dQZy5kLUJnVkgwaUhadzFHaXVENHczWTJCVVJwdWc%3D")
|
||||
" http://localhost/confirm/InVzZXJAdXNlci5jb20iLkFmS0dQZy5kLUJnVkgwaUhadzFHaXVENHczWTJCVVJwdWc")
|
||||
|
||||
ctf_name = get_config('ctf_name')
|
||||
email_msg = MIMEText(msg)
|
||||
|
|
|
@ -118,7 +118,7 @@ def test_expired_confirmation_links():
|
|||
client = login_as_user(app, name="user", password="password")
|
||||
|
||||
# user@user.com "2012-01-14 03:21:34"
|
||||
confirm_link = 'http://localhost/confirm/InVzZXJAdXNlci5jb20iLkFmS0dQZy5kLUJnVkgwaUhadzFHaXVENHczWTJCVVJwdWc%3D'
|
||||
confirm_link = 'http://localhost/confirm/InVzZXJAdXNlci5jb20iLkFmS0dQZy5kLUJnVkgwaUhadzFHaXVENHczWTJCVVJwdWc'
|
||||
r = client.get(confirm_link)
|
||||
|
||||
assert "Your confirmation link has expired" in r.get_data(as_text=True)
|
||||
|
@ -137,7 +137,7 @@ def test_invalid_confirmation_links():
|
|||
client = login_as_user(app, name="user", password="password")
|
||||
|
||||
# user@user.com "2012-01-14 03:21:34"
|
||||
confirm_link = 'http://localhost/confirm/a8375iyu<script>alert(1)<script>hn3048wueorighkgnsfg%3D%3D'
|
||||
confirm_link = 'http://localhost/confirm/a8375iyu<script>alert(1)<script>hn3048wueorighkgnsfg'
|
||||
r = client.get(confirm_link)
|
||||
|
||||
assert "Your confirmation token is invalid" in r.get_data(as_text=True)
|
||||
|
@ -180,7 +180,7 @@ def test_invalid_reset_password_link():
|
|||
|
||||
with app.test_client() as client:
|
||||
# user@user.com "2012-01-14 03:21:34"
|
||||
forgot_link = 'http://localhost/reset_password/5678ytfghjiu876tyfg<>hvbnmkoi9u87y6trdfcgvhbnm,lp09iujmk%3D'
|
||||
forgot_link = 'http://localhost/reset_password/5678ytfghjiu876tyfg<>hvbnmkoi9u87y6trdfcgvhbnm,lp09iujmk'
|
||||
r = client.get(forgot_link)
|
||||
|
||||
assert "Your reset token is invalid" in r.get_data(as_text=True)
|
||||
|
|
|
@ -132,6 +132,46 @@ def test_submitting_correct_flag():
|
|||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_submitting_correct_static_case_insensitive_flag():
|
||||
"""Test that correct static flags are correct if the static flag is marked case_insensitive"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
register_user(app)
|
||||
client = login_as_user(app)
|
||||
chal = gen_challenge(app.db)
|
||||
flag = gen_flag(app.db, chal=chal.id, flag='flag', data="case_insensitive")
|
||||
with client.session_transaction() as sess:
|
||||
data = {
|
||||
"key": 'FLAG',
|
||||
"nonce": sess.get('nonce')
|
||||
}
|
||||
r = client.post('/chal/{}'.format(chal.id), data=data)
|
||||
assert r.status_code == 200
|
||||
resp = json.loads(r.data.decode('utf8'))
|
||||
assert resp.get('status') == 1 and resp.get('message') == "Correct"
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_submitting_correct_regex_case_insensitive_flag():
|
||||
"""Test that correct regex flags are correct if the regex flag is marked case_insensitive"""
|
||||
app = create_ctfd()
|
||||
with app.app_context():
|
||||
register_user(app)
|
||||
client = login_as_user(app)
|
||||
chal = gen_challenge(app.db)
|
||||
flag = gen_flag(app.db, chal=chal.id, key_type='regex', flag='flag', data="case_insensitive")
|
||||
with client.session_transaction() as sess:
|
||||
data = {
|
||||
"key": 'FLAG',
|
||||
"nonce": sess.get('nonce')
|
||||
}
|
||||
r = client.post('/chal/{}'.format(chal.id), data=data)
|
||||
assert r.status_code == 200
|
||||
resp = json.loads(r.data.decode('utf8'))
|
||||
assert resp.get('status') == 1 and resp.get('message') == "Correct"
|
||||
destroy_ctfd(app)
|
||||
|
||||
|
||||
def test_submitting_incorrect_flag():
|
||||
"""Test that incorrect flags are incorrect"""
|
||||
app = create_ctfd()
|
||||
|
|
|
@ -567,7 +567,7 @@ def test_user_can_confirm_email(mock_smtp):
|
|||
assert r.location == "http://localhost/confirm" # We got redirected to /confirm
|
||||
|
||||
# Use precalculated confirmation secret
|
||||
r = client.get('http://localhost/confirm/InVzZXJAdXNlci5jb20iLkFmS0dQZy5kLUJnVkgwaUhadzFHaXVENHczWTJCVVJwdWc%3D')
|
||||
r = client.get('http://localhost/confirm/InVzZXJAdXNlci5jb20iLkFmS0dQZy5kLUJnVkgwaUhadzFHaXVENHczWTJCVVJwdWc')
|
||||
assert r.location == 'http://localhost/challenges'
|
||||
|
||||
# The team is now verified
|
||||
|
|
Loading…
Reference in New Issue