[1] major Файл: SERVER_SETUP.sh Строка: 21‑34 Тип: bug Описание: Проверка порта ограничивается только запретом 3000/3001, но не гарантирует, что выбранный порт — число, находится в легитимном диапазоне и свободен. Если пользователь введёт строки, пустую строку или порт, уже занятой другим сервисом (например, 3002 всё ещё занят), приложение сломается дальше по скрипту, а также не исключено, что будет выбран порт 0 или отрицательное число. Решение: добавить цикл валидации (`read -r`), проверять ввод на регулярку `^[1-9][0-9]{1,4}$`, разрешать только порты ≥1024 и ≤65535, и перепроверять (через `ss`/`lsof` или `nc -z`) что порт свободен перед записью. При невалидном вводе повторно спрашивать порт вместо немедленного `exit`. [2] major Файл: SERVER_SETUP.sh Строка: 39‑56 Тип: security Описание: Команды `sed -i "s|^PORT=.*|PORT=$SELECTED_PORT|"` и аналог по `NEXTAUTH_URL` строятся с прямой подстановкой `SELECTED_PORT`, полученного от пользователя, без экранирования. Любая кавычка, обратная косая или символ `;` разрежет строку и позволит выполнить произвольную команду (`sed -i "…PORT=3002\" && rm -rf /"`). То же касается вставки `NEXTAUTH_URL`, если когда-либо добавится вводимый URL. Решение: валидировать порт как цифры, использовать `printf`/`sed` с безопасной подстановкой (например, `printf '%q' "$SELECTED_PORT"` или менять файл через Node/awk), либо использовать `perl -0pe`/ `python` для изменения строки без запуска shell-кода с пользовательскими токенами. [3] major Файл: ai-review.js Строка: 145‑172 Тип: bug Описание: После проверки существования `deepseek_review_prompt.txt` файл читается `fs.readFileSync` без оболочки `try/catch`. Если файл существует, но нечитаем (например, прав доступа нет), весь скрипт упадёт с необработанным исключением и потеряет контекст. Решение: обернуть `fs.readFileSync` (и аналогично другие синхронные чтения промптов) в `try/catch`, логировать ошибку и переходить к следующему источнику (например, fallback на `codex_review_prompt.txt`), чтобы приложение завершалось контролируемо и с понятным сообщением. [4] major Файл: ai-review.js Строка: 326‑344 Тип: security Описание: Бранч `targetBranch` подставляется «как есть» в строку `execSync("git diff --name-only … origin/${targetBranch}...HEAD")`. Это окружная переменная CI, её можно подменить на «ветку» вида `master; rm -rf /`, что приведёт к выполнению произвольных команд при запуске скрипта. Решение: не вызывать `execSync` со строкой, содержащей внешние значения, а использовать `child_process.execFileSync`/`spawnSync` с массивом аргументов, либо проверять `targetBranch` регуляркой `[A-Za-z0-9._/-]+` и экранировать через `shQuote` перед подстановкой. [5] critical Файл: ai-review.js Строка: 1275‑1295 Тип: security Описание: `mkdirCommand` строится как `sshpass -p "${CONFIG.sshPassword}" ssh … "mkdir -p ${serverPath}"` без никакого экранирования. Попадание в `CONFIG.sshPassword` символов `"`, `;` или `$(...)` приведёт к выполнению произвольной команды на контролируемом хосте и утечке пароля через таблицу процессов. Решение: не конструировать shell-команду вручную, а запускать `sshpass`/`ssh` через `child_process.spawn`/`execFile`, передавая каждый аргумент отдельно. Либо перейти на аутентификацию по ключу и избегать `sshpass`/пароля полностью, чтобы исключить необходимость вставки секретов в командную строку. [6] critical Файл: ai-review.js Строка: 1310‑1325 Тип: security Описание: Аналогично предыдущему, `scpCommand` подставляет `CONFIG.sshPassword`, пути `tempFile`, `fullServerPath` и имя пользователя/хоста в одну строку, что делает команду уязвимой к инъекциям (`file"` или `host; rm`). Кроме того, пароль снова попадает в `ps`. Решение: использовать `child_process.spawn` с массивом аргументов (или `ssh -i`), предварительно экранировать пути через `path.basename` + `path.resolve`, и по возможности отказаться от пароля в пользу ключей/`ssh-agent`. [7] major Файл: ai-review.js Строка: 62‑70 Тип: code-smell Описание: Функция `log` содержит цепочку из четырёх `if/else if`, что увеличивает цикломатическую сложность и затрудняет расширение (например, добавление нового уровня логирования потребует расширять ветки в нескольких местах). Решение: заменить на мапу `const LEVEL_FN = { error: logger.error, warning: logger.warning, … }` и просто `LEVEL_FN[level] ?? logger.info`. Такое упрощение облегчает поддержку и исключает ошибки, когда уровень пропущен. [8] info Файл: ai-review.js Строка: 1106‑1144 Тип: bug (false positive) Описание: В описанной зоне JSON-ответы DeepSeek уже обрабатываются: есть обёртка `try/catch` вокруг `JSON.parse`, проверка `statusCode >= 400`, логирование структуры и генерация понятного сообщения при ошибках. Поэтому утверждение «отсутствует обработка ошибок при парсинге JSON» не подтверждается, исправления не требуется. [9] major Файл: ai-review.js Строка: 416‑440 Тип: performance Описание: `buildFilesContentPrompt` загружает содержимое всех изменённых файлов в память целиком и затем конкатенирует их в один промпт, даже если объём сотни мегабайт. Если в `changedFiles` попадёт файл JSON или изображения (в случае ошибок в `isLocalFile`), это приведёт к значительному потреблению памяти и замедлению. Решение: ограничить размер читаемых файлов (например, пропустить >1 МБ и логировать), читать и отправлять кусками (stream), либо включать в промпт только релевантные участки (`head`, `diff`, `context`), чтобы не хранить весь файл одновременно.