Репозиторий показывал 0 открытых Dependabot-алертов — зелёная галочка, спокойствие. Тот же код, прогнанный через osv-scanner, дал больше полусотни уязвимых пакетов, среди них high-severity. Оба инструмента не врут — они отвечают на разные вопросы: из разной базы advisory, по разной глубине дерева зависимостей, разным методом. «Ноль у Dependabot» — это не «чисто», а «чисто в пределах того, что Dependabot вообще смотрит на этом репозитории». Статья — про то, как устроен SCA (Software Composition Analysis), почему инструменты расходятся, и как реально закрывать уязвимости в зависимостях, включая транзитивные, которые нельзя «просто поднять на версию выше».
Смежная тема — провенанс и целостность цепочки поставки (SBOM, подпись образов)Скоро: там про «из чего собран артефакт и точно ли это мы его собрали», здесь — про «нет ли в этом составе известных дыр и как их закрыть».
В статье
- Что такое SCA — и чего он не делает
- Инструменты и их базы
- Почему «ноль у Dependabot» обманчив
- Прямая зависимость против транзитивной
- Плейбук починки по экосистемам
- Честные края: no-fix, ложные, расхождение баз
- CI-гейт
- Верификация починки
- Демо и версии
- Документация и первоисточники
Что такое SCA — и чего он не делает
SCA (Software Composition Analysis) как дисциплина шире одного скана: это инвентаризация состава (что вообще у вас в дереве), лицензионный анализ, policy-compliance и — интересующий нас здесь срез — управление уязвимостями. В этой статье под SCA я имею в виду именно последнее: есть ли в ваших зависимостях компоненты с известными уязвимостями — теми, на которые уже выпущен идентификатор (CVE, GHSA, RUSTSEC, GO-…) и запись в базе advisory. Это скан чужого кода, который вы тянете, а не своего.
Важно держать SCA на своём месте среди других проверок, чтобы не считать зелёный SCA за «безопасно»:
- SAST анализирует ваш исходник на паттерны уязвимостей (см. secure coding и инъекцииСкоро);
- DAST бьёт по запущенному сервису снаружи;
- secret-scan ищет утёкшие ключи;
- SBOM и подпись отвечают за состав и происхождение артефакта;
- SCA — известные CVE в зависимостях.
Чего SCA не гарантирует: 0-day без публичного advisory, логические дефекты, неизвестный вредонос (typosquatting до момента, пока его не занесли в базу). Оговорка про вредонос: SCA не значит «слеп к малвари» — OSV агрегирует в том числе OpenSSF Malicious Packages, поэтому уже каталогизированный вредоносный пакет (даже без CVE) sca-сканер найдёт; не находит он лишь то, чего ещё нет в базе. SCA — необходимая, но не достаточная линия: он закрывает «известное плохое», и только его.
Инструменты и их базы
Ключ к тому, почему инструменты расходятся, — они смотрят в разные базы и разным методом.
| Инструмент | База advisory | Метод | Охват |
|---|---|---|---|
| Dependabot | GitHub Advisory DB | presence (по dependency graph) | только поддерживаемые манифесты и зависимости, попавшие в dependency graph дефолтной ветки |
| osv-scanner | OSV (агрегатор: GitHub Advisory + Go vuln DB + RustSec + PyPA + …) | presence; для Go на собирающихся модулях — авто-reachability (experimental_analysis) |
lock-файлы и манифесты (go.mod, pom.xml, requirements.txt, …), много экосистем |
| govulncheck | Go vuln DB | call-graph | только Go, но флагует лишь реально вызываемые уязвимые символы |
| cargo audit | RustSec | presence (по Cargo.lock) |
только Rust |
| Trivy / Grype | своя + NVD/GHSA | presence | образы, ФС, манифесты |
Оговорки к охвату, чтобы не переоценивать «видит всё»: osv-scanner читает не только lock-файлы, но и манифесты (pom.xml, requirements.txt, go.mod), и «всё дерево» не гарантировано — например, Maven test-scope зависимости не поддерживаются, а резолюция через deps.dev может отличаться от вашего private registry и активных профилей. И про метод — тут у osv-scanner 2.4.0 есть нюанс, который легко описать неточно. Базово это presence-скан, но для Go на собирающихся модулях он делает анализ достижимости по умолчанию: в JSON-выводе Go-записи получают поле experimental_analysis.called (проверено на нашем снимке — для NATS x/crypto → GO-2026-5932: called=false, флаг --call-analysis при этом не передавался). В человекочитаемой таблице called:false он прячет, поэтому кажется «чистым presence». Для не-Go экосистем и для не-собирающихся модулей — presence. Итог: у osv-scanner reachability для Go есть и без явного флага, но полноценный call-graph-инструмент — это govulncheck (см. края и govulncheck-nats.txt в снимке).
Два принципиальных различия:
- База. OSV агрегирует несколько источников — GitHub Advisory DB, Go vuln DB, RustSec, PyPA и другие. Это не строгое надмножество GitHub Advisory DB: импорт может задерживаться или отбраковываться по quality-bar, так что «в OSV всегда есть всё, что в GitHub» — неверно. Но на практике запись
GO-2026-…илиRUSTSEC-…, которой нет в GitHub Advisory DB, у Dependabot не всплывёт, а уosv-scanner— да. - Метод.
presence-based(Dependabot, osv-scanner) флагует пакет по факту его наличия в дереве.call-graph(govulncheck) идёт дальше: уязвимость в подпакете, который вы не импортируете, он не покажет — это радикально режет ложные срабатывания в Go. Но call-graph — не гарантия отсутствия риска: вызовы через рефлексию иunsafeон не видит (возможен false negative), а интерфейсные и через указатель-на-функцию — анализирует консервативно. «govulncheckмолчит» означает «на статически видимом графе символ не достижим», а не «уязвимости точно нет».
Ставится osv-scanner как обычный Go-бинарь:
go install github.com/google/osv-scanner/v2/cmd/osv-scanner@latest
osv-scanner scan --recursive .Почему «ноль у Dependabot» обманчив
История с конкретного репозитория. Стартовое состояние по Dependabot — сотни алертов; сводку удобно снять через API:
gh api -X GET repos/OWNER/REPO/dependabot/alerts \
-f state=open -f per_page=100 --paginate \
| jq -r '.[].security_advisory.severity' | sort | uniq -c329 open → 136 critical, 60 high, 124 moderate, 9 low136 critical свернулись всего в три Go-пакета — golang.org/x/crypto (SSH-CVE, фикс 0.52.0), jackc/pgx/v5 (memory-safety, фикс 5.9.x) и google.golang.org/grpc (обход авторизации, фикс 1.79.3). После адресных бампов по всем затронутым go.mod и push Dependabot пересканировал ветку и показал 0. Зелёная галочка.
Дальше — контрольный прогон osv-scanner по тем же веткам. И вот тут «ноль» рассыпался:
PyPI urllib3 1.26.20 → 10 advisory (до 8.9), фикс 2.7.0 requirements.txt
PyPI idna 3.9.0 → фикс 3.15 requirements.txt
Maven httpclient5 5.6 → фикс 5.6.1 (high) pom.xml (транзитив)
Maven io.netty:* → десятки advisory pom.xml (транзитив)
Maven jackson-databind → серия advisory pom.xml (транзитив)
crates rsa 0.9.10 → RUSTSEC, фикса нет Cargo.lock (транзитив)
...Почему Dependabot этого не показал (осторожно с формулировками — «просто не включено» тут неточно):
- Состояние графа для Python.
requirements.txtподдерживается dependency graph, а Dependabot alerts считаются по дефолтной ветке. Так что причина не «экосистема не поддерживается», а конкретное состояние: попал ли манифест в граф на нужной ветке и по нужному пути, дошёл ли до alerts. У нас пластurllib3/idnaне всплыл — это надо диагностировать в Insights → Dependency graph (какой манифест виден, на какой ветке), а не списывать одной фразой. - Глубина/транзитив. Часть транзитива (
netty,jackson,httpclient5) видна только при полной submission дерева — если её нет, эти узлы графу неизвестны. - Другая база.
RUSTSEC-записи и часть Go vuln DB (GO-…) в GitHub Advisory DB на момент прогона отсутствовали — это уже про источник, а не про граф.
Вывод, который стоит закрепить: один сканер — не истина в последней инстанции. Кросс-проверка вторым инструментом с другой базой (osv-scanner поверх Dependabot, govulncheck для отсева ложных) — не паранойя, а базовая гигиена.
Прямая зависимость против транзитивной
Главный водораздел в починке — прямая ли для вас зависимость или транзитивная. И определяется это не наличием строки в манифесте, а тем, зависит ли ваш код от компонента непосредственно: манифесты и lock-файлы этой разницы сами по себе не проводят. Go, например, пишет транзитивы в тот же go.mod с пометкой // indirect; lock-файлы (Cargo.lock, package-lock.json) вообще содержат всё дерево; а constraints/overrides явно называют именно транзитивный пакет, не делая его прямым.
- Прямая — вы объявили её сами и непосредственно от неё зависите. Это не только «прикладной код импортирует пакет»: сюда же runtime-, build-, plugin- и tool-зависимости (кодогенераторы, линтеры, build-плагины), которые из прикладного кода не импортируются, но объявлены вами напрямую. Чинится очевидно — поднять версию в вашем манифесте.
- Транзитивная — приезжает через вашу прямую зависимость; вы её не объявляли. Поднять «на версию выше» в обычном смысле негде — её тянет чужой манифест. Три пути:
- пин через механизм менеджера (заставить резолвер выбрать безопасную версию);
- апгрейд родителя — той прямой зависимости, что тянет дыру, до версии, где она уже пропатчена;
- замена родителя — если он заброшен и фикса не будет.
На реальном прогоне подавляющее большинство находок были именно транзитивными. Поэтому дальше — плейбук ровно про этот случай, по каждой экосистеме.
Плейбук починки по экосистемам
Go
Прямые и indirect-пины двигаются одним go get. Важная оговорка: MVS (minimal version selection) выбирает максимальную из минимально требуемых версий в текущем графе, но это не значит, что случайно понизить нельзя — go get pkg@vX умеет и downgrade, и вслед за целевым модулем понизить/удалить связанные (это прямо описано в Go Modules Reference). Поэтому целиться нужно в max(текущая, фикс) и после бампа проверять, что уже-пропатченные соседи не откатились назад.
go get golang.org/x/crypto@v0.52.0
go get github.com/jackc/pgx/v5@v5.9.2
go mod tidyДве тонкости, на которых легко ошибиться:
- Одиночная
require. Версию нельзя вытаскивать наивным regex поgo.mod: пакет в блоке пишется\tpkg vX, а одиночная прямая зависимость —require pkg vXс префиксом. Скрипт, ловивший только блочную форму, пропустилrequire github.com/jackc/pgx/v5 v5.7.6и оставил два critical открытыми. Правильно — читать выбранную версию черезgo list:
go list -m -f '{{ .Version }}' github.com/jackc/pgx/v5- Директива
goможет подняться. Если пропатченные зависимости требуют более свежий язык (уgolang.org/x/cryptov0.52.0 в go.mod указаноgo 1.25.0),go build -mod=modподнимет строкуgoв вашемgo.modдо этого минимума. Это не шум и не ошибка, а необходимое следствие апгрейда — просто держите в голове, что минимальный тулчейн для сборки вырос.
Отдельно — govulncheck. Он по call-graph отсекает то, что presence-сканеры флагуют зря (подробнее — в разделе про края):
govulncheck ./...Maven
Транзитив в Maven пинится через <dependencyManagement> — он форсирует версию для всего дерева, не меняя того, кто её тянет:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.6.1</version>
</dependency>
</dependencies>
</dependencyManagement>Для семейств (Netty, Jackson) чище импортировать BOM (netty-bom, jackson-bom) — один пин на весь набор артефактов, чтобы версии не разъезжались. Альтернатива пину — апгрейд самого родителя (клиентской библиотеки), который тянет дыру, если у него есть свежий релиз с уже поднятой транзитивной версией.
Проверять, что пин победил, — по дереву:
mvn dependency:tree | grep httpclient5
# +- org.apache.httpcomponents.client5:httpclient5:jar:5.6.1:compilePython
Транзитивные urllib3/idna (их тянет, например, requests/клиент) ограничивают. Правильный инструмент для этого — constraints.txt, а не строка в requirements.txt: pip прямо рекомендует constraints для дополнительных ограничений транзитива, потому что запись в requirements.txt превращает пакет в явно устанавливаемую прямую зависимость (вы берёте на себя urllib3 как свою, хотя он транзитив). Constraints же только ограничивают версию, если пакет вообще будет затянут:
# security-ограничения транзитивов: старые urllib3/idna уязвимы
urllib3>=2.7.0,<3
idna>=3.15pip install -r requirements.txt -c constraints.txt(В нашем демо-стенде для простоты пины стоят прямо в requirements.txt — это работает и его тоже видит osv-scanner, но в проде чище constraints.txt.)
Осторожно с env-маркерами. У пакета-родителя ограничение может быть условным по версии Python:
urllib3<1.27,>=1.26.19 ; python_version < "3.10"
urllib3!=2.2.0,!=2.2.1,<3,>=1.26.19 ; python_version >= "3.10"osv-scanner при разборе requirements.txt может слить оба ограничения без учёта маркера и выдать мнимый конфликт >=2.7.0,<3 против <1.27. На деле на Python 3.10+ применяется только вторая строка, и pip install -r резолвит urllib3 2.7.0 без конфликта. Проверять — реальным резолвом, а не доверять предупреждению сканера:
pip install --dry-run -r requirements.txtRust
Транзитив из Cargo.lock часто нельзя починить бампом самого крейта — он может быть заброшен и фикса не иметь. Тогда работает апгрейд родителя. Реальный случай: tokio-tar 0.3.1 под advisory без фикса, тянется как dev-dependency через testcontainers. Апгрейд testcontainers-modules уводит дерево на поддерживаемый форк:
cargo tree -i tokio-tar
# tokio-tar v0.3.1
# └── testcontainers v0.23.3
# └── testcontainers-modules v0.11.6 (dev-dependency)После testcontainers-modules = "0.15" в Cargo.toml:
cargo tree -i tokio-tar
# error: package ID specification `tokio-tar` did not match any packages
cargo tree -i astral-tokio-tar
# astral-tokio-tar v0.6.3 (advisory фиксит 0.5.6 → 0.6.3 не уязвим)tokio-tar ушёл из дерева совсем, а пришедший astral-tokio-tar 0.6.3 выше фикс-версии 0.5.6. Проверка — cargo test --no-run (компиляция тестов подтверждает, что API родителя не сломался) и cargo audit.
Честные края: no-fix, ложные, расхождение баз
Не всякий алерт закрывается бампом, и не всякий алерт — реальная дыра. Три ситуации, где нужна голова, а не автопилот.
No-fix advisory — фикса не существует. Пример: rsa (крейт) под RUSTSEC про Marvin timing-attack — исправления нет и не предвидится без потери производительности. Аналогично lz4-java имел два advisory: одно закрылось бампом до 1.8.1, второе (GHSA-cmp6-m4wj-q63q) фикса не имеет. Решения честные: убрать/заменить зависимость, либо осознанный dismiss с обоснованием (dev-only, не на пути недоверенного ввода, риск принят) — но не «замьютили и забыли».
Корректная presence-находка, недостижимая в вашем приложении. Важно не путать это с «ошибкой сканера». GO-2026-5932 на golang.org/x/crypto — про подпакет openpgp («unmaintained, unsafe by design»), затронуты все версии, фикса нет. Presence-сканер сработал правильно: уязвимый модуль в дереве действительно есть. Но подпакет openpgp в стенде не вызывается — и govulncheck по call-graph помещает запись в раздел «modules you require, but your code doesn’t appear to call these» (проверено, govulncheck-nats.txt в снимке). То есть сканер не соврал — это вы, зная, что символ недостижим, осознанно понижаете приоритет находки. Формулировать это как «ложное срабатывание» неточно: находка верная, снижает применимость контекст приложения.
Расхождение диапазонов между базами. Конкретный воспроизводимый случай: GHSA-5jmj-h7xm-6q6v (jackson-databind, case-insensitive deserialization bypass). После бампа до 2.18.9 Dependabot алерт закрыл (по данным GitHub Advisory эта версия — фикс для 2.x-линии). А osv-scanner (проверено на 2.4.0) на той же 2.18.9 продолжает флагать. Разбор записи в OSV точнее, чем «весь 2.x уязвим»: для старого координата com.fasterxml.jackson.core:jackson-databind заведено несколько introduced-диапазонов (2.8.0, 2.19.0, 2.22.0) — и ни у одного из них нет события fixed (есть лишь last_known_affected_version_range). Чистое fixed (3.1.4) кодируется только на новом координате tools.jackson.core:jackson-databind (Jackson 3, пакет переименован). Из-за отсутствующих fixed-событий на старом координате сканер трактует подходящие 2.x-версии как затронутые. Это не «OSV умнее/строже», а именно как закодирован диапазон в конкретной записи и как сканер это интерпретирует: две базы описали один дефект по-разному. Практический вывод: понимать, чью трактовку вы гейтите, и не тянуть в стенд миграцию на мажор ради строки в одной из баз — но и знать, что «зелено у Dependabot» не равно «зелено у OSV».
CI-гейт
Скан имеет смысл в двух точках: до пуша (быстрая обратная связь разработчику) и периодически по ветке/реестру (новые advisory появляются на уже вмёрженный код). Простой гейт на GitHub Actions:
Важно: у google/osv-scanner-action корневой action.yml не исполняемый — это не обычный step-action. Официальный способ — reusable workflow. И режимы нужно разделить: на PR — дифференциальный osv-scanner-reusable-pr.yml (только новые уязвимости относительно базовой ветки, иначе PR будет падать на всём унаследованном долге), на push/schedule — полный osv-scanner-reusable.yml. Пиновать сторонний workflow — по полному commit SHA (версия — в комментарии): tag можно переписать, SHA — нет; это единственный неизменяемый способ подключения (рекомендация GitHub «Security hardening»).
name: sca
on:
pull_request:
branches: [main]
push:
branches: [main]
schedule:
- cron: "0 6 * * 1" # еженедельно — на новые CVE на уже вмёрженном коде
permissions:
actions: read
contents: read
security-events: write # выгрузка SARIF в Security-таб
jobs:
pr-diff: # PR: только НОВЫЕ уязвимости vs базовая ветка
if: github.event_name == 'pull_request'
uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable-pr.yml@9a498708959aeaef5ef730655706c5a1df1edbc2" # v2.3.8
full: # push/schedule: полный скан всего дерева
if: github.event_name != 'pull_request'
uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@9a498708959aeaef5ef730655706c5a1df1edbc2" # v2.3.8
with:
scan-args: |-
--recursive
./Три правила, чтобы гейт помогал, а не бесил:
- Severity +
fixable— целиком ваша логика поверх.osv-scanner.tomlне умеет переопределять severity: он поддерживает игнор по ID, package-overrides и license-overrides — и только. Порог по severity и учётfixableреализуются обработкой JSON-вывода (--format json) отдельным шагом, который решает exit-code. Отдельно — политика для advisory без CVSS: тот жеGO-2026-5932приходит с severityUnknown, и наивный порог «блокировать от High» его молча пропустит; дляUnknownнужно решать явно (ревью/allowlist), а не «ниже порога — значит ок». - No-fix ≠ «всегда пропускать». Унаследованный no-fix долг не должен валить каждый PR — да. Но новая no-fix-зависимость, которую добавляет сам PR, наоборот должна блокироваться или требовать явного risk-acceptance:
fixableописывает способ устранения, а не допустимость риска. Различайте «старый долг» (не блокируем, ведём в allowlist) и «новый ввод» (блокируем/принимаем осознанно). - Allowlist с причиной и сроком. Каждый игнор — с обоснованием и датой пересмотра: в
osv-scanner.tomlу ignore-записи для этого есть поляreasonиignoreUntil(после этой даты запись снова начинает флагаться). Молчаливый мьют без срока = дыра, про которую забыли.
Верификация починки
Пин в манифесте — это ещё не починка. Нужно подтвердить два факта: менеджер резолвит фикс-версию и проект собирается. Пин, который не победил в дереве (перебит транзитивным требованием) или сломал сборку, — хуже, чем его отсутствие, потому что создаёт ложную уверенность.
- Резолв:
go list -m,mvn dependency:tree,pip install --dry-run,cargo tree— увидеть глазами, что стоит фикс-версия. - Сборка:
go build ./...,mvn verify,cargo test --no-run— что пин ничего не сломал.
Практические подводные камни самой проверки (собраны на реальном прогоне через WSL):
- чужой
~/.m2подroot→ писать в свежий локальный репо-Dmaven.repo.local=$HOME/.m2-osv; - сборка Maven на смонтированном Windows-диске (drvfs) роняет запись
target/*.classи mojo-status → копировать стенд на нативную ФС и собирать там; для multi-module — весь реактор, сохранивparent.relativePath; - разные
release-таргеты стендов (17/21/25) → под каждый нужен свой JDK (portable-дистрибутив ставится без root).
Демо и версии
- Материал якорится на реальном security-sweep двух репозиториев:
github.com/khorost-tech/digital-cookbook(публичный) иdigital-cookbook-staging(приватный). Это снимок во времени — счётчики advisory меняются с выходом новых записей, так что цифры ниже привязаны к дате, а не «навсегда». - Временная шкала (снимок 2026-07-13):
- старт: Dependabot по staging — 329 открытых (136 critical, 60 high, 124 moderate, 9 low), critical сворачивались в три Go-пакета (
x/crypto,pgx/v5,grpc); - после Go/Java/Rust-фиксов и push: Dependabot → 0 в обоих репозиториях;
- контрольный
osv-scannerпо тем же веткам нашёл ещё пласт (Pythonurllib3/idna, транзитивный Mavennetty/jackson/httpclient5, Rustrsa); - остаточное состояние: в public — 0, в staging держатся 3 high — все по
org.lz4:lz4-java(GHSA-cmp6-m4wj-q63q, no-fix), см. раздел про края.
- старт: Dependabot по staging — 329 открытых (136 critical, 60 high, 124 moderate, 9 low), critical сворачивались в три Go-пакета (
- Живые примеры (реальные пакеты и исходы):
- закрыто бампом/пином — Go
x/crypto/pgx/x/net/grpc, Mavenhttpclient5/bcprov/commons-lang3, Pythonurllib3/idna; - нет фикса — Rust
rsa(Marvin),lz4-java(второе advisory),tokio-tar(закрыт апгрейдом родителя, не бампом); - корректная presence-находка, недостижимая в приложении — Go
x/crypto/openpgp(govulncheckкладёт в «не вызывается»); мнимый конфликт самого сканера — Python env-marker.
- закрыто бампом/пином — Go
- Воспроизводимый публичный снимок —
digital-cookbook/security/sca-snapshot/(ссылка зафиксирована на иммутабельный commit): сканируемый исходник — дерево на commit76ca051, сами файлы снимка добавлены позже (README с worktree-инструкцией +osv-results.json+govulncheck-nats.txt). На этом снимке (уже после ремедиации) контраст в чистом виде: Dependabot — 0, аosv-scanner— 2 уникальных package/version, найденных в 3 манифестах, оба «краевые» кейсы статьи (x/cryptoopenpgp— корректная находка, недостижимая в приложении;jackson-databind 2.18.9— расхождение баз). Приватная половина «пути 329 → 0» (transitive Maven, Rustrsa) в staging иллюстративна — читателю недоступна, поэтому опорой воспроизводимости служит публичный снимок. Оговорка: OSV — живая база, при повторном прогоне позже числа могут отличаться; зафиксированный результат — вosv-results.json. - Точные версии на момент снимка:
osv-scanner2.4.0 (требует Go ≥ 1.26.4 — приGOTOOLCHAIN=autoкомандаgoсама подтягивает более новый toolchain, напр. 1.26.5),govulncheckv1.6.0,cargo-audit0.22.2, Dependabot (GitHub Advisory DB); тулчейны Go 1.26.3, JDK 17/21/25 (Temurin), Python 3.12, Maven 3.9.16, cargo 1.96.1. - Оговорка про Go-пример: полный call-analysis на ORM-стенде (
go/orm-gorm-vs-jet) не самодостаточен — требует предварительной PostgreSQL-миграции и codegen (jet.gen), без них падает. Prerequisites — в README стенда. Для presence-скана этого не нужно. - SCA зависимостей ≠ проверка stdlib/toolchain. Тот же
govulncheck-nats.txtиз снимка честно показывает, что код достижимо затронут тремя уязвимостями стандартной библиотеки Go 1.26.3 (GO-2026-5856crypto/tls,GO-2026-5039net/textproto,GO-2026-5037crypto/x509; фиксы в 1.26.4/1.26.5). Это не про манифесты: dependency-remediation завершена (Dependabot 0), но toolchain требует обновления Go — и ловит этоgovulncheckпо версииgo, а не osv/Dependabot по зависимостям. Полное «в ноль» = бампы зависимостей плюс свежий Go. - Смежное: supply-chain: SBOM и подписьСкоро, secure codingСкоро, CI-пайплайн на GitHub ActionsСкоро.
Документация и первоисточники
- OSV.dev и osv-scanner — открытая база и сканер, агрегирующие advisory многих экосистем.
- GitHub Dependabot — алерты и security-updates на базе GitHub Advisory Database.
- govulncheck и Go vulnerability database — call-graph-анализ для Go.
- RustSec Advisory Database и cargo-audit — уязвимости крейтов.
- Maven dependency management — как
<dependencyManagement>и BOM форсируют версии транзитива.
Комментарии