Code Property Graph (CPG) — единая модель программы. В ней синтаксическая структура связана с потоком управления, вызовами, типами, зависимостями данных и исходными координатами. CodeGraph использует эти связи для поиска дефектов, архитектурных нарушений и метрик аудита.
Эта статья отвечает на два вопроса:
- как текущий GoCPG строит и обогащает граф;
- чем отличаются способы хранения графа, запросов и применения результатов в GoCPG и Joern.
Формулы, которые превращают замечания и метрики графа в Q1–Q12 и Health Score, описаны в методологии Dashboard.
Роль GoCPG в CodeGraph
GoCPG отвечает за построение и хранение CPG проекта. Языковой адаптер
(frontend) создаёт базовые узлы и связи, затем упорядоченные проходы анализа
(passes) добавляют семантические слои.
Результат сохраняется в DuckDB; CodeGraph получает типизированные результаты
через сервисные контракты. Анализаторы работают с общей моделью проекта,
поэтому найденный вызов, архитектурная зависимость и метрика относятся к одним
и тем же исходным сущностям.
Профиль определяет состав конвейера. К стандартным проходам можно добавить доменную разметку, сведения об изменениях из системы контроля версий (VCS-enrichment) и профильные анализаторы.
1. От исходников до базового графа
Выбор языкового адаптера
GoCPG строит граф кода для C, C++, Go, Python, JavaScript, TypeScript, Java, Kotlin, C#, PHP и 1C. Для каждого языка отдельный адаптер извлекает структуру программы и связи между её элементами.
Языковой адаптер извлекает непосредственно из текста:
- файлы, namespaces/modules, типы, методы, параметры и локальные переменные;
- вызовы, литералы, идентификаторы, блоки и управляющие конструкции;
- импорты, аннотации, комментарии и координаты в исходном коде;
- связи AST, аргументов, получателей вызова и исходных файлов.
Языковой адаптер извлекает синтаксическую форму динамического вызова. Восстановление типов и разрешение импортов затем уточняют его целевой метод.
Материализация
Полная сборка проходит так:
- GoCPG выбирает файлы с учётом языка и правил включения и исключения.
- Языковой адаптер разбирает исходники и извлекает объявления, вызовы и управляющие конструкции.
- Следующие стадии добавляют сведения о типах, целях вызовов и потоках данных.
- GoCPG сохраняет граф в DuckDB для запросов и дальнейшего анализа.
GoCPG сохраняет ошибки разбора вместе с путями файлов. По ним команда определяет, какие части исходников нужно обработать повторно.
В реализации адаптеры накапливают изменения в DiffGraph, а CPGGraph
представляет граф для последующих стадий. Состав и порядок проходов задаёт
конвейер gocpg/pkg/passes/pipeline.go.
База знаний типов (Type Knowledge Base) помогает восстанавливать типы;
семантический хеш (semantic digest) позволяет сопоставлять результаты анализа.
Контракты архитектурного анализа находятся в
gocpg/pkg/architecture.
2. Семантический конвейер
Порядок стадий определяет доступные факты. Разрешение импортов и восстановление типов подготавливают граф вызовов; потоки управления и данных подготавливают PDG.
| Стадия | Что добавляется | Для чего используется |
|---|---|---|
| Базовая топология | метаданные, файлы, пространства имён, узлы и объявления типов, наследование, связи параметров | Единая идентичность сущностей и типов. |
| Восстановление типов | типы идентификаторов и вызовов, происхождение и уверенность для узлов вызовов | Более точные FQN и цели вызовов в динамических языках. |
| Поток управления | CFG, доминаторы, постдоминаторы, CDG | Достижимость, ветвления и зависимости управления. |
| Импорты и вызовы | карты импортов, распространение полного имени вызова (FQN), разрешение вызовов, динамическая диспетчеризация, рёбра межъязыкового взаимодействия (FFI) | Межфайловые и межъязыковые связи вызовов. |
| Поток данных | ссылки, анализ псевдонимов, достигающие определения, межпроцедурное распространение | Источник значения, побочные эффекты и пути между методами. |
| Анализ рисков | неинициализированные переменные, диапазоны значений, утечки ресурсов, разыменование null, конкурентность, taint, переполнение целых и буферов | Замечания с привязкой к графовому пути и исходному коду. |
| Зависимости программы | EVAL_TYPE, распространение возвращаемых типов, PDG и DDG | Совместный анализ зависимостей управления и данных. |
| Объектная модель | привязки методов, таблицы виртуальных методов, связывание динамических вызовов | Интерфейсные и виртуальные вызовы, связи конструктора с инициализатором. |
| Обогащение | метрики методов, комментарии, вложенность, замечания, структурный поиск шаблонов | Метрики аудита, поиск шаблонов и объяснимые результаты. |
| Опциональные слои | доменная разметка, распространение сценариев использования, метки системы контроля версий | Доменный контекст, точки входа, авторство и частота изменений. |
Каждый проход объявляет зависимости. Проходы с интерфейсом IncrementalPass
также сообщают о поддержке инкрементального выполнения и обрабатывают выбранные
файлы. Планировщик запускает независимые части графа зависимостей (DAG)
параллельно и соблюдает порядок зависимых стадий.
3. Восстановление типов в Python и JavaScript
Для компилируемого проекта тип часто доступен из объявления или инструментов сборки. В Python и JavaScript один и тот же идентификатор может получить тип только во время выполнения. GoCPG собирает статические свидетельства, применяет их итеративно и сохраняет происхождение и уверенность там, где это предусмотрено схемой.
Источники типовых фактов
Первая, внутрипроцедурная стадия использует:
- аннотации и уже извлечённые объявления типов;
- присваивания вида
x = ClassName(...); - вызовы конструкторов;
- значения локальных переменных и одноимённые идентификаторы;
- поля
self/thisи присваивания членам объекта; - проверки Python
isinstance; - возвращаемые типы встроенных функций и методов из языковой базы знаний типов.
Для Python и JavaScript есть отдельные наборы встроенных функций и типовых
заглушек. Они предоставляют возвращаемые типы известных функций и методов.
Для метапрограммирования, monkey patching, eval, динамических импортов и
изменения структуры объектов проход сохраняет доступные статические факты;
неразрешённые типы остаются в результате анализа.
Итерации и сходимость
На последующих итерациях GoCPG распространяет:
- возвращаемый тип от разрешённого метода к месту вызова;
- тип результата вызова к переменной в присваивании;
- тип из операторов возврата и свойств;
- факты динамической диспетчеризации из базы знаний типов;
- временный тип
<returnValue>для вызова, цель которого известна, а точный возвращаемый тип — нет.
Обновления сначала собираются параллельно, затем применяются детерминированно. По умолчанию конфигурация сохраняет совместимость с тремя базовыми итерациями, но адаптивный цикл может продолжаться до десяти. После третьей итерации он останавливается, если отношение новых обновлений к предыдущей итерации ниже 1%, или раньше, если обновлений нет.
Для узлов вызова сохраняются type_origin и type_confidence. Примеры
источников: builtin, tkb, constructor, propagated, inferred.
Факты конструктора и встроенных функций имеют более высокую уверенность, временный
<returnValue> — более низкую. Это позволяет последующим анализаторам отличить
явный факт от приближения.
Как типы улучшают граф вызовов
После восстановления типов отдельный проход переписывает method_full_name
вызова, используя тип объекта-получателя и карту импортов. Затем разрешение
вызовов связывает узел вызова с наиболее подходящим узлом метода. Динамический
компоновщик добавляет виртуальную диспетчеризацию и связи конструктора с __init__.
Граф показывает связи, восстановленные из исходников. Для каждого вызова анализ сохраняет разрешённую цель или отмечает, что цель требует уточнения. Происхождение и уверенность помогают разобрать результат, а сведения о полноте определяют, достаточно ли фактов для выбранной проверки.
Пример: тип объекта и цель вызова
В этом Python-коде переменная greeter создаётся без аннотации типа:
class Greeter:
def greet(self) -> str:
return "Hello"
def say_hello():
greeter = Greeter()
return greeter.greet()
При стандартном анализе GoCPG использует вызов конструктора, чтобы определить
тип объекта, и аннотацию -> str, чтобы определить тип результата метода.
| Выражение | Полученный тип | Связь в графе |
|---|---|---|
Greeter() |
Greeter |
Создание объекта класса Greeter. |
greeter.greet() |
str |
Вызов связан с методом Greeter.greet в example.py. |
По этой связи CodeGraph может найти вызываемый метод и продолжить анализ через границу вызова. Для исследования влияния изменения метода доступны его вызывающие участки кода.
4. Потоки управления и данных
GoCPG строит несколько связанных представлений:
- CFG показывает допустимый порядок выполнения внутри метода;
- деревья доминаторов и постдоминаторов нужны для анализа обязательных путей;
- CDG связывает узел с условием, от которого зависит его выполнение;
- REF и рёбра достигающих определений связывают использование переменной с её значением;
- анализ псевдонимов уточняет ссылки, которые могут указывать на один объект;
- межпроцедурные достигающие определения переносят факты через места вызова;
- DDG материализует зависимости данных;
- PDG объединяет зависимости данных и управления.
Анализаторы потоков недоверенных данных, утечек ресурсов, разыменования null и других рисков работают поверх этих слоёв. Поэтому замечание должно сохранять сообщение вместе с координатой в исходном коде, связанный узел и, где применимо, путь доказательства.
Разные CPG-реализации могут материализовать разные вспомогательные узлы и рёбра. Точность проверяют по ожидаемым фактам в размеченном наборе исходников. Количество узлов и рёбер CALL или CDG показывает размер полученного графа.
5. Структурный анализ Architecture v1
Структурный анализ классифицирует сущности CPG по компонентам и проверяет разрешённые отношения между ними.
Модель и правила
- Каждая сущность, участвующая в классификации, получает стабильный UID компонента. Отображаемый
idможно менять;uidвходит в идентичность и отпечаток сущности. - Родительские компоненты группируют конечные компоненты; классификация сущностей выполняется в конечных компонентах.
- Область, слой, тип, платформа и среда выполнения — независимые измерения. Для обязательной проверки классификация должна быть полной и однозначной.
- Архитектурные правила используют
spec_version: "2.0",kind: architecture, стабильный идентификатор правила и селекторыall,any,not. - Ссылки на наборы и правила всегда фиксируют версию:
id@version.latestи неявное наследование статуса соседнего языка отклоняются.
Architecture v1 содержит три общих шаблона и одиннадцать языковых наборов: 1C, C, C++, C#, Go, Java, JavaScript, Kotlin, PHP, Python и TypeScript. Для каждого языка проверяются отдельный манифест возможностей и квалификация примеров. При запуске анализатор сопоставляет требования выбранных правил с фактами конкретного проекта и формирует результат проверки покрытия.
Полнота, baseline и waiver
Реестр возможностей объявляет семейства фактов, которые извлекает каждый
языковой адаптер. Если обязательная возможность отсутствует, проверка получает
BLOCKED_INCOMPLETE.
Инкрементальный запуск получает авторитетный статус incremental_complete,
если его отпечатки, состояния принятого долга и исключений, а также семантическая
контрольная сумма совпадают с полным запуском. Изменение публичного API, модели,
правил, принятого долга, исключений, рёбер или сильносвязных компонент (SCC)
расширяет область повторного анализа. Для частичного результата система один
раз синхронно повторяет полный анализ. Свежий полный результат заменяет частичный.
Базовый набор принятого долга (baseline) связывает замечание с точным правилом, версией отпечатка, отпечатком замечания и семантическим хешем. Подавление замечаний использует принятую запись этого набора. Согласованное исключение (waiver) действует ограниченное время; его запрашивают и утверждают разные люди. Подпись фиксирует проект, репозиторий, коммит, ветку, хеши и ревизию. Ключи доверия и состояние отзыва система получает из контура управления или доверенной конфигурации CI.
Результаты и release gate
Канонический JSON содержит запуск с привязкой к ревизии, доказательство полноты, результат каждого активного правила, замечания, покрытие и метрики. SARIF, Mermaid и PlantUML — детерминированные проекции того же результата; они сохраняют отпечаток результата. Сервис gRPC QueryService читает один сохранённый запуск, экспортирует и проверяет результаты, возвращает краткое описание запуска и объяснение замечания.
Профили discovery и advisory не разрешают выпуск. enforce_new,
enforce_all и strict могут вернуть PASS или FAIL_VIOLATIONS, но
только при свежих и полных фактах и достаточном покрытии возможностей.
Отсутствующее, устаревшее, частичное, ошибочное доказательство или нулевой
знаменатель даёт
BLOCKED_INCOMPLETE.
Эти результаты питают прежде всего Q2, Q4, Q6 и Q11 аудита, но не заменяют другие сценарии.
6. Карта решения: GoCPG и Joern
Joern также строит CPG через языковые адаптеры, добавляет семантические слои проходами и предоставляет язык запросов CPGQL на Scala. Официальная документация описывает интерактивную консоль, сценарии, серверный режим, расширяемые проходы и плагины. Языковой адаптер и набор семантических слоёв зависят от версии, поэтому их нужно фиксировать в каждом сравнении.
| Аспект | GoCPG в CodeGraph | Joern |
|---|---|---|
| Основной процесс | Импорт проекта, автоматические анализаторы, Dashboard, CLI/MCP/REST и управляемые доказательства. | Интерактивное исследование уязвимостей через запросы, сценарии, сканирование и серверный режим. |
| Хранение | DuckDB в области проекта, атомарная запись и типизированные сервисные контракты. | Собственная графовая база в памяти и модель проектов Joern. |
| Запросы | Сервисы Go, gRPC и контролируемые интерфейсы проекта; SQL остаётся внутри границы хранения. | CPGQL на Scala, REPL, сценарии и HTTP-сервер. |
| Расширение | Языковые адаптеры, Go-проходы с зависимостями, правила шаблонов и архитектуры, доменные и VCS-слои. | Языковые адаптеры, CPG-проходы, расширения запросов и плагины JVM. |
| Динамические типы | Общий итеративный проход, отдельные типовые заглушки, происхождение/уверенность и распространение FQN. | Реализация зависит от адаптера и слоёв зафиксированной версии. |
| Структурный контроль | Версионированная модель и presets, реестр возможностей, подписанное управление, эквивалентность полного/инкрементального анализа и запрет при неполных данных. | Архитектурные запросы и расширения строятся средствами CPGQL, проходов и плагинов; правила принятия решения нужно оценивать отдельно. |
Joern подходит командам, которым нужен интерактивный анализ через CPGQL. GoCPG встроен в автоматизированный жизненный цикл CodeGraph, где важны хранение в области проекта, воспроизводимые замечания, свежесть, управление и доказательства готовности к выпуску.
Официальные источники Joern для сверки:
Сервер Joern выполняет запросы в интерпретаторе. При развёртывании настройте аутентификацию и отдельную изоляцию процесса и сети: согласно руководству сервера, изоляция выполняемых запросов обеспечивается средствами окружения.
7. Как выбрать критерии пилота
Начните с задачи команды. Для исследования отдельной уязвимости сравните удобство запросов и просмотра путей данных. Для регулярной проверки проекта сравните импорт, сохранение результатов, повторные запуски и включение архитектурных правил в процесс выпуска.
| Задача команды | Что проверить в пилоте | Полезный результат |
|---|---|---|
| Разобрать конкретную уязвимость | Найти источник данных, опасную операцию и путь между ними. | Путь по исходному коду, который исследователь может проверить. |
| Оценить последствия изменения метода | Восстановить цель вызова и найти вызывающие участки. | Список мест, которые нужно проверить после изменения. |
| Контролировать зависимости перед выпуском | Описать компоненты и запрещённую связь, затем выполнить правило. | Замечание с исходной координатой и результат проверки для этой ревизии. |
| Проводить регулярный аудит | Повторить импорт и анализ после изменения выбранных файлов. | Сопоставимые результаты двух запусков и понятная стоимость повторной проверки. |
Выберите несколько ожидаемых фактов: цель вызова, поток данных от входа к опасной операции и запрещённую зависимость между компонентами. Эти факты позволят проверить результат анализа на одном и том же наборе исходников. Измерения времени и ресурсов проводите по протоколу ниже.
8. Воспроизводимое сравнение производительности
Для численного сравнения зафиксируйте условия в неизменяемом манифесте измерения
(immutable benchmark manifest):
- коммиты GoCPG/CodeGraph и корпуса, точный выпуск Joern и контрольные суммы сборок;
- языки, правила включения и исключения исходников, список ожидаемых фактов;
- оборудование, ОС, ограничения выполнения, состояние кеша и параллельную нагрузку;
- точные команды импорта, запросов, экспорта и очистки;
- порядок запусков с пустым и прогретым кешем, число повторов, время ожидания, ошибки и правила обработки выбросов;
- длительность запуска, использование CPU, пиковую память, объём хранения и полноту результата;
- исходные журналы, машиночитаемые результаты, их контрольные суммы и независимого рецензента.
Измерьте весь путь от исходников до полезного ответа. Вместе со временем импорта и размером графа проверьте:
- полноту разбора и импорта;
- правильность и полноту ожидаемых фактов на размеченном корпусе;
- задержку запросов после импорта;
- полное время получения результата;
- неразрешённые вызовы и неподдерживаемые конструкции;
- память, объём хранения и инкрементальное обновление;
- трудозатраты оператора, развёртывание, безопасность, обновление и откат.
Запускайте каждый инструмент в чистом рабочем каталоге, подготовленном по его инструкции. Включайте ошибки импорта, частичные графы и ручную настройку в результаты сравнения. Манифест позволяет повторить измерение и проверить численные выводы. Выводы об архитектуре и процессе подтверждайте соответствующими исходниками и документацией.
Как применить GoCPG в проекте
Начните с настройки архитектурного контроля: руководство проводит от подготовки проекта до проверки зависимостей.
- Модель и правила — опишите компоненты и допустимые связи между ними.
- Запуск и управление проверками — выберите профиль и выполните полный учебный пример.
- Результаты и интерфейсы — разберите найденную зависимость и выгрузите результаты.
- Методология Dashboard — проследите, как замечания и метрики входят в оценку проекта.