diff --git a/.dockerignore b/.dockerignore index 8b29db5..8ae8ce5 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,2 @@ __pycache__/ -.idea/ \ No newline at end of file +.idea/ diff --git a/.gitignore b/.gitignore index c9863f8..77ee514 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,4 @@ __pycache__/ .env .venv env/ -venv/ \ No newline at end of file +venv/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..0fca092 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,16 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-added-large-files +- repo: https://github.com/pylint-dev/pylint + rev: v4.0.2 + hooks: + - id: pylint + args: ['--disable', 'R1705,C0301,W0511,R0903,W0718,R0911'] + language: system diff --git a/Dockerfile b/Dockerfile index 328e5e4..cc194ad 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,4 +8,4 @@ RUN python3 -m pip install --no-cache-dir -r requirements.txt && \ sed -i 's/from jinja2 import/from markupsafe import/g' /usr/local/lib/python3.13/site-packages/flask_recaptcha.py EXPOSE 5000 -CMD ["waitress-serve", "--host=0.0.0.0", "--port=5000", "--threads=8", "run:app"] \ No newline at end of file +CMD ["waitress-serve", "--host=0.0.0.0", "--port=5000", "--threads=8", "run:app"] diff --git a/README.md b/README.md index 4fab36e..f5d04dc 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Features -- Multi containers +- Multi containers - Multi exposed ports - Each challenge are in a separate network - Relation between containers using `hostname` @@ -50,7 +50,7 @@ export DEBUG=1 sudo -E python3 run.py ``` -> You can utilize the `ADMIN_ONLY` flag to restrict login to administrators only. It's useful for testing your challenges before the beginning of the CTF. +> You can utilize the `ADMIN_ONLY` flag to restrict login to administrators only. It's useful for testing your challenges before the beginning of the CTF. ## Deployment @@ -100,7 +100,6 @@ All the slaves must build all docker images present in the `config.json` file (i ## Todo -- pylint - add more docs about `config.json` format - Extend instance feature - Display connection string (ex: ssh -p ..., http://host:port, nc host port, ...) diff --git a/app/app.py b/app/app.py index eced117..5d9dd70 100644 --- a/app/app.py +++ b/app/app.py @@ -1,3 +1,5 @@ +''' Base flask ''' + import logging from os import getenv from secrets import token_hex @@ -8,6 +10,7 @@ def create_app(): + ''' Base flask app ''' app = Flask(__name__) app.secret_key = token_hex() diff --git a/app/auth.py b/app/auth.py index ebd5a6f..420b775 100644 --- a/app/auth.py +++ b/app/auth.py @@ -1,9 +1,12 @@ +''' App authentication ''' + from functools import wraps from flask import redirect, session, url_for def login_required(f): + ''' Login check ''' @wraps(f) def wrap(*args, **kwargs): if session and session["verified"]: @@ -15,6 +18,7 @@ def wrap(*args, **kwargs): def admin_required(f): + ''' Admin check ''' @wraps(f) def wrap(*args, **kwargs): if session and session["admin"]: diff --git a/app/config.py b/app/config.py index a56c8ce..85f0889 100644 --- a/app/config.py +++ b/app/config.py @@ -1,3 +1,5 @@ +''' Configuration ''' + #!/usr/bin/env python3 from json import load from os import getenv @@ -7,7 +9,7 @@ DEBUG = getenv("DEBUG", "").strip().upper() in ["1", "TRUE"] ADMIN_ONLY = getenv("ADMIN_ONLY", "").strip().upper() in ["1", "TRUE"] -with open("config.json", "r") as config_file: +with open("config.json", "r", encoding="utf-8") as config_file: config = load(config_file) WEBSITE_TITLE = config["website_title"] diff --git a/app/database.py b/app/database.py index 2e1eeb6..d9714b9 100644 --- a/app/database.py +++ b/app/database.py @@ -1,3 +1,5 @@ +''' Database connexion ''' + from flask_sqlalchemy import SQLAlchemy -db = SQLAlchemy() \ No newline at end of file +db = SQLAlchemy() diff --git a/app/models.py b/app/models.py index 98b5808..1daa305 100644 --- a/app/models.py +++ b/app/models.py @@ -1,3 +1,5 @@ +''' Database models ''' + from datetime import datetime from app.database import db diff --git a/app/templates/admin.html b/app/templates/admin.html index 05e2560..d2cdffa 100644 --- a/app/templates/admin.html +++ b/app/templates/admin.html @@ -11,20 +11,20 @@

{{ instances_count }} container{% if instances_count > 1 %}s{% endif %} runn

All Instances

- +
-
-

- + @@ -117,7 +117,7 @@

Notification

// Search functionality const searchInput = document.getElementById('search-input'); const clearSearchBtn = document.getElementById('clear-search'); - + function updateClearButton() { if (clearSearchBtn && searchInput) { if (searchInput.value && searchInput.value.trim() !== '') { @@ -129,7 +129,7 @@

Notification

} } } - + if (searchInput) { searchInput.addEventListener('input', function(e) { const query = e.target.value.toLowerCase().trim(); @@ -220,17 +220,17 @@

Notification

function filterAndRender(query) { let filtered = allContainers; - + if (query) { filtered = allContainers.filter(container => { const team = (container.team || '').toLowerCase(); const username = (container.username || '').toLowerCase(); const image = (container.image || '').toLowerCase(); const instanceName = (container.instance_name || '').toLowerCase(); - - return team.includes(query) || - username.includes(query) || - image.includes(query) || + + return team.includes(query) || + username.includes(query) || + image.includes(query) || instanceName.includes(query); }); } @@ -267,7 +267,7 @@

Notification

// Get current search query const searchInput = document.getElementById('search-input'); const query = searchInput ? searchInput.value.toLowerCase().trim() : ''; - + // Filter and sort let filtered = allContainers; if (query) { @@ -276,10 +276,10 @@

Notification

const username = (container.username || '').toLowerCase(); const image = (container.image || '').toLowerCase(); const instanceName = (container.instance_name || '').toLowerCase(); - - return team.includes(query) || - username.includes(query) || - image.includes(query) || + + return team.includes(query) || + username.includes(query) || + image.includes(query) || instanceName.includes(query); }); } @@ -361,17 +361,17 @@

Notification

titleEl.textContent = title; messageEl.textContent = message; - + // Remove previous styling classes modalContent.classList.remove('alert-success', 'alert-error'); - + // Add appropriate styling based on title if (title === 'Success') { modalContent.classList.add('alert-success'); } else if (title === 'Error') { modalContent.classList.add('alert-error'); } - + modal.classList.add('show'); const close = () => { diff --git a/app/templates/base.html b/app/templates/base.html index 3195deb..eca8d8c 100644 --- a/app/templates/base.html +++ b/app/templates/base.html @@ -27,7 +27,7 @@ {% endif %} {% endblock %} - + {% block modal %} {% if session and session.get("verified") %} @@ -56,7 +56,7 @@

Information

{% block main %} {% endblock %} - + {% block scripts %} {% if session and session.get("verified") %}