From 8dfab7613d0f026634e1ce338db4ce273c9242d8 Mon Sep 17 00:00:00 2001 From: lekss361 <47113017+lekss361@users.noreply.github.com> Date: Thu, 14 May 2026 23:29:12 +0300 Subject: [PATCH] fix(nspd-geo): replace f-string bulk INSERT with parametrized batch execute (#123) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per code review audit (May 14): real SQL injection vector в `enqueue_geo_job`. Manual `chr(39)*2` quote escape НЕ injection-safe — не защищает от backslash escape, Unicode quote tricks. Если cad_num когда-либо придёт из user input — exploitable. ## Change `backend/app/workers/tasks/nspd_geo.py:298-310` — заменил f-string VALUES concatenation на SQLAlchemy `text(...)` + list of dicts (psycopg v3 handles batch executemany под капотом). Pattern consistent с остальными INSERT'ами в этом же файле (_log, _start_job, _save_quarter, _save_parcel, _save_building). ## Preserved - ON CONFLICT (job_id, cad_num, thematic_id) DO NOTHING — verbatim - Return value (int(job_id)) - Surrounding tx/commit/close logic - DB schema, function signature, caller interface — unchanged ## Tests - No test_nspd_geo.py существует — skipped - ruff check passed ## Vault `fixes/Bug_Nspd_Geo_Sql_Injection_May14.md` — created (status: resolved). No backward compat / migration concerns — pure in-place query rewrite. Co-authored-by: lekss361 --- backend/app/workers/tasks/nspd_geo.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/backend/app/workers/tasks/nspd_geo.py b/backend/app/workers/tasks/nspd_geo.py index 96cd9882..dc3fcda9 100644 --- a/backend/app/workers/tasks/nspd_geo.py +++ b/backend/app/workers/tasks/nspd_geo.py @@ -295,17 +295,18 @@ def enqueue_geo_job( }, ).scalar_one() - # Bulk INSERT targets + # Bulk INSERT targets — parametrized to prevent SQL injection if cad_nums_with_thematic: - values = ",".join( - f"({job_id}, '{c.replace(chr(39), chr(39)*2)}', {t}, 'pending')" - for c, t in cad_nums_with_thematic - ) db.execute( text( - f"INSERT INTO nspd_geo_targets (job_id, cad_num, thematic_id, status) " - f"VALUES {values} ON CONFLICT (job_id, cad_num, thematic_id) DO NOTHING" - ) + "INSERT INTO nspd_geo_targets (job_id, cad_num, thematic_id, status) " + "VALUES (:job_id, :cad_num, :thematic_id, 'pending') " + "ON CONFLICT (job_id, cad_num, thematic_id) DO NOTHING" + ), + [ + {"job_id": job_id, "cad_num": c, "thematic_id": t} + for c, t in cad_nums_with_thematic + ], ) db.commit() return int(job_id)