From 6d05b40021479a35c0f087864fa4c277da635eb7 Mon Sep 17 00:00:00 2001 From: ComputerTech Date: Fri, 27 Mar 2026 12:16:03 +0000 Subject: [PATCH] Fix: UTC expiry, ID-collision retry, init_db leak, view_paste race, debug warning, Prism SRI hashes --- app.py | 76 ++++++++++++++++++++++++++++----------------- templates/base.html | 62 ++++++++++++++++++------------------ 2 files changed, 79 insertions(+), 59 deletions(-) diff --git a/app.py b/app.py index a449e5e..b2e39a0 100644 --- a/app.py +++ b/app.py @@ -2,6 +2,7 @@ import json import os import re import sqlite3 +import sys import uuid import datetime from flask import Flask, render_template, request, jsonify, abort, Response @@ -32,6 +33,10 @@ _ui = CFG['ui'] app = Flask(__name__) app.config['SECRET_KEY'] = _server.get('secret_key', 'change-me') +if _server.get('debug', False): + print('WARNING: debug=true is set in config.json — never use debug mode in production!', + file=sys.stderr, flush=True) + DATABASE = _db_cfg.get('path', 'bastebin.db') MAX_ENCRYPTED_BYTES = _pastes.get('max_size_bytes', 2 * 1024 * 1024) MAX_TITLE_BYTES = 200 @@ -73,23 +78,25 @@ def get_db_connection(): return conn def init_db(): - conn = sqlite3.connect(DATABASE) - cursor = conn.cursor() - cursor.execute("PRAGMA table_info(pastes)") - columns = {row[1] for row in cursor.fetchall()} - if columns and 'content' in columns and 'encrypted_data' not in columns: - cursor.execute("DROP TABLE pastes") - cursor.execute(''' - CREATE TABLE IF NOT EXISTS pastes ( - id TEXT PRIMARY KEY, - encrypted_data TEXT NOT NULL, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - expires_at TIMESTAMP, - views INTEGER DEFAULT 0 - ) - ''') - conn.commit() - conn.close() + conn = sqlite3.connect(DATABASE) + try: + cursor = conn.cursor() + cursor.execute("PRAGMA table_info(pastes)") + columns = {row[1] for row in cursor.fetchall()} + if columns and 'content' in columns and 'encrypted_data' not in columns: + cursor.execute("DROP TABLE pastes") + cursor.execute(''' + CREATE TABLE IF NOT EXISTS pastes ( + id TEXT PRIMARY KEY, + encrypted_data TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + expires_at TIMESTAMP, + views INTEGER DEFAULT 0 + ) + ''') + conn.commit() + finally: + conn.close() # ── Helpers ─────────────────────────────────────────────────────────────────── @@ -112,7 +119,7 @@ def _get_paste_or_abort(paste_id): abort(404) if paste['expires_at']: expires_at = datetime.datetime.fromisoformat(paste['expires_at']) - if expires_at < datetime.datetime.now(): + if expires_at < datetime.datetime.utcnow(): abort(410) return paste @@ -165,16 +172,24 @@ def create_paste(): '1week': datetime.timedelta(weeks=1), '1month': datetime.timedelta(days=30), } - expires_at = datetime.datetime.now() + delta_map[expires_in] + expires_at = datetime.datetime.utcnow() + delta_map[expires_in] - paste_id = generate_paste_id() + paste_id = None conn = get_db_connection() try: - conn.execute( - 'INSERT INTO pastes (id, encrypted_data, expires_at) VALUES (?, ?, ?)', - (paste_id, store_data, expires_at) - ) - conn.commit() + for _ in range(5): + paste_id = generate_paste_id() + try: + conn.execute( + 'INSERT INTO pastes (id, encrypted_data, expires_at) VALUES (?, ?, ?)', + (paste_id, store_data, expires_at) + ) + conn.commit() + break + except sqlite3.IntegrityError: + continue + else: + return jsonify({'error': 'Service temporarily unavailable'}), 503 finally: conn.close() return jsonify({'paste_id': paste_id, 'url': f'/{paste_id}'}) @@ -183,12 +198,17 @@ def create_paste(): def view_paste(paste_id): if not _PASTE_ID_RE.match(paste_id): abort(404) - paste = _get_paste_or_abort(paste_id) - conn = get_db_connection() + conn = get_db_connection() try: + paste = conn.execute('SELECT * FROM pastes WHERE id = ?', (paste_id,)).fetchone() + if not paste: + abort(404) + if paste['expires_at']: + expires = datetime.datetime.fromisoformat(paste['expires_at']) + if expires < datetime.datetime.utcnow(): + abort(410) conn.execute('UPDATE pastes SET views = views + 1 WHERE id = ?', (paste_id,)) conn.commit() - paste = conn.execute('SELECT * FROM pastes WHERE id = ?', (paste_id,)).fetchone() finally: conn.close() return render_template('view.html', paste=paste) diff --git a/templates/base.html b/templates/base.html index 3cb9910..dac9015 100644 --- a/templates/base.html +++ b/templates/base.html @@ -11,8 +11,8 @@ document.documentElement.setAttribute('data-theme','dark'); })(); - - + + @@ -35,35 +35,35 @@ {% block content %}{% endblock %} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% block scripts %}{% endblock %}