Внедрение инструмента для поиска недостатков в коде, где простые регулярные выражения не работают, и использование статического тестирования безопасности приложений было бы излишним.
Semgrep заявляет о себе как:
инструмент для простого обнаружения и предотвращения ошибок и антипаттернов в вашей кодовой базе. Он сочетает в себе удобство grep с правильностью синтаксического и семантического поиска.
Однако это не просто прославленный grep. Он занимает место где-то между grep и инструментом SAST — более выразительно, чем grep, но не так сложно настраивать и изучать, как SAST.
Примером, демонстрирующим его способность, выходящую за рамки простого поиска по шаблону, является поиск дескриптора файла, который открыт, но не закрывается. То есть я хочу знать, что после $FILE = open(…) где-то в потоке кода также есть $FILE.close().
В этом случае grep потерпит неудачу, потому что регулярные выражения уведут вас так далеко, и он должен работать на нескольких строках, а также потому, что grep не может работать в более широком контексте и контролировать поток кода.
Самым простым, но все же чрезвычайно мощным шаблоном, который предлагает Semgrep в этом случае, является оператор Ellipsis, который с помощью правила, написанного на YAML, подобного следующему, может удовлетворять условию поиска отсутствующего вызова $FILE.close():
rules:
- id: open-never-closed
patterns:
- pattern: $FILE = open(...)
- pattern-not-inside: |
$FILE = open(...)
...
$FILE.close()
message: "file object opened without
corresponding close"
languages: [python]
severity: ERROR
Это правило ищет файлы, которые открываются, но никогда не закрываются. Это достигается путем поиска шаблона open(…), а не следующего шаблона close().
Метапеременная $FILE гарантирует, что в вызовах open и close используется одно и то же имя переменной. Оператор многоточия позволяет передавать любые аргументы для открытия и любую последовательность операторов кода между вызовами open и close.
Нам все равно, как вызывается open или что происходит до вызова close, нам просто нужно убедиться, что вызывается close.
Другой приведенный пример — поиск строк с вызовом setcookie(), но обслуживающих все экземпляры функции, поскольку он может принимать переменное количество аргументов.
Правила Semgrep могут быть такими же простыми, как $X == $X, который ищет ложное равенство, например if (node.id == node.id), где кодировщик на самом деле имел в виду, что node.id == ‘node.id’, но может также более сложный, как уже рассмотренный пример FILE.open.
Что еще лучше, так это то, что существует целый реестр правил, в котором вы можете найти все виды правил для проверки вашего кода на поддерживаемых языках Python, Javascript, Go и Java.
Примеры правил на Java:
java.jax-rs.security.jax-rs-path-traversal.jax-rs-path-traversal
Сообщение Обнаружено возможное прохождение пути. Злоумышленник может контролировать расположение этого файла, включая переход в обратном направлении по каталогу с помощью ‘../’. Чтобы решить эту проблему, убедитесь, что управляемые пользователем переменные в путях к файлам очищены. Вы также можете рассмотреть возможность использования служебного метода, такого как org.apache.commons.io.FilenameUtils.getName(…), чтобы получить имя файла только из пути.
Шаблон правила:
- pattern-either:
- pattern: |
$RETURNTYPE $FUNC (..., @PathParam(...) $TYPE $VAR, ...) {
...
new File(..., $VAR, ...);
...
}
- pattern: |-
$RETURNTYPE $FUNC (..., @javax.ws.rs.PathParam(...) $TYPE $VAR, ...) {
...
new File(..., $VAR, ...);
...
}
на Javascript:
contrib.nodejsscan.eval_yaml_deserialize.yaml_deserialize
Данные, контролируемые пользователем в функции yaml.load(), могут привести к удаленной инъекции кода.
Образец правила
- pattern-inside: |
var $X = require('js-yaml');
...
- pattern: |
$X.load(...)
и так далее.
Отличительной особенностью такого совместного реестра является то, что вы можете использовать опыт людей, пишущих правила в своей области знаний, отправляя их в реестр, чтобы другие могли импортировать и повторно использовать. Конечно, правила тоже можно настроить.
Помимо написания правил для поиска ошибок безопасности, Semgrep также может использоваться для обеспечения соблюдения конкретных шаблонов кода, передовых методов и сканирования PR на наличие уязвимостей.
На конференции HELLA Security Дрю Деннисон из r2c, сопровождающий инструмент, продемонстрировал мощь инструмента, запустив его в реальном времени с репозиторием Apache Libcloud на GitHub по шаблону $X == $X, который обнаружил настоящую ошибку в кодовой базе! Затем ему пришлось открыть PR, чтобы уведомить владельцев репо.
Вы также можете сделать это самостоятельно и просканировать свои репозитории GitHub с помощью Live Editor Semgrep по адресу https://semgrep.live/ и его опции сканирования. Там вы также можете поиграть с примерами и правилами, чтобы прочувствовать это.
На самом деле я провожу сканирование собственного репозитория Android на соответствие правилам Java java.lang.correctness и java.lang.security. Через несколько мгновений результат стал чистым. Какое облегчение!
Помимо общих языковых правил, вы также можете использовать правила для конкретного домена, например, в java.spring. Это означает, что когда эксперт в этом домене пишет правило и отправляет его в реестр, вы можете сразу же им воспользоваться.
Инструмент распространяется как двоичные файлы для macOS и как скрипт для Ubuntu, а для всех остальных существует также образ Docker.
Итак, с открытым исходным кодом, лучше, чем grep, проще, чем SAST, и с гораздо более привлекательной ценой — бесплатно!