Semgrep — больше, чем просто прославленный Grep


Внедрение инструмента для поиска недостатков в коде, где простые регулярные выражения не работают, и использование статического тестирования безопасности приложений было бы излишним.

Semgrep заявляет о себе как:

инструмент для простого обнаружения и предотвращения ошибок и антипаттернов в вашей кодовой базе. Он сочетает в себе удобство grep с правильностью синтаксического и семантического поиска.

Semgrep

Однако это не просто прославленный 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, и с гораздо более привлекательной ценой — бесплатно!


Добавить комментарий