news 2026/10/14 15:37:36

Bisq 的独立 CVE 扫描:基于版本目录与 OWASP Dependency-Check 的供应链漏洞防线

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Bisq 的独立 CVE 扫描:基于版本目录与 OWASP Dependency-Check 的供应链漏洞防线
  • 区块链
  • DeFi
  • 金融科技

【免费下载链接】bisq

A decentralized bitcoin exchange network

项目地址:https://gitcode.com/gh_mirrors/bi/bisq
点击查看免费下载

Bisq 仓库在tools/cve-scan/下内置了一个独立的 OWASP Dependency-Check 扫描工程,专门针对 Gradle 版本目录gradle/libs.versions.toml中声明的全部依赖做已知 CVE 扫描。本文围绕 tools/cve-scan/README.md 展开,结合 tools/cve-scan/build.gradle 与 tools/cve-scan/settings.gradle 的源码实现,讲清这套扫描是如何做到"与主构建完全隔离、依赖清单单点汇聚、结果可门禁化"的,读完你可以直接在自己的环境中复现整套 CVE 扫描流程并理解其节流、门禁与报告机制。

一、为什么要把 CVE 扫描做成独立工程

Bisq 的主构建对依赖安全有非常严格的要求:gradle/README.md 说明gradle/目录下维护着完整的依赖校验体系——verification-metadata.xml 记录每个依赖构件的 SHA-256 校验和与 PGP 签名,verification-keyring.keys 保存签名验证用的公钥,另有 dependency-checksum-fallback-allowlist.tsv 处理仅用校验和兜底的构件。任何会污染这条校验链或构建类路径的工具,接入成本都很高。

因此 CVE 扫描被刻意设计为一个独立 Gradle 工程,README 明确列出它的三条隔离承诺:

  • 不修改gradle/verification-metadata.xml;
  • 不触碰主构建的 build classpath;
  • 不引入任何运行时依赖——扫描工具自身的传递依赖只存在于tools/cve-scan/子目录内。

这一点从根 settings.gradle 可以得到印证:主构建include了proto、core、desktop、seednode等十多个子项目,并通过includeBuild 'bitcoind'引入独立构建,但没有包含tools/cve-scan。它只能以-p tools/cve-scan的方式单独运行,与主构建共享仓库,却不共享项目模型。

二、工程结构:settings.gradle 如何读取主仓库的版本目录

tools/cve-scan/settings.gradle 只有 13 行,核心是dependencyResolutionManagement块:

rootProject.name = 'cve-scan' dependencyResolutionManagement { repositories { mavenCentral() maven { url 'https://jitpack.io' } } versionCatalogs { libs { from(files('../../gradle/libs.versions.toml')) } } }

关键设计在于from(files('../../gradle/libs.versions.toml')):这个独立工程直接复用主仓库的版本目录文件,而不是维护一份拷贝。仓库的依赖版本全部集中在 gradle/libs.versions.toml 中集中声明,例如bouncycastle = { strictly = '1.84' }、grpc = { strictly = '1.81.0' }、protobuf = { strictly = '3.25.8' },以及bitcoinj、netlayer等来自com.github.bisq-network的 JitPack 构件——这也解释了 settings.gradle 里为何要同时声明 Maven Central 和 jitpack 两个仓库:扫描时这些依赖必须真实可解析,JitPack 来源的库走不了 Maven Central。

版本目录的[versions]表用strictly约束锁死版本,[libraries]表把别名映射到group:artifact坐标(如bouncycastle-bcpg-jdk18on、grpc-core、zxing-core等),还定义了[bundles](如jersey-libs)和[plugins](protobuf 插件)。CVE 扫描的输入正是这份单一事实来源。

三、扫描输入:把版本目录里每个库都变成依赖

tools/cve-scan/build.gradle 的前半部分做了两件事:

plugins { id 'java' id 'org.owasp.dependencycheck' version '12.1.0' } def catalog = extensions.getByType(VersionCatalogsExtension).named('libs') dependencies { catalog.libraryAliases.each { alias -> implementation catalog.findLibrary(alias).get() } }
  • 引入org.owasp.dependencycheck插件(固定版本 12.1.0)和java插件;
  • 通过VersionCatalogsExtension拿到名为libs的版本目录,遍历其全部库别名,逐个加进implementation配置。

这样做的效果是:扫描范围自动等于[libraries]表的全部条目。新增依赖只需要在主仓库的版本目录里加一行,下次运行 CVE 扫描时新依赖就自动进入检查范围,无需改动扫描工程本身。README 的 Scope 一节也确认了这一点:扫描覆盖[libraries]表每一项,包括传递依赖(OWASP Dependency-Check 的dependencyCheckAggregate任务会解析完整依赖图并对每个构件做 CVE 比对)。

有两类依赖不在扫描范围内,README 明确交代:

  1. 以原始"group:artifact:version"字符串写在各子项目build.gradle里的依赖——Bisq 把所有依赖集中在版本目录,因此"预期不存在"这类写法;
  2. bitcoind这个 included build。从根 settings.gradle 可以看到它通过includeBuild 'bitcoind'以独立 Gradle 构建接入,不经过 cve-scan 的版本目录加载路径,因此不在覆盖范围内。

四、运行扫描:cveCheck 任务与 24 小时节流机制

README 给出的三条运行命令都从仓库根目录执行:

# Throttled scan: skips if last run <24h ago AND libs.versions.toml unchanged ./gradlew -p tools/cve-scan cveCheck # Force re-scan (ignore throttle) ./gradlew -p tools/cve-scan cveCheck --rerun-tasks # Direct task (same throttle applies) ./gradlew -p tools/cve-scan dependencyCheckAggregate

cveCheck是一个自定义包装任务,build.gradle 第 76-80 行 定义了它:

tasks.register('cveCheck') { group = 'verification' description = 'Run CVE scan (throttled: skips if <24h old and libs.versions.toml unchanged).' dependsOn 'dependencyCheckAggregate' }

真正的节流逻辑挂在dependencyCheckAggregate任务上,见 build.gradle 第 60-74 行:

def cveMarker = layout.buildDirectory.file('cve-check.ok').get().asFile def versionsFile = file('../../gradle/libs.versions.toml') def cveTtlMs = 24L * 60 * 60 * 1000 tasks.named('dependencyCheckAggregate').configure { onlyIf { if (!cveMarker.exists()) return true if (versionsFile.exists() && versionsFile.lastModified() > cveMarker.lastModified()) return true return (System.currentTimeMillis() - cveMarker.lastModified()) > cveTtlMs } doLast { cveMarker.parentFile.mkdirs() cveMarker.text = "ok ${new Date()}\n" } }

可以推断其完整判定语义:

  • 首次运行:tools/cve-scan/build/cve-check.ok标记文件不存在,onlyIf返回 true,正常扫描;
  • 依赖变了:libs.versions.toml的修改时间新于标记文件,立即重扫——也就是说,只要有人升级了依赖版本,节流自动失效;
  • TTL 窗口:距上次成功扫描不足 24 小时且依赖未变,任务被跳过(cveTtlMs = 24h就是 README Configuration 一节提到的可调节流窗口);
  • 成功落标记:任务执行完毕后写入cve-check.ok并盖上时间戳;
  • 强制重扫:--rerun-tasks会忽略 Gradle 的 up-to-date 判定,绕过onlyIf的跳过路径。

这个设计的价值在于:NVD 数据每日更新、依赖版本又很少变化,每天最多真正执行一次全量 CVE 扫描,既省时间又省 NVD 配额。

五、NVD API Key:从"十分钟起步"到"秒级同步"

OWASP Dependency-Check 的 CVE 数据来自 NVD(National Vulnerability Database)。README 的 NVD API key 一节给出了明确的使用建议:

  • 不带 key 时 NVD API 节流非常激进,首次运行可能超过 10 分钟;
  • 带 key 后同步只需几秒;
  • key 需在 NVD 官方开发者页面申请(README 给出入口https://nvd.nist.gov/developers/request-an-api-key,属外部站点,此处不再贴链接);
  • NVD 数据feed缓存在~/.gradle/dependency-check-data,跨运行复用。

传递 key 的两种方式:

export NVD_API_KEY=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx # or ./gradlew -p tools/cve-scan cveCheck -PnvdApiKey=xxxx

两种传法在源码中的对应实现见 build.gradle 第 38-45 行:

def nvdKey = System.getenv('NVD_API_KEY') ?: (project.findProperty('nvdApiKey') ?: '') nvd { if (nvdKey) { apiKey = nvdKey } // Re-sync NVD feed at most once every 24h. validForHours = 24 }

取值顺序是:环境变量NVD_API_KEY优先,其次项目属性-PnvdApiKey,都没有则留空。另外validForHours = 24让 NVD feed 本身也遵循"每天最多重新同步一次"的策略,与cveTtlMs的 24 小时节流窗口在节奏上保持一致。

六、门禁与报告:failBuildOnCVSS = 0 的硬约束

README 明确:Build fails with non-zero exit on any CVE(failBuildOnCVSS = 0),即任何 CVSS 评分 ≥ 0 的 CVE 都会让构建以非零码失败。在 build.gradle 第 34-36 行 中:

dependencyCheck { failBuildOnCVSS = 0 formats = ['HTML', 'JSON'] ... }

报告输出到tools/cve-scan/build/reports/dependency-check-report.html,同时生成同名 JSON(对应formats配置)。README 给出的调参建议:

  • failBuildOnCVSS = 0—— 任何 CVE 都失败;想放宽到"只拦高危"可提高到例如7,允许 low/medium 通过;
  • formats = ['HTML', 'JSON']—— 可按需追加'SARIF'、'XML'等格式,方便接入 CI 生态;
  • TTLcveTtlMs—— 节流窗口,默认 24 小时。

对 Bisq 这样的开源交易所项目,"零 CVE 容忍"的门禁策略配合strictly版本锁定,构成了供应链安全的最后一道静态防线:版本升级(libs.versions.toml变化)会自动触发重扫,任何引入已知漏洞的版本都会在门禁处被拦下。

七、分析器调优:为什么禁用 assembly、Nuspec 与 OSS Index

build.gradle 第 47-57 行 针对 Bisq 的技术栈做了分析器裁剪:

analyzers { // Java-only project — disable irrelevant or rate-limited analyzers. assemblyEnabled = false // .NET dll/exe scanner (tor-binary jars trip this) nuspecEnabled = false nugetconfEnabled = false ossIndex { // Anonymous OSS Index rate-limits aggressively → every jar errors and // the task fails. NVD already covers the same CVE data. enabled = false } }

逐条对应 README 隐含的意图:

  • assemblyEnabled = false:OWASP 的 Assembly 分析器面向 .NET 的 dll/exe 包,而 Bisq 依赖中的 tor 二进制 jar(版本目录里可见netlayer-tor-external、netlayer-tor-native两个 Tor 相关构件)会让它误报,Java 项目直接关掉;
  • nuspecEnabled/nugetconfEnabled:项目不含 .NET 依赖,NuGet 分析器无意义;
  • ossIndex.enabled = false:匿名调用 OSS Index 限流极严,会导致每个 jar 都报错并使任务整体失败;而 NVD 数据源已经覆盖相同的 CVE 信息,重复扫描收益为零。

这些都是"从源码注释与配置结构看"得出的工程取舍:宁可关停用不上的数据源,也不让限流把整条扫描流水线拖垮。

八、边界与 CI 集成方式

README 的 Scope 与 CI 两节划定了这套工具的适用边界:

  • 扫描范围:[libraries]表全部条目 + 传递依赖;子项目build.gradle中的裸坐标依赖不在范围(项目约定所有依赖集中在版本目录,因此"预期不存在");
  • 不覆盖:bitcoindincluded build(独立 Gradle 构建,见根 settings.gradle 的includeBuild 'bitcoind');
  • CI 策略:该工具被有意设计为 local-only,不绑定特定 CI 平台。若要定时执行,只需在 CI workflow 中调用./gradlew -p tools/cve-scan cveCheck——节流标记文件与validForHours = 24会自然把频率压到每天一次量级。

配合仓库既有体系看,这套 CVE 扫描与 gradle/README.md 描述的依赖校验(checksum + PGP 签名,报告见 docs/dependency-signature-report.md)是互补关系:前者保证"拿到的构件是发布方签过名的、内容未被篡改",后者保证"所选版本不携带已知漏洞"。两者都建立在依赖集中声明于gradle/libs.versions.toml这一单一事实来源之上,这也是整个供应链安全流程可以低成本自动化(依赖一变动即触发 CVE 重扫)的根本原因。

小结

tools/cve-scan以 13 行 settings.gradle + 80 行 build.gradle 的极小体量,实现了 Bisq 依赖链的 CVE 门禁:通过版本目录别名遍历把[libraries]全表变成扫描输入,用cve-check.ok标记 + 24 小时 TTL + 依赖文件 mtime 比较实现智能节流,用failBuildOnCVSS = 0把任何 CVE 变成构建失败,并以 HTML/JSON 双格式报告输出到tools/cve-scan/build/reports/。整套设计与主构建的依赖签名校验完全解耦,为大型多模块 Java 项目提供了一个可复制的"依赖清单单点汇聚 + 独立 CVE 门禁"参考实现。

  • 区块链
  • DeFi
  • 金融科技

【免费下载链接】bisq

A decentralized bitcoin exchange network

项目地址:https://gitcode.com/gh_mirrors/bi/bisq
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/14 15:37:15

学术图表绘制困境,硕词 AI 科研图表生成实操路径

图表是科研成果可视化表达的载体&#xff0c;图表质量直接影响论文可读性。很多研究生写作中遭遇图表难题&#xff1a;统计图表格式混乱&#xff0c;坐标轴、图例、图注不符合学术规范&#xff1b;机理示意图、工程原理图手绘效果差&#xff0c;配色杂乱&#xff1b;图表与正文…

作者头像 李华
网站建设 2026/10/14 15:37:10

STM32+SR8201F通用驱动包:百兆以太网PHY移植与调试指南

简介&#xff1a;针对SR8201F物联网芯片在STM32平台上的通用驱动代码&#xff0c;面向嵌入式开发与物联网终端设计工程师&#xff0c;解决SR8201F以太网控制器在STM32平台上的底层适配与快速移植问题。压缩包内共2个文件&#xff0c;采用1个头文件加1个源文件的精简结构&#x…

作者头像 李华
网站建设 2026/10/14 15:36:58

SSM框架图书借阅管理系统毕业设计全流程解析

简介&#xff1a;基于Java的图书馆借阅管理系统毕业设计资源&#xff0c;采用SSM框架&#xff0c;主要面向计算机专业毕业生、课程设计学习者&#xff0c;可完整解决图书借阅管理系统的需求分析、数据库设计与功能实现等问题&#xff0c;并配套毕业论文设计与答辩素材。压缩包共…

作者头像 李华
网站建设 2026/10/14 15:36:32

Python GUI 开发实战:Tkinter、PySide6 与 CustomTkinter 选型及打包交付

简介&#xff1a;这份资源面向希望将Python脚本转化为可视化操作界面的初学者与进阶开发者&#xff0c;围绕Tkinter标准库讲解GUI编程的核心思路&#xff0c;帮助非程序员也能通过窗口、按钮、菜单等控件与程序交互。压缩包共12个文件&#xff0c;约791KB&#xff0c;包含1个py…

作者头像 李华
网站建设 2026/10/14 15:36:05

superpowers、openspec、graphify、simplifier全流程执行

/using-superpowers/graphify/openspec-explore/openspec-propose/code-simplifier【当轮任务】&#xff08;以下由用户填写&#xff1b;【】内为当前取值&#xff0c;按需改掉&#xff09;功能名称&#xff1a;【填写】例如&#xff1a;合同列表增加虚拟合同筛选与列展示一句话…

作者头像 李华