Есть система, которая дает юзерам возможность работать с файлами в браузере. Стек стандартный: Java, Spring, React, Postgres. Файлы хранятся в файловой системе на бэке, метаданные файлов в БД. Команда реализовала фичу - переименование файла. Этот метод по сути ключевой для переименования файла на бэке. Нужно провести его ревью, где необходимо выявить потенциальные проблемы в существующем коде и предложить варианты их исправления. @Transactional public void process(String oldName, String newName) { Long id = exec("select id from file where name='" + oldName + "'"); processFile(oldName, newName); // переименование файла на диске exec("update file set name='" + newName + "' where id = " + id); }
Ответ от нейросети
sobes.tech AI
В представленном методе есть несколько проблем:
-
SQL-инъекция: Использование конкатенации строк для формирования SQL-запросов (
"select id from file where name='" + oldName + "'") уязвимо для SQL-инъекций. -
Отсутствие обработки ошибок: Если файл с именем
oldNameне найден, методexecможет вернуть null или вызвать ошибку, что не обрабатывается. -
Потенциальная рассинхронизация: Переименование файла на диске и обновление записи в БД не атомарны, несмотря на аннотацию
@Transactional, так как файловая система не участвует в транзакции БД. -
Отсутствие проверки успешности переименования файла: Если
processFileне сработает, база данных уже будет обновлена, что приведет к рассинхронизации. -
Небезопасное обновление имени файла: Нет проверки, что
newNameне конфликтует с уже существующими именами.
Рекомендации по исправлению:
- Использовать параметризованные запросы или ORM (например, JPA) для безопасного доступа к базе.
- Добавить проверку существования файла и обработку ошибок.
- Сделать переименование файла и обновление БД атомарными с помощью механизма компенсации или отложенного обновления.
- Проверять, что новое имя не занято.
- Логировать операции и ошибки.
Пример исправленного кода с использованием JPA и обработки ошибок:
@Transactional
public void process(String oldName, String newName) {
Optional<FileEntity> fileOpt = fileRepository.findByName(oldName);
if (!fileOpt.isPresent()) {
throw new FileNotFoundException("File with name " + oldName + " not found");
}
if (fileRepository.existsByName(newName)) {
throw new IllegalArgumentException("File name " + newName + " already exists");
}
FileEntity file = fileOpt.get();
try {
processFile(oldName, newName); // переименование на диске
} catch (IOException e) {
throw new RuntimeException("Failed to rename file on disk", e);
}
file.setName(newName);
fileRepository.save(file);
}
Такой подход повышает безопасность и надежность операции.