Marking 1.2.0 (#628)

* Mark 1.2.0.
* Update CHANGELOG.
* Add `window.challenge.data` object. 
* Don't raise a 500 when an endpoint can't be found but was POST'ed too. Mostly from scanners...
    * Add test for not found endpoints.
* Fixing issue with clearing logo on config update.
* Truncate scoreboard team name to 50 characters.
selenium-screenshot-testing 1.2.0
Kevin Chung 2018-05-04 17:24:02 -04:00 committed by GitHub
parent 36c83b59bc
commit 9cedf456b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 129 additions and 13 deletions

View File

@ -1,3 +1,46 @@
1.2.0 / 2018-05-04
==================
**General**
* Updated to Flask 1.0 & switched documentation to suggest using `flask run` instead of `python serve.py`.
* Added the ability to make static & regex flags case insensitive.
* The `/chals` endpoint no longer lists the details of challenges.
* The `/chals/:id` endpoint is now used to load challenge information before display.
* Admins can now see what users have solved a given challenge from the admin panel.
* Fixed issue with imports extracting files outside of the CTFd directory.
* Added import zipfile validation and optional size restriction.
* The ctftime, authentication, and admin restrictions have been converted to decorators to improve code reuse.
* 403 is now a more common status code. Previously it only indicated CSRF failure, now it can indicate login failure
or other Forbidden access situations.
* Challenge previews now work consistently instead of occasionally failing to show.
* Tests are now randomly ordered with `nose-randomly`.
**Themes**
* Admins now have the ability to upload a CTF logo from the config panel.
* Switched from the `marked` library to `Markdown-It` for client side markdown rendering.
* This will break Challenge type plugins that override the markdown renderer since we are no longer using the marked renderers.
* Introduced the `ezpg()` JS function to make it easier to draw a progressbar modal.
* Introduced the `$.patch()` AJAX wrapper.
* Team names are truncated properly to 50 characters in `teams.html`.
* The admin panel now uses Bootstrap badges instead of buttons to indicate properties such as `admin`, `verified`, `visible`.
**Plugins**
* Challenge type plugins now use a global challenge object with exposed functions to specify how to display a challenge.
(`preRender()`, `render()`, `postRender()`, `submit()`).
* Challenge type plugins also have access to window.challenge.data which allow for the previously mentioned functions to
process challenge data and change logic accordingly.
* Challenge type plugins now get full control over how a challenge is displayed via the nunjucks files.
* Challenge plugins should now pass the entire flag/key object to a Custom flag type.
* This allows the flag type to make use of the data column to decide how to operate on the flag. This is used to implement
case insensitive flags.
* Challenge modals (`modal.njk`) now use `{{ description }}` instead of `{{ desc }}` properly aligning with the database schema.
* The update and create modals now inject data into the modal via nunjucks instead of client side Javascript.
* The `utils.base64decode()` & `utils.base64encode()` functions no longer expose url encoding/decoding parameters.
1.1.4 / 2018-04-05
==================

View File

@ -17,7 +17,7 @@ if sys.version_info[0] < 3:
reload(sys)
sys.setdefaultencoding("utf-8")
__version__ = '1.1.4'
__version__ = '1.2.0'
class CTFdFlask(Flask):

View File

@ -147,8 +147,8 @@ 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']
if request.files.get('ctf_logo_file', None):
ctf_logo = request.files['ctf_logo_file']
file_id, file_loc = utils.upload_file(ctf_logo, None)
utils.set_config("ctf_logo", file_loc)
elif request.form.get('ctf_logo') == '':

View File

@ -1,3 +1,5 @@
window.challenge.data = undefined;
window.challenge.renderer = new markdownit({
html: true,
});

View File

@ -37,6 +37,8 @@ function render_challenge_preview(chal_id){
$.get(script_root + obj.type_data.templates.modal, function (template_data) {
var template = nunjucks.compile(template_data);
window.challenge.data = obj;
window.challenge.preRender()
obj['description'] = window.challenge.render(obj['description']);

View File

@ -55,19 +55,20 @@
<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');
$('#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');
$(this).text('Logo will be removed on next update');
">
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 %}>
<input class="form-control" id="ctf_logo_file" name='ctf_logo_file' type='file' placeholder="CTF Logo">
<input id='ctf_logo' name='ctf_logo' type='hidden' value="{% if ctf_logo is defined %}{{ ctf_logo }}{% endif %}"
placeholder="CTF Logo">
</div>
<div class="form-group">

View File

@ -33,6 +33,8 @@ function updateChalWindow(obj) {
var nonce = $('#nonce').val();
window.challenge.data = challenge_data;
window.challenge.preRender();
challenge_data['description'] = window.challenge.render(challenge_data['description']);

View File

@ -46,7 +46,7 @@
{% for team in teams %}
<tr>
<th scope="row" class="text-center">{{ loop.index }}</th>
<td><a href="{{ request.script_root }}/team/{{ team.teamid }}">{{ team.name }}</a></td>
<td><a href="{{ request.script_root }}/team/{{ team.teamid }}">{{ team.name | truncate(50) }}</a></td>
<td>{{ team.score }}</td>
</tr>
{% endfor %}

View File

@ -192,7 +192,10 @@ def init_utils(app):
@app.before_request
def csrf():
func = app.view_functions[request.endpoint]
try:
func = app.view_functions[request.endpoint]
except KeyError:
abort(404)
if hasattr(func, '_bypass_csrf'):
return
if not session.get('nonce'):

View File

@ -34,7 +34,7 @@ CTFd is a Capture The Flag framework focusing on ease of use and customizability
## Install
1. Run `./prepare.sh` to install dependencies using apt.
2. Modify [CTFd/config.py](https://github.com/CTFd/CTFd/blob/master/CTFd/config.py) to your liking.
3. Use `python serve.py` in a terminal to drop into debug mode.
3. Use `flask run` in a terminal to drop into debug mode.
Or you can use Docker with the following command:

View File

@ -0,0 +1,51 @@
"""Update regex flags to be case_insensitive
Revision ID: dab615389702
Revises: d5a224bf5862
Create Date: 2018-05-03 18:18:53.343075
"""
from CTFd.models import db
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import mysql
from sqlalchemy.sql import text, table, column, and_
# revision identifiers, used by Alembic.
revision = 'dab615389702'
down_revision = 'd5a224bf5862'
branch_labels = None
depends_on = None
keys_table = table('keys',
column('id', db.Integer),
column('type', db.Text),
column('data', db.Text)
)
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
connection = op.get_bind()
connection.execute(
keys_table.update().where(
keys_table.c.type == 'regex'
).values(
data='case_insensitive'
)
)
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
connection = op.get_bind()
connection.execute(
keys_table.update().where(
keys_table.c.type == 'regex'
).values(
data=None
)
)
# ### end Alembic commands ###

View File

@ -20,6 +20,18 @@ def test_index():
destroy_ctfd(app)
def test_not_found():
"""Should return a 404 for pages that are not found"""
app = create_ctfd()
with app.app_context():
with app.test_client() as client:
r = client.get('/this-should-404')
assert r.status_code == 404
r = client.post('/this-should-404')
assert r.status_code == 404
destroy_ctfd(app)
def test_page():
"""Test that users can access pages that are created in the database"""
app = create_ctfd()