Инструменты статического анализа кода для разработчиков Rust

Инструментарий разработчика Rust: лучшие инструменты статического анализа кода

Компилятор Rust — это самый «требовательный» инструмент проверки кода, с которым когда-либо придётся работать большинству разработчиков. Его система владения памятью, проверка заимствований и контроль времени жизни памяти устраняют целые категории ошибок, которые преследуют кодовые базы на C и C++. Но компилятор, предотвращающий ошибки памяти, — это не то же самое, что всеобъемлющая система контроля качества. Логические ошибки, небезопасные цепочки зависимостей, небезопасное неправильное использование блоков, антипаттерны производительности и дрейф стиля — всё это остаётся нетронутым. rustcКоманды, которые выпускают надежный Rust в больших масштабах, сочетают компилятор с многоуровневым набором инструментов статического анализа, каждый из которых охватывает отдельную часть поверхности качества.

Выбор неправильной комбинации приводит к потере времени CI и создает информационный шум, который разработчики учатся игнорировать. Выбор правильной комбинации позволяет воспринимать строгость компилятора как нижний, а не верхний предел. Инструменты в этом руководстве организованы по их фактическому назначению, а не в алфавитном порядке, чтобы команды могли создать стек, охватывающий все три уровня качества: линтинг и идиомы, безопасность зависимостей и глубокая проверка кода, критически важного для безопасности или чувствительного к безопасности.

Содержание

SMART TS XL

Поддержание качества при разработке современного Rust — непростая задача, даже несмотря на надежные гарантии безопасности этого языка. SMART TS XL Разработано, чтобы помочь командам создавать надежное, поддерживаемое и безопасное программное обеспечение, предлагая возможности глубокого статического анализа, адаптированные к уникальным возможностям Rust. Оно поддерживает профессиональные инженерные процессы, выявляя проблемы на ранних этапах, обеспечивая согласованность и сокращая объем ручной проверки.

Три уровня статического анализа ржавчины

Понимание того, какой инструмент где должен использоваться, предотвращает распространенную ошибку: использование формального верификатора там, где достаточно линтера, или полагание на линтер там, где требуется сканер безопасности. Каждый зрелый проект на Rust должен охватывать все три уровня.

Слой 1: Удаление ворса и стиль Выявляет идиоматические нарушения, распространённые ошибки и проблемы с поддержкой кода. Инструменты: Clippy, rustfmt, rust-analyzer.

Уровень 2: Зависимости и безопасность цепочки поставок Проводит аудит сторонних контейнеров на предмет известных уязвимостей и нарушений политики. Инструменты: cargo-audit, cargo-deny, cargo-auditable.

Уровень 3: Глубокая проверка Выявляет проблемы безопасности памяти в небезопасном коде, неопределенном поведении и формально доказывает свойства критически важных функций. Инструменты: Miri, Kani, MIRAI, Rudra, Creusot, Prusti.

В таблице ниже приведено сопоставление каждого основного инструмента с его уровнем, стоимостью, пригодностью для CI и основным сценарием использования:

ИнструментСлойСтоимостьПодходит для КИОсновной вариант использования
ClippyпылениеБесплатно / OSSДаИдиоматическая проверка синтаксиса, стиль, распространённые ошибки.
rustfmtпылениеБесплатно / OSSДаКонтроль за форматированием кода
анализатор ржавчиныСнятие ворса / DXБесплатно / OSSЧастичныйДиагностика IDE, анализ в режиме реального времени
грузовой аудитБезопасность зависимостейБесплатно / OSSДаИзвестные уязвимости CVE в крейтах
запрет на грузБезопасность зависимостейБесплатно / OSSДаЛицензия, дубликаты, рекомендации
грузопроверяемыйБезопасность зависимостейБесплатно / OSSДаВстраивание данных о зависимостях в бинарные файлы
MiriГлубокая проверкаБесплатно / OSSВыборочный запускНеопределенное поведение в небезопасном коде
КаниГлубокая проверкаБесплатно / OSSВыборочный запускФормальная верификация, проверка модели
MIRAIГлубокая проверкаБесплатно / OSSВыборочный запускАбстрактная интерпретация, анализ загрязнений
РудраГлубокая проверкаБесплатно / OSSИсследованиеОшибки безопасности памяти в небезопасном Rust
СемгрепСквозныеБесплатный уровень + платныйДаНастраиваемые шаблоны безопасности, многоязычность
СонарОблакоСквозныеБесплатный уровень + платныйДаНепрерывный контроль качества, панели мониторинга

Уровень 1: Проверка синтаксиса, стили и удобство работы для разработчиков.

Clippy

Clippy — это идеальная отправная точка для статического анализа кода на Rust. Поставляемый с официальным набором инструментов Rust, он предоставляет более 700 проверок на корректность, производительность, стиль, сложность и ограничения. Это инструмент, который большинство команд разработчиков на Rust используют ежедневно, и не без причины: он выявляет реальные ошибки, а не просто замечания по стилю.

колотить

# Run all lints including pedantic and nursery groups
cargo clippy -- -W clippy::all -W clippy::pedantic

# Fail CI on any warning
cargo clippy -- -D warnings

# Check specific lint group
cargo clippy -- -W clippy::correctness -W clippy::suspicious

Стоит подробнее разобраться в категориях проверки синтаксиса Clippy:

КатегорияЧто он ловит
correctnessКод, который почти наверняка неверен (всегда отклонять).
suspiciousКод, который, вероятно, неверен или очень неожиданный.
styleНеидиоматические паттерны с явным улучшением
complexityИзлишне сложные конструкции
performanceКод, который компилируется, но работает медленнее, чем должен.
pedanticСтрогие правила, субъективные (включать выборочно)
restrictionПравила, которые вы, возможно, захотите применять в конкретных ситуациях.
nurseryНовые фильтры могут давать ложные срабатывания.

проверка груза против клипи: cargo check Проверяет корректность типов и компиляцию без генерации бинарного файла. Работает быстро и подходит для проверки сборки. cargo clippy работает cargo check плюс весь анализ кода. Для CI запустите cargo check для быстрой обратной связи по результатам компиляции и cargo clippy -- -D warnings как отдельный контрольный пункт качества. Они не взаимозаменяемы: cargo check не выявит идиоматических ошибок, и cargo clippy медленнее.

Настройка Clippy с помощью clippy.toml или встроенные атрибуты позволяют командам контролировать лишнюю информацию:

TomL

# clippy.toml
avoid-breaking-exported-api = false
msrv = "1.70"

ржавчина

// Suppress a lint for one function with documented reason
#[allow(clippy::too_many_arguments)]
fn complex_setup(...) { ... }

Clippy не охватывает: углубленный анализ потоков данных между модулями, сканирование уязвимостей безопасности в зависимостях, формальное подтверждение корректности или пользовательские правила, специфичные для конкретной организации. Для этого требуются инструменты на других уровнях.

rustfmt

rustfmt обеспечивает единообразное форматирование во всей кодовой базе с помощью cargo fmt, rustfmt.toml Конфигурационный файл задает правила стиля для всего проекта. В системе непрерывной интеграции (CI) cargo fmt -- --check Завершает работу с ненулевым кодом, если какой-либо файл будет переформатирован, что делает его надежным инструментом контроля качества без лишних затрат на настройку:

колотить

# Check formatting without modifying files (CI mode)
cargo fmt -- --check

# Apply formatting (developer mode)
cargo fmt

Интеграция анализатора Rust и IDE

rust-analyzer — это реализация протокола языкового сервера для Rust, обеспечивающая диагностику в реальном времени, автодополнение, переход к определению и встроенные предупреждения Clippy в любом редакторе, совместимом с LSP. Он заменил устаревший RLS (Rust Language Server) и должен быть единственным языковым сервером, используемым в новых проектах.

С точки зрения статического анализа, rust-analyzer отображает линтинг Clippy непосредственно во время ввода кода разработчиками, предоставляя немедленную обратную связь о проблемах стиля и корректности в момент их возникновения, вместо того чтобы требовать отдельного запуска в терминале:

JSON

// VS Code settings.json: enable Clippy via rust-analyzer
{
  "rust-analyzer.check.command": "clippy",
  "rust-analyzer.check.extraArgs": [
    "--", "-W", "clippy::all", "-W", "clippy::pedantic"
  ]
}

rust-analyzer также предоставляет подсказки для выведенных типов, времени жизни заимствований и имен параметров, что полезно для проверки незнакомого кода без необходимости переходить к определениям.

Уровень 2: Зависимости и безопасность цепочки поставок

грузовой аудит

cargo-audit проверяет Cargo.lock Это файл, который проверяется в базе данных RustSec Advisory, выявляя известные уязвимости CVE в прямых и транзитивных зависимостях. Это минимальный инструмент обеспечения безопасности зависимостей, который должен использоваться в каждой системе непрерывной интеграции (CI) в проекте на Rust.

колотить

# Install
cargo install cargo-audit

# Run audit
cargo audit

# Ignore a specific advisory (document the reason in comments)
cargo audit --ignore RUSTSEC-2024-0001

# JSON output for pipeline integration
cargo audit --json | jq '.vulnerabilities'

cargo-audit охватывает только известные, опубликованные уведомления. Он не может обнаружить уязвимости в вашем собственном коде, проблемы, еще не внесенные в базу данных RustSec, или нарушения политики, такие как нежелательные лицензии.

запрет на груз

cargo-deny расширяет возможности управления зависимостями, выходя за рамки известных уязвимостей CVE и включая соответствие лицензионным требованиям, обнаружение дубликатов крейтов и пользовательские политики запрета/разрешения. Это более комплексный инструмент для организаций, предъявляющих требования к соответствию нормативным требованиям:

TomL

# deny.toml
[advisories]
vulnerability = "deny"
unmaintained = "warn"

[licenses]
allow = ["MIT", "Apache-2.0", "ISC", "BSD-2-Clause", "BSD-3-Clause"]
deny = ["GPL-2.0"]
copyleft = "warn"

[bans]
multiple-versions = "warn"

колотить

cargo install cargo-deny
cargo deny check

грузопроверяемый

cargo-auditable встраивает полное дерево зависимостей в скомпилированные бинарные файлы Rust в виде структурированных метаданных в специальный раздел компоновщика. Это позволяет проводить аудит развернутых бинарных файлов без доступа к исходному коду, что ценно для проверки безопасности после развертывания и обеспечения прозрачности цепочки поставок.

колотить

cargo install cargo-auditable
cargo auditable build --release
# The binary now contains auditable dependency metadata
# Audit a binary directly:
cargo audit bin ./target/release/my-service

Уровень 3: Глубокая проверка

Инструменты этого уровня требуют более тщательной настройки, большей квалификации и более длительного времени анализа. Они подходят не для каждой функции в каждом коде. Они необходимы для работы с небезопасным кодом, криптографическими реализациями, логикой парсера, обрабатывающей ненадежные входные данные, и любым кодом, где корректность необходимо доказывать, а не тестировать.

Miri: Обнаружение неопределенного поведения

Miri — это интерпретатор промежуточного представления среднего уровня (MIR) языка Rust, который выполняет код таким образом, чтобы обнаруживать неопределенное поведение, разрешенное компилятором, но нарушающее гарантии безопасности Rust: доступ к памяти за пределами допустимого диапазона в небезопасных блоках, использование памяти после освобождения, разыменование указателей с неправильным выравниванием, состояния гонки данных в многопоточном небезопасном коде и нарушения модели псевдонимов Rust.

колотить

# Install Miri (requires nightly)
rustup +nightly component add miri

# Run tests under Miri
cargo +nightly miri test

# Enable strict aliasing validation
MIRIFLAGS="-Zmiri-strict-provenance" cargo +nightly miri test

Что обнаруживает Мири: неопределенное поведение в unsafe блоки, нарушения доступа к памяти, использование памяти после освобождения, невыровненные обращения, переполнение целых чисел в отладочных сборках, состояния гонки данных в небезопасном параллельном коде.

Что Мири не охватывает: логические ошибки в безопасном коде, не связанные с неопределенным поведением, уязвимостями зависимостей, стилем кода или проблемами производительности.

интеграция CIМири бежит в 10-50 раз медленнее, чем cargo testЗапустите его выборочно на модулях, содержащих unsafe код или по ночному расписанию, а не при каждом коммите:

YAML

- name: Miri on unsafe modules
  run: |
    rustup +nightly component add miri
    cargo +nightly miri test --package my-unsafe-crate

Кани: Проверка моделей и формальная верификация

Kani, разработанный Amazon Web Services, — это средство проверки моделей, которое формально проверяет свойства кода Rust, исчерпывающе исследуя пути выполнения до заданного предела. В то время как Miri запускает реальные тестовые примеры и находит конкретные ошибки, Kani доказывает или опровергает, что свойства выполняются для всех входных данных в пределах области анализа.

Для работы с Kani требуется написание верификационных инструментов: функций, определяющих входное пространство и свойства, подлежащие проверке:

ржавчина

#[cfg(kani)]
mod verification {
    use super::*;

    #[kani::proof]
    fn verify_add_no_overflow() {
        let a: u32 = kani::any();
        let b: u32 = kani::any();
        if a.checked_add(b).is_some() {
            let result = safe_add(a, b);
            assert!(result == a + b);
        }
    }
}

колотить

cargo install --locked kani-verifier
cargo kani setup
cargo kani

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

MIRAI: Абстрактная интерпретация и анализ загрязнений.

MIRAI, разработанная исследовательским подразделением Facebook, использует абстрактную интерпретацию для анализа MIR в Rust. Она выполняет межпроцедурный анализ, отслеживая потоки значений через вызовы функций, выявляя потенциальные паники, нарушения контрактов и распространение заражения кода.

ржавчина

use mirai_annotations::*;

fn divide(a: i32, b: i32) -> i32 {
    precondition!(b != 0);  // MIRAI verifies all callers satisfy this
    a / b
}

Для работы MIRAI требуется ночной компилятор Rust, и его настройка довольно сложна, что делает его наиболее подходящим для команд, имеющих выделенные ресурсы в области проектирования систем безопасности или ориентированных на исследования требований к качеству.

Рудра: Безопасность памяти в небезопасном библиотечном коде

Rudra выполняет анализ всей программы по всем крейтам для обнаружения ошибок безопасности памяти, специфичных для модели владения в Rust: нарушения безопасности трейтов Send/Sync, проблемы с временем жизни объектов более высокого порядка, допускающие некорректное продление времени жизни, и проблемы безопасности, связанные с паниками в небезопасном коде. Первоначально разработанная исследователями, она работает как плагин компилятора и требует определенной ночной версии. Запускайте ее по расписанию для крейтов библиотек с небезопасной внутренней структурой, а не при каждом коммите.

Крезо и Прусти: дедуктивная верификация

Creusot и Prusti — это инструменты дедуктивной верификации, доказывающие математические свойства программ на Rust. Оба требуют формальных спецификаций (предусловия, постусловия, инварианты циклов) и используют автоматизированные средства доказательства теорем для проверки реализаций на соответствие этим спецификациям. Они ориентированы на ограниченное подмножество безопасного Rust и требуют знаний в области формальных методов. Подходят для регулируемых отраслей, разработки криптографических библиотек или любого контекста, где математическое доказательство корректности является договорным или нормативным требованием.

Дополнительные инструменты, которые стоит включить

Semgrep для пользовательских шаблонов безопасности

Semgrep предоставляет статический анализ на основе шаблонов, который работает одновременно на нескольких языках. В случае Rust он позволяет командам писать собственные правила, обеспечивающие соблюдение организационных политик безопасности и специфических для кодовой базы шаблонов, которые Clippy не может выразить:

YAML

# Custom rule: flag .unwrap() in production paths
rules:
  - id: no-unwrap-in-production
    pattern: $X.unwrap()
    message: "Use proper error handling instead of .unwrap()"
    languages: [rust]
    severity: WARNING

Semgrep также предоставляет управляемый реестр правил безопасности Rust, поддерживаемый сообществом.

SonarCloud / SonarQube для непрерывных контрольных точек качества

SonarCloud обеспечивает непрерывный анализ качества кода с помощью панели мониторинга, отслеживания тенденций и оформления запросов на слияние. Поддержка Rust позволяет выявлять распространенные шаблоны ошибок, уязвимости безопасности и «запахи кода». Для корпоративных команд, уже использующих SonarCloud для других языков, добавление Rust в тот же конвейер обеспечивает единую видимость качества на всем стеке.

Создание многоуровневого конвейера CI/CD

Представленный ниже рабочий процесс GitHub Actions представляет собой практическую отправную точку для команд, которые хотят получить всестороннее покрытие кода на Rust без излишнего усложнения:

YAML

# .github/workflows/quality.yml
name: Code Quality and Security

on: [push, pull_request]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable
        with:
          components: clippy, rustfmt

      - name: Check formatting
        run: cargo fmt -- --check

      - name: Clippy (treat warnings as errors)
        run: cargo clippy -- -D warnings

  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@stable

      - name: Install security tools
        run: |
          cargo install cargo-audit
          cargo install cargo-deny

      - name: Dependency audit
        run: cargo audit --deny warnings

      - name: License and policy check
        run: cargo deny check

  verify:
    runs-on: ubuntu-latest
    # Scheduled or manually triggered, not on every push
    if: github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'
    steps:
      - uses: actions/checkout@v4
      - uses: dtolnay/rust-toolchain@nightly
        with:
          components: miri

      - name: Miri on unsafe modules
        run: cargo +nightly miri test --package my-unsafe-crate

Какой инструмент вам следует использовать? Руководство по принятию решений

Начинаю новый проект на Rust.Clippy + rustfmt + cargo-audit. Настраивается менее чем за пять минут, выявляет большинство проблем качества и безопасности, с которыми сталкиваются команды.

Сервис для производства с внешними зависимостями: Добавить запрет на перевозку грузов для обеспечения соблюдения лицензионных требований и контроля за соблюдением политики цепочки поставок, помимо уязвимостей CVE.

Библиотечный ящик с небезопасными внутренними элементами.Добавьте Miri в свой набор тестов для модулей, содержащих небезопасный код. Запускайте Rudra по расписанию.

Код, критически важный для безопасности, или криптографический кодРазработайте верификационные шаблоны Kani для наиболее важных функций. Рассмотрите Creusot или Prusti, если у команды есть опыт работы с формальными методологиями.

Организация, ориентированная на безопасность: Добавлена ​​поддержка Semgrep с настраиваемыми правилами для политик безопасности организации, а также возможность аудита Cargo в релизных сборках.

Непрерывная прозрачность для большой командыSonarCloud — инструмент для отслеживания трендов, оформления запросов на слияние и контроля качества, а также инструменты с открытым исходным кодом.

Многоязычная корпоративная среда В тех случаях, когда Rust взаимодействует с COBOL, Java или другими устаревшими языками: одноязычные инструменты Rust не могут преодолеть языковую границу. SMART TS XL предоставляет межъязыковой анализ, охватывающий всю систему.

Когда статический анализ Rust встречается с корпоративными системами

В организациях, использующих COBOL, Java, PL/I или RPG на мэйнфреймах или устаревших платформах, Rust всё чаще применяется для создания новых сервисов. В таких средах картина статического анализа выходит за рамки возможностей любого инструмента, разработанного специально для Rust. Микросервис на Rust может вызывать программу на COBOL, записывать данные в общую базу данных или обрабатывать события, генерируемые устаревшей пакетной системой. Анализ качества и зависимостей, важный для корпоративного архитектора, охватывает все эти системы одновременно.

SMART TS XL Этот пробел устраняется за счет предоставления анализа межъязыковых зависимостей по всему портфелю приложений. В то время как Clippy и cargo-audit видят только Rust, SMART TS XL Он отображает зависимость сервиса Rust от общих структур данных, устаревших программ и корпоративных API, а также показывает, какое влияние окажет изменение любой из этих зависимостей. Это делает его предпочтительным инструментом для анализ воздействия и планирование модернизации устаревших систем в средах, где Rust — лишь один из многих языков.

Для команд, внедряющих Rust постепенно в рамках более крупной корпоративной кодовой базы, SMART TS XLАвтора статический анализ кода Обеспечивает структурную прозрачность, необходимую для понимания того, как компоненты Rust вписываются в более крупную систему, прежде чем вносить изменения, которые могут повлиять на компоненты, написанные на других языках. Как рассматривалось в контексте Графы зависимостей и риски приложенийИменно составление полной карты зависимостей перед расширением системы отличает плавные изменения от тех, которые приводят к сбоям в компонентах, которые никто не удосужился протестировать.

Суть в многоуровневом подходе.

Статический анализ в Rust — это не выбор одного инструмента. Это многоуровневая дисциплина, которая начинается с того, что компилятор предоставляет вам бесплатно, и распространяется в зависимости от профиля риска вашего кода. Clippy и rustfmt — это базовый уровень. cargo-audit — обязательный инструмент для любого проекта с внешними зависимостями. Miri необходим в любом проекте с небезопасным кодом. Kani и инструменты формальной верификации предназначены для функций, где ошибки недопустимы.

Команды, которые правильно это понимают, интегрируют статический анализ в цикл разработки: Clippy — непосредственно в редактор, cargo-audit — в каждый запуск CI, а Miri — в ночную задачу, которая выявляет ошибки, которые одни только тесты обнаружить не могут. Результатом является не просто меньшее количество ошибок. Это уверенность в кодовой базе, которая позволяет командам быстро вносить изменения, зная, что уровни анализа между разработчиком и продакшеном работают корректно.