Termux стал практичной средой для автоматизации задач на Android: от обслуживания устройств до локальной аналитики и интеграций с командной строкой. Однако Bash‑скрипты часто пишутся «быстро и по месту», а затем начинают выполняться в более сложных сценариях, где растут риски: подмена параметров, нежелательные расширения переменных, небезопасная работа с временными файлами и появление гонок (race‑conditions).
В этой статье разберём подход к статическому анализу и hardening Bash‑скриптов в Termux с опорой на три уровня защиты: ShellCheck (логические и безопасностные предупреждения), shfmt (единообразие и предотвращение «скрытых» ошибок форматирования) и custom‑lint правила (локальные, специфичные для ваших скриптов стандарты).
Почему статический анализ важнее «проверить вручную»
Многие уязвимости в Bash появляются не из‑за очевидных «дыр», а из‑за того, как интерпретируются переменные, кавычки, подстановки, редиректы и временные файлы. Статический анализ помогает обнаружить типовые ошибки до выполнения скрипта, а также ускоряет ревью кода: предупреждения становятся единым языком команды.
Отдельная проблема Bash — чувствительность к обработке строк. Любая команда, формируемая из строк, где присутствуют пользовательские входные данные, может привести к инъекциям команд. Параллельное выполнение (через cron, background‑задачи или вызовы из нескольких сессий Termux) способно проявить race‑conditions при создании/использовании файлов.
Базовый hardening Bash: настройки, которые стоит включить всегда
Прежде чем запускать линтеры, полезно установить «защитный режим» поведения shell. Это уменьшает вероятность того, что скрипт продолжит работу в неожиданном состоянии.
#!/data/data/com.termux/files/usr/bin/env bash
set -Eeuo pipefail
IFS=$'
\t'
# Рекомендуется явно включать безопасный поиск по PATH только если это нужно.
# export PATH="..." # при необходимостиКлючевые моменты:
set -e— остановка при ошибках (осторожно, если у вас есть ожидаемые ошибки в проверках).-u— запрет на неинициализированные переменные (снижает риск «подстановки пустоты»).-o pipefail— корректная обработка ошибок в конвейерах.IFS— уменьшает сюрпризы при разбиении строк.
Если скрипт должен быть совместим с sh, переносите стиль аккуратно: часть опций и синтаксиса может отличаться.
Установка и запуск ShellCheck в Termux
ShellCheck — это статический анализатор Bash/Shell, который указывает на проблемные места: кавычки, небезопасные паттерны, неиспользуемые переменные, спорные конструкции, ошибки логики.
Пример ориентировочной установки и запуска (команды могут отличаться по версии пакетов в вашем окружении):
pkg update
pkg install shellcheckДалее анализируйте скрипт:
shellcheck ./script.shДля более «жёсткого» подхода можно собирать отчёт и фиксировать порог качества (в реальном процессе CI) — но даже простой прогон shellcheck регулярно даёт заметный эффект.
Какие категории проблем ShellCheck особенно полезны для hardening
Для задач безопасности и надёжности чаще всего встречаются следующие классы предупреждений:
- Небезопасные кавычки — когда переменные используются без
"...", строки могут расширяться неожиданно, включая разбиение на слова и globbing. - Инъекции команд — когда данные попадают в конструкцию выполнения команд (например, в
evalили при формировании команд строкой). - Проблемы с IFS/word splitting — разделение на слова ломает логику и может привести к неверным аргументам.
- Ошибки с временными файлами — когда файл создаётся без уникальности/проверок, появляется шанс race‑condition или перезаписи.
- Параллельное выполнение и TOCTOU — проверка «существует/не существует» и затем «создать» между шагами может быть атакована гонкой или просто сломана конкурентностью.
Пример: типичная ошибка и исправление
Наглядно: если переменная подставляется в команду без кавычек, скрипт становится чувствительным к пробелам и спецсимволам.
# Плохо (пример)
file=$1
rm -f $file
# shellcheck обычно будет ругаться на word splitting/globbing.
# Хорошо
file="$1"
rm -f -- "$file"Исправления:
- Кавычки вокруг аргумента.
- Флаг
--помогает не воспринимать путь как опции команды.
shfmt: единый стиль и меньше «скрытых» ошибок
shfmt форматирует POSIX‑like shell (включая значимую часть Bash‑стиля), приводя код к единообразию. На практике это помогает:
- уменьшить вероятность пропуска скобок/переносов, которые иначе «теряются» в визуальном чтении;
- сделать diff предсказуемым и облегчить ревью;
- повысить дисциплину: сначала исправляем стиль, затем линтим.
Установка (ориентировочно):
pkg install shfmtФорматирование скрипта:
shfmt -w ./script.shЕсли у вас в репозитории несколько файлов, удобно прогонять по glob‑паттерну (учитывайте оболочку и кавычки в вашем окружении).
Custom‑lint: как выстроить правила под «ваши» риски
ShellCheck — общий инструмент. Но в больших задачах полезны custom‑lint правила, которые учитывают ваш стиль и конкретные угрозы: например, запрет на создание временных файлов в «общих» местах или запрет на eval без исключений.
Пример простого custom‑lint на Bash (скрипт‑проверка, которая ищет запрещённые конструкции):
#!/data/data/com.termux/files/usr/bin/env bash
set -Eeuo pipefail
target="${1:-}"
if [ -z "$target" ] || [ ! -f "$target" ]; then
echo "Usage: $0 path/to/script.sh" >&2
exit 2
fi
# Пример: запрет на eval (как класс риска)
grep -nE '\beval\b' "$target" && {
echo "Custom-lint: запрещено использовать eval. Обсудите альтернативу." >&2
exit 1
}
# Пример: запрет на /tmp без уникальности (очень условно)
grep -nE '/tmp/' "$target" && {
echo "Custom-lint: проверьте работу с /tmp на race-condition и коллизии." >&2
exit 1
}
echo "Custom-lint: OK"Важно: такие правила лучше делать точными и постепенно ужесточать. Иначе линтер начнёт «шуметь» и команда будет игнорировать предупреждения.
Предотвращение инъекций команд в Bash
Ключевые практики:
- Не используйте
evalдля данных пользователя. Если требуется «динамика» — лучше применять маппинг:case, списки команд или массивы аргументов. - Кавычки для всех переменных, которые попадают в команды:
cmd -- "$var". - Не конструируйте команды строкой. Предпочитайте вызов с аргументами через массивы:
cmd -- "$arg1" "$arg2". - Валидация входа: если аргумент должен быть числом — проверяйте регулярным выражением или арифметикой.
Пример безопасного паттерна (без «склеивания строк»):
mode="${1:-}"
case "$mode" in
fast|safe)
;;
*)
echo "Unknown mode: $mode" >&2
exit 2
;;
esac
# Аргументы передаются как отдельные элементы
set -- "--mode" "$mode"
mytool "$@"Предотвращение race‑conditions: временные файлы, TOCTOU и конкуренция
Race‑conditions в shell чаще всего проявляются при:
- проверке
[ -e file ], затем созданииtouch file— между действиями состояние может измениться; - работе с фиксированными путями во
/tmpили в текущей директории; - использовании файлов без атомарности.
Практики:
- Используйте уникальные временные имена (например, через
mktemp). - Если нужно блокирование — применяйте file-lock подходы (в зависимости от требований вашего сценария).
- Старайтесь избегать TOCTOU: «сначала проверь — потом сделай» заменяйте на «сделай и обработай результат».
Пример: корректная работа с временным файлом через mktemp:
tmpfile="$(mktemp -t termux-script-XXXXXX)"
trap 'rm -f -- "$tmpfile"' EXIT
# дальнейшая работа
printf '%s
' "payload" > "$tmpfile"Подход с trap помогает избежать утечек временных файлов при ошибках.
Практический пайплайн: как встроить линтинг в ваш рабочий процесс Termux
Рекомендованный цикл:
- Форматирование: прогоняйте
shfmt -wна исходниках, чтобы убрать визуальные расхождения. - Статический анализ: запускайте
shellcheckи фиксируйте критические предупреждения (особенно связанные с кавычками и инъекциями). - Custom‑lint: проверяйте ваши «запреты» и соглашения (например, отсутствие
eval, ограничения на работу с временными файлами). - Проверка конкурентности: если скрипт потенциально запускается параллельно — проверьте коллизии имён и атомарность операций.
В результате код становится более предсказуемым, а риски — управляемыми.
Про тестирование: минимальный набор проверок перед релизом
Статический анализ не заменяет тесты, но дополняет их. Минимально полезно:
- Проверить скрипт на «краевых» аргументах (пустые строки, пробелы, спецсимволы) — убедиться, что кавычки работают правильно.
- Проверить сценарии параллельного запуска, если это реально в вашем контексте (хотя бы на уровне «двумя запускать — что происходит с временными файлами»).
- Проверить обработку ошибок: что происходит при отказе команд, и не скрывается ли сбой.
Заключение
Hardening Bash‑скриптов в Termux — это дисциплина: правильный базовый режим shell (set -Eeuo pipefail и дисциплина кавычек), регулярный статический анализ через shellcheck, унификация стиля с shfmt и дополнение собственными custom‑lint правилами для ваших рисков (инъекции команд, race‑conditions, TOCTOU). Такой подход снижает вероятность инцидентов и делает автоматизацию надёжной.
Если вам нужно выстроить качественный процесс линтинга и безопасной разработки под ваши сценарии Termux, команда РыбинскЛАБ поможет с аудитом скриптов, настройкой пайплайна (ShellCheck/shfmt/custom‑lint) и внедрением практик hardening в ваш рабочий процесс.