"""PatchBay web frontend (runs on the target only).""" import datetime import secrets from flask import Flask, g from . import api, auth, db, security, views from .conf import Config def prepare_config(conf): """Replaces a plaintext SysopPassword in the file with its hash.""" pw = conf.get("sysoppassword") if pw and not security.is_hashed(pw): conf.update({"SysopPassword": security.hash_password(pw)}) def create_app(conf_path, testing=False): conf = Config(conf_path) prepare_config(conf) db.init(conf.get("database")) app = Flask(__name__) conn = db.connect(conf.get("database")) row = conn.execute("SELECT value FROM settings WHERE key = 'secret_key'").fetchone() if row: secret = row[0] else: secret = secrets.token_hex(32) conn.execute("INSERT INTO settings (key, value) VALUES ('secret_key', ?)", (secret,)) conn.close() app.config.update( PB_CONF=conf, PB_DB=conf.get("database"), SECRET_KEY=secret, TESTING=testing, SESSION_COOKIE_NAME="patchbay_session", SESSION_COOKIE_SECURE=not testing, SESSION_COOKIE_HTTPONLY=True, SESSION_COOKIE_SAMESITE="Strict", PERMANENT_SESSION_LIFETIME=datetime.timedelta(hours=conf.getint("sessionhours", 12)), MAX_CONTENT_LENGTH=2 * 1024 * 1024, ) app.register_blueprint(auth.bp) app.register_blueprint(views.bp) app.register_blueprint(api.bp) app.teardown_appcontext(db.close) @app.context_processor def inject(): return {"csrf_token": auth.csrf_token, "user": g.get("user")} @app.after_request def headers(resp): resp.headers["Content-Security-Policy"] = ( "default-src 'self'; img-src 'self' data:; style-src 'self'; script-src 'self'; " "frame-ancestors 'none'; base-uri 'none'; form-action 'self'") resp.headers["X-Content-Type-Options"] = "nosniff" resp.headers["Referrer-Policy"] = "no-referrer" if not testing: resp.headers["Strict-Transport-Security"] = "max-age=31536000" return resp return app