Files
progcode/editorial/agent-rewrites/167.json
T
huncode 2d914b543f
Build and deploy / deploy (push) Failing after 15s
Publish rewritten technical article archive
2026-08-02 22:19:34 +03:00

8 lines
18 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"index": 167,
"slug": "editorial-2023-05-mechanism-static-analysis",
"title": "SARIF без контекста: как читать результат статического анализа",
"excerpt": "SARIF переносит результат проверки, но не принимает решение за команду. Разбираем границы между правилом, совпадением, строкой в коде и контекстом, который нужен для действия.",
"contentHtml": "<p>В pull request появляется результат статического анализа. В нём есть <code>ruleId</code>, сообщение, URI файла и номер строки. Один инженер предлагает заблокировать слияние. Другой называет результат ложным срабатыванием и хочет отключить правило. Оба решения преждевременны: файл описывает наблюдение инструмента, но не объясняет, что происходит в приложении.</p>\n<p>Цена ошибки зависит от выбранного обхода. Глобальное отключение убирает сигнал и для следующих участков кода. Безусловное блокирование превращает каждое совпадение формы в аварию. Команда тратит время на споры, а важный результат может затеряться среди шумных комментариев. Нужна простая граница: формат хранит данные, правило формулирует гипотезу, результат указывает на совпадение, а решение требует контекста проекта.</p>\n<h2>Что именно сообщает анализатор</h2>\n<p>SARIF 2.1.0 — формат обмена результатами статического анализа. В нём можно передать версию формата, инструмент, набор правил, результат и позицию в артефакте. Это общий контейнер для CI, анализатора и просмотрщика. Он не знает, является ли участок достижимым в нужном релизе, кто владеет компонентом и разрешено ли исключение в конкретной команде.</p>\n<p>Правило задаёт проверяемую гипотезу. Например: «значение из условно недоверенного источника передали в построение команды». Совпадение с шаблоном показывает только то, что форма кода похожа на гипотезу. Оно не доказывает источник значения, исполнение ветки или наличие уязвимости.</p>\n<p>Результат связывает гипотезу с наблюдением. <code>ruleId</code> показывает, какое правило сработало. <code>message</code> объясняет, что заметил инструмент. <code>fingerprint</code> помогает сопоставить результат между запусками. <code>location</code> указывает на файл и строку. Эти поля нужны для навигации и повторной проверки. Они не заменяют проверку исходника и границ данных.</p>\n<table><caption>Граница между наблюдением и решением</caption><thead><tr><th scope=\"col\">Слой</th><th scope=\"col\">Пример данных</th><th scope=\"col\">Что это означает</th><th scope=\"col\">Чего не доказывает</th></tr></thead><tbody><tr><td>Формат</td><td><code>version: 2.1.0</code></td><td>Как читать log</td><td>Качество проверки</td></tr><tr><td>Правило</td><td><code>id</code>, revision, level</td><td>Какая гипотеза задана</td><td>Риск именно в этом месте</td></tr><tr><td>Результат</td><td><code>ruleId</code>, message, fingerprint</td><td>Какое совпадение найдено</td><td>Достижимость и влияние</td></tr><tr><td>Позиция</td><td>URI и номер строки</td><td>Где искать наблюдение</td><td>Что код исполняется</td></tr><tr><td>Контекст</td><td>asset, boundary, owner, scope</td><td>В каких условиях принимать решение</td><td>Полное покрытие сценариев</td></tr></tbody></table>\n<h2>Минимальный контекст для review</h2>\n<p>Чтобы выбрать действие, добавьте к результату пять полей. <code>asset</code> называет компонент или поток данных. <code>entryPoint</code> показывает предполагаемую точку входа. <code>trustBoundary</code> фиксирует, почему значение считают недоверенным. <code>owner</code> указывает роль или человека, который может подтвердить устройство компонента. <code>releaseScope</code> связывает проверку с изменением, веткой или релизом.</p>\n<p>Поле может быть неизвестно. Тогда запишите это прямо. Если не найден entry point, статус должен быть «контекст неполный», а не «безопасно». Если неизвестна граница доверия, нельзя объявлять значение проверенным. Такая запись сохраняет отрицательный путь: отсутствие доказательств не превращается ни в finding, ни в false positive.</p>\n<h2>Пример: результат не равен вердикту</h2>\n<p>Ниже приведён искусственный объект в памяти. Он не читает файл, не запускает Semgrep, не вызывает shell и не описывает настоящий finding. Значения <code>src/demo-command.js</code>, строки и fingerprint нужны только для показа связей между полями.</p>\n<pre><code>const result = {\n ruleId: 'demo.untrusted-command-construction',\n message: { text: 'Проверить передачу значения в команду' },\n partialFingerprints: {\n primaryLocationLineHash: 'demo-fingerprint-001'\n },\n locations: [{\n physicalLocation: {\n artifactLocation: { uri: 'src/demo-command.js' },\n region: { startLine: 14 }\n }\n }]\n};\n\nconst context = {\n asset: 'demo-export-job',\n entryPoint: 'demo-http-handler',\n trustBoundary: 'demo-request-parameter',\n owner: 'demo-security-owner',\n releaseScope: 'demo-change-2023-05'\n};</code></pre>\n<p>Объект результата отвечает на вопрос «что и где совпало». Контекст отвечает на вопрос «какие условия нужно проверить перед действием». В примере нет исходного файла и нет доказательства, что строка исполняется. Поэтому допустимый вывод ограничен: нужно открыть соответствующую ревизию кода, проверить поток значения и подтвердить владельца.</p>\n<figure><img src=\"/assets/editorial/2023/static-analysis-2023-rule-context.svg\" alt=\"Схема границ данных: правило и результат передают наблюдение, а контекст добавляет границу доверия, владельца и область изменения перед решением\" loading=\"lazy\" /><figcaption>Правило задаёт гипотезу, result указывает на совпадение, а project context связывает его с конкретным решением. Иллюстрация не показывает реальный запуск анализатора.</figcaption></figure>\n<h2>Симптом → причина → проверка → действие</h2>\n<table><caption>Диагностика спорного результата</caption><thead><tr><th scope=\"col\">Симптом</th><th scope=\"col\">Причина</th><th scope=\"col\">Проверка</th><th scope=\"col\">Действие</th></tr></thead><tbody><tr><td>Один ruleId повторяется в разных файлах</td><td>Синтаксическая форма шире проектного контекста</td><td>Сверить intent и revision правила, затем проверить источники значений</td><td>Оставить сигнал или уточнить правило с новой revision</td></tr><tr><td>В сообщении есть строка, но нет решения</td><td>Location приняли за доказательство исполнения</td><td>Проверить актуальную ревизию, entry point и достижимость ветки</td><td>Записать контекст; не повышать result до вердикта</td></tr><tr><td>Команда хочет убрать правило целиком</td><td>Шум одного результата смешали с политикой для всех файлов</td><td>Сравнить scope исключения с областью будущих результатов</td><td>Выбрать точечное исключение или изменить pattern</td></tr><tr><td>Результат исчез после обновления</td><td>Нет fingerprint и версии правила в записи review</td><td>Сопоставить base commit, tool version и revision</td><td>Повторить проверку и сохранить исходный result</td></tr><tr><td>Никто не подтверждает безопасность</td><td>У контекста нет owner или trust boundary</td><td>Назначить владельца и явно отметить неизвестные поля</td><td>Оставить result видимым до получения evidence</td></tr></tbody></table>\n<h2>Порядок проверки</h2>\n<ol><li><strong>Зафиксируйте симптом.</strong> Сохраните <code>ruleId</code>, revision анализатора, fingerprint, URI, строку и commit. Не добавляйте вывод о риске, которого нет в данных.</li><li><strong>Прочитайте intent правила.</strong> Определите, какую форму оно ищет, какие языки и файлы входят в scope, какие условия считаются исключением.</li><li><strong>Проверьте исходник.</strong> Откройте ту же ревизию файла. Найдите entry point, источник значения, преобразования и вызов, на котором сработало правило.</li><li><strong>Заполните контекст.</strong> Назовите asset, trust boundary, owner и release scope. Неизвестные значения пометьте как неизвестные.</li><li><strong>Выберите узкое действие.</strong> Keep оставляет правило без изменений. Tune меняет гипотезу и получает новую revision. Scoped suppress ограничивает конкретный идентифицируемый результат и хранит причину, owner и дату пересмотра.</li><li><strong>Проверьте отрицательный путь.</strong> Если контекст не собран, не отключайте правило и не называйте совпадение подтверждённой уязвимостью. Назначьте следующий проверяемый шаг.</li><li><strong>Зафиксируйте rollback.</strong> Для изменения policy сохраните прежнюю revision и область действия. Возврат должен быть отдельным изменением конфигурации, а не устной договорённостью.</li></ol>\n<h2>Почему location и severity недостаточны</h2>\n<p>Строка в SARIF может устареть между анализом и review. Файл мог измениться, ветка могла не попасть в релиз, а код мог быть недостижимым при нужной конфигурации. Поэтому location — это адрес для проверки, а не доказательство runtime-пути.</p>\n<p>Severity тоже не является итоговой оценкой. Уровень правила задаёт ожидаемую реакцию инструмента. Он не учитывает бизнес-ценность asset, права вызывающего кода, компенсирующие проверки и область релиза. Переносить его напрямую в слово «критично» нельзя.</p>\n<p>Fingerprint полезен для повторного review, но это не score риска. Он помогает увидеть, что один результат сохранился, переместился или исчез. Причину изменения нужно искать в diff, версии правила и коде, а не в самом fingerprint.</p>\n<h2>Ограничения метода</h2>\n<p>Разделение слоёв не даёт гарантии, что анализатор найдёт все ошибки. SARIF может быть неполным или заполненным по-разному разными producer. Static analysis может не знать о динамической загрузке, feature flag, сгенерированном коде и runtime-конфигурации. Контекстная запись не заменяет тест, ручной data-flow review, проверку доступа или воспроизводимый запуск инструмента.</p>\n<p>Не каждое правило стоит расширять. Более широкий pattern может поднять шум и увеличить стоимость review. Не каждое исключение стоит запрещать. Узкое, временное исключение с понятным объектом иногда лучше, чем изменение общего правила ради одного безопасного участка. Важны область действия, владелец, причина и дата повторной проверки.</p>\n<p>Пример в этой статье синтетический. Он проверяет только смысл полей и порядок рассуждения. Он не сообщает число срабатываний, coverage, false-positive rate, production effect или факт запуска в каком-либо репозитории.</p>\n<h2>Проверяемый критерий готовности</h2>\n<p>Результат можно передавать в review, когда выполнены четыре условия: правило и его revision известны; location проверена на актуальном commit; asset, trust boundary и owner записаны либо явно отмечены как неизвестные; выбранное действие имеет scope и способ отмены. Для tune должна существовать новая revision и описание изменённой гипотезы. Для scoped suppress нужны точный объект результата, причина и дата пересмотра.</p>\n<p>Если хотя бы одно условие не выполнено, готовый статус — «контекст не собран». Это проверяемый результат: указан недостающий факт, назначен владелец и определён следующий шаг. Такой статус сохраняет сигнал и не обещает того, чего не подтверждают данные.</p>\n<h2>Проверяемые источники</h2>\n<ul><li><a href=\"https://docs.oasis-open.org/sarif/sarif/v2.1.0/os/sarif-v2.1.0-os.html\" target=\"_blank\" rel=\"noopener noreferrer\">OASIS: Static Analysis Results Interchange Format (SARIF) Version 2.1.0</a> — спецификация формата log, runs, rules, results и locations.</li><li><a href=\"https://csrc.nist.gov/pubs/sp/800/218/final\" target=\"_blank\" rel=\"noopener noreferrer\">NIST SP 800-218, Secure Software Development Framework</a> — официальная рамка практик безопасной разработки, а не доказательство эффективности отдельного правила.</li></ul>"
}