Промпт: ревью кода
Разбор кода: ошибки, безопасность, читаемость, с исправлениями. Готовый промт на русском: скопируйте, подставьте свои данные и вставьте в GigaChat, YandexGPT, ChatGPT, Claude или Gemini.
Текст промпта
Что подставить в промт
| Поле | Пример |
|---|---|
| {language} | Python |
| {context} | обработчик формы обратной связи на сайте |
| {code} | def save(request): name = request.GET["name"] db.execute("INSERT INTO leads VALUES ('" + name + "')") return "ok" |
Ответы нейросетей на этот промпт
Ревью
Ошибки/Неправильная работа:
- Строка 2:
request.GETиспользуется для получения значения поляname. Если ключ"name"отсутствует в GET-параметрах запроса, возникнет исключениеKeyError.
Проблема: Код падает с ошибкой, если поле имени не передано.
Исправление:
name = request.GET.get("name") # возвращает None, если ключа нет
Безопасность:
- Строка 3: Прямая конкатенация строки SQL-запроса и данных из клиентского запроса — типичная ошибка, приводящая к SQL-инъекциям.
Проблема: Любой злоумышленник сможет отправить специально сформированный запрос, который выполнит произвольную команду в базе данных.
Исправление: Использовать параметризированные запросы через механизм плацебо (%s, %d) или аргументов функции базы данных:
db.execute("INSERT INTO leads VALUES (%s)", [name])
Производительность:
Нет критичных проблем с производительностью.
Читаемость:
- Нет комментариев и пояснений к коду.
- Название метода
save()слишком общее и ничего не говорит о сути выполняемой операции.
Что улучшить:
Добавьте комментарий перед функцией, чтобы объяснить её назначение и входные параметры.
Переименуйте метод на более осмысленное название, например: handle_feedback_form()
Что сделано хорошо?
- Простота реализации: минимум необходимого функционала для сохранения записи.
- Использование функциональности запросов HTTP (GET).
Итоговый исправленный вариант:
def handle_feedback_form(request):
"""
Обработчик формы обратной связи сайта.
Сохраняет имя отправителя в базу данных.
"""
name = request.GET.get("name")
if not name:
return "Ошибка: Имя не указано"
try:
db.execute("INSERT INTO leads VALUES (%s)", [name])
except Exception as e:
return f"Ошибка обработки запроса: {str(e)}"
return "ok"Ревью кода
Исходный код:
1 def save(request):
2 name = request.GET["name"]
3 db.execute("INSERT INTO leads VALUES ('" + name + "')")
4 return "ok"
1. Ошибки, из-за которых код может работать неправильно
Строка 2 — прямое обращение к request.GET["name"]
Проблема
name = request.GET["name"]
Если параметр name отсутствует в запросе, код упадёт с ошибкой KeyError.
Чем грозит
Пользователь получит ошибку сервера вместо понятного ответа.
Также это может засорять логи и мешать обработке некорректных запросов.
Исправленный фрагмент
name = request.GET.get("name")
if not name:
return "name is required"
Лучше также удалить лишние пробелы:
name = request.GET.get("name", "").strip()
if not name:
return "name is required"
Строка 3 — INSERT INTO leads VALUES (...) без указания колонок
Проблема
db.execute("INSERT INTO leads VALUES ('" + name + "')")
Запрос зависит от порядка и количества колонок в таблице leads.
Чем грозит
Если в таблицу добавят новую колонку или изменят порядок колонок, запрос начнёт падать или записывать данные не туда.
Исправленный фрагмент
db.execute(
"INSERT INTO leads (name) VALUES (?)",
[name]
)
Синтаксис параметров зависит от используемой БД/драйвера. Например:
Для SQLite:
db.execute(
"INSERT INTO leads (name) VALUES (?)",
(name,)
)
Для PostgreSQL через psycopg2:
cursor.execute(
"INSERT INTO leads (name) VALUES (%s)",
(name,)
)
2. Безопасность
Строка 3 — SQL-инъекция
Проблема
db.execute("INSERT INTO leads VALUES ('" + name + "')")
Значение name напрямую склеивается с SQL-запросом.
Чем грозит
Злоумышленник может передать значение, которое изменит SQL-запрос. Например:
name=Robert'); DROP TABLE leads;--
В зависимости от драйвера и настроек это может привести к порче данных, удалению таблиц или утечке информации.
Исправленный фрагмент
Нужно использовать параметризованные запросы:
db.execute(
"INSERT INTO leads (name) VALUES (?)",
(name,)
)
Или для драйверов с %s:
db.execute(
"INSERT INTO leads (name) VALUES (%s)",
(name,)
)
Главное — не склеивать SQL-строку вручную.
Строка 2 — использование GET для сохранения данных
Проблема
name = request.GET["name"]
Обработчик сохраняет данные, но читает их из GET.
Чем грозит
GET-запросы предназначены для получения данных, а не изменения состояния. Это может привести к проблемам:
- URL с персональными данными попадёт в историю браузера;
- параметры могут попасть в логи веб-сервера;
- ссылку можно случайно открыть повторно;
- сложнее защищаться от CSRF;
- поисковые роботы или превью-сервисы могут случайно вызвать URL.
Исправленный фрагмент
Лучше использовать POST:
name = request.POST.get("name", "").strip()
if not name:
return "name is required"
Строка 2 — отсутствует валидация пользовательского ввода
Проблема
Код принимает любое значение name.
Чем грозит
В базу могут попасть:
- пустые строки;
- слишком длинные значения;
- мусорные данные;
- потенциально опасные строки, которые позже могут вызвать XSS при отображении на сайте, если вывод не экранируется.
Исправленный фрагмент
name = request.POST.get("name", "").strip()
if not name:
return "name is required"
if len(name) > 100:
return "name is too long"
При выводе имени на страницах сайта его также нужно экранировать средствами шаблонизатора.
3. Производительность
Критичных проблем производительности в этом коротком фрагменте нет.
Единственное практическое улучшение — параметризованные запросы могут лучше переиспользоваться драйвером/БД, но основной плюс здесь всё же безопасность.
4. Читаемость и поддерживаемость
Строка 3 — SQL-запрос собирается вручную
Проблема
db.execute("INSERT INTO leads VALUES ('" + name + "')")
Такая строка хуже читается и сложнее поддерживается.
Чем грозит
Повышается риск ошибок при расширении запроса, добавлении новых полей или изменении схемы таблицы.
Исправленный фрагмент
db.execute(
"INSERT INTO leads (name) VALUES (?)",
(name,)
)
Возможный исправленный вариант
Вариант с POST, валидацией и параметризованным запросом:
def save(request):
name = request.POST.get("name", "").strip()
if not name:
return "name is required"
if len(name) > 100:
return "name is too long"
db.execute(
"INSERT INTO leads (name) VALUES (?)",
(name,)
)
return "ok"
Если используется PostgreSQL/psycopg2 или похожий драйвер, параметр может выглядеть так:
db.execute(
"INSERT INTO leads (name) VALUES (%s)",
(name,)
)
Что в коде сделано хорошо
- Код короткий и понятный.
- Логика обработчика простая: получить имя, сохранить его, вернуть ответ.
- Нет лишней сложности.
- Есть явное место, где выполняется запись в базу, поэтому код легко доработать: добавить валидацию, защиту от SQL-инъекций и обработку ошибок.
Ревью кода: обработчик формы обратной связи
1. Ошибки, из-за которых код работает неправильно
Строка 2: name = request.GET["name"]
Проблема: Форма обратной связи — это операция записи данных (создание лида), а используется GET. Это нарушает семантику HTTP (GET должен быть идемпотентным и не иметь побочных эффектов) и создаёт риски:
- Данные могут попасть в логи сервера, историю браузера, прокси-кэши.
- Поисковые боты или антивирусы, обходящие ссылки, могут случайно создавать записи (CSRF-подобный эффект).
- Параметр
nameможет отсутствовать →KeyError→ 500-я ошибка без обработки.
Исправление:
name = request.POST.get("name", "").strip()
if not name:
return HttpResponseBadRequest("Поле 'name' обязательно")
Строка 3: db.execute(...)
Проблема: Нет commit() (если драйвер БД этого требует) — запись может не сохраниться. Нет обработки исключений при сбое БД — упадёт с необработанным исключением.
Исправление:
try:
db.execute(query, (name,))
db.commit()
except DatabaseError as e:
logger.error("Failed to save lead: %s", e)
return HttpResponseServerError("Ошибка сохранения")
2. Безопасность
Строка 3: "INSERT INTO leads VALUES ('" + name + "')"
Проблема: SQL-инъекция. Критическая уязвимость. Злоумышленник может передать name, например:
'); DROP TABLE leads; --
и выполнить произвольный SQL, украсть или уничтожить данные всей БД.
Чем грозит: полная компрометация БД, утечка персональных данных, удаление таблиц.
Исправление (параметризованный запрос + явный список колонок):
db.execute("INSERT INTO leads (name) VALUES (%s)", (name,))
Указание колонок ((name)) также защищает от поломки запроса при изменении схемы таблицы.
Строка 2 (повторно): отсутствие валидации ввода
Проблема: Нет проверки длины, допустимых символов, экранирования при последующем отображении (если name потом выводится в админке — риск XSS, Stored XSS через поле формы).
Исправление:
name = name[:255] # ограничение длины
# при рендере в HTML — экранирование через шаблонизатор (autoescape),
# не собирать HTML вручную
Общее по безопасности: нет защиты от CSRF (для POST-форм) и от спам-ботов (rate limiting/капча) — стоит добавить на уровне фреймворка (@csrf_protect, throttling).
3. Производительность
Существенных проблем для одного запроса нет (сам код простой), но стоит отметить:
- Если
db.executeсоздаёт новое соединение на каждый вызов — лучше использовать пул соединений (connection pool), особенно при высокой нагрузке на форму. - Не критично в текущем виде, добавлять нечего без контекста инфраструктуры.
4. Читаемость
Строка 4: return "ok"
Проблема: Возвращается "сырая" строка вместо явного HTTP-ответа (в зависимости от фреймворка это может даже не работать как валидный response). Неясно, что ожидает клиент — JSON, текст, редирект?
Исправление:
return JsonResponse({"status": "ok"})
Итоговый исправленный вариант
import logging
from django.http import JsonResponse, HttpResponseBadRequest, HttpResponseServerError
from django.views.decorators.csrf import csrf_protect
from django.views.decorators.http import require_POST
logger = logging.getLogger(__name__)
@csrf_protect
@require_POST
def save(request):
name = request.POST.get("name", "").strip()
if not name:
return HttpResponseBadRequest("Поле 'name' обязательно")
name = name[:255]
try:
db.execute("INSERT INTO leads (name) VALUES (%s)", (name,))
db.commit()
except Exception as e:
logger.error("Failed to save lead: %s", e)
return HttpResponseServerError("Ошибка сохранения")
return JsonResponse({"status": "ok"})
Что сделано хорошо
- Функция лаконична и выполняет одну понятную задачу — легко читается с первого взгляда.
- Чёткое разделение: получение данных → запись → ответ, что облегчает доработку кода выше.
На этом плюсы, к сожалению, заканчиваются — код в текущем виде нельзя пускать в продакшен из-за SQL-инъекции.
Отлично, давайте проведем ревью этого фрагмента кода, следуя вашим приоритетам.
Ревью кода: Обработчик формы обратной связи
Исходный код:
def save(request):
name = request.GET["name"]
db.execute("INSERT INTO leads VALUES ('" + name + "')")
return "ok"
Находки по приоритетам:
1. Ошибки, из-за которых код работает неправильно
Находок нет. Код, скорее всего, будет работать, но с серьезными проблемами безопасности и надежности.
2. Безопасность
Находка 1: Использование request.GET для данных формы.
- Строка:
name = request.GET["name"] - В чём проблема: Данные формы обратной связи обычно отправляются методом POST, а не GET. Использование
request.GETозначает, что данные будут передаваться в URL, что нежелательно для конфиденциальности и может быть ограничено длиной URL. Если форма отправляется методом POST, тоrequest.GET["name"]вызовет ошибкуKeyError. - Чем грозит: Неправильная обработка запроса, ошибка при попытке доступа к несуществующему ключу, если форма отправлена методом POST.
- Исправленный фрагмент:
``python``
# Предполагаем, что форма отправляется методом POST
name = request.POST.get("name")
# Или, если данные могут приходить как GET, так и POST, но POST предпочтительнее:
# name = request.POST.get("name") or request.GET.get("name")
Находка 2: SQL-инъекция.
- Строка:
db.execute("INSERT INTO leads VALUES ('" + name + "')") - В чём проблема: Переменная
nameнапрямую конкатенируется со строкой SQL-запроса без какой-либо очистки или параметризации. - Чем грозит: Это критическая уязвимость SQL-инъекции. Злоумышленник может ввести в поле
nameстроку типа' OR 1=1; DROP TABLE leads; --или' OR 1=1; SELECT * FROM users; --, что приведет к выполнению произвольных SQL-команд, включая удаление таблиц, извлечение конфиденциальных данных или изменение данных. - Исправленный фрагмент:
``python``
# Используйте параметризованные запросы (prepared statements)
# Предполагая, что 'db' - это объект, поддерживающий параметризованные запросы (например, psycopg2, sqlite3, MySQLdb)
db.execute("INSERT INTO leads VALUES (?)", (name,)) # Для SQLite
# Или для других баз данных:
# db.execute("INSERT INTO leads VALUES (%s)", (name,)) # Для PostgreSQL/MySQL
# db.execute("INSERT INTO leads VALUES (:name)", {'name': name}) # Для Oracle/SQLAlchemy
Находка 3: Отсутствие проверки ввода.
- Строка:
name = request.GET["name"] - В чём проблема: Значение
nameне проверяется на тип, длину, наличие специальных символов или пустоту. - Чем грозит:
- Пустые значения: Если
nameпустое, в базу данных будет записана пустая строка, что может быть нежелательно. - Слишком длинные значения: Если
nameочень длинное, оно может превысить лимит длины поля в базе данных, вызвав ошибку или усечение данных. - Нежелательные символы: Хотя параметризованные запросы решают проблему SQL-инъекций, все равно может быть желательно ограничить символы, разрешенные в имени, для предотвращения XSS (если имя потом отображается на странице без экранирования) или просто для поддержания чистоты данных.
- Исправленный фрагмент:
```python
name = request.POST.get("name") # Используем .get() для безопасного доступа
if not name:
return "Error: Name cannot be empty", 400 # Возвращаем ошибку и соответствующий статус
# Ограничение длины
MAX_NAME_LENGTH = 100
if len(name) > MAX_NAME_LENGTH:
return f"Error: Name exceeds maximum length of {MAX_NAME_LENGTH} characters", 400
# Очистка/валидация (опционально, в зависимости от требований)
# Например, удаление лишних пробелов
name = name.strip()
# Возможно, проверка на допустимые символы (регулярные выражения)
# import re
# if not re.match(r"^[a-zA-Zа-яА-Я\s\-']+$", name):
# return "Error: Invalid characters in name", 400
# Затем параметризованный запрос
db.execute("INSERT INTO leads VALUES (?)", (name,))
```
Находка 4: Секреты в коде (неявная, но важная).
- Строка:
db.execute(...) - В чём проблема: Предполагается, что
db- это глобальная переменная или переменная, которая каким-то образом инициализируется. Если подключение к базе данных (логин, пароль, хост) жестко закодировано в файле, это является проблемой безопасности. - Чем грозит: Компрометация учетных данных базы данных при утечке кода.
- Исправленный фрагмент: (Невозможно показать напрямую, так как это касается инициализации
db, но общий принцип такой)
``python``
# Конфигурация базы данных должна храниться в переменных окружения
# или в отдельном файле конфигурации, который не попадает в систему контроля версий.
# Например:
# import os
# DB_HOST = os.getenv("DB_HOST", "localhost")
# DB_USER = os.getenv("DB_USER", "user")
# DB_PASSWORD = os.getenv("DB_PASSWORD", "password")
# db = connect_to_db(DB_HOST, DB_USER, DB_PASSWORD)
3. Производительность
Находок нет. Для такого простого запроса производительность не является критической проблемой. Основные проблемы здесь - безопасность и корректность.
4. Читаемость
Находка 1: Возвращаемое значение.
- Строка:
return "ok" - В чём проблема: Возвращение простой строки "ok" не является стандартным или информативным способом ответа для веб-приложения. В реальном веб-фреймворке вы бы возвращали объект ответа (например,
HttpResponseв Django,Responseво Flask) с соответствующим HTTP-статусом. - Чем грозит: Клиенту (браузеру, API-клиенту) сложнее понять, что произошло. Отсутствие HTTP-статуса (например, 200 OK, 400 Bad Request, 500 Internal Server Error) затрудняет обработку ответа.
- Исправленный фрагмент:
```python
# Предполагая, что это часть веб-фреймворка (например, Flask):
from flask import jsonify, make_response
# ...
# В случае успеха
return make_response(jsonify({"message": "Lead saved successfully"}), 200)
# В случае ошибки валидации
# return make_response(jsonify({"error": "Name cannot be empty"}), 400)
# В случае внутренней ошибки сервера
# return make_response(jsonify({"error": "Internal server error"}), 500)
```
Находка 2: Отсутствие обработки исключений.
- Строка:
db.execute(...) - В чём проблема: Если выполнение SQL-запроса завершится ошибкой (например, из-за проблем с подключением к БД, нарушением ограничений полей, ошибкой синтаксиса), код упадет, и пользователь получит сообщение об ошибке сервера вместо корректного ответа.
- Чем грозит: Нестабильность приложения, плохой пользовательский опыт, потенциальная утечка информации об ошибках сервера.
- Исправленный фрагмент:
``python``
try:
db.execute("INSERT INTO leads VALUES (?)", (name,))
# Если используется транзакция, нужно закоммитить
# db.commit()
return make_response(jsonify({"message": "Lead saved successfully"}), 200)
except Exception as e:
# Если используется транзакция, нужно откатить
# db.rollback()
# Логирование ошибки
print(f"Database error: {e}")
return make_response(jsonify({"error": "Failed to save lead"}), 500)
Что в коде сделано хорошо:
- Простота и прямолинейность: Код очень прост и прямолинеен в своей попытке выполнить задачу. Для небольшого скрипта, не предназначенного для продакшена, это может быть преимуществом.
- Единообразный стиль: Хотя код очень короткий, он следует единообразному стилю (например, использование конкатенации строк, если бы не проблема безопасности).
- Ясное намерение: Сразу понятно, что код пытается сделать: взять имя из запроса и сохранить его в базу данных.
Итоговый исправленный и улучшенный код (пример с Flask):
from flask import Flask, request, jsonify, make_response
import sqlite3
import re # Для валидации
app = Flask(__name__)
# Функция для получения подключения к БД (для примера используем SQLite)
def get_db_connection():
conn = sqlite3.connect('leads.db')
conn.row_factory = sqlite3.Row # Для доступа к колонкам по имени
return conn
# Инициализация базы данных (создание таблицы, если её нет)
def init_db():
conn = get_db_connection()
conn.execute('''
CREATE TABLE IF NOT EXISTS leads (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL
);
''')
conn.commit()
conn.close()
# Вызываем инициализацию при старте приложения
init_db()
@app.route('/save_lead', methods=['POST']) # Указываем, что ожидаем POST-запрос
def save_lead():
# 1. Получение данных (предполагаем POST)
name = request.form.get("name") # Используем .get() для безопасного доступа к данным формы
# 2. Валидация ввода
if not name:
return make_response(jsonify({"error": "Name cannot be empty"}), 400)
name = name.strip() # Удаляем лишние пробелы
MAX_NAME_LENGTH = 100
if len(name) > MAX_NAME_LENGTH:
return make_response(jsonify({"error": f"Name exceeds maximum length of {MAX_NAME_LENGTH} characters"}), 400)
# Опциональная валидация символов
if not re.match(r"^[a-zA-Zа-яА-Я\s\-']+$", name):
return make_response(jsonify({"error": "Invalid characters in name. Only letters, spaces, hyphens, and apostrophes are allowed."}), 400)
conn = None # Инициализируем conn для finally блока
try:
conn = get_db_connection()
# 3. Безопасное выполнение запроса с параметризацией
conn.execute("INSERT INTO leads (name) VALUES (?)", (name,))
conn.commit() # Коммит транзакции
return make_response(jsonify({"message": "Lead saved successfully"}), 200)
except sqlite3.Error as e: # Ловим специфические ошибки базы данных
if conn:
conn.rollback() # Откатываем транзакцию в случае ошибки
print(f"Database error: {e}") # Логируем ошибку на сервере
return make_response(jsonify({"error": "Failed to save lead due to a database error."}), 500)
except Exception as e: # Ловим любые другие неожиданные ошибки
print(f"An unexpected error occurred: {e}")
return make_response(jsonify({"error": "An unexpected error occurred."}), 500)
finally:
if conn:
conn.close() # Всегда закрываем соединение с БД
if __name__ == '__main__':
app.run(debug=True) # debug=True только для разработки
Этот улучшенный код решает все основные проблемы безопасности, надежности и читаемости, при этом оставаясь достаточно простым для понимания.
Советы
- Не вставляйте в чат ключи API и пароли из кода: замените на заглушки.
- Скопируйте промпт кнопкой выше.
- Замените поля в фигурных скобках своими данными.
- Отправьте в нейросеть и сравните ответ с примером на этой странице.
Подробнее о структуре хорошего запроса: гид AI University.
Похожие промпты
Промпты работают лучше, когда понимаешь модель
56 официальных курсов Anthropic, OpenAI, Google и других на русском: первые уроки бесплатно, без VPN. Наборы промптов входят в полный доступ.