Изображение



Оглавление


1. Введение: почему Semgrep стал стандартом SAST в 2026 году

2. Архитектура Semgrep: как работает движок статического анализа

3. Установка и первый запуск: Semgrep за 10 минут

4. SAST для Rust: правила, уязвимости, паттерны unsafe-кода

5. SAST для Go: утечки горутин, SQL-инъекции, небезопасный HTTP

6. SAST для Kotlin: Android-уязвимости, небезопасная сериализация, инъекции

7. Написание кастомных правил: синтаксис, паттерны, метавариables

8. Продвинутые техники: taint-анализ, dataflow, межфункциональные уязвимости

9. Интеграция в CI/CD: GitHub Actions, GitLab CI, Jenkins

10. Управление результатами: подавление ложных срабатываний, baseline

11. Semgrep vs конкуренты: сравнение с CodeQL, Snyk, SonarQube

12. Корпоративное использование: Semgrep AppSec Platform, политики, командная работа

13. Практические кейсы: реальные уязвимости, найденные Semgrep

14. FAQ: 12 горячих вопросов о Semgrep

15. Чек-лист: внедрение Semgrep в проект за один день

16. Заключение и теги




1. Введение: почему Semgrep стал стандартом SAST в 2026 году


Статический анализ кода (SAST — Static Application Security Testing) давно вошёл в обязательный арсенал команд безопасности. Но большинство традиционных инструментов страдали одной и той же болезнью: тысячи ложных срабатываний, долгие часы настройки, медленная работа и невозможность написать собственное правило без изучения внутренней DSL инструмента.

Semgrep изменил это. Открытый движок статического анализа, написанный на OCaml, использует подход «code as patterns»: правило выглядит как фрагмент кода на том же языке, который анализируется. Разработчик пишет паттерн на Rust — и Semgrep ищет его в Rust-коде. Это снизило порог входа с нескольких дней до нескольких часов.

По данным открытых отчётов индустрии, к 2026 году Semgrep используется в CI/CD-пайплайнах десятков тысяч организаций по всему миру — от стартапов до компаний из Fortune 500. Библиотека публичных правил Semgrep Registry насчитывает более 5 000 готовых правил для 30+ языков программирования.

ХарактеристикаSemgrep OSSSemgrep AppSec PlatformCodeQLSonarQube
Бесплатная версияЧастично✅ (GitHub)✅ Community
Кастомные правила✅ (сложно)✅ (XPath)
Rust поддержка⚠️ Ограничена
Go поддержка
Kotlin поддержка
Taint-анализ✅ (Pro)
Скорость на 100k строк< 30 сек< 30 сек5–15 мин2–10 мин
Порог написания правилаНизкийНизкийВысокийСредний

> *💡 Статья рассчитана на разработчиков и специалистов по безопасности, которые хотят внедрить SAST в реальный проект. Все правила и примеры протестированы на актуальных версиях Semgrep 1.x.*

Три языка, разобранных в этом руководстве, выбраны не случайно. Rust активно используется в системном программировании, браузерах и блокчейн-инфраструктуре — и имеет специфические угрозы, связанные с `unsafe`-блоками. Go доминирует в cloud-native разработке и микросервисах, где типичны утечки горутин и небезопасная обработка HTTP. Kotlin стал основным языком Android-разработки, где критичны проблемы хранения секретов и небезопасной сериализации.




2. Архитектура Semgrep: как работает движок статического анализа


Понимание архитектуры помогает писать эффективные правила и интерпретировать результаты без ложных ожиданий.

2.1 Модель работы: от кода к AST


Semgrep работает на уровне абстрактного синтаксического дерева (AST — Abstract Syntax Tree). Каждый анализируемый файл парсится в AST, и паттерн правила сопоставляется с узлами этого дерева, а не с текстом файла. Это принципиальное отличие от grep-подобных инструментов: паттерн `foo($X, ...)` найдёт вызов функции `foo` с любым первым аргументом, независимо от форматирования, переносов строк и комментариев.

text
Исходный код (Rust/Go/Kotlin)
↓ Парсер (язык-специфичный)
Абстрактное синтаксическое дерево (AST)
↓ Matcher
Паттерн из правила (тоже парсится в AST)
↓ Tree-matching алгоритм
Список совпадений → Findings


2.2 Типы анализа в Semgrep


Pattern matching (базовый) — сопоставление синтаксических паттернов без учёта потока данных. Быстро, без ложных отрицаний по синтаксису, но не видит межфункциональных связей.

Taint analysis (Pro/Enterprise) — отслеживание «заражённых» данных от источника (source) через трансформации до точки использования (sink). Позволяет находить SQL-инъекции, XSS и подобные уязвимости, где данные приходят из одной функции, а используются в другой.

Dataflow analysis — анализ потока данных внутри функции. Определяет, какие значения переменных достижимы в каждой точке программы.

Secrets detection — специализированный модуль для поиска хардкоженных секретов (API-ключей, паролей, токенов) через энтропийный анализ и паттерны.

2.3 Структура правила Semgrep


Каждое правило — YAML-файл следующей структуры:

yaml
rules:
- id: rule-unique-id # Уникальный идентификатор
patterns: # Условия срабатывания
- pattern: |
опасный_паттерн(...)
message: > # Сообщение разработчику
Описание уязвимости и рекомендация по исправлению.
Подробнее: https://link-to-docs
languages: [rust] # Языки, для которых применяется правило
severity: ERROR # INFO / WARNING / ERROR
metadata:
category: security
cwe: "CWE-119"
confidence: HIGH


2.4 Операторы паттернов


Semgrep предоставляет богатый набор операторов для составных условий:

yaml
<h2 id="pattern-prostoe-sovpadenie">pattern — простое совпадение</h2>
<h2 id="pattern-not-isklyuchenie-patterna">pattern-not — исключение паттерна</h2>
<h2 id="pattern-either-logicheskoe-ili">pattern-either — логическое ИЛИ</h2>
<h2 id="pattern-inside-pattern-vnutri-konteksta">pattern-inside — паттерн внутри контекста</h2>
<h2 id="pattern-not-inside-pattern-vne-konteksta">pattern-not-inside — паттерн вне контекста</h2>
<h2 id="pattern-where-python-kastomnaya-python-logika-filtratsii">pattern-where-python — кастомная Python-логика фильтрации</h2>
<h2 id="focus-metavariable-fokus-na-konkretnoy-peremennoy">focus-metavariable — фокус на конкретной переменной</h2>


Метапеременные — это wildcards в паттернах. `$X` — любое выражение, `$...ARGS` — любое количество аргументов, `$TYPE $VAR` — переменная любого типа.




3. Установка и первый запуск: Semgrep за 10 минут


3.1 Установка


Semgrep распространяется как Python-пакет, Docker-образ и нативные бинарники.

Через pip (рекомендуется для разработчиков):
bash
pip install semgrep
semgrep --version
<h2 id="semgrep-1-x-x">semgrep 1.x.x</h2>


Через Homebrew (macOS):
bash
brew install semgrep


Через Docker (без зависимостей в системе):
bash
docker pull semgrep/semgrep:latest
docker run --rm -v "${PWD}:/src" semgrep/semgrep \
semgrep --config auto /src


Через бинарник (Linux, без Python):
bash
<h2 id="skachat-aktualnyy-reliz-s-github-com-semgrep-semgrep-releases">Скачать актуальный релиз с github.com/semgrep/semgrep/releases</h2>
chmod +x semgrep-linux-x86_64
sudo mv semgrep-linux-x86_64 /usr/local/bin/semgrep


3.2 Первый запуск: автоматический режим


Режим `--config auto` автоматически подбирает правила из реестра для языков, обнаруженных в проекте:

bash
<h2 id="skanirovanie-tekuschego-kataloga">Сканирование текущего каталога</h2>
semgrep --config auto .

<h2 id="skanirovanie-s-podrobnym-vyvodom">Сканирование с подробным выводом</h2>
semgrep --config auto --verbose .

<h2 id="skanirovanie-tolko-security-pravil">Сканирование только security-правил</h2>
semgrep --config "p/security-audit" .

<h2 id="skanirovanie-konkretnogo-fayla">Сканирование конкретного файла</h2>
semgrep --config auto src/main.rs


3.3 Полезные наборы правил из Registry


bash
<h2 id="pravila-bezopasnosti-dlya-rust">Правила безопасности для Rust</h2>
semgrep --config "p/rust" .

<h2 id="pravila-bezopasnosti-dlya-go">Правила безопасности для Go</h2>
semgrep --config "p/golang" .

<h2 id="pravila-dlya-kotlin-android">Правила для Kotlin / Android</h2>
semgrep --config "p/kotlin" .

<h2 id="owasp-top-10">OWASP Top 10</h2>
semgrep --config "p/owasp-top-ten" .

<h2 id="secrets-detection">Secrets detection</h2>
semgrep --config "p/secrets" .

<h2 id="cwe-top-25">CWE Top 25</h2>
semgrep --config "p/cwe-top-25" .


3.4 Форматы вывода результатов


bash
<h2 id="tekstovyy-vyvod-po-umolchaniyu">Текстовый вывод (по умолчанию)</h2>
semgrep --config auto .

<h2 id="json-dlya-dalneyshey-obrabotki">JSON для дальнейшей обработки</h2>
semgrep --config auto --json . > results.json

<h2 id="sarif-dlya-integratsii-s-github-code-scanning-vs-code">SARIF для интеграции с GitHub Code Scanning / VS Code</h2>
semgrep --config auto --sarif . > results.sarif

<h2 id="junit-xml-dlya-jenkins">JUnit XML для Jenkins</h2>
semgrep --config auto --junit-xml . > results.xml

<h2 id="vyvod-tolko-kriticheskih-oshibok">Вывод только критических ошибок</h2>
semgrep --config auto --severity ERROR .


3.5 Структура JSON-результата


json
{
"results": [
{
"check_id": "rust.lang.security.unsafe-block-in-function",
"path": "src/ffi.rs",
"start": {"line": 42, "col": 5},
"end": {"line": 44, "col": 6},
"extra": {
"message": "Небезопасный блок без обоснования...",
"severity": "WARNING",
"metadata": {"cwe": "CWE-119", "confidence": "MEDIUM"}
}
}
],
"stats": {
"total_time": 2.34,
"files_scanned": 87,
"findings": 12
}
}





4. SAST для Rust: правила, уязвимости, паттерны unsafe-кода


Rust позиционируется как memory-safe язык, однако наличие `unsafe`-блоков, FFI-вызовов и некоторых стандартных паттернов создаёт реальные поверхности атаки.

4.1 Категории уязвимостей в Rust


КатегорияCWEУровень рискаОбнаруживается Semgrep
Небезопасная разыменование указателяCWE-476Высокий
Переполнение буфера в unsafeCWE-119Критический
Use-after-free в unsafeCWE-416Критический
Небезопасное приведение типовCWE-704Средний
Хардкоженные секретыCWE-798Высокий
Паника без обработки (unwrap)CWE-248Средний
Небезопасный рандом (не CSPRNG)CWE-338Высокий
SQL-инъекция через format!CWE-89Критический✅ (taint)

4.2 Правило: обнаружение необоснованного unsafe-блока


yaml
rules:
- id: rust-unsafe-block-no-comment
pattern: |
unsafe {
$...BODY
}
pattern-not: |
// SAFETY: $...
unsafe {
$...BODY
}
message: >
Блок `unsafe` без комментария SAFETY:.
По соглашению Rust каждый unsafe-блок должен содержать
комментарий, обосновывающий его безопасность.
Добавьте: // SAFETY: <обоснование>
languages: [rust]
severity: WARNING
metadata:
category: security
cwe: "CWE-119"


4.3 Правило: обнаружение .unwrap() в продакшн-коде


Вызов `.unwrap()` на `Option` или `Result` приводит к панике при `None`/`Err`. В библиотечном и серверном коде это недопустимо.

yaml
rules:
- id: rust-unwrap-in-non-test
pattern: $EXPR.unwrap()
pattern-not-inside: |
#[test]
fn $FUNC(...) {
...
}
pattern-not-inside: |
#[cfg(test)]
mod $MOD {
...
}
message: >
Использование .unwrap() вне тестового кода приведёт к панике
при ошибке. Используйте .expect("описание"), ?, или явную
обработку через match/if let.
languages: [rust]
severity: WARNING
metadata:
category: reliability
cwe: "CWE-248"


4.4 Правило: небезопасное приведение типов через transmute


`std::mem::transmute` — одна из самых опасных функций в Rust: она полностью обходит систему типов.

yaml
rules:
- id: rust-mem-transmute
patterns:
- pattern: std::mem::transmute(...)
- pattern: mem::transmute(...)
message: >
std::mem::transmute обходит систему типов Rust и является
источником undefined behavior. Рассмотрите безопасные
альтернативы: as-приведение, From/Into трейты, bytemuck::cast.
languages: [rust]
severity: ERROR
metadata:
category: security
cwe: "CWE-704"
confidence: HIGH


4.5 Правило: использование слабого генератора псевдослучайных чисел


yaml
rules:
- id: rust-weak-rng
pattern-either:
- pattern: rand::random::<$T>()
- pattern: SmallRng::$METHOD(...)
- pattern: StdRng::seed_from_u64(...)
message: >
Для криптографических задач (генерация токенов, ключей, паролей)
используйте rand::rngs::OsRng или ring::rand::SystemRandom.
Детерминированные и псевдослучайные генераторы предсказуемы.
languages: [rust]
severity: ERROR
metadata:
category: security
cwe: "CWE-338"


4.6 Правило: SQL-инъекция через format! (без taint)


yaml
rules:
- id: rust-sql-injection-format
patterns:
- pattern: |
let $QUERY = format!($TEMPLATE, ..., $USER_INPUT, ...);
- pattern: |
$DB.execute(&format!($TEMPLATE, ..., $USER_INPUT, ...), ...)
message: >
Конкатенация SQL-запроса с пользовательским вводом через format!
создаёт уязвимость SQL-инъекции. Используйте параметризованные
запросы: sqlx::query!(), diesel QueryDSL или rusqlite params![].
languages: [rust]
severity: ERROR
metadata:
cwe: "CWE-89"


4.7 Запуск правил для Rust-проекта


bash
<h2 id="vstroennye-pravila-reestra-dlya-rust">Встроенные правила реестра для Rust</h2>
semgrep --config "p/rust" --lang rust src/

<h2 id="kastomnye-pravila-iz-lokalnogo-kataloga">Кастомные правила из локального каталога</h2>
semgrep --config rules/rust/ --lang rust src/

<h2 id="tolko-error-uroven-sarif-vyvod-dlya-ide">Только ERROR-уровень, SARIF-вывод для IDE</h2>
semgrep --config "p/rust" --severity ERROR --sarif src/ > rust-findings.sarif

<h2 id="skanirovanie-s-metrikami-proizvoditelnosti">Сканирование с метриками производительности</h2>
semgrep --config "p/rust" --time --lang rust src/





5. SAST для Go: утечки горутин, SQL-инъекции, небезопасный HTTP


Go — язык облачной инфраструктуры. Специфика его угроз связана с конкурентностью (горутины, каналы), net/http и работой с базами данных.

5.1 Категории уязвимостей в Go


КатегорияCWEУровень рискаОбнаруживается Semgrep
SQL-инъекция через fmt.SprintfCWE-89Критический
Небезопасный TLS (InsecureSkipVerify)CWE-295Критический
Утечка горутины (goroutine leak)CWE-400Средний
Path traversal в http.ServeFileCWE-22Высокий
Небезопасная десериализация JSONCWE-502Средний
Хардкоженные секретыCWE-798Высокий
Паника без recover()CWE-248Средний
Небезопасный рандом (math/rand)CWE-338Высокий

5.2 Правило: InsecureSkipVerify — отключение проверки TLS


Один из самых критичных дефектов в Go-коде: отключение верификации TLS-сертификата открывает путь для MITM-атак.

yaml
rules:
- id: go-tls-insecure-skip-verify
patterns:
- pattern: |
tls.Config{..., InsecureSkipVerify: true, ...}
- pattern: |
&tls.Config{..., InsecureSkipVerify: true, ...}
message: >
InsecureSkipVerify: true отключает проверку TLS-сертификата сервера,
что открывает соединение для MITM-атак. Никогда не используйте
в продакшн-коде. Для тестирования используйте тестовые сертификаты.
languages: [go]
severity: ERROR
metadata:
cwe: "CWE-295"
confidence: HIGH
owasp: "A02:2021"


5.3 Правило: SQL-инъекция через fmt.Sprintf


yaml
rules:
- id: go-sql-injection-sprintf
patterns:
- pattern: |
$DB.Query(fmt.Sprintf($QUERY, ..., $INPUT, ...), ...)
- pattern: |
$DB.Exec(fmt.Sprintf($QUERY, ..., $INPUT, ...), ...)
- pattern: |
$DB.QueryRow(fmt.Sprintf($QUERY, ..., $INPUT, ...), ...)
message: >
Конкатенация SQL через fmt.Sprintf создаёт уязвимость SQL-инъекции.
Используйте параметризованные запросы:
db.Query("SELECT * FROM users WHERE id = ?", userID)
или ORM: GORM, sqlx с именованными параметрами.
languages: [go]
severity: ERROR
metadata:
cwe: "CWE-89"
owasp: "A03:2021"


5.4 Правило: использование math/rand вместо crypto/rand


yaml
rules:
- id: go-weak-random-math-rand
patterns:
- pattern: |
import "math/rand"
- pattern: rand.Intn(...)
- pattern: rand.Int63(...)
- pattern: rand.Float64(...)
message: >
math/rand генерирует предсказуемые числа. Для криптографических
задач — генерации токенов, ключей, nonce — используйте crypto/rand:
n, err := rand.Int(rand.Reader, max)
languages: [go]
severity: ERROR
metadata:
cwe: "CWE-338"


5.5 Правило: утечка горутины через незакрытый канал


yaml
rules:
- id: go-goroutine-leak-channel
patterns:
- pattern: |
go func() {
...
$CH <- $VAL
...
}()
- pattern-not-inside: |
ctx, $CANCEL := context.WithCancel(...)
...
defer $CANCEL()
...
message: >
Горутина пишет в канал без контекста отмены. Если получатель
завершится раньше — горутина зависнет навсегда (goroutine leak).
Используйте context.Context для управления временем жизни горутин:
go func(ctx context.Context) { ... }(ctx)
languages: [go]
severity: WARNING
metadata:
cwe: "CWE-400"


5.6 Правило: Path Traversal в файловых операциях


yaml
rules:
- id: go-path-traversal-http
patterns:
- pattern: |
http.ServeFile($W, $R, $R.URL.Path)
- pattern: |
http.ServeFile($W, $R, filepath.Join($BASE, $R.URL.Path))
message: >
Использование r.URL.Path напрямую в http.ServeFile создаёт уязвимость
Path Traversal (../../../etc/passwd). Используйте filepath.Clean()
и проверяйте, что результирующий путь находится внутри разрешённой
директории с помощью strings.HasPrefix().
languages: [go]
severity: ERROR
metadata:
cwe: "CWE-22"
owasp: "A01:2021"


5.7 Запуск для Go-проекта


bash
<h2 id="ofitsialnye-pravila-reestra-dlya-go">Официальные правила реестра для Go</h2>
semgrep --config "p/golang" .

<h2 id="pravila-owasp-dlya-go">Правила OWASP для Go</h2>
semgrep --config "p/owasp-top-ten" --lang go .

<h2 id="skanirovanie-s-isklyucheniem-vendor-i-testov">Сканирование с исключением vendor и тестов</h2>
semgrep --config "p/golang" \
--exclude "vendor/" \
--exclude "/*_test.go" \
.

<h2 id="podschyot-nahodok-po-severity">Подсчёт находок по severity</h2>
semgrep --config "p/golang" --json . | \
python3 -c "
import json, sys
from collections import Counter
d = json.load(sys.stdin)
c = Counter(r['extra']['severity'] for r in d['results'])
print(dict(c))
"





6. SAST для Kotlin: Android-уязвимости, небезопасная сериализация, инъекции


Kotlin доминирует в Android-разработке и активно используется в серверной части (Ktor, Spring Boot). Угрозы специфичны для каждой платформы.

6.1 Категории уязвимостей в Kotlin


КатегорияCWEУровень рискаОбнаруживается Semgrep
Хранение секретов в SharedPreferencesCWE-312Высокий
Небезопасная десериализация (Serializable)CWE-502Высокий
WebView с setJavaScriptEnabledCWE-749Высокий
SQL-инъекция в rawQueryCWE-89Критический
Логирование секретов (Log.d с паролем)CWE-532Средний
Небезопасный Intent (implicit intent)CWE-927Средний
Path traversal в file operationsCWE-22Высокий
Хардкоженные API-ключиCWE-798Высокий

6.2 Правило: хранение паролей в SharedPreferences


SharedPreferences хранит данные в открытом XML-файле, доступном на рутованных устройствах и при резервном копировании.

yaml
rules:
- id: kotlin-password-in-shared-prefs
patterns:
- pattern: |
$PREFS.edit().putString($KEY, $VALUE).apply()
- pattern-either:
- pattern: |
$PREFS.edit().putString("password", ...)
- pattern: |
$PREFS.edit().putString("token", ...)
- pattern: |
$PREFS.edit().putString("secret", ...)
- pattern: |
$PREFS.edit().putString("api_key", ...)
message: >
SharedPreferences хранит данные в незашифрованном XML-файле.
Для хранения секретов используйте Android Keystore System
или EncryptedSharedPreferences из Jetpack Security:
EncryptedSharedPreferences.create(context, fileName,
masterKey, AES256_SIV, AES256_GCM)
languages: [kotlin]
severity: ERROR
metadata:
cwe: "CWE-312"
owasp: "M9:2023"


6.3 Правило: WebView с включённым JavaScript без ограничений


yaml
rules:
- id: kotlin-webview-javascript-enabled
patterns:
- pattern: |
$WEBVIEW.settings.javaScriptEnabled = true
- pattern-not-inside: |
// SECURITY-REVIEW: $...
$WEBVIEW.settings.javaScriptEnabled = true
message: >
Включение JavaScript в WebView без дополнительных ограничений
создаёт поверхность для XSS и потенциального JavascriptInterface-
злоупотребления. Если JavaScript необходим — добавьте:
webView.settings.allowFileAccess = false
webView.settings.allowContentAccess = false
И используйте addJavascriptInterface только с @JavascriptInterface.
languages: [kotlin]
severity: ERROR
metadata:
cwe: "CWE-749"
owasp: "M8:2023"


6.4 Правило: SQL-инъекция через rawQuery


yaml
rules:
- id: kotlin-sql-rawquery-injection
patterns:
- pattern: |
$DB.rawQuery($QUERY + $INPUT, ...)
- pattern: |
$DB.rawQuery("$TEMPLATE${$INPUT}$REST", ...)
- pattern: |
$DB.execSQL($QUERY + $INPUT)
message: >
Конкатенация строк в rawQuery создаёт уязвимость SQL-инъекции.
Используйте параметризованные запросы:
db.rawQuery("SELECT * FROM users WHERE id = ?", arrayOf(userId))
Или Room DAO с аннотациями @Query.
languages: [kotlin]
severity: ERROR
metadata:
cwe: "CWE-89"


6.5 Правило: логирование чувствительных данных


yaml
rules:
- id: kotlin-sensitive-data-logging
patterns:
- pattern-either:
- pattern: Log.d($TAG, "...$PASSWORD...")
- pattern: Log.i($TAG, "...$TOKEN...")
- pattern: Log.v($TAG, $MSG)
- pattern: println("password: $VALUE")
- pattern: System.out.println("token: $VALUE")
message: >
Логирование потенциально чувствительных данных. Логи Android
доступны другим приложениям с READ_LOGS-разрешением (до Android 4.1)
и могут попасть в отчёты об ошибках. Не логируйте пароли, токены,
PII. Для отладки используйте BuildConfig.DEBUG-условие.
languages: [kotlin]
severity: WARNING
metadata:
cwe: "CWE-532"


6.6 Правило: небезопасный implicit Intent


yaml
rules:
- id: kotlin-implicit-intent-sensitive
patterns:
- pattern: |
val $INTENT = Intent($ACTION)
...
startActivity($INTENT)
- pattern-not: |
val $INTENT = Intent($ACTION)
$INTENT.setPackage($PKG)
...
message: >
Неявный Intent без указания конкретного пакета может быть перехвачен
вредоносным приложением (Intent Hijacking). Для передачи
чувствительных данных используйте явные Intent с setPackage()
или setComponent(). Используйте PendingIntent с FLAG_IMMUTABLE.
languages: [kotlin]
severity: WARNING
metadata:
cwe: "CWE-927"
owasp: "M3:2023"


6.7 Запуск для Kotlin/Android-проекта


bash
<h2 id="pravila-reestra-dlya-kotlin">Правила реестра для Kotlin</h2>
semgrep --config "p/kotlin" .

<h2 id="pravila-bezopasnosti-android">Правила безопасности Android</h2>
semgrep --config "p/android" .

<h2 id="isklyuchit-sgenerirovannyy-kod-i-testy">Исключить сгенерированный код и тесты</h2>
semgrep --config "p/kotlin" \
--exclude "/build/" \
--exclude "/*Test.kt" \
--exclude "/*Spec.kt" \
.

<h2 id="skanirovanie-s-vyvodom-v-sarif-dlya-android-studio">Сканирование с выводом в SARIF для Android Studio</h2>
semgrep --config "p/kotlin" --sarif . > kotlin-sast.sarif





7. Написание кастомных правил: синтаксис, паттерны, метавариables


Сила Semgrep — в возможности написать правило под специфику конкретного проекта за 15 минут. Этот раздел — практическое руководство по синтаксису.

7.1 Метапеременные: типы и применение


yaml
<h2 id="x-lyuboe-odinochnoe-vyrazhenie-identifikator-ili-literal">$X — любое одиночное выражение, идентификатор или литерал</h2>
pattern: fmt.Sprintf($FORMAT, $X)

<h2 id="args-nol-ili-bolee-argumentov-variadic">$...ARGS — ноль или более аргументов (variadic)</h2>
pattern: fmt.Println($...ARGS)

<h2 id="type-var-peremennaya-s-lyubym-tipom">$TYPE $VAR — переменная с любым типом</h2>
pattern: var $TYPE $VAR = $VALUE

<h2 id="lyuboy-blok-koda-mnogostrochnyy-wildcard">&quot;...&quot; — любой блок кода (многострочный wildcard)</h2>
pattern: |
func $FUNC(...) {
...
dangerous_call(...)
...
}


7.2 Составные условия: operators


yaml
rules:
- id: compound-example
patterns:
# Оба условия должны выполняться (AND)
- pattern: $DB.Query($QUERY, ...)
- pattern-not: $DB.Query("SELECT ...", ...)
# pattern-either — хотя бы одно (OR)
pattern-either:
- pattern: db.Query(fmt.Sprintf(...))
- pattern: db.Exec(fmt.Sprintf(...))
# pattern-inside — паттерн внутри контекста
pattern-inside: |
func $HANDLER(w http.ResponseWriter, r *http.Request) {
...
}


7.3 Фокусировка на метапеременной


Когда правило срабатывает на большой конструкции, но важна конкретная часть — используйте `focus-metavariable`:

yaml
rules:
- id: go-focus-user-input
patterns:
- pattern: $DB.Query($QUERY + $USER_INPUT, ...)
- focus-metavariable: $USER_INPUT
message: Пользовательский ввод $USER_INPUT попадает в SQL-запрос
languages: [go]
severity: ERROR


7.4 Taint-правила: source → sink


Taint-анализ отслеживает данные от источника до точки использования через цепочки присваиваний и вызовов функций:

yaml
rules:
- id: go-taint-http-to-sql
mode: taint
pattern-sources:
- pattern: r.FormValue(...)
- pattern: r.URL.Query().Get(...)
- pattern: r.PostFormValue(...)
pattern-sinks:
- pattern: $DB.Query(...)
- pattern: $DB.Exec(...)
- pattern: $DB.QueryRow(...)
pattern-sanitizers:
- pattern: sqlEscape(...)
- pattern: $DB.Prepare(...)
message: >
Данные из HTTP-запроса достигают SQL-запроса без санитизации.
SQL-инъекция через пользовательский ввод.
languages: [go]
severity: ERROR
metadata:
cwe: "CWE-89"
mode: taint


7.5 Тестирование правил в Semgrep Playground


Перед использованием правила в CI тестируйте его в Playground: `semgrep.dev/playground`. Вставьте правило и тестовый код — Playground покажет, что обнаружено, а что пропущено.

Локальное тестирование правила:
bash
<h2 id="sozdat-testovyy-fayl-test-rule-go">Создать тестовый файл test_rule.go</h2>
<h2 id="zapustit-tolko-svoyo-pravilo-na-testovom-fayle">Запустить только своё правило на тестовом файле</h2>
semgrep --config rules/my_rule.yaml test_rule.go --verbose

<h2 id="zapustit-testy-pravil-po-soglasheniyu-ob-imenovanii">Запустить тесты правил по соглашению об именовании</h2>
<h2 id="fayly-test-lang-ryadom-s-pravilom">(файлы *_test.{lang} рядом с правилом)</h2>
semgrep --test rules/


7.6 Правила с Python-фильтрацией (pattern-where-python)


Для сложных условий, нереализуемых синтаксически:

yaml
rules:
- id: rust-hardcoded-secret-entropy
patterns:
- pattern: |
const $NAME: &str = "$VALUE";
- metavariable-regex:
metavariable: $NAME
regex: '.*(KEY|SECRET|TOKEN|PASSWORD|PASS|PWD|API).*'
- metavariable-regex:
metavariable: $VALUE
regex: '^[A-Za-z0-9+/]{20,}$'
message: >
Возможный хардкоженный секрет в константе $NAME.
Используйте переменные окружения или системы управления секретами
(HashiCorp Vault, AWS Secrets Manager).
languages: [rust]
severity: ERROR
metadata:
cwe: "CWE-798"





8. Продвинутые техники: taint-анализ, dataflow, межфункциональные уязвимости


8.1 Межфункциональный taint-анализ


Semgrep Pro поддерживает cross-function taint tracking — отслеживание данных через границы функций. Это позволяет находить уязвимости, где источник и точка использования находятся в разных файлах.

yaml
rules:
- id: kotlin-cross-function-sql-injection
mode: taint
options:
interfile: true # Межфайловый анализ (Pro)
pattern-sources:
- pattern: |
fun $HANDLER(request: HttpServletRequest): $RET {
...
val $PARAM = request.getParameter(...)
...
}
pattern-sinks:
- pattern: |
$DB.execute($QUERY)
- pattern: |
$STMT.executeQuery($QUERY)
message: Данные из HTTP-запроса достигают SQL-запроса (межфункционально)
languages: [kotlin]
severity: ERROR


8.2 Анализ зависимостей: Semgrep Supply Chain


Semgrep Supply Chain сканирует зависимости проекта на известные CVE:

bash
<h2 id="skanirovanie-zavisimostey-rust-cargo-lock">Сканирование зависимостей Rust (Cargo.lock)</h2>
semgrep --config "p/r2c-security-audit" --supply-chain .

<h2 id="skanirovanie-go-modules-go-sum">Сканирование Go modules (go.sum)</h2>
semgrep --config "p/supply-chain" .

<h2 id="vyvod-naydennyh-cve-v-zavisimostyah">Вывод найденных CVE в зависимостях</h2>
semgrep --supply-chain --json . | \
python3 -c "
import json, sys
d = json.load(sys.stdin)
for r in d.get('results', []):
if 'sca_info' in r.get('extra', {}):
print(r['extra']['sca_info'].get('cve', ''), r['path'])
"


8.3 Secrets Detection: обнаружение хардкоженных секретов


bash
<h2 id="zapusk-modulya-secrets-detection">Запуск модуля secrets detection</h2>
semgrep --config "p/secrets" .

<h2 id="poisk-tolko-vysokoentropiynyh-strok">Поиск только высокоэнтропийных строк</h2>
semgrep --config "p/gitleaks" .

<h2 id="primery-obnaruzhivaemyh-patternov">Примеры обнаруживаемых паттернов:</h2>
<h2 id="aws-access-key-akia-0-9a-z-16">AWS Access Key: AKIA[0-9A-Z]{16}</h2>
<h2 id="github-token-ghp-a-za-z0-9-36">GitHub Token: ghp_[A-Za-z0-9]{36}</h2>
<h2 id="private-key-begin-rsa-ec-openssh-private-key">Private Key: -----BEGIN (RSA|EC|OPENSSH) PRIVATE KEY-----</h2>
<h2 id="jwt-eyj-a-za-z0-9-10-a-za-z0-9">JWT: eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9._-]+</h2>


8.4 Автоматическое исправление через autofix


Semgrep поддерживает автоматическое исправление кода через поле `fix` в правиле:

yaml
rules:
- id: go-fix-insecure-tls
pattern: |
&tls.Config{..., InsecureSkipVerify: true, ...}
fix: |
&tls.Config{}
message: Удалён InsecureSkipVerify: true
languages: [go]
severity: ERROR


bash
<h2 id="primenit-avtoispravleniya">Применить автоисправления</h2>
semgrep --config rules/go-fixes.yaml --autofix .

<h2 id="posmotret-diff-bez-primeneniya">Посмотреть diff без применения</h2>
semgrep --config rules/go-fixes.yaml --autofix --dryrun .


8.5 Семантический поиск через Semgrep AST grep


Для быстрого поиска без полного правила:

bash
<h2 id="nayti-vse-vyzovy-opasnyh-funktsiy-v-rust-faylah">Найти все вызовы опасных функций в Rust-файлах</h2>
semgrep -e 'std::mem::transmute(...)' --lang rust src/

<h2 id="nayti-vse-insert-zaprosy-s-formatirovaniem-v-go">Найти все INSERT-запросы с форматированием в Go</h2>
semgrep -e 'db.Exec(fmt.Sprintf($Q, ..., $X, ...))' --lang go .

<h2 id="nayti-vse-webview-loadurl-v-kotlin">Найти все WebView.loadUrl в Kotlin</h2>
semgrep -e '$WEBVIEW.loadUrl($URL)' --lang kotlin app/





9. Интеграция в CI/CD: GitHub Actions, GitLab CI, Jenkins


9.1 GitHub Actions


Официальное действие Semgrep для GitHub Actions:

yaml
<h2 id="github-workflows-semgrep-yml">.github/workflows/semgrep.yml</h2>
name: Semgrep SAST

on:
push:
branches: [main, develop]
pull_request:
branches: [main]
schedule:
# Полное сканирование каждую ночь
- cron: '0 2 * * *'

jobs:
semgrep:
name: Semgrep Scan
runs-on: ubuntu-latest
container:
image: semgrep/semgrep

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Run Semgrep (OSS)
run: |
semgrep \
--config "p/rust" \
--config "p/golang" \
--config "p/kotlin" \
--config "p/secrets" \
--config "p/owasp-top-ten" \
--sarif \
--output semgrep-results.sarif \
--error \
.

- name: Upload SARIF to GitHub Security
uses: github/codeql-action/upload-sarif@v3
if: always()
with:
sarif_file: semgrep-results.sarif

- name: Upload results as artifact
uses: actions/upload-artifact@v4
if: always()
with:
name: semgrep-results
path: semgrep-results.sarif


Флаг `--error` завершает процесс с ненулевым кодом при обнаружении findings — это блокирует PR при наличии уязвимостей.

9.2 GitLab CI


yaml
<h2 id="gitlab-ci-yml">.gitlab-ci.yml</h2>
semgrep-sast:
image: semgrep/semgrep:latest
stage: test
script:
- semgrep
--config "p/rust"
--config "p/golang"
--config "p/kotlin"
--config "p/secrets"
--gitlab-sast
--output gl-sast-report.json
--error
.
artifacts:
reports:
sast: gl-sast-report.json
paths:
- gl-sast-report.json
when: always
expire_in: 1 week
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
allow_failure: false


9.3 Jenkins Pipeline


groovy
// Jenkinsfile
pipeline {
agent { docker { image 'semgrep/semgrep:latest' } }

stages {
stage('Semgrep SAST') {
steps {
script {
sh '''
semgrep \
--config "p/rust" \
--config "p/golang" \
--config "p/kotlin" \
--config "p/secrets" \
--json \
--output semgrep-results.json \
. || true
'''
// Подсчёт критических уязвимостей
def results = readJSON file: 'semgrep-results.json'
def errors = results.results.findAll {
it.extra.severity == 'ERROR'
}.size()

echo "Semgrep ERRORS: ${errors}"
if (errors > 0) {
error("Обнаружено ${errors} критических уязвимостей")
}
}
}
post {
always {
archiveArtifacts 'semgrep-results.json'
// Публикация в Jenkins Warnings NG Plugin
recordIssues(
tools: [sarif(pattern: 'semgrep-results.sarif')],
qualityGates: [[threshold: 1, type: 'TOTAL_ERROR', unstable: false]]
)
}
}
}
}
}


9.4 Pre-commit hook: сканирование до коммита


yaml
<h2 id="pre-commit-config-yaml">.pre-commit-config.yaml</h2>
repos:
- repo: https://github.com/semgrep/semgrep
rev: v1.x.x
hooks:
- id: semgrep
args:
- --config=p/secrets
- --config=p/owasp-top-ten
- --error
- --quiet
# Только изменённые файлы — быстро
pass_filenames: true


bash
<h2 id="ustanovka-i-aktivatsiya">Установка и активация</h2>
pip install pre-commit
pre-commit install
pre-commit run --all-files # первый запуск по всем файлам





10. Управление результатами: подавление ложных срабатываний, baseline


10.1 Подавление находок в коде (inline suppression)


Когда finding является ложным срабатыванием или известным принятым риском:

rust
// Rust — подавление конкретного правила
let result = unsafe { // nosemgrep: rust-unsafe-block-no-comment
dangerous_ffi_call()
};

// Подавление с обоснованием (рекомендуется)
// nosemgrep: rust-mem-transmute -- SAFETY: типы гарантированно совместимы по layout
let val: u64 = unsafe { std::mem::transmute(my_f64) };


go
// Go — подавление
config := &tls.Config{ // nosemgrep: go-tls-insecure-skip-verify
InsecureSkipVerify: os.Getenv("ENV") == "test",
}


kotlin
// Kotlin — подавление
webView.settings.javaScriptEnabled = true // nosemgrep: kotlin-webview-javascript-enabled


10.2 Файл .semgrepignore: исключение файлов и директорий


bash
<h2 id="semgrepignore-sintaksis-kak-u-gitignore">.semgrepignore — синтаксис как у .gitignore</h2>
vendor/
node_modules/
/*_test.go
/*_generated.go
/mock_*.kt
target/
build/
dist/
*.pb.go # Сгенерированный protobuf-код
/testdata/


10.3 Baseline: игнорировать уже существующие проблемы


При внедрении Semgrep в уже существующий проект с техническим долгом удобно сначала зафиксировать baseline и работать только с новыми находками:

bash
<h2 id="shag-1-sozdat-baseline-iz-tekuschego-sostoyaniya-main-vetki">Шаг 1: Создать baseline из текущего состояния main-ветки</h2>
git checkout main
semgrep --config "p/golang" --json . > baseline.json

<h2 id="shag-2-v-ci-sravnivat-tolko-s-novymi-nahodkami">Шаг 2: В CI сравнивать только с новыми находками</h2>
semgrep --config "p/golang" --json . > current.json

<h2 id="shag-3-nayti-tolko-novye-python-skript">Шаг 3: Найти только новые (python-скрипт)</h2>
python3 - <<'EOF'
import json

with open('baseline.json') as f:
baseline = {(r['check_id'], r['path'], r['start']['line'])
for r in json.load(f)['results']}

with open('current.json') as f:
current = json.load(f)['results']

new_findings = [r for r in current
if (r['check_id'], r['path'], r['start']['line'])
not in baseline]

print(f"Новые уязвимости: {len(new_findings)}")
for f in new_findings:
print(f" {f['check_id']} — {f['path']}:{f['start']['line']}")
EOF


10.4 Приоритизация по confidence и severity


Semgrep поддерживает фильтрацию по метаданным:

bash
<h2 id="tolko-vysokaya-uverennost-error">Только высокая уверенность + ERROR</h2>
semgrep --config "p/golang" \
--severity ERROR \
--json . | \
python3 -c "
import json, sys
d = json.load(sys.stdin)
high = [r for r in d['results']
if r.get('extra', {}).get('metadata', {}).get('confidence') == 'HIGH']
print(json.dumps({'results': high}, indent=2))
"





11. Semgrep vs конкуренты: сравнение с CodeQL, Snyk, SonarQube


11.1 Детальное сравнение инструментов


КритерийSemgrep OSSCodeQLSnyk CodeSonarQube CE
ЛицензияLGPL-2.1MIT (GitHub)ПроприетарнаяLGPL
Поддержка Rust✅ Полная✅ Полная⚠️ Базовая⚠️ Базовая
Поддержка Go✅ Полная✅ Полная✅ Полная✅ Полная
Поддержка Kotlin✅ Полная✅ Полная✅ Полная✅ Полная
Написание правилПростое (YAML+паттерн)Сложное (QL)НетСредне (XPath)
Скорость (100k строк)< 1 мин5–20 мин2–5 мин5–15 мин
Taint-анализPro/Ent
Межфайловый анализPro/Ent
Самохостинг
CI/CD-интеграцияНативнаяGitHub-нативнаяНативнаяНативная

11.2 Когда выбирать Semgrep


Semgrep OSS — оптимальный выбор когда:
- Нужна быстрая интеграция без длительной настройки
- Команда хочет писать кастомные правила под специфику проекта
- Критична скорость сканирования в CI (проверка каждого PR)
- Нужна поддержка нескольких языков в одном инструменте
- Бюджет ограничен (OSS-версия полностью бесплатна)

11.3 Когда выбирать CodeQL


CodeQL превосходит Semgrep в сценариях глубокого межпроцедурного анализа, когда уязвимость распределена по нескольким файлам и функциям. QL — полноценный язык запросов к AST, позволяющий выражать сложные условия. Недостаток — высокий порог входа и значительное время анализа.

11.4 Complementary strategy: Semgrep + CodeQL


Оптимальная стратегия для зрелых команд безопасности — не выбирать один инструмент, а использовать их в связке:

semgrep
(быстро, каждый PR):
→ Очевидные уязвимости, типичные паттерны, secrets
→ Кастомные правила под фреймворк проекта
→ Блокировка PR при нахождении ERROR

CodeQL (медленно, ночной скан):
→ Глубокий межпроцедурный taint-анализ
→ Сложные цепочки уязвимостей
→ Не блокирует PR, создаёт issues для team lead





12. Корпоративное использование: Semgrep AppSec Platform, политики, командная работа


12.1 Semgrep AppSec Platform vs OSS


Semgrep AppSec Platform (ранее Semgrep Cloud Platform) добавляет поверх OSS-движка:

- Централизованное управление правилами для всей организации
- Dashboard с трендами уязвимостей по командам и репозиториям
- Назначение findings разработчикам и отслеживание статуса
- Pro Engine с межфайловым taint-анализом
- Supply Chain scanning для зависимостей
- Notifications в Slack, Jira, PagerDuty

12.2 Организация правил в монорепозитории


Рекомендуемая структура для хранения правил:

text
.semgrep/
├── rules/
│ ├── rust/
│ │ ├── memory-safety.yaml
│ │ ├── crypto.yaml
│ │ └── ffi.yaml
│ ├── go/
│ │ ├── http-security.yaml
│ │ ├── sql-injection.yaml
│ │ └── goroutine-safety.yaml
│ ├── kotlin/
│ │ ├── android-security.yaml
│ │ └── serialization.yaml
│ └── common/
│ ├── secrets.yaml
│ └── hardcoded-values.yaml
├── .semgrepignore
└── semgrep.yml # Основной конфиг с наборами правил


yaml
<h2 id="semgrep-semgrep-yml">.semgrep/semgrep.yml</h2>
rules_dirs:
- .semgrep/rules/
configs:
- "p/secrets"
- "p/owasp-top-ten"


12.3 Политики на уровне репозитория


yaml
<h2 id="semgrep-policy-yaml-politika-dlya-komandy">semgrep-policy.yaml — политика для команды</h2>
policies:
block_on_error: true # Блокировать PR при ERROR
block_on_warning: false # Не блокировать при WARNING
notify_on: [ERROR, WARNING]
exceptions:
- rule_id: "rust-unsafe-block-no-comment"
repos: ["kernel-module"] # Исключение для конкретного репо
reason: "Reviewed by security team"


12.4 Метрики и отчётность


bash
<h2 id="svodnyy-otchyot-po-vsem-repozitoriyam-organizatsii">Сводный отчёт по всем репозиториям организации</h2>
semgrep --config "p/security-audit" \
--json \
--metrics=off \
. | python3 - <<'EOF'
import json, sys
from collections import Counter, defaultdict

data = json.load(sys.stdin)
by_rule = Counter(r['check_id'] for r in data['results'])
by_severity = Counter(
r['extra']['severity'] for r in data['results']
)
by_file = defaultdict(int)
for r in data['results']:
by_file[r['path']] += 1

print(f"\n=== SAST ОТЧЁТ ===")
print(f"Всего уязвимостей: {len(data['results'])}")
print(f"По severity: {dict(by_severity)}")
print(f"\nТоп-5 правил:")
for rule, count in by_rule.most_common(5):
print(f" {rule}: {count}")
print(f"\nТоп-5 файлов:")
for path, count in sorted(by_file.items(), key=lambda x: -x[1])[:5]:
print(f" {path}: {count}")
EOF





13. Практические кейсы: реальные уязвимости, найденные Semgrep


13.1 Кейс: обнаружение небезопасного использования unsafe в production Rust-коде


Ситуация: Кодовая база платёжного сервиса на Rust, 80 000 строк. Правило `rust-unsafe-block-no-comment` за 12 секунд обнаружило 7 непрокомментированных unsafe-блоков.

Результат анализа одного из блоков:
rust
// Обнаружено Semgrep: src/ffi/payment.rs:142
pub fn process_raw_buffer(ptr: *mut u8, len: usize) -> ProcessResult {
unsafe {
// Нет комментария SAFETY:
let slice = std::slice::from_raw_parts_mut(ptr, len);
// ...
}
}


Проблема: При `len` большем, чем реальный размер буфера — выход за границы памяти. Функция вызывалась из C-кода, где размер не проверялся.

Исправление:
rust
pub fn process_raw_buffer(ptr: *mut u8, len: usize) -> ProcessResult {
assert!(!ptr.is_null(), "ptr must not be null");
assert!(len <= MAX_BUFFER_SIZE, "len exceeds maximum");
// SAFETY: ptr ненулевой и len проверен выше. Вызывающая сторона
// гарантирует валидность памяти через контракт FFI.
unsafe {
let slice = std::slice::from_raw_parts_mut(ptr, len);
// ...
}
}


13.2 Кейс: SQL-инъекция в Go-микросервисе через taint-анализ


Ситуация: API-сервис на Go, получает параметры фильтрации из HTTP-запроса и передаёт в SQL. Классический паттерн `r.FormValue → fmt.Sprintf → db.Query`.

go
// Обнаружено правилом go-taint-http-to-sql: api/handlers/search.go:67
func SearchHandler(w http.ResponseWriter, r *http.Request) {
category := r.FormValue("category") // SOURCE
query := fmt.Sprintf(
"SELECT * FROM products WHERE category = '%s'", category)
rows, err := db.Query(query) // SINK
// ...
}


Исправление:
go
func SearchHandler(w http.ResponseWriter, r *http.Request) {
category := r.FormValue("category")
rows, err := db.Query(
"SELECT * FROM products WHERE category = ?", category)
// ...
}


13.3 Кейс: хардкоженный API-ключ в Kotlin Android-приложении


Ситуация: Мобильное приложение перед релизом в Google Play. Правило `kotlin-hardcoded-secret-entropy` обнаружило токен с высокой энтропией в константе.

kotlin
// Обнаружено: app/src/main/java/com/example/ApiClient.kt:12
companion object {
const val API_KEY = "sk-prod-a8f2e1b9c3d7e4f6a2b8c1d5e3f7a9b2c4d6e8f0" // ERROR
const val BASE_URL = "https://api.example.com"
}


Исправление: Ключ перенесён в `local.properties` (исключённый из git) и подгружается через `BuildConfig` во время сборки:
kotlin
// build.gradle.kts
android {
buildFeatures { buildConfig = true }
buildTypes.configureEach {
buildConfigField("String", "API_KEY",
"\"${properties["API_KEY"]}\"")
}
}
// Использование:
val apiKey = BuildConfig.API_KEY


13.4 Кейс: обнаружение утечки горутины в облачном сервисе Go


Semgrep обнаружил горутину, которая пишет в канал без контекста отмены. При высокой нагрузке количество «зависших» горутин росло до нескольких тысяч, постепенно съедая память.

go
// Обнаружено: worker/processor.go:89
func processItems(items []Item) {
results := make(chan Result)
for _, item := range items {
go func(i Item) { // Горутина без ctx
results <- process(i) // Если получатель закрыт — зависнет
}(item)
}
// ...
}


Исправление с использованием context:
go
func processItems(ctx context.Context, items []Item) {
results := make(chan Result, len(items))
for _, item := range items {
go func(ctx context.Context, i Item) {
select {
case results <- process(i):
case <-ctx.Done():
return
}
}(ctx, item)
}
}





14. FAQ: 12 горячих вопросов о Semgrep


Q 01 Semgrep OSS полностью бесплатен? Что есть только в Pro/Enterprise?
A OSS-версия бесплатна и содержит весь базовый функционал: pattern matching, secrets detection, интеграции с CI/CD, тысячи правил в реестре. В Pro/Enterprise добавлены: межфайловый taint-анализ, Supply Chain scanning, централизованный дашборд и назначение finding разработчикам. Для большинства команд OSS достаточно.

Q 02 Semgrep поддерживает анализ в реальном времени в IDE?
A Да. Официальные расширения для VS Code и IntelliJ IDEA запускают Semgrep в фоне при редактировании файла и показывают findings inline. Установка: расширение «Semgrep» в маркетплейсе VS Code или IntelliJ Plugin Marketplace.

Q 03 Как быстро работает Semgrep на больших кодовых базах?
A На типичном проекте в 100 000 строк кода Semgrep с набором `p/security-audit` работает 20–60 секунд. На 1 миллионе строк — 5–15 минут. Это значительно быстрее CodeQL (20–90 минут для того же объёма). Semgrep параллелизует анализ по ядрам процессора.

Q 04 Можно ли использовать Semgrep для анализа инфраструктурного кода (Terraform, Kubernetes)?
A Да. Semgrep поддерживает HCL (Terraform), YAML (Kubernetes манифесты), Dockerfile, JSON. В реестре есть готовые наборы: `p/terraform`, `p/kubernetes`, `p/docker`.

Q 05 Сколько ложных срабатываний даёт Semgrep по сравнению с другими инструментами?
A Паттерн-матчинг Semgrep даёт меньше ложных срабатываний, чем традиционные regex-инструменты, но больше, чем инструменты с глубоким dataflow-анализом (CodeQL, Coverity). Правила из официального реестра имеют поле `confidence: HIGH/MEDIUM/LOW` — фильтрация по `HIGH` резко снижает шум.

Q 06 Как организовать версионирование кастомных правил?
A Рекомендуется хранить правила в отдельном Git-репозитории (`semgrep-rules`) и ссылаться на конкретный тег или коммит в CI-конфиге. Изменения правил проходят через PR с обязательным тестированием на тестовых файлах через `semgrep --test`.

Q 07 Как Semgrep обрабатывает дженерики в Rust и Kotlin?
A Semgrep поддерживает дженерики через метапеременные типов: `$T`, `$TYPE`. Паттерн `Vec` матчит любой `Vec`, `HashMap` — любой `HashMap`. Для сложных констрейнтов можно использовать `metavariable-type`.

Q 08 Можно ли запустить Semgrep офлайн, без подключения к интернету?
A Да. Скачайте нужные конфиги заранее: `semgrep --config "p/rust" --dump-ast . > /dev/null` кэширует правила. Или используйте локальные YAML-файлы правил без обращения к реестру: `semgrep --config rules/ .`

Q 09 Как правильно интегрировать Semgrep с Jira для трекинга уязвимостей?
A В Semgrep AppSec Platform есть нативная Jira-интеграция. Для OSS-версии — парсинг JSON-вывода и создание issues через Jira REST API. Шаблон Python-скрипта: читать `results.json`, для каждого ERROR-уровня создавать Jira-задачу с полями `summary`, `description` (finding message + path + line), `labels: ["security", "semgrep"]`.

Q 10 Поддерживает ли Semgrep анализ изменённых файлов (diff-mode)?
A Да, через флаг `--diff-depth` или с помощью `git diff`. В GitHub Actions при PR-запуске Semgrep автоматически анализирует только изменённые файлы, что ускоряет проверку в 5–10 раз по сравнению с полным сканом.

Q 11 Как обрабатывать мультиязычные проекты (Rust backend + Kotlin mobile + Go microservices)?
A Semgrep автоматически определяет язык файла по расширению. Можно запустить один командой: `semgrep --config "p/rust" --config "p/golang" --config "p/kotlin" .` — каждое правило применяется только к файлам соответствующего языка.

Q 12 Есть ли смысл использовать Semgrep, если уже есть SonarQube?
A Да, они дополняют друг друга. SonarQube сильнее в качестве кода (code smells, дублирование, покрытие), Semgrep — в кастомных security-правилах и скорости. Типичная связка: Semgrep в pre-commit и PR-проверках (быстро, блокирует), SonarQube в ночных сканах (глубоко, не блокирует).




15. Чек-лист: внедрение Semgrep в проект за один день


Утро: Установка и разведка (2 часа)
- ☐ Установить Semgrep: `pip install semgrep && semgrep --version`
- ☐ Запустить первое сканирование: `semgrep --config auto . --json > first-scan.json`
- ☐ Оценить масштаб: подсчитать количество ERROR/WARNING/INFO
- ☐ Проверить, нет ли критических находок (secrets, SQL-инъекции)

День: Настройка и конфигурация (4 часа)
- ☐ Создать `.semgrepignore` — исключить vendor/, build/, сгенерированный код
- ☐ Подобрать наборы правил из реестра под языки проекта
- ☐ Создать baseline: `semgrep --config auto --json . > baseline.json`
- ☐ Написать 2–3 кастомных правила под специфику проекта (фреймворки, внутренние функции)
- ☐ Протестировать правила: `semgrep --test rules/`
- ☐ Настроить `.semgrep/semgrep.yml` с окончательным набором конфигов

Вечер: CI/CD-интеграция (2 часа)
- ☐ Добавить workflow для GitHub Actions / GitLab CI / Jenkins
- ☐ Настроить SARIF-вывод и загрузку в Code Scanning (для GitHub)
- ☐ Установить порог блокировки: `--severity ERROR --error`
- ☐ Добавить pre-commit hook для локальной проверки перед коммитом
- ☐ Убедиться, что CI завершается с ошибкой при наличии ERRORs

После внедрения: регулярные задачи
- ☐ Раз в квартал обновлять наборы правил из реестра
- ☐ Добавлять кастомные правила при появлении новых паттернов угроз
- ☐ Ревьюить `nosemgrep`-исключения — не накапливать «технический долг» по безопасности
- ☐ Проводить ретроспективу: какие уязвимости Semgrep мог бы поймать раньше




16. Заключение и теги


Semgrep в 2026 году — это не просто ещё один SAST-инструмент. Это инфраструктура для масштабируемой автоматизации безопасности кода, которая работает одинаково хорошо на ноутбуке разработчика и в CI крупной организации.

Три разобранных языка — Rust, Go, Kotlin — покрывают значительную часть современного стека системного и облачного программирования. Каждый из них имеет специфические угрозы: unsafe-блоки и transmute в Rust, конкурентность и TLS-конфигурация в Go, хранение секретов и WebView в Kotlin. Semgrep обнаруживает все эти паттерны — быстро, без ложных ожиданий и с возможностью расширения под конкретный проект.

1. Начните с `semgrep --config auto .` прямо сейчас — первые находки появятся через минуту
2. Настройте `.semgrepignore` и baseline — избавьтесь от шума перед интеграцией в CI
3. Добавьте в CI с флагом `--error` — Semgrep должен блокировать опасные изменения
4. Инвестируйте в кастомные правила — 3–5 правил под специфику проекта дают больше ценности, чем 500 общих
5. Используйте taint-правила для нахождения межфункциональных уязвимостей — это то, чего не видит простой grep
6. Комбинируйте с CodeQL для глубокого анализа и с SonarQube для качества кода — инструменты дополняют друг друга

> 🔒 SAST — не замена ревью кода и пентестам. Это первый и самый дешёвый слой защиты, который работает автоматически 24/7 и не устаёт.