From 23688da90a784885444d539583446bb30bba54fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9CNaeel=E2=80=9D?= Date: Fri, 31 Jul 2026 18:20:33 +0400 Subject: [PATCH] =?UTF-8?q?v1.2.22:=203rd=20audit=20fixes=20=E2=80=94=20Un?= =?UTF-8?q?iqueViolation=E2=86=92409,=20lock=5Fcheck=20no-db=E2=86=92False?= =?UTF-8?q?,=20escName=20add=20&=20escape?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- site/app.py | 2 +- site/db/scenario_defs.py | 2 +- site/routes/api_scenario_run.py | 4 ++++ site/static/js/scenario-list.js | 2 +- 4 files changed, 7 insertions(+), 3 deletions(-) diff --git a/site/app.py b/site/app.py index 803cf82..800b58b 100644 --- a/site/app.py +++ b/site/app.py @@ -32,7 +32,7 @@ from routes.api_scenario_defs import bp_defs as api_scenario_defs_bp # Версия — показывается в топбаре UI. Меняется при КАЖДОМ изменении кода. # Нужна для фильтрации истории (пользователь видит только записи своей версии). -VERSION = "1.2.21" +VERSION = "1.2.22" # Flask-приложение с Jinja2-шаблонами из папки templates/ app = Flask(__name__, template_folder="templates", static_folder="static") diff --git a/site/db/scenario_defs.py b/site/db/scenario_defs.py index 48fa687..3d4f859 100644 --- a/site/db/scenario_defs.py +++ b/site/db/scenario_defs.py @@ -218,7 +218,7 @@ def lock_check(client_id, stand): False — нельзя (уже есть RUNNING) → 409 Conflict""" conn = get_conn() if not conn: - return True # без БД — разрешаем (partial unique index недоступен) + return False # без БД — не разрешаем (partial unique index недоступен) try: cur = conn.cursor() cur.execute(""" diff --git a/site/routes/api_scenario_run.py b/site/routes/api_scenario_run.py index aedee0b..cde72c2 100644 --- a/site/routes/api_scenario_run.py +++ b/site/routes/api_scenario_run.py @@ -74,6 +74,10 @@ def api_scenario_run(): conn.commit() cur.close() except Exception as e: + # Unique violation (23505) → другая параллельная вставка уже создала RUNNING + if hasattr(e, 'pgcode') and e.pgcode == '23505': + conn.rollback() + return jsonify({"error": "Another scenario is already running"}), 409 print(f"[API] create scenario_run error: {e}", flush=True) conn.rollback() return jsonify({"error": "Failed to create scenario_run"}), 500 diff --git a/site/static/js/scenario-list.js b/site/static/js/scenario-list.js index 1a8abf7..a4141a1 100644 --- a/site/static/js/scenario-list.js +++ b/site/static/js/scenario-list.js @@ -125,7 +125,7 @@ async function toggleScenarioSteps(defId) { // \ → \\ (backslash) // ' → \' (terminate JS string literal) // HTML-escape уже не нужен — внутри JS-строки в атрибуте HTML-теги не парсятся. - const escName = def.name.replace(/\\/g,'\\\\').replace(/'/g,"\\'").replace(/"/g,'"'); + const escName = def.name.replace(/&/g,'&').replace(/\\/g,'\\\\').replace(/'/g,"\\'").replace(/"/g,'"'); html += ``; html += ``; html += ``;