欢迎光临
我们一直在努力

打造 DevSecOps:以 Dependency-Check 为核心的安全实践指南


版本说明:本文基于 Dependency-Check 12.1.0 版本编写。请注意,该项目已于 2025 年 9 月 27 日归档,不再维护,但现有版本仍可正常使用。

文章目录

    • 📄 文档概述
    • 引言
    • 第一部分:Dependency-Check 基础
      • 1.1 Dependency-Check 是什么?为什么你需要它?
      • 1.2 为什么需要 SCA(软件成分分析)
      • 1.3 Dependency-Check 工作原理
      • 1.4 漏洞数据库与 CVSS 评分体系
    • 第二部分:安装与基础使用
      • 2.1 环境准备
      • 2.2 命令行使用方式
      • 2.3 Docker 方式使用
      • 2.4 配置文件详解
    • 第三部分:全栈集成实战
      • 3.1 Java/Maven 项目集成
      • 3.2 前端/npm 项目集成
      • 3.3 Jenkins CI/CD 集成
      • 3.4 与 SonarQube 联动
    • 第四部分:高级配置与最佳实践
      • 4.1 扫描规则与阈值配置
      • 4.2 抑制文件(Suppression)管理误报
      • 4.3 报告解析与结果处理
      • 4.4 性能优化
    • 第五部分:完整项目案例
      • 5.1 项目背景与架构
      • 5.2 后端 Maven 配置
      • 5.3 前端 npm 配置
      • 5.4 Jenkins Pipeline 完整配置
      • 5.5 生产级 Pipeline 实践(多模块 + 条件执行)
      • 5.6 前端项目 Pipeline 实践(Node.js + CLI 工具)
      • 5.7 NVD 数据库定时更新 Job
      • 5.8 安全门禁与质量门禁
      • 5.9 漏洞修复流程
    • 第六部分:DevSecOps 扩展
      • 6.1 与 DAST、SAST 工具的配合
      • 6.2 容器安全扫描(Trivy)集成
      • 6.3 安全运营与持续改进
    • 第七部分:常见问题与故障排查
      • Q1:首次扫描为什么这么慢?
      • Q2:如何处理误报?
      • Q3:内网环境如何使用?
      • Q4:扫描失败怎么办?
      • Q5:如何提升扫描速度?
      • Q6:如何集成到现有的 CI/CD 流程?
      • Q7:如何设置安全门禁?
      • Q8:如何处理大型项目的扫描?
    • 结语
    • 参考资料

📄 文档概述

  • 创建时间:2026-02-06
  • 作者:zuozewei
  • 功能:DevSecOps 安全实践完整指南,以 Dependency-Check 为核心
  • 技术栈:Dependency-Check、Maven、npm、Jenkins、Docker、SonarQube、Trivy
  • 项目路径:https://github.com/zuozewei/blog-example/tree/master/Jenkins-ci/devsecops-dependency-check-guide

引言

还记得 Log4j2 漏洞爆发那天吗?2021 年 12 月,整个技术圈都炸了——一个看似普通的日志库,竟然能让全球数百万应用瞬间沦陷。那天晚上,我们团队紧急排查了所有项目,发现竟然有 15 个应用都用了这个"定时炸弹"。

这就是现实:一个普通的 Java 企业级应用,平均依赖超过 100 个第三方库,而这些依赖的依赖又会引入数百个间接依赖。任何一个环节出问题,都可能成为攻击者的突破口。

今天聊聊 OWASP Dependency-Check——这个开源的软件成分分析工具,如何帮你提前发现这些"定时炸弹",把它们扼杀在摇篮里。


第一部分:Dependency-Check 基础

1.1 Dependency-Check 是什么?为什么你需要它?

先说结论:如果你的项目用了第三方依赖(99% 的项目都是),那你就需要 SCA 工具。而 Dependency-Check,就是开源世界里最好用的那个。

OWASP Dependency-Check 是一个开源的**软件成分分析(Software Composition Analysis, SCA)**工具,由 OWASP(开放式 Web 应用程序安全项目)维护。它能够自动检测应用程序依赖项中已知的公开安全漏洞。

⚠️ 重要提示:该项目已于 2025 年 9 月 27 日归档,不再进行主动维护。本文基于最后一个稳定版本 12.1.0(2025 年 2 月 17 日发布)编写。现有版本仍可正常使用,但建议关注其他活跃维护的替代方案,如:

  • Snyk(商业,有免费版)
  • Trivy(开源,支持容器和依赖扫描)
  • Grype(开源,专注于漏洞扫描)

核心能力:

  • 自动识别项目使用的第三方依赖(直接和传递依赖)
  • 与 NVD(National Vulnerability Database)等漏洞数据库比对
  • 生成详细的漏洞报告,包含 CVE 编号、CVSS 评分、修复建议
  • 支持多种编程语言和构建工具

支持的语言与平台:

语言/平台支持的文件类型说明
Java JAR, WAR, EAR, pom.xml 完整的 Maven/Gradle 支持
JavaScript/Node.js package.json, package-lock.json, npm-shrinkwrap.json 前端和 Node.js 项目
Python requirements.txt, setup.py, Pipfile Python 依赖分析
.NET packages.config, .csproj, .vbproj .NET Framework 和 Core
Ruby Gemfile, Gemfile.lock Ruby 项目
Go go.mod, go.sum Go 模块支持
PHP composer.json, composer.lock PHP 项目
C/C++ 源代码分析 有限支持

1.2 为什么需要 SCA(软件成分分析)

🤔 思考一下:

你的项目有多少个依赖?你能说出每个依赖的版本吗?如果其中一个依赖被发现有严重漏洞,你能在多长时间内发现并修复?


开源依赖的安全现状:

根据 Sonatype 发布的《软件供应链状况报告》,现状不容乐观:

  • 现代应用程序中 70-90% 的代码来自第三方开源组件
  • 96% 的下载存在已知漏洞的组件版本
  • 29% 的热门项目包含至少一个已知漏洞的依赖
  • 2023 年开源供应链攻击增长了 742%

这些数字背后,是无数个潜在的安全漏洞。

真实案例:

  • Log4j2 漏洞(CVE-2021-44228):影响全球数百万应用,CVSS 评分 10.0(最高危)
  • Spring4Shell(CVE-2022-22965):Spring Framework 核心漏洞,影响大量 Java 应用
  • XZ 后门事件(2024):一个被广泛使用的压缩库被植入后门,险些成为史上最严重的供应链攻击

这些事件凸显了 SCA 工具的必要性。

只有持续监控依赖安全,才能在漏洞被利用前及时发现并修复。

1.3 Dependency-Check 工作原理

Dependency-Check 的工作流程可分为四个阶段:

┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 1. 依赖识别 │ → │ 2. 特征提取 │ → │ 3. 漏洞匹配 │ → │ 4. 报告生成 │
└─────────────────┘ └─────────────────┘ └─────────────────┘ └─────────────────┘

阶段一:依赖识别——“你是谁?”

这一步就像是在做人口普查。工具会扫描你的项目文件(pom.xml、package.json 等),把所有依赖都揪出来——无论是直接依赖还是传递依赖,一个都跑不掉。

实际踩坑发现:有些项目会通过本地 JAR 包引入依赖,这种情况下 Dependency-Check 可能会漏掉。如果你有这种情况,记得手动指定扫描路径。

阶段二:特征提取——“你的特征是什么?”

拿到依赖列表后,工具开始提取每个依赖的"身份证"信息:

  • Maven:groupId:artifactId:version
  • npm:package@version
  • PyPI:package==version

同时还会计算文件哈希值(SHA1、MD5)和提取 CPE 标识符。这就像给每个人拍照、录指纹,确保后续能准确识别。

阶段三:漏洞匹配——“你有没有案底?”

有了特征信息,工具就开始查"案底"了——将这些特征与漏洞数据库比对。

主要数据源包括:

  • NVD:美国国家标准与技术研究院维护的官方漏洞库
  • RetireJS:JavaScript 特定漏洞库
  • Node Security Platform:Node.js 漏洞数据

工具使用 Lucene 索引实现高效匹配,就像警察查数据库一样快。

阶段四:报告生成——“给你看证据”

最后一步,工具会生成详细的"调查报告",包含:

  • 漏洞详情(CVE 编号、CVSS 评分)
  • 修复建议(升级到哪个版本)
  • 支持多种格式(HTML、XML、JSON、CSV)

如果发现误报,还可以通过抑制文件排除——就像警察撤销错误的案底记录。

1.4 漏洞数据库与 CVSS 评分体系

NVD(National Vulnerability Database) NVD 是美国国家标准与技术研究院(NIST)维护的漏洞数据库,包含:

  • CVE(Common Vulnerabilities and Exposures)标识符
  • CPE(Common Platform Enumeration)标识符
  • CVSS(Common Vulnerability Scoring System)评分
  • 漏洞描述、参考链接、修复信息

CVSS 评分体系: CVSS 是业界通用的漏洞严重程度评估标准,目前主要使用 v3.1 版本。

评分范围严重程度处理建议
0.0 None 无风险
0.1 – 3.9 Low 按计划修复
4.0 – 6.9 Medium 优先修复
7.0 – 8.9 High 尽快修复,考虑阻断发布
9.0 – 10.0 Critical 立即修复,必须阻断发布

CVSS 评分维度:

  • 基础评分(Base Score):漏洞本身的固有特征
    • 攻击向量(网络/本地/物理)
    • 攻击复杂度
    • 所需权限
    • 用户交互要求
    • 影响范围
    • 机密性/完整性/可用性影响
  • 时效评分(Temporal Score):随时间变化的特征
  • 环境评分(Environmental Score):特定环境的影响

在 DevSecOps 实践中,通常使用基础评分作为质量门禁的阈值依据。

🤔 思考一下:

如果你的项目发现了 CVSS 9.0 的严重漏洞,你会怎么做?是立即修复还是先评估影响?


第二部分:安装与基础使用

2.1 环境准备

系统要求:

要求项最低配置推荐配置
Java 版本 Java 8 Java 11+
内存 2GB 4GB+
磁盘空间 2GB 10GB+(用于缓存漏洞数据库)
网络 需要连接 NVD 稳定连接,建议申请 API Key

NVD API Key 申请(强烈推荐):

💡 小贴士: NVD API Key 是免费的,但申请需要几天时间。建议提前申请,避免临时抱佛脚。

NVD API 限制(2024 年更新):

  • 无 API Key:每分钟 5 次请求
  • 有 API Key:每分钟 50 次请求
  • 每日配额:5,000 次请求(有 API Key)
  • 注意:NVD 偶尔会临时调整限制,建议关注官方公告

未使用 API Key 时,首次下载漏洞数据库可能需要数小时。申请 API Key 后可大幅提升下载速度。

📖 点击展开:NVD API Key 详细申请步骤

  • 访问 https://nvd.nist.gov/developers/request-an-api-key
  • 填写姓名和组织信息
  • 邮箱验证后即可获得 API Key
  • 妥善保存 API Key,后续配置需要用到
  • ⚠️ 注意事项:

    • API Key 是 40 字符的十六进制字符串
    • 每个组织/个人只能申请一个 Key
    • 不要共享 API Key,避免超过速率限制
    • 使用 API Key 的产品需要显示声明:“This product uses the NVD API but is not endorsed or certified by NVD.”

    2.2 命令行使用方式

    下载与安装:

    # 下载最新版本(以 12.1.0 为例)
    wget https://github.com/jeremylong/DependencyCheck/releases/download/v12.1.0/dependency-check-12.1.0-release.zip || {
    echo "错误:下载失败,请检查网络连接"
    exit 1
    }

    # 解压
    unzip dependency-check-12.1.0-release.zip -d /opt/dependency-check || {
    echo "错误:解压失败"
    exit 1
    }

    # 添加到 PATH(Linux/Mac)
    export PATH=$PATH:/opt/dependency-check/bin

    # 验证安装
    dependency-check.sh –version || {
    echo "错误:安装验证失败"
    exit 1
    }

    echo "✅ Dependency-Check 安装成功"

    首次运行(更新漏洞数据库):

    # 仅更新数据库(推荐首次运行)
    dependency-check.sh –nvdApiKey YOUR_API_KEY –updateonly || {
    echo "错误:数据库更新失败"
    echo "请检查:"
    echo "1. NVD API Key 是否正确"
    echo "2. 网络连接是否正常"
    exit 1
    }

    基础扫描命令:

    # 基础扫描
    dependency-check.sh \\
    –project "MyApplication" \\
    –scan "/path/to/project" \\
    –out "/path/to/reports" \\
    –format HTML

    # 完整参数示例
    dependency-check.sh \\
    –project "E-Commerce Platform" \\
    –scan "/workspace/ecommerce" \\
    –out "/reports/dependency-check" \\
    –format ALL \\
    –nvdApiKey YOUR_API_KEY \\
    –failOnCVSS 7 \\
    –enableExperimental

    常用参数说明:

    参数短格式说明示例
    –project -p 项目名称 –project “MyApp”
    –scan -s 扫描路径(可多次指定) –scan ./lib –scan ./src
    –out -o 输出目录 –out ./reports
    –format -f 报告格式(HTML/XML/JSON/CSV/ALL) –format ALL
    –failOnCVSS CVSS 阈值(超过则返回非0) –failOnCVSS 7
    –suppression 抑制文件路径 –suppression suppress.xml
    –nvdApiKey NVD API 密钥 –nvdApiKey xxx
    –enableExperimental 启用实验性分析器
    –disableRetireJS 禁用 RetireJS 分析器
    –disableNodeJS 禁用 Node.js 分析器

    📌 本节要点:

    • ✅ 首次运行记得用 –updateonly 更新数据库
    • ✅ 生产环境务必申请 NVD API Key
    • ✅ 使用 –failOnCVSS 7 设置安全门禁
    • ✅ 抑制文件可以处理误报,但不要滥用

    ⚠️ 避坑指南:

    • 不要在 CI/CD 中使用 –updateonly,会拖慢构建速度
    • Windows 用户注意路径分隔符问题
    • 首次扫描需要下载完整数据库(约2GB),耐心等待

    2.3 Docker 方式使用

    Docker 方式是使用 Dependency-Check 最便捷的方式,无需本地安装,适合 CI/CD 场景。

    基础使用:

    # 拉取镜像
    docker pull owasp/dependency-check:latest

    # 基础扫描
    docker run –rm \\
    -v $(pwd):/src \\
    -v $(pwd)/reports:/report \\
    owasp/dependency-check:latest \\
    –scan /src \\
    –format ALL \\
    –project "MyProject" \\
    –out /report

    带数据缓存的优化方案:

    #!/bin/bash
    # dependency-check.sh – 封装脚本

    set -e # 遇到错误立即退出

    DC_VERSION="latest"
    DC_DIRECTORY="$HOME/OWASP-Dependency-Check"
    DATA_DIRECTORY="$DC_DIRECTORY/data"
    REPORT_DIRECTORY="$(pwd)/reports"

    # 检查 NVD API Key
    if [ -z "${NVD_API_KEY}" ]; then
    echo "错误:未设置 NVD_API_KEY 环境变量"
    echo "请先设置:export NVD_API_KEY=your_api_key"
    exit 1
    fi

    # 创建必要目录
    echo "创建数据目录:$DATA_DIRECTORY"
    mkdir -p "$DATA_DIRECTORY" || {
    echo "错误:创建数据目录失败"
    exit 1
    }

    echo "创建报告目录:$REPORT_DIRECTORY"
    mkdir -p "$REPORT_DIRECTORY" || {
    echo "错误:创建报告目录失败"
    exit 1
    }

    # 运行扫描
    echo "开始扫描项目:$(basename $(pwd))"
    docker run –rm \\
    -e user=$USER \\
    -u $(id -u ${USER}):$(id -g ${USER}) \\
    –volume $(pwd):/src:ro \\
    –volume "$DATA_DIRECTORY":/usr/share/dependency-check/data:rw \\
    –volume "$REPORT_DIRECTORY":/report:rw \\
    owasp/dependency-check:$DC_VERSION \\
    –nvdApiKey ${NVD_API_KEY} \\
    –scan /src \\
    –format "ALL" \\
    –project "$(basename $(pwd))" \\
    –out /report \\
    –failOnCVSS 7 || {
    echo "错误:依赖扫描失败"
    exit 1
    }

    echo "扫描完成!报告已保存到:$REPORT_DIRECTORY"

    数据持久化的重要性:

    • 漏洞数据库(~2GB)只需定期更新,无需每次重新下载
    • 使用本地卷映射缓存数据,大幅提升后续扫描速度
    • 建议设置定时任务(cron)定期更新数据库

    实战经验分享:

    我们团队刚开始用 Docker 方式时,遇到过一个坑:每次构建都重新下载漏洞数据库,导致构建时间从 5 分钟暴涨到 30 分钟。

    后来发现,只需要把数据目录挂载到宿主机,问题就解决了。这就是上面提供的优化方案——现在我们的构建时间稳定在 8 分钟左右。

    另一个小技巧: 如果你的 Jenkins 节点资源有限,可以考虑使用 Docker 的 –memory 参数限制内存占用:

    docker run –rm \\
    –memory="2g" \\
    –memory-swap="2g" \\
    -v $(pwd):/src \\
    -v $(pwd)/dc-data:/usr/share/dependency-check/data \\
    owasp/dependency-check:latest \\
    –scan /src

    还有个踩坑记录: Windows 环境下使用 Docker 时,路径映射可能会出问题。记得用正斜杠 / 而不是反斜杠 \\,或者使用 $(pwd) 让系统自动处理路径转换。

    # Windows PowerShell – 正确写法
    docker run –rm `
    -v "${PWD}:/src" `

    -v "${PWD}\\dc-data:/usr/share/dependency-check/data" `
    owasp/dependency-check:latest `

    –scan /src

    # 错误写法(会导致路径问题)
    docker run –rm \\
    -v "C:\\project:/src" \\
    owasp/dependency-check:latest \\
    –scan /src

    2.4 配置文件详解

    Dependency-Check 支持通过属性文件进行配置,便于团队统一管理和版本控制。

    配置文件位置:

    • 命令行:–propertyfile /path/to/config.properties
    • 默认查找:dependency-check.properties(当前目录或 classpath)

    完整配置示例(dependency-check.properties):

    # ===========================================
    # OWASP Dependency-Check 配置文件
    # ===========================================

    # NVD 配置
    nvd.api.key=YOUR_NVD_API_KEY_HERE
    nvd.api.delay=2000
    nvd.api.validforhours=24

    # 数据目录
    data.directory=/opt/dependency-check/data

    # 报告配置
    report.output.directory=./reports
    report.format=HTML,JSON

    # 扫描配置
    scan.depth=10
    analyzer.archive.enabled=true
    analyzer.jar.enabled=true
    analyzer.maven.enabled=true
    analyzer.npm.enabled=true
    analyzer.python.enabled=true
    analyzer.ruby.gemspec.enabled=true

    # 抑制文件
    suppression.file=./dependency-check-suppressions.xml

    # 网络配置
    proxy.server=proxy.company.com
    proxy.port=8080
    proxy.username=${PROXY_USER}
    proxy.password=${PROXY_PASS}

    # 失败阈值
    fail.on.cvss=7

    # 高级配置
    analyzer.dependencybundling.enabled=true
    analyzer.dependencymerging.enabled=true

    使用配置文件:

    dependency-check.sh –propertyfile dependency-check.properties –scan ./project

    📌 本节要点:

    • ✅ 配置文件便于团队统一管理和版本控制
    • ✅ 敏感信息(API Key)使用环境变量 ${env.VAR_NAME}
    • ✅ 代理配置适合内网环境
    • ✅ 抑制文件路径可以相对路径或绝对路径

    ⚠️ 避坑指南:

    • 不要把 API Key 直接写在配置文件里,用环境变量
    • 配置文件要纳入版本控制,但敏感信息除外
    • 代理配置只在需要时启用,否则会影响性能

    第三部分:全栈集成实战

    3.1 Java/Maven 项目集成

    Maven 是 Java 项目最常用的构建工具,Dependency-Check 提供了完善的 Maven 插件支持。

    基础配置(pom.xml):

    <project>

    <build>
    <plugins>
    <!– OWASP Dependency-Check Plugin –>
    <plugin>
    <groupId>org.owasp</groupId>
    <artifactId>dependency-check-maven</artifactId>
    <version>12.1.0</version>
    <configuration>
    <!– NVD API Key –>
    <nvdApiKey>${nvd.api.key}</nvdApiKey>

    <!– 失败阈值:CVSS >= 7 时构建失败 –>
    <failBuildOnCVSS>7</failBuildOnCVSS>

    <!– 输出格式 –>
    <format>ALL</format>

    <!– 输出目录 –>
    <outputDirectory>${project.build.directory}/dependency-check</outputDirectory>

    <!– 抑制文件 –>
    <suppressionFiles>
    <suppressionFile>dependency-check-suppressions.xml</suppressionFile>
    </suppressionFiles>

    <!– 扫描选项 –>
    <skipProvidedScope>false</skipProvidedScope>
    <skipRuntimeScope>false</skipRuntimeScope>
    <skipTestScope>true</skipTestScope>

    <!– 启用实验性分析器 –>
    <enableExperimental>true</enableExperimental>
    </configuration>
    <executions>
    <execution>
    <goals>
    <goal>check</goal>
    </goals>
    </execution>
    </executions>
    </plugin>
    </plugins>
    </build>

    <!– 属性配置 –>
    <properties>
    <nvd.api.key>${env.NVD_API_KEY}</nvd.api.key>
    </properties>
    </project>

    多模块项目配置:

    对于多模块 Maven 项目,建议在父 POM 中统一配置:

    <project>
    <groupId>com.company</groupId>
    <artifactId>parent-project</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>

    <modules>
    <module>module-a</module>
    <module>module-b</module>
    <module>module-c</module>
    </modules>

    <build>
    <pluginManagement>
    <plugins>
    <plugin>
    <groupId>org.owasp</groupId>
    <artifactId>dependency-check-maven</artifactId>
    <version>12.1.0</version>
    <configuration>
    <nvdApiKey>${nvd.api.key}</nvdApiKey>
    <failBuildOnCVSS>7</failBuildOnCVSS>
    <format>ALL</format>
    <!– 聚合报告 –>
    <aggregate>true</aggregate>
    </configuration>
    </plugin>
    </plugins>
    </pluginManagement>
    </build>
    </project>

    常用 Maven 命令:

    💡 提示:以下命令是 Dependency-Check Maven 插件的基础用法。更多高级用法和 Pipeline 集成示例,请参考第五部分的完整项目案例。

    # 运行依赖扫描
    mvn dependency-check:check

    # 仅更新漏洞数据库
    mvn dependency-check:update-only

    # 聚合多模块报告
    mvn dependency-check:aggregate

    # 跳过检查(紧急情况)
    mvn clean package -Ddependency-check.skip=true

    # 仅生成报告,不失败构建
    mvn dependency-check:check -DfailBuildOnCVSS=11

    命令说明:

    • dependency-check:check:执行完整的依赖扫描并生成报告
    • dependency-check:update-only:仅更新 NVD 漏洞数据库,不执行扫描
    • dependency-check:aggregate:聚合多模块项目的扫描结果
    • -Ddependency-check.skip=true:跳过依赖扫描(紧急情况使用)
    • -DfailBuildOnCVSS=11:设置 CVSS 阈值,超过则构建失败(11 表示不失败)

    📖 相关章节:

    • 5.2 节:后端 Maven 完整配置示例
    • 5.4 节:Jenkins Pipeline 中的 Maven 集成
    • 5.9 节:漏洞修复实战案例

    实战经验分享:

    我们团队在集成 Maven 插件时,遇到过几个典型问题,分享给大家:

    问题一:多模块项目扫描时间过长

    有个 20 个模块的微服务项目,每次全量扫描都要 40 分钟。后来我们采用了分层扫描策略:

    # 核心模块(每次 CI 都扫)
    mvn -f core/pom.xml dependency-check:check

    # 业务模块(按需扫描,只扫变更的)
    git diff –name-only HEAD~1 HEAD | grep "pom.xml" | while read pom; do
    module_dir=$(dirname "$pom")
    mvn -f "$module_dir/pom.xml" dependency-check:check
    done

    这样把平均扫描时间降到了 15 分钟左右。

    问题二:NVD API Key 在 CI 环境中泄露

    一开始我们把 API Key 直接写在 pom.xml 里,结果代码仓库被公开后,Key 就泄露了。正确做法是通过环境变量传递:

    <properties>
    <nvd.api.key>${env.NVD_API_KEY}</nvd.api.key>
    </properties>

    然后在 Jenkins 中配置 Credentials:

    environment {
    NVD_API_KEY = credentials('nvd-api-key')
    }

    问题三:内存溢出(OutOfMemoryError)

    大项目扫描时经常遇到内存溢出,特别是在 Jenkins 节点资源有限的情况下。解决方案是增加 JVM 内存:

    <plugin>
    <groupId>org.owasp</groupId>
    <artifactId>dependency-check-maven</artifactId>
    <configuration>
    <jvmArgs>-Xmx4g -Xms2g</jvmArgs>
    </configuration>
    </plugin>

    或者在命令行中设置:

    export MAVEN_OPTS="-Xmx4g -Xms2g"
    mvn dependency-check:check

    问题四:抑制文件管理混乱

    刚开始大家随便加抑制规则,结果有些真正的漏洞被抑制了。后来我们建立了严格的审批流程:

  • 每条抑制规则必须包含详细的 <notes> 说明
  • 抑制文件需要安全团队审核
  • 定期(每季度)审查抑制规则,移除不再需要的
  • <suppress>
    <notes><![CDATA[
    审批人:张三
    审批日期:2024-01-15
    原因:测试依赖,生产环境不受影响
    计划移除:2024-06-01
    ]]>
    </notes>
    <packageUrl regex="true">^pkg:maven/org\\.junit/.*$</packageUrl>
    </suppress>

    还有一个实用技巧: 在本地开发时,可以临时跳过依赖扫描来加快构建速度:

    # 本地开发快速构建
    mvn clean package -Ddependency-check.skip=true

    # 提交前再运行完整检查
    mvn dependency-check:check

    3.2 前端/npm 项目集成

    前端项目通常使用 npm/yarn 管理依赖,Dependency-Check 通过分析 package.json 和 lock 文件来识别依赖。

    Node.js 扫描配置:

    # 命令行扫描 Node.js 项目
    dependency-check.sh \\
    –project "Frontend App" \\
    –scan ./package.json \\
    –scan ./package-lock.json \\
    –out ./reports \\
    –format ALL \\
    –enableExperimental

    npm 脚本集成(package.json):

    {
    "name": "my-frontend-app",
    "version": "1.0.0",
    "scripts": {
    "security:check": "dependency-check –scan package.json –scan package-lock.json –format HTML –out reports",
    "security:check-ci": "dependency-check –scan package.json –scan package-lock.json –format JSON –failOnCVSS 7",
    "prepush": "npm run security:check-ci"
    },
    "devDependencies": {
    "dependency-check": "^5.0.0"
    }
    }

    与 npm audit 的对比:

    特性npm auditDependency-Check
    数据源 npm 仓库 NVD + RetireJS + 其他
    漏洞覆盖 npm 包 多语言、多平台
    报告格式 命令行/JSON HTML/XML/JSON/CSV
    CI/CD 集成 基础 完善
    抑制机制 简单 强大的抑制文件

    最佳实践: 建议同时使用 npm audit 和 Dependency-Check,两者互补:

    • npm audit:快速检查,npm 特定漏洞
    • Dependency-Check:全面扫描,统一报告

    实战经验分享:

    前端项目的依赖扫描有几个特殊之处,这里分享一些踩坑经验:

    问题一:package-lock.json 和 yarn.lock 冲突

    有些项目同时存在 package-lock.json 和 yarn.lock,导致 Dependency-Check 扫描结果不一致。我们团队的做法是:

  • 明确锁定一个包管理器(推荐 npm)
  • 删除另一个 lock 文件
  • 在 .gitignore 中添加:
  • # 只保留一个 lock 文件
    yarn.lock
    # package-lock.json 保留

    问题二:devDependencies 误报

    开发依赖(如 eslint、webpack)经常报出漏洞,但生产环境根本用不到。可以在 package.json 中配置:

    {
    "scripts": {
    "security:check": "dependency-check –scan package.json –scan package-lock.json –format HTML –out reports –exclude devDependencies"
    }
    }

    或者在抑制文件中排除:

    <suppress>
    <notes><![CDATA[
    开发依赖,生产环境不受影响
    ]]>
    </notes>
    <packageUrl regex="true">^pkg:npm/webpack@.*$</packageUrl>
    </suppress>

    问题三:私有 npm 包扫描失败

    公司内部搭建的私有 npm 仓库,Dependency-Check 无法识别。解决方案:

  • 使用 –disableNodeAudit 跳过 Node Security Platform 检查
  • 在抑制文件中排除私有包:
  • <suppress>
    <notes><![CDATA[
    私有 npm 包,已通过内部安全审查
    ]]>
    </notes>
    <packageUrl regex="true">^pkg:npm/@company/.*$</packageUrl>
    </suppress>

    问题四:Monorepo 项目扫描慢

    对于使用 lerna、nx 等 Monorepo 工具的项目,全量扫描非常慢。我们采用增量扫描:

    # 只扫描变更的包
    CHANGED_PACKAGES=$(lerna changed –json | jq -r '.[].location')

    for pkg in $CHANGED_PACKAGES; do
    dependency-check.sh \\
    –project "$(basename $pkg)" \\
    –scan "$pkg/package.json" \\
    –scan "$pkg/package-lock.json" \\
    –out "reports/$(basename $pkg)"
    done

    还有一个实用技巧: 结合 Husky 在 git commit 前自动检查:

    {
    "husky": {
    "hooks": {
    "pre-commit": "npm run security:check"
    }
    }
    }

    这样可以在代码提交前就发现问题,而不是等到 CI 阶段才发现。

    📌 本节要点:

    • ✅ 前端项目建议同时使用 npm audit 和 Dependency-Check
    • ✅ 明确锁定一个包管理器(推荐 npm)
    • ✅ 开发依赖可以通过抑制文件或配置排除
    • ✅ 私有 npm 包需要在抑制文件中排除

    ⚠️ 避坑指南:

    • 不要同时存在 package-lock.json 和 yarn.lock
    • devDependencies 漏洞可以忽略,但要记录原因
    • Monorepo 项目采用增量扫描,避免全量扫描
    • Husky 可以在提交前发现问题,但会增加提交时间

    3.3 Jenkins CI/CD 集成

    Jenkins 是 DevSecOps 实践中使用最广泛的 CI 工具。本节介绍基础集成方案,完整的生产级 Pipeline 示例请参考第五部分。

    插件安装:

  • 进入 Jenkins Dashboard → Manage Jenkins → Manage Plugins
  • 切换到 Available 标签
  • 搜索 “OWASP Dependency-Check”
  • 勾选并安装
  • 全局工具配置:

  • Manage Jenkins → Global Tool Configuration
  • 找到 “OWASP Dependency-Check”
  • 添加安装:
    • Name: dependency-check
    • Install automatically: 勾选
    • Version: 选择最新版本
  • 基础 Pipeline 示例:

    pipeline {
    agent any

    environment {
    NVD_API_KEY = credentials('nvd-api-key')
    }

    tools {
    maven 'maven-3.9'
    jdk 'jdk-17'
    dependencyCheck 'dependency-check'
    }

    stages {
    stage('Checkout') {
    steps {
    checkout scm
    }
    }

    stage('Build') {
    steps {
    sh 'mvn clean compile -DskipTests'
    }
    }

    stage('Security Scan') {
    steps {
    dependencyCheck additionalArguments: """
    –project "${env.JOB_NAME}"
    –scan ./
    –format HTML
    –format XML
    –failOnCVSS 7
    –nvdApiKey ${NVD_API_KEY}
    """
    ,
    odcInstallation: 'dependency-check'
    }
    }

    stage('Package') {
    steps {
    sh 'mvn package -DskipTests'
    }
    }
    }

    post {
    always {
    dependencyCheckPublisher pattern: 'dependency-check-report.xml'
    archiveArtifacts artifacts: 'dependency-check-report.*', allowEmptyArchive: true
    }
    }
    }

    Pipeline 关键点说明:

    配置项说明
    credentials('nvd-api-key') 从 Jenkins Credentials 中安全获取 API Key
    –failOnCVSS 7 CVSS >= 7 时命令返回非0,导致阶段失败
    dependencyCheckPublisher 在 Jenkins UI 中显示漏洞趋势图

    📖 进阶内容:

    • 5.4 节:完整的多模块项目 Pipeline 配置
    • 5.5 节:生产级 Pipeline(条件执行、多环境)
    • 5.6 节:前端项目 Pipeline 实践(Node.js + CLI 工具)

    3.4 与 SonarQube 联动

    SonarQube 是最流行的代码质量平台,将 Dependency-Check 报告导入 SonarQube 可以实现统一的质量视图。

    配置步骤:

  • 安装 SonarQube 插件

    • 进入 SonarQube Administration → Marketplace
    • 安装 “Dependency-Check” 插件
    • 重启 SonarQube
  • Maven 配置(pom.xml):

  • <profiles>
    <profile>
    <id>sonar</id>
    <build>
    <plugins>
    <!– Dependency-Check –>
    <plugin>
    <groupId>org.owasp</groupId>
    <artifactId>dependency-check-maven</artifactId>
    <version>12.1.0</version>
    <configuration>
    <format>XML</format>
    <outputDirectory>${project.build.directory}</outputDirectory>
    </configuration>
    </plugin>

    <!– SonarQube –>
    <plugin>
    <groupId>org.sonarsource.scanner.maven</groupId>
    <artifactId>sonar-maven-plugin</artifactId>
    <version>3.9.1.2184</version>
    </plugin>
    </plugins>
    </build>
    </profile>
    </profiles>

    <properties>
    <!– SonarQube 配置 –>
    <sonar.host.url>http://sonarqube.company.com:9000</sonar.host.url>
    <sonar.dependencyCheck.reportPath>${project.build.directory}/dependency-check-report.xml</sonar.dependencyCheck.reportPath>
    <sonar.dependencyCheck.htmlReportPath>${project.build.directory}/dependency-check-report.html</sonar.dependencyCheck.htmlReportPath>
    </properties>

  • 执行扫描:
  • # 运行 Dependency-Check
    mvn dependency-check:check

    # 上传到 SonarQube
    mvn sonar:sonar -Dsonar.login=YOUR_TOKEN

  • 查看结果:
    • 进入 SonarQube 项目页面
    • 在 “Measures” → “Dependencies” 中查看漏洞统计
    • 点击具体依赖查看详细漏洞信息
  • 集成效果:

    • 统一查看代码质量和依赖安全
    • 利用 SonarQube 的权限管理和通知功能
    • 在 SonarQube 仪表盘中跟踪安全债务

    第四部分:高级配置与最佳实践

    4.1 扫描规则与阈值配置

    CVSS 阈值策略:

    在 DevSecOps 实践中,建议采用渐进式阈值策略:

    // Jenkins Pipeline 中的渐进式阈值
    stage('Security Gate') {
    steps {
    script {
    def report = readJSON file: 'dependency-check-report.json'
    def vulnerabilities = report.dependencies.collectMany { it.vulnerabilities ?: [] }

    def critical = vulnerabilities.count { it.cvssv3?.baseScore >= 9.0 }
    def high = vulnerabilities.count { it.cvssv3?.baseScore >= 7.0 }
    def medium = vulnerabilities.count { it.cvssv3?.baseScore >= 4.0 }

    // 严重漏洞:零容忍
    if (critical > 0) {
    error "❌ 发现 ${critical} 个严重漏洞,必须立即修复!"
    }

    // 高危漏洞:允许少量,需审批
    if (high > 3) {
    input message: "发现 ${high} 个高危漏洞,是否继续部署?",
    ok: '确认部署',
    submitterParameter: 'APPROVER'
    }

    // 中危漏洞:记录并跟踪
    if (medium > 10) {
    unstable "⚠️ 中危漏洞较多(${medium}个),建议安排修复"
    }

    echo "✅ 安全门禁通过"
    }
    }
    }

    按环境设置不同阈值:

    // 根据分支设置不同严格程度
    def getCVSSThreshold() {
    switch(env.BRANCH_NAME) {
    case 'main':
    case 'master':
    return 5 // 生产分支严格
    case 'release/*':
    return 7 // 发布分支适中
    default:
    return 9 // 特性分支宽松
    }
    }

    stage('Security Scan') {
    steps {
    dependencyCheck additionalArguments: """
    –failOnCVSS ${getCVSSThreshold()}

    """

    }
    }

    4.2 抑制文件(Suppression)管理误报

    抑制文件用于排除误报或已知不适用于当前场景的漏洞,是 DevSecOps 实践中的必备技能。

    抑制文件结构:

    <?xml version="1.0" encoding="UTF-8"?>
    <suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd">

    <!– ============================================ –>
    <!– 抑制规则说明: –>
    <!– 1. 每个 suppress 元素对应一条抑制规则 –>
    <!– 2. 必须包含 notes 说明抑制原因 –>
    <!– 3. 建议包含审核日期和审核人 –>
    <!– 4. 定期审查抑制规则(建议每季度) –>
    <!– ============================================ –>

    <!– 示例1:按 CVE 编号抑制特定漏洞 –>
    <suppress>
    <notes><![CDATA[
    [抑制原因] CVE-2021-1234 是误报
    [分析详情] 该漏洞影响的是 log4j 1.x,而我们使用的是 log4j 2.x
    [审核日期] 2024-01-15
    [审核人] 张三
    [下次审查] 2024-04-15
    ]]>
    </notes>
    <cve>CVE-2021-1234</cve>
    </suppress>

    <!– 示例2:按文件名模式抑制 –>
    <suppress>
    <notes><![CDATA[
    [抑制原因] 内部工具包不受此漏洞影响
    [分析详情] 该漏洞需要特定配置触发,内部工具未使用该配置
    [审核日期] 2024-01-15
    [审核人] 李四
    ]]>
    </notes>
    <filePath regex="true">.*internal-tool-.*\\.jar</filePath>
    <cve>CVE-2023-5678</cve>
    </suppress>

    <!– 示例3:按 CPE 抑制 –>
    <suppress>
    <notes><![CDATA[
    [抑制原因] 仅用于开发测试,不部署到生产
    [分析详情] H2 数据库仅用于单元测试,生产使用 MySQL
    [审核日期] 2024-01-15
    [审核人] 王五
    ]]>
    </notes>
    <cpe>cpe:/a:h2database:h2</cpe>
    </suppress>

    <!– 示例4:按包名和版本范围抑制 –>
    <suppress>
    <notes><![CDATA[
    [抑制原因] 低风险,且升级不兼容
    [分析详情] 该漏洞需要本地访问权限,无法远程利用
    [审核日期] 2024-01-15
    [审核人] 赵六
    ]]>
    </notes>
    <gav regex="true">^com\\.example:legacy-lib:1\\..*</gav>
    <cve>CVE-2022-9999</cve>
    </suppress>

    <!– 示例5:临时抑制(带过期日期) –>
    <suppress until="2024-03-01Z">
    <notes><![CDATA[
    [抑制原因] 临时抑制,等待官方补丁
    [修复计划] 厂商预计 2024-02-15 发布补丁
    [审核日期] 2024-01-15
    [审核人] 钱七
    ]]>
    </notes>
    <cve>CVE-2024-0001</cve>
    </suppress>

    </suppressions>

    抑制规则类型:

    元素说明示例
    <cve> 按 CVE 编号抑制 <cve>CVE-2021-44228</cve>
    <cpe> 按 CPE 标识抑制 <cpe>cpe:/a:apache:log4j</cpe>
    <filePath> 按文件路径抑制 <filePath regex="true">.*test.*</filePath>
    <gav> 按 Maven GAV 抑制 <gav>com.example:lib:1.0</gav>
    <sha1> 按文件 SHA1 抑制 <sha1>ABC123…</sha1>
    <until> 设置过期日期 until="2024-12-31Z"

    抑制文件管理最佳实践:

  • 版本控制:抑制文件纳入 Git 管理,与代码一同审查
  • 审批流程:抑制规则需要安全团队审批
  • 定期审查:每季度审查抑制规则,移除过期的
  • 文档化:每条抑制规则必须有详细的说明
  • 最小化原则:尽量精确抑制,避免过于宽泛的规则
  • 4.3 报告解析与结果处理

    JSON 报告结构解析:

    {
    "reportSchema": "1.1",
    "scanInfo": {
    "engineVersion": "12.1.0",
    "dataSource": [
    {"name": "NVD", "timestamp": "2024-01-15T00:00:00"}
    ]
    },
    "projectInfo": {
    "name": "MyApplication",
    "reportDate": "2024-01-15T10:30:00",
    "credits": "OWASP Dependency-Check"
    },
    "dependencies": [
    {
    "fileName": "log4j-core-2.14.0.jar",
    "filePath": "/path/to/log4j-core-2.14.0.jar",
    "md5": "abc123…",
    "sha1": "def456…",
    "description": "Apache Log4j 2",
    "license": "Apache-2.0",
    "vulnerabilities": [
    {
    "name": "CVE-2021-44228",
    "source": "NVD",
    "severity": "CRITICAL",
    "cvssv3": {
    "baseScore": 10.0,
    "attackVector": "NETWORK",
    "attackComplexity": "LOW",
    "privilegesRequired": "NONE",
    "userInteraction": "NONE",
    "scope": "CHANGED",
    "confidentialityImpact": "HIGH",
    "integrityImpact": "HIGH",
    "availabilityImpact": "HIGH"
    },
    "description": "Apache Log4j2 远程代码执行漏洞",
    "references": [
    {
    "source": "NVD",
    "url": "https://nvd.nist.gov/vuln/detail/CVE-2021-44228",
    "name": "NVD"
    }
    ],
    "vulnerableSoftware": [
    {
    "software": "cpe:2.3:a:apache:log4j:*:*:*:*:*:*:*:*",
    "versionStartIncluding": "2.0",
    "versionEndExcluding": "2.15.0"
    }
    ]
    }
    ]
    }
    ]
    }

    自动化处理脚本(Python):

    #!/usr/bin/env python3
    """
    Dependency-Check 报告解析器
    用于 CI/CD 流水线中的自动化处理
    """

    import json
    import sys
    from dataclasses import dataclass
    from typing import List, Optional
    from datetime import datetime

    @dataclass
    class Vulnerability:
    cve: str
    severity: str
    cvss_score: float
    description: str
    fixed_version: Optional[str] = None

    @dataclass
    class Dependency:
    name: str
    version: str
    file_path: str
    vulnerabilities: List[Vulnerability]

    class DependencyCheckParser:
    def __init__(self, report_path: str):
    with open(report_path, 'r', encoding='utf-8') as f:
    self.report = json.load(f)

    def get_summary(self) > dict:
    """获取漏洞汇总统计"""
    vulns = self._extract_vulnerabilities()

    return {
    'total_dependencies': len(self.report.get('dependencies', [])),
    'vulnerable_dependencies': len([d for d in self.report.get('dependencies', [])
    if d.get('vulnerabilities')]),
    'total_vulnerabilities': len(vulns),
    'critical': len([v for v in vulns if v.cvss_score >= 9.0]),
    'high': len([v for v in vulns if 7.0 <= v.cvss_score < 9.0]),
    'medium': len([v for v in vulns if 4.0 <= v.cvss_score < 7.0]),
    'low': len([v for v in vulns if 0.1 <= v.cvss_score < 4.0]),
    }

    def get_vulnerabilities_by_severity(self, min_cvss: float = 0) > List[Vulnerability]:
    """按严重程度获取漏洞列表"""
    all_vulns = self._extract_vulnerabilities()
    return [v for v in all_vulns if v.cvss_score >= min_cvss]

    def generate_markdown_report(self) > str:
    """生成 Markdown 格式的报告摘要"""
    summary = self.get_summary()

    report = f"""# Dependency-Check 安全报告

    ## 扫描概况

    – **扫描时间**: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
    – **总依赖数**:
    {summary['total_dependencies']}
    – **存在漏洞的依赖**:
    {summary['vulnerable_dependencies']}
    – **漏洞总数**:
    {summary['total_vulnerabilities']}

    ## 漏洞分布

    | 严重程度 | 数量 | 说明 |
    |———|——|——|
    | 🔴 Critical | {summary['critical']} | CVSS 9.0-10.0 |
    | 🟠 High |
    {summary['high']} | CVSS 7.0-8.9 |
    | 🟡 Medium |
    {summary['medium']} | CVSS 4.0-6.9 |
    | 🟢 Low |
    {summary['low']} | CVSS 0.1-3.9 |

    ## 高危漏洞详情

    """

    critical_high = self.get_vulnerabilities_by_severity(7.0)
    for vuln in critical_high[:10]: # 只显示前10个
    report += f"""### {vuln.cve} ({vuln.severity})

    – **CVSS Score**: {vuln.cvss_score}
    – **影响组件**:
    {vuln.name}
    – **漏洞描述**:
    {vuln.description[:200]}

    """

    return report

    def _extract_vulnerabilities(self) > List[Vulnerability]:
    """提取所有漏洞信息"""
    vulnerabilities = []

    for dep in self.report.get('dependencies', []):
    for vuln_data in dep.get('vulnerabilities', []):
    cvssv3 = vuln_data.get('cvssv3', {})
    vuln = Vulnerability(
    cve=vuln_data.get('name', 'Unknown'),
    severity=vuln_data.get('severity', 'Unknown'),
    cvss_score=cvssv3.get('baseScore', 0),
    description=vuln_data.get('description', 'No description'),
    fixed_version=self._extract_fixed_version(vuln_data)
    )
    vulnerabilities.append(vuln)

    return vulnerabilities

    def _extract_fixed_version(self, vuln_data: dict) > Optional[str]:
    """提取修复版本信息"""
    # 从漏洞数据中解析建议的修复版本
    for ref in vuln_data.get('references', []):
    if 'upgrade' in ref.get('name', '').lower():
    return ref.get('name')
    return None

    def main():
    if len(sys.argv) < 2:
    print("Usage: python parse_report.py <report.json>")
    sys.exit(1)

    parser = DependencyCheckParser(sys.argv[1])
    summary = parser.get_summary()

    print(json.dumps(summary, indent=2))

    # 如果有严重漏洞,返回非0退出码
    if summary['critical'] > 0:
    sys.exit(1)

    if __name__ == '__main__':
    main()

    4.4 性能优化

    数据库缓存优化:

    # 使用共享数据目录(Docker)
    docker run –rm \\
    -v /shared/dependency-check-data:/usr/share/dependency-check/data:rw \\
    ...

    # 定期更新(cron 任务)
    0 2 * * * /opt/dependency-check/bin/dependency-check.sh –updateonly –nvdApiKey xxx

    并行扫描优化:

    // Jenkins Pipeline 中并行执行不同类型的扫描
    pipeline {
    agent any

    stages {
    stage('Security Scans') {
    parallel {
    stage('Dependency-Check') {
    steps {
    dependencyCheck
    }
    }
    stage('SonarQube') {
    steps {
    withSonarQubeEnv('SonarQube') {
    sh 'mvn sonar:sonar'
    }
    }
    }
    stage('Container Scan') {
    steps {
    sh 'trivy image myapp:latest'
    }
    }
    }
    }
    }
    }

    增量扫描(实验性功能):

    <plugin>
    <groupId>org.owasp</groupId>
    <artifactId>dependency-check-maven</artifactId>
    <configuration>
    <!– 仅扫描变更的依赖(需要配合 Maven 的增量构建) –>
    <skipArtifactType>pom</skipArtifactType>
    </configuration>
    </plugin>


    第五部分:完整项目案例

    5.1 项目背景与架构

    项目名称: E-Shop 电商平台

    技术栈:

    • 后端:Spring Boot 3.2 + Java 17 + Maven
    • 前端:Vue 3 + TypeScript + Vite
    • 数据库:MySQL 8.0 + Redis
    • 部署:Docker + Kubernetes
    • CI/CD:Jenkins

    项目结构:

    e-shop/
    ├── backend/
    │ ├── pom.xml # Maven 配置
    │ ├── dependency-check-suppressions.xml
    │ └── src/…
    ├── frontend/
    │ ├── package.json # npm 配置
    │ └── src/…
    ├── docker-compose.yml
    ├── Jenkinsfile
    └── README.md

    5.2 后端 Maven 配置

    pom.xml 完整配置:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
    http://maven.apache.org/xsd/maven-4.0.0.xsd"
    >

    <modelVersion>4.0.0</modelVersion>

    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.0</version>
    <relativePath/>
    </parent>

    <groupId>com.example</groupId>
    <artifactId>e-shop-backend</artifactId>
    <version>1.0.0</version>
    <packaging>jar</packaging>

    <properties>
    <java.version>17</java.version>
    <nvd.api.key>${env.NVD_API_KEY}</nvd.api.key>
    </properties>

    <dependencies>
    <!– Spring Boot Starters –>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    <!– Database –>
    <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.33</version>
    </dependency>

    <!– JWT –>
    <dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.12.3</version>
    </dependency>

    <!– Test –>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    </dependency>
    </dependencies>

    <build>
    <plugins>
    <!– Spring Boot Plugin –>
    <plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>

    <!– OWASP Dependency-Check Plugin –>
    <plugin>
    <groupId>org.owasp</groupId>
    <artifactId>dependency-check-maven</artifactId>
    <version>12.1.0</version>
    <configuration>
    <nvdApiKey>${nvd.api.key}</nvdApiKey>
    <failBuildOnCVSS>7</failBuildOnCVSS>
    <format>ALL</format>
    <outputDirectory>${project.build.directory}/dependency-check</outputDirectory>
    <suppressionFiles>
    <suppressionFile>dependency-check-suppressions.xml</suppressionFile>
    </suppressionFiles>
    <skipTestScope>true</skipTestScope>
    <enableExperimental>true</enableExperimental>
    </configuration>
    <executions>
    <execution>
    <id>dependency-check</id>
    <phase>verify</phase>
    <goals>
    <goal>check</goal>
    </goals>
    </execution>
    </executions>
    </plugin>

    <!– SonarQube Plugin –>
    <plugin>
    <groupId>org.sonarsource.scanner.maven</groupId>
    <artifactId>sonar-maven-plugin</artifactId>
    <version>3.10.0.2594</version>
    </plugin>
    </plugins>
    </build>

    <profiles>
    <!– 开发环境:不阻断构建 –>
    <profile>
    <id>dev</id>
    <properties>
    <dependency-check.failBuildOnCVSS>11</dependency-check.failBuildOnCVSS>
    </properties>
    </profile>

    <!– 生产环境:严格检查 –>
    <profile>
    <id>prod</id>
    <properties>
    <dependency-check.failBuildOnCVSS>5</dependency-check.failBuildOnCVSS>
    </properties>
    </profile>
    </profiles>
    </project>

    抑制文件(dependency-check-suppressions.xml):

    <?xml version="1.0" encoding="UTF-8"?>
    <suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd">

    <!– 测试依赖的漏洞(不影响生产) –>
    <suppress>
    <notes><![CDATA[
    仅用于单元测试的 H2 数据库,生产环境使用 MySQL
    ]]>
    </notes>
    <gav regex="true">^com\\.h2database:h2:.*</gav>
    <cpe>cpe:/a:h2database:h2</cpe>
    </suppress>

    </suppressions>

    5.3 前端 npm 配置

    前端扫描脚本配置:

    // frontend/package.json
    {
    "name": "e-shop-frontend",
    "version": "1.0.0",
    "type": "module",
    "scripts": {
    "dev": "vite",
    "build": "vue-tsc && vite build",
    "preview": "vite preview",
    "test": "vitest",
    "lint": "eslint . –ext .vue,.ts,.tsx –fix",

    "security:check": "npm audit && npm run owasp:check",
    "security:check-ci": "npm audit –audit-level=moderate && npm run owasp:check-ci",

    "owasp:check": "dependency-check –project E-Shop-Frontend –scan package.json –scan package-lock.json –format HTML –format JSON –out reports –enableExperimental",
    "owasp:check-ci": "dependency-check –project E-Shop-Frontend –scan package.json –scan package-lock.json –format JSON –failOnCVSS 7 –out reports –enableExperimental",

    "precommit": "npm run lint && npm run security:check-ci"
    },
    "dependencies": {
    "vue": "^3.3.8",
    "vue-router": "^4.2.5",
    "pinia": "^2.1.7",
    "axios": "^1.6.2"
    },
    "devDependencies": {
    "@vitejs/plugin-vue": "^4.5.0",
    "typescript": "^5.2.2",
    "vite": "^5.0.0",
    "vue-tsc": "^1.8.22",
    "vitest": "^0.34.6",
    "eslint": "^8.54.0",
    "@vue/eslint-config-typescript": "^12.0.0"
    }
    }

    Git 预提交钩子(frontend/.husky/pre-commit):

    #!/bin/sh
    . "$(dirname "$0")/_/husky.sh"

    cd frontend

    # 运行 lint
    npm run lint
    if [ $? -ne 0 ]; then
    echo "❌ 代码检查失败,请修复后重试"
    exit 1
    fi

    # 运行依赖扫描(快速模式)
    npm audit –audit-level=moderate
    if [ $? -ne 0 ]; then
    echo "❌ 发现 npm 依赖漏洞"
    exit 1
    fi

    echo "✅ 预提交检查通过"

    5.4 Jenkins Pipeline 完整配置

    Jenkinsfile:

    pipeline {
    agent any

    environment {
    NVD_API_KEY = credentials('nvd-api-key')
    SONAR_TOKEN = credentials('sonar-token')
    DOCKER_REGISTRY = 'registry.company.com'
    APP_NAME = 'e-shop'
    }

    tools {
    maven 'maven-3.9'
    nodejs 'nodejs-20'
    dependencyCheck 'dependency-check'
    }

    options {
    buildDiscarder(logRotator(numToKeepStr: '10'))
    timestamps()
    timeout(time: 30, unit: 'MINUTES')
    }

    stages {
    stage('Checkout') {
    steps {
    checkout scm
    sh 'git log –oneline -5'
    }
    }

    stage('Backend Build & Test') {
    steps {
    dir('backend') {
    sh 'mvn clean compile test'
    }
    }
    post {
    always {
    dir('backend') {
    junit 'target/surefire-reports/*.xml'
    }
    }
    }
    }

    stage('Frontend Build & Test') {
    steps {
    dir('frontend') {
    sh 'npm ci'
    sh 'npm run build'
    sh 'npm test'
    }
    }
    }

    stage('Security Scans') {
    parallel {
    stage('Backend Dependency-Check') {
    steps {
    dir('backend') {
    // 使用 Maven 插件运行
    sh 'mvn dependency-check:check'
    }
    }
    post {
    always {
    dir('backend') {
    dependencyCheckPublisher pattern: 'target/dependency-check-report.xml'
    archiveArtifacts artifacts: 'target/dependency-check-report.*', allowEmptyArchive: true
    }
    }
    }
    }

    stage('Frontend Dependency-Check') {
    steps {
    dir('frontend') {
    dependencyCheck additionalArguments: """
    –project "E-Shop-Frontend"
    –scan package.json
    –scan package-lock.json
    –format HTML
    –format XML
    –format JSON
    –failOnCVSS 7
    –nvdApiKey ${NVD_API_KEY}
    –out reports
    –enableExperimental
    """
    ,
    odcInstallation: 'dependency-check'
    }
    }
    post {
    always {
    dir('frontend') {
    dependencyCheckPublisher pattern: 'reports/dependency-check-report.xml'
    archiveArtifacts artifacts: 'reports/dependency-check-report.*', allowEmptyArchive: true
    }
    }
    }
    }

    stage('SonarQube Analysis') {
    steps {
    dir('backend') {
    withSonarQubeEnv('SonarQube') {
    sh """
    mvn sonar:sonar \\
    -Dsonar.projectKey=e-shop-backend \\
    -Dsonar.projectName="E-Shop Backend" \\
    -Dsonar.dependencyCheck.reportPath=target/dependency-check-report.xml
    """

    }
    }
    }
    }

    stage('npm Audit') {
    steps {
    dir('frontend') {
    sh 'npm audit –audit-level=moderate'
    }
    }
    }
    }
    }

    stage('Security Gate') {
    steps {
    script {
    def backendReport = readJSON file: 'backend/target/dependency-check-report.json'
    def frontendReport = readJSON file: 'frontend/reports/dependency-check-report.json'

    def backendVulns = backendReport.dependencies.collectMany { it.vulnerabilities ?: [] }
    def frontendVulns = frontendReport.dependencies.collectMany { it.vulnerabilities ?: [] }

    def allVulns = backendVulns + frontendVulns

    def critical = allVulns.count { it.cvssv3?.baseScore >= 9.0 }
    def high = allVulns.count { it.cvssv3?.baseScore >= 7.0 && it.cvssv3?.baseScore < 9.0 }
    def medium = allVulns.count { it.cvssv3?.baseScore >= 4.0 && it.cvssv3?.baseScore < 7.0 }

    echo "🔒 依赖扫描结果汇总:"
    echo " 严重: ${critical}"
    echo " 高危: ${high}"
    echo " 中危: ${medium}"

    // 安全门禁策略
    if (critical > 0) {
    error "❌ 发现 ${critical} 个严重漏洞,阻塞发布!请立即修复。"
    }

    if (high > 3) {
    input message: "发现 ${high} 个高危漏洞,是否继续部署?",
    ok: '确认部署',
    submitter: 'security-team,devops-team'
    }

    if (medium > 20) {
    unstable "⚠️ 中危漏洞数量较多(${medium}个),建议安排修复计划"
    }

    echo "✅ 安全门禁通过"
    }
    }
    }

    stage('Build Docker Images') {
    steps {
    script {
    def version = sh(script: 'git describe –tags –always', returnStdout: true).trim()
    env.IMAGE_TAG = version

    // Build backend image
    dir('backend') {
    sh """
    docker build \\
    -t ${DOCKER_REGISTRY}/${APP_NAME}-backend:${version} \\
    -t ${DOCKER_REGISTRY}/${APP_NAME}-backend:latest \\
    .
    """

    }

    // Build frontend image
    dir('frontend') {
    sh """
    docker build \\
    -t ${DOCKER_REGISTRY}/${APP_NAME}-frontend:${version} \\
    -t ${DOCKER_REGISTRY}/${APP_NAME}-frontend:latest \\
    .
    """

    }
    }
    }
    }

    stage('Container Security Scan') {
    steps {
    script {
    // Scan backend image
    sh """
    trivy image \\
    –severity HIGH,CRITICAL \\
    –exit-code 1 \\
    ${DOCKER_REGISTRY}/${APP_NAME}-backend:${IMAGE_TAG}
    """

    // Scan frontend image
    sh """
    trivy image \\
    –severity HIGH,CRITICAL \\
    –exit-code 1 \\
    ${DOCKER_REGISTRY}/${APP_NAME}-frontend:${IMAGE_TAG}
    """

    }
    }
    }

    stage('Push Images') {
    when {
    anyOf {
    branch 'main'
    branch 'release/*'
    }
    }
    steps {
    script {
    sh """
    docker push ${DOCKER_REGISTRY}/${APP_NAME}-backend:${IMAGE_TAG}
    docker push ${DOCKER_REGISTRY}/${APP_NAME}-backend:latest
    docker push ${DOCKER_REGISTRY}/${APP_NAME}-frontend:${IMAGE_TAG}
    docker push ${DOCKER_REGISTRY}/${APP_NAME}-frontend:latest
    """

    }
    }
    }

    stage('Deploy to Test') {
    when {
    branch 'develop'
    }
    steps {
    sh '''
    kubectl set image deployment/e-shop-backend \\
    backend=${DOCKER_REGISTRY}/${APP_NAME}-backend:${IMAGE_TAG} \\
    -n test
    kubectl rollout status deployment/e-shop-backend -n test
    '''

    }
    }
    }

    post {
    always {
    // 生成安全报告汇总
    script {
    def summary = """
    ## 构建报告

    – **项目**: ${env.JOB_NAME}
    – **构建号**: ${env.BUILD_NUMBER}
    – **分支**: ${env.BRANCH_NAME}
    – **提交**: ${env.GIT_COMMIT?.take(8)}

    ### 构建结果: ${currentBuild.currentResult}

    详细报告请查看:${env.BUILD_URL}
    """
    echo summary
    }

    cleanWs()
    }

    failure {
    emailext (
    subject: "❌ 构建失败: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
    body: """构建失败,可能发现安全问题。

    项目: ${env.JOB_NAME}
    分支: ${env.BRANCH_NAME}
    构建号: ${env.BUILD_NUMBER}

    查看详情: ${env.BUILD_URL}

    请检查 Dependency-Check 报告。""",
    to: "${env.CHANGE_AUTHOR_EMAIL ?: 'dev-team@company.com'}",
    recipientProviders: [developers(), requestor()]
    )
    }

    unstable {
    emailext (
    subject: "⚠️ 构建不稳定: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
    body: """发现安全问题,需要关注。

    查看详情: ${env.BUILD_URL}""",
    to: "security-team@company.com"
    )
    }
    }
    }

    5.5 生产级 Pipeline 实践(多模块 + 条件执行)

    以下是一个实际生产环境中使用的 Jenkins Pipeline,适用于多模块 Maven 项目,并根据不同环境(DEV/TEST/PROD)执行不同的阶段。

    特点:

    • 参数化构建:动态选择环境、服务器和分支
    • 条件执行:DEV 环境执行代码质量和依赖扫描,TEST/PROD 执行编译部署
    • 多模块支持:使用 aggregate 生成统一报告
    • 离线优化:跳过数据库更新,避免网络问题

    完整 Pipeline:

    // 流水线属性配置
    properties([
    // 管道速度/耐久性优化
    durabilityHint('PERFORMANCE_OPTIMIZED'),
    // 保留已完成构建的储藏
    preserveStashes(),
    // 配置联动选框参数
    parameters([
    activeChoice(
    choiceType: 'PT_SINGLE_SELECT',
    description: '选择部署环境',
    filterLength: 1,
    filterable: false,
    name: 'Environment',
    script: scriptlerScript(
    isSandboxed: true,
    scriptlerBuilder: [
    builderId: '1726884996564_17',
    scriptId: 'Environments.groovy'
    ]
    )
    ),
    reactiveChoice(
    choiceType: 'PT_CHECKBOX',
    description: '选择需要构建的应用',
    filterLength: 1,
    filterable: true,
    name: 'Servers',
    referencedParameters: 'Environment',
    script: scriptlerScript(
    isSandboxed: true,
    scriptlerBuilder: [
    builderId: '1726884996566_18',
    scriptId: 'BackendApps.groovy'
    ]
    )
    )
    ])
    ])

    // 动态 JDK 版本选择
    def jdkVersion = params.Environment?.contains('DEV') ? 'jdk-17' : 'jdk-11'

    pipeline {
    agent any

    tools {
    maven 'local-mvn'
    jdk jdkVersion
    }

    options {
    buildDiscarder(logRotator(numToKeepStr: '10'))
    }

    parameters {
    string(
    defaultValue: 'http://gitlab.example.com/project/backend.git',
    description: '代码仓库地址',
    name: 'Gitlab_Registry_URL'
    )
    gitParameter(
    branch: '',
    branchFilter: '.*',
    defaultValue: 'develop',
    description: '选择代码分支',
    name: 'branch',
    type: 'GitParameterDefinition'
    )
    }

    environment {
    commitLog = ''
    commit_author = ''
    SonarQube_URL = 'http://sonarqube.example.com:9000'
    SonarQube_Secret = credentials('sonar-token')
    ProjectName = "${env.JOB_NAME}"
    scannerHome = tool 'SonarQube_Scanner_4.8'
    build_time = ''
    DOCKER_REPOSITORY = 'registry.example.com/project'
    commit_counters = ''
    }

    stages {
    stage('代码拉取') {
    steps {
    echo "当前使用的 JDK 版本: ${jdkVersion}"
    checkout scmGit(
    branches: [[name: '${branch}']],
    extensions: [],
    userRemoteConfigs: [[
    credentialsId: 'gitlab-credentials',
    url: '${Gitlab_Registry_URL}'
    ]]
    )

    script {
    // 获取代码提交信息
    commitLog = sh(
    script: 'git log –oneline -n 1',
    returnStdout: true
    ).trim()
    commit_author = sh(
    script: 'git log -n 1 –pretty=format:"%an"',
    returnStdout: true
    ).trim()
    commit_counters = sh(
    script: 'git log –oneline | wc -l',
    returnStdout: true
    ).trim()
    build_time = sh(
    script: 'date +%Y%m%d%H%M',
    returnStdout: true
    ).trim()

    echo "提交信息: ${commitLog}"
    echo "提交人: ${commit_author}"
    echo "提交次数: ${commit_counters}"
    }
    }
    }

    // DEV 环境执行代码质量检查
    stage('SonarQube Analysis') {
    when {
    expression {
    return params.Environment.contains('DEV')
    }
    }
    steps {
    echo '执行 SonarQube 代码质量分析'
    script {
    def sonarProperties = [
    "sonar.projectKey=${ProjectName}",
    "sonar.host.url=${SonarQube_URL}",
    "sonar.login=${SonarQube_Secret}"
    ]
    withSonarQubeEnv('SonarQube') {
    sh "mvn clean install sonar:sonar -D${sonarProperties.join(' -D')} -DskipTests -Ddockerfile.skip=true"
    }
    // 等待质量门禁结果
    timeout(time: 5, unit: 'MINUTES') {
    waitForQualityGate abortPipeline: false
    }
    }
    }
    }

    // DEV 环境执行依赖安全检查
    stage('Dependency-Check Analysis') {
    when {
    expression {
    return params.Environment.contains('DEV')
    }
    }
    steps {
    echo '执行 OWASP Dependency-Check 依赖安全扫描'
    script {
    // 多模块项目使用 aggregate 生成汇总报告
    // skipAllUpdates=true: 跳过数据库更新(适用于离线环境)
    // retirejs.skip=true: 跳过 RetireJS 分析器
    // assemblyAnalyzerEnabled=false: 禁用 .NET Assembly 分析器
    sh """mvn org.owasp:dependency-check-maven:12.1.0:aggregate \\
    -Ddependency-check.reportFormat=ALL \\
    -Dformats=XML,HTML \\
    -Ddependency-check.skipAllUpdates=true \\
    -Ddependency-check.retirejs.skip=true \\
    -DassemblyAnalyzerEnabled=false \\
    -Ddependency-check.outputDirectory=target"""

    }
    }
    }

    // 发布依赖扫描结果到 Jenkins
    stage('Publish Dependency-Check Results') {
    when {
    expression {
    return params.Environment.contains('DEV')
    }
    }
    steps {
    echo '发布依赖安全检查结果'
    script {
    // 发布 HTML 报告到 Jenkins 侧边栏
    publishHTML([
    allowMissing: true,
    alwaysLinkToLastBuild: true,
    keepAll: true,
    reportDir: 'target',
    reportFiles: 'dependency-check-report.html',
    reportName: 'Dependency-Check Report'
    ])

    // 生成漏洞趋势图
    dependencyCheckPublisher pattern: 'target/dependency-check-report.xml'
    }
    }
    }

    // TEST/PROD 环境执行编译
    stage('编译') {
    when {
    anyOf {
    expression { return params.Environment.contains('TEST') }
    expression { return params.Environment.contains('PROD') }
    }
    }
    steps {
    echo '编译打包'
    sh "mvn clean install -Dmaven.test.skip=true -Ddockerfile.skip=true"
    }
    }

    // TEST/PROD 环境构建并推送镜像
    stage('打包镜像并推送仓库') {
    when {
    anyOf {
    expression { return params.Environment.contains('TEST') }
    expression { return params.Environment.contains('PROD') }
    }
    }
    steps {
    echo '构建 Docker 镜像'
    script {
    for (server in params.Servers.tokenize(',')) {
    // 构建镜像
    sh "docker build -t ${DOCKER_REPOSITORY}/${server}:${build_time}_${commit_counters} ${server} –no-cache"
    // 推送镜像到仓库
    sh "docker push ${DOCKER_REPOSITORY}/${server}:${build_time}_${commit_counters}"
    // 清理本地镜像
    sh "docker rmi ${DOCKER_REPOSITORY}/${server}:${build_time}_${commit_counters}"
    }
    }
    }
    }

    // 测试环境部署
    stage('测试环境部署') {
    when {
    expression { return params.Environment.contains('TEST') }
    }
    steps {
    echo '部署到测试环境'
    script {
    for (server in params.Servers.tokenize(',')) {
    sh "scp ${server}/target/*.jar user@test-server:/home/app/${server}/"
    sh "ssh user@test-server bash -l -c /home/app/${server}/restart.sh"
    }
    }
    }
    }

    // 生产环境部署
    stage('生产环境部署') {
    when {
    expression { return params.Environment.contains('PROD') }
    }
    steps {
    echo '部署到生产环境'
    script {
    for (server in params.Servers.tokenize(',')) {
    sh "scp ${server}/target/*.jar user@prod-server:/home/app/${server}/"
    sh "ssh user@prod-server bash -l -c /home/app/${server}/restart.sh"
    }
    }
    }
    }
    }

    post {
    always {
    cleanWs()
    }
    failure {
    emailext(
    subject: "❌ 构建失败: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
    body: """构建失败!

    项目: ${env.JOB_NAME}
    分支: ${env.BRANCH_NAME}
    环境: ${params.Environment}
    提交人: ${commit_author}
    提交信息: ${commitLog}

    查看详情: ${env.BUILD_URL}""",
    to: "${commit_author}@company.com"
    )
    }
    }
    }

    关键配置说明:

    配置项说明适用场景
    aggregate 多模块项目生成汇总报告 多模块 Maven 项目
    skipAllUpdates=true 跳过 NVD 数据库更新 内网/离线环境
    retirejs.skip=true 跳过 JavaScript 分析器 纯后端项目
    assemblyAnalyzerEnabled=false 禁用 .NET 分析器 无 .NET 依赖的项目
    publishHTML 发布 HTML 报告到 Jenkins UI 需要可视化报告
    dependencyCheckPublisher 生成漏洞趋势图 需要历史趋势对比

    5.6 前端项目 Pipeline 实践(Node.js + CLI 工具)

    💡 说明:本节提供完整的生产级 Pipeline,展示前端项目的依赖扫描实践。3.3 节介绍了基础的 Jenkins 集成方案,5.4 节展示了后端 Maven 项目的完整 Pipeline。

    以下是一个前端 Vue 项目的 Jenkins Pipeline 示例,使用 Dependency-Check CLI 工具进行依赖安全扫描。

    特点:

    • 前端项目(Vue.js + npm)构建
    • 使用 CLI 工具(dependency-check.sh)而非 Maven 插件
    • 条件执行:DEV 环境执行代码质量和依赖扫描
    • 跳过可选分析器避免错误(Yarn Audit、RetireJS)
    • 使用 –noupdate 禁用数据库更新,加速构建

    完整 Pipeline:

    pipeline {
    agent any

    options {
    buildDiscarder(logRotator(numToKeepStr: '10'))
    }

    tools {
    jdk 'jdk-17'
    nodejs 'nodejs-14.17.6'
    }

    parameters {
    string(
    defaultValue: 'http://gitlab.example.com/project/frontend.git',
    description: '代码仓库地址',
    name: 'Gitlab_Registry_URL'
    )
    gitParameter(
    branch: '',
    branchFilter: '.*',
    defaultValue: 'release-dev',
    description: '选择代码分支',
    name: 'branch',
    type: 'GitParameterDefinition'
    )
    choice(
    name: 'Environment',
    choices: ['DEV', 'TEST', 'PROD'],
    description: '选择部署环境'
    )
    }

    environment {
    commitLog = ''
    commit_author = ''
    ProjectName = "${env.JOB_NAME}"
    scannerHome = tool 'SonarQube_Scanner_4.8'
    dependencyCheckHome = tool 'dependency-check-12.1.0'
    SonarQube_URL = 'http://sonarqube.example.com:9000'
    SonarQube_Secret = credentials('sonar-token')
    }

    stages {
    stage('代码拉取') {
    steps {
    echo '拉取 Git 代码'
    checkout scmGit(
    branches: [[name: '${branch}']],
    extensions: [],
    userRemoteConfigs: [[
    credentialsId: 'gitlab-credentials',
    url: '${Gitlab_Registry_URL}'
    ]]
    )

    script {
    commitLog = sh(
    script: 'git log –oneline -n 1',
    returnStdout: true
    ).trim()
    commit_author = sh(
    script: 'git log -n 1 –pretty=format:"%an"',
    returnStdout: true
    ).trim()

    echo "提交信息: ${commitLog}"
    echo "提交人: ${commit_author}"
    }
    }
    }

    // DEV 环境执行 SonarQube 代码质量检查
    stage('SonarQube Analysis') {
    when {
    expression {
    return params.Environment.contains('DEV')
    }
    }
    steps {
    echo '执行 SonarQube 代码质量分析'
    script {
    withSonarQubeEnv('SonarQube') {
    sh '''
    ${scannerHome}/bin/sonar-scanner \\
    -Dsonar.projectKey=${ProjectName} \\
    -Dsonar.sources=. \\
    -Dsonar.host.url=${SonarQube_URL} \\
    -Dsonar.login=${SonarQube_Secret}
    '''

    }
    // 等待质量门禁
    timeout(time: 3, unit: 'MINUTES') {
    waitForQualityGate abortPipeline: false
    }
    }
    }
    }

    // DEV 环境执行 Dependency-Check 依赖扫描
    stage('Dependency-Check Analysis') {
    when {
    expression {
    return params.Environment.contains('DEV')
    }
    }
    steps {
    echo '执行依赖安全检查'
    script {
    // 安装 npm 依赖
    try {
    sh '''
    npm cache clean –force || echo "警告:npm cache clean 失败,继续执行"
    npm install –unsafe-perm || {
    echo "错误:npm install 失败"
    exit 1
    }
    '''

    } catch (Exception e) {
    error "依赖安装失败:${e.getMessage()}"
    }

    // 确保输出目录存在
    sh 'mkdir -p target || { echo "错误:创建目标目录失败"; exit 1; }'

    // 使用 CLI 工具执行扫描
    // –noupdate: 禁用数据库更新,加速构建
    // –disableYarnAudit: 禁用 Yarn Audit(避免报错)
    // –disableRetireJS: 禁用 RetireJS(避免报错)
    // –disableAssembly: 禁用 Assembly 分析器
    try {
    sh """
    ${dependencyCheckHome}/bin/dependency-check.sh \\
    –project "${ProjectName}" \\
    –scan . \\
    –out target \\
    –format HTML \\
    –format XML \\
    –disableAssembly \\
    –noupdate \\
    –disableYarnAudit \\
    –disableRetireJS
    """

    } catch (Exception e) {
    error "依赖扫描失败:${e.getMessage()}"
    }
    }
    }
    }

    // 发布扫描结果
    stage('Publish Dependency-Check Results') {
    when {
    expression {
    return params.Environment.contains('DEV')
    }
    }
    steps {
    echo '发布依赖安全检查结果'
    script {
    // 发布 HTML 报告
    publishHTML([
    allowMissing: true,
    alwaysLinkToLastBuild: true,
    keepAll: true,
    reportDir: 'target',
    reportFiles: 'dependency-check-report.html',
    reportName: 'Dependency-Check Report'
    ])

    // 生成漏洞趋势图
    dependencyCheckPublisher pattern: 'target/dependency-check-report.xml'
    }
    }
    }

    // TEST/PROD 环境编译构建
    stage('编译构建') {
    when {
    anyOf {
    expression { return params.Environment.contains('TEST') }
    expression { return params.Environment.contains('PROD') }
    }
    }
    steps {
    script {
    try {
    sh '''
    npm cache clean –force || echo "警告:npm cache clean 失败,继续执行"
    npm install –unsafe-perm || {
    echo "错误:npm install 失败"
    exit 1
    }
    npm run build:test || {
    echo "错误:构建失败"
    exit 1
    }
    '''

    } catch (Exception e) {
    error "编译构建失败:${e.getMessage()}"
    }
    }
    }
    }

    // 测试环境部署
    stage('测试环境部署') {
    when {
    expression { return params.Environment.contains('TEST') }
    }
    steps {
    script {
    try {
    sh '''
    # 检查构建产物是否存在
    if [ ! -d "dist" ]; then
    echo "错误:构建产物目录 dist 不存在"
    exit 1
    fi

    # 备份旧版本
    ssh user@test-server "mv /home/app/front/acm /home/app/front/acm-`date +%Y%m%d%H%M`" || {
    echo "警告:备份旧版本失败,可能首次部署"
    }

    # 部署新版本
    rm -rf acm || { echo "错误:清理临时目录失败"; exit 1; }
    mv dist acm || { echo "错误:重命名构建产物失败"; exit 1; }
    scp -r acm user@test-server:/home/app/front/acm || {
    echo "错误:上传到测试服务器失败"
    exit 1
    }

    echo "测试环境部署成功"
    '''
    } catch (Exception e) {
    error "测试环境部署失败:${e.getMessage()}"
    }
    }
    }
    }

    // 生产环境部署
    stage('生产环境部署') {
    when {
    expression { return params.Environment.contains('PROD') }
    }
    steps {
    script {
    try {
    sh '''
    # 检查构建产物是否存在
    if [ ! -d "dist" ]; then
    echo "错误:构建产物目录 dist 不存在"
    exit 1
    fi

    # 备份旧版本(生产环境必须成功)
    ssh user@prod-server "mv /home/app/front/acm /home/app/front/acm-`date +%Y%m%d%H%M`" || {
    echo "错误:备份生产环境旧版本失败"
    exit 1
    }

    # 部署新版本
    rm -rf acm || { echo "错误:清理临时目录失败"; exit 1; }
    mv dist acm || { echo "错误:重命名构建产物失败"; exit 1; }
    scp -r acm user@prod-server:/home/app/front/acm || {
    echo "错误:上传到生产服务器失败"
    exit 1
    }

    echo "生产环境部署成功"
    '''
    } catch (Exception e) {
    error "生产环境部署失败:${e.getMessage()}"
    }
    }
    }
    }
    }
    }

    关键配置说明:

    配置项说明适用场景
    dependency-check.sh CLI 工具路径 Jenkins 全局工具配置
    –scan . 扫描当前目录 前端项目根目录
    –noupdate 跳过 NVD 数据库更新 加速构建,离线环境
    –disableYarnAudit 禁用 Yarn Audit 避免 npm 项目报错
    –disableRetireJS 禁用 RetireJS 避免网络超时
    –disableAssembly 禁用 Assembly 分析器 前端项目不需要
    publishHTML 发布 HTML 报告 可视化查看
    dependencyCheckPublisher 生成趋势图 Jenkins 漏洞统计

    5.7 NVD 数据库定时更新 Job

    在实际生产环境中,为了避免每次构建都下载漏洞数据库(耗时且不稳定),建议创建一个独立的 Jenkins Job 专门用于定期更新 NVD 数据库。业务项目的 Pipeline 则配置为跳过更新(–noupdate 或 skipAllUpdates=true),直接使用本地缓存的数据。

    特点:

    • 定时执行:每天凌晨自动更新
    • 独立运行:不影响业务构建
    • 与业务项目使用相同版本的插件

    完整 Pipeline:

    // 用于定期更新 NVD 数据库的独立 Jenkins Job
    // 建议配置为定时任务(如每天凌晨)
    properties([
    // 管道速度/耐久性覆盖
    durabilityHint('PERFORMANCE_OPTIMIZED'),
    // 配置定时任务,每天凌晨2点执行
    pipelineTriggers([cron('0 2 * * *')])
    ])

    pipeline {
    agent any

    // 使用与主项目相同的JDK版本
    tools {
    maven 'local-mvn'
    jdk 'jdk-17'
    }

    stages {
    stage('Update NVD Database') {
    steps {
    echo '开始更新 NVD 数据库…'
    script {
    try {
    // 仅执行数据库更新操作,不进行扫描
    // 使用与主项目相同版本的dependency-check插件
    sh """
    mvn org.owasp:dependency-check-maven:12.1.0:update-only || {
    echo "错误:NVD 数据库更新失败"
    echo "请检查:"
    echo "1. 网络连接是否正常"
    echo "2. NVD API Key 是否有效"
    echo "3. 磁盘空间是否充足"
    exit 1
    }
    """

    echo "NVD 数据库更新成功"
    } catch (Exception e) {
    error "NVD 数据库更新失败:${e.getMessage()}"
    }
    }
    }
    }
    }

    post {
    success {
    echo 'NVD 数据库更新成功'
    }
    failure {
    echo 'NVD 数据库更新失败,请检查日志'
    }
    }
    }

    配置说明:

    配置项说明建议值
    pipelineTriggers 定时触发器 cron('0 2 * * *') 每天凌晨2点
    update-only 仅更新数据库,不扫描 适用于定时任务
    插件版本 与业务项目保持一致 12.1.0

    使用方式:

  • 在 Jenkins 中创建一个新的 Pipeline Job
  • 将上述脚本粘贴到 Pipeline 定义中
  • 确保 Jenkins 服务器可以访问 NVD 官网(https://nvd.nist.gov)
  • 首次运行会下载完整数据库(约2GB),后续仅下载增量更新
  • 业务项目的 Pipeline 添加 –noupdate 或 skipAllUpdates=true 参数
  • 5.8 安全门禁与质量门禁

    门禁策略总结:

    检查项阻断阈值警告阈值处理方式
    严重漏洞 (CVSS 9.0+) >= 1 立即阻断,必须修复
    高危漏洞 (CVSS 7.0-8.9) > 3 1-3 超过3个需安全团队审批
    中危漏洞 (CVSS 4.0-6.9) > 20 记录技术债务,计划修复
    npm audit moderate+ 阻断构建
    容器扫描 HIGH+ 阻断构建

    5.9 漏洞修复流程

    发现漏洞后的处理流程:

    发现漏洞

    评估影响
    ├─ 确认是否适用(查看漏洞详情)
    ├─ 评估修复难度
    └─ 确定优先级

    选择修复方案
    ├─ 升级依赖版本(首选)
    ├─ 更换依赖(不兼容时)
    ├─ 缓解措施(临时)
    └─ 接受风险(需审批)

    实施修复

    验证修复
    ├─ 重新运行依赖扫描
    ├─ 功能回归测试
    └─ 验证抑制文件(如适用)

    关闭漏洞

    修复示例:

    # 1. 发现 log4j-core 存在漏洞
    # mvn dependency-check:check 报告 CVE-2021-44228

    # 2. 查看当前版本
    echo "检查 log4j 依赖…"
    mvn dependency:tree | grep log4j || {
    echo "错误:未找到 log4j 依赖"
    exit 1
    }

    # 3. 查看可用修复版本(访问 Maven Central)
    # https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core

    # 4. 更新 pom.xml 中的版本
    # <log4j.version>2.17.1</log4j.version>

    # 5. 验证修复
    echo "验证修复…"
    mvn clean compile || {
    echo "错误:编译失败,请检查版本兼容性"
    exit 1
    }

    mvn dependency-check:check || {
    echo "错误:依赖扫描失败,漏洞可能未修复"
    exit 1
    }

    echo "✅ 修复成功!漏洞已解决"

    # 6. 提交修复
    git add pom.xml || {
    echo "错误:git add 失败"
    exit 1
    }

    git commit -m "security: 升级 log4j 到 2.17.1 修复 CVE-2021-44228" || {
    echo "错误:git commit 失败"
    exit 1
    }

    git push || {
    echo "错误:git push 失败,请检查远程仓库配置"
    exit 1
    }

    echo "✅ 修复已提交并推送到远程仓库"


    第六部分:DevSecOps 扩展

    6.1 与 DAST、SAST 工具的配合

    完整 DevSecOps 安全工具链:

    开发阶段 构建阶段 测试阶段 部署阶段 运行阶段
    │ │ │ │ │
    ▼ ▼ ▼ ▼ ▼
    ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
    │ IDE插件 │ │ SCA │ │ SAST │ │ 镜像扫描 │ │ RASP │
    │SonarLint│ → │Dependency│ → │SonarQube│ → │ Trivy │ → │ WAF │
    │ ESLint │ │ Check │ │Checkmarx│ │ Snyk │ │监控告警 │
    └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘
    │ │ │
    ▼ ▼ ▼
    ┌─────────┐ ┌─────────┐ ┌─────────┐
    │ npm │ │ DAST │ │ IaC扫描 │
    │ audit │ │ OWASP │ │Checkov │
    │ │ │ ZAP │ │ │
    └─────────┘ └─────────┘ └─────────┘

    各阶段工具配置:

    阶段工具类型推荐工具集成方式
    开发 IDE 插件 SonarLint, ESLint IDE 自动检查
    构建 SCA Dependency-Check Maven/Gradle 插件
    构建 SAST SonarQube Maven 插件 + Jenkins
    测试 DAST OWASP ZAP Jenkins Pipeline
    部署 镜像扫描 Trivy Jenkins Pipeline
    部署 IaC 扫描 Checkov Jenkins Pipeline
    运行 运行时防护 RASP, WAF 基础设施部署

    6.2 容器安全扫描(Trivy)集成

    Trivy 安装与使用:

    # 安装 Trivy
    # macOS
    brew install trivy

    # Linux
    curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh

    # Docker
    docker pull aquasec/trivy

    扫描镜像:

    # 基础扫描
    trivy image myapp:latest

    # 只显示高危漏洞
    trivy image –severity HIGH,CRITICAL myapp:latest

    # 生成报告
    trivy image –format sarif –output trivy-report.sarif myapp:latest

    # 扫描 Dockerfile
    trivy config Dockerfile

    Jenkins Pipeline 集成:

    stage('Container Security Scan') {
    steps {
    script {
    // 安装 Trivy(如果未安装)
    sh '''
    if ! command -v trivy &> /dev/null; then
    curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
    fi
    '''

    // 扫描镜像
    sh """
    trivy image \\
    –severity HIGH,CRITICAL \\
    –format json \\
    –output trivy-report.json \\
    –exit-code 1 \\
    ${DOCKER_IMAGE}:${VERSION}
    """

    }
    }
    post {
    always {
    // 归档报告
    archiveArtifacts artifacts: 'trivy-report.json', allowEmptyArchive: true
    }
    }
    }

    6.3 安全运营与持续改进

    安全度量指标:

    指标说明目标值
    依赖扫描覆盖率 参与扫描的项目比例 > 95%
    漏洞发现平均时间 从引入到发现的时间 < 1天
    高危漏洞修复时间 MTTR for High+ < 7天
    严重漏洞修复时间 MTTR for Critical < 1天
    安全门禁通过率 首次通过比例 > 80%
    误报率 抑制规则占比 < 10%

    持续改进流程:

  • 每日审查:安全团队审查新发现的漏洞
  • 周度回顾:分析趋势,识别系统性问题
  • 月度报告:向管理层汇报安全状况
  • 季度优化:更新工具配置,优化流程
  • 建立安全知识库:

    # 安全知识库

    ## 常见漏洞修复指南

    ### Log4j 相关
    – 受影响版本:2.0-beta9 到 2.14.1
    – 修复版本:>= 2.17.1
    – 修复方法:升级依赖版本

    ### Spring Framework
    – …

    ## 抑制规则库

    ### 已批准的抑制规则
    – [H2 Database](link-to-rule)
    – [Test Dependencies](link-to-rule)


    第七部分:常见问题与故障排查

    Q1:首次扫描为什么这么慢?

    A: 首次扫描需要下载完整的 NVD 数据库(约 2GB),可能需要 2-4 小时。后续扫描会快很多。

    详细说明:

    • NVD 数据库包含所有已公开的漏洞信息
    • 下载速度取决于网络带宽和 NVD API 限制
    • 使用 NVD API Key 可以显著提升下载速度(从每分钟 5 次请求提升到 50 次)
    • 数据库会缓存在本地,后续扫描只需下载增量更新

    优化建议:

    # 1. 申请 NVD API Key(强烈推荐)
    # 访问 https://nvd.nist.gov/developers/request-an-api-key

    # 2. 首次运行时仅更新数据库
    dependency-check.sh –nvdApiKey YOUR_API_KEY –updateonly

    # 3. 使用 Docker 方式持久化缓存
    docker run –rm \\
    -v $(pwd)/dc-data:/usr/share/dependency-check/data \\
    owasp/dependency-check:latest \\
    –nvdApiKey YOUR_API_KEY \\
    –updateonly

    Q2:如何处理误报?

    A: 使用抑制文件(suppression file),详见 4.3 节。

    常见误报场景:

    场景原因处理方式
    测试依赖漏洞 测试环境不受影响 使用 <suppression> 排除
    内部库误匹配 CPE 标识符冲突 使用 <packageUrl> 精确匹配
    已修复但未更新 供应商延迟发布 使用 <cve> 指定 CVE
    不受影响版本 代码路径不可达 使用 <notes> 记录原因

    抑制文件示例(dependency-check-suppressions.xml):

    <?xml version="1.0" encoding="UTF-8"?>
    <suppressions>
    <!– 排除测试依赖 –>
    <suppress>
    <notes><![CDATA[
    测试依赖,生产环境不受影响
    ]]>
    </notes>
    <packageUrl regex="true">^pkg:maven/org\\.junit/.*$</packageUrl>
    </suppress>

    <!– 排除特定 CVE(已确认不适用) –>
    <suppress>
    <notes><![CDATA[
    CVE-2021-44228 不适用,项目使用 log4j 1.2.17
    ]]>
    </notes>
    <cve>CVE-2021-44228</cve>
    </suppress>

    <!– 排除特定版本的漏洞 –>
    <suppress>
    <notes><![CDATA[
    内部库,已通过代码审查确认安全
    ]]>
    </notes>
    <packageUrl regex="true">^pkg:maven/com\\.company/internal-lib@.*$</packageUrl>
    <cve>CVE-2023-12345</cve>
    </suppress>
    </suppressions>

    抑制文件管理最佳实践:

  • 将抑制文件纳入版本控制
  • 每条抑制规则必须包含详细的 <notes> 说明
  • 定期审查抑制规则,移除不再需要的规则
  • 建立抑制规则审批流程,防止滥用
  • Q3:内网环境如何使用?

    A: 有两种方案:1)离线下载漏洞数据库;2)搭建本地 NVD 镜像。

    方案一:离线下载漏洞数据库

    # 步骤 1:在有外网的机器上下载完整数据库
    dependency-check.sh –nvdApiKey YOUR_API_KEY –updateonly –data /path/to/data

    # 步骤 2:打包数据库目录
    cd /path/to
    tar -czf dependency-check-data.tar.gz data/

    # 步骤 3:传输到内网机器(使用 U 盘、内网传输等)
    # 步骤 4:在内网机器上解压
    tar -xzf dependency-check-data.tar.gz -d /opt/dependency-check/

    # 步骤 5:配置数据目录
    dependency-check.sh \\
    –data /opt/dependency-check/data \\
    –project "MyProject" \\
    –scan ./src \\
    –out ./reports \\
    –noupdate # 关键:不尝试更新数据库

    方案二:搭建本地 NVD 镜像

    # 使用开源工具搭建本地镜像
    # 参考:https://github.com/dependency-check/Dependency-Check/issues/4249

    # 方案 A:使用代理服务器
    # 配置 Dependency-Check 使用代理
    dependency-check.sh \\
    –proxy.server proxy.company.com \\
    –proxy.port 8080 \\
    –proxy.username user \\
    –proxy.password pass \\
    –scan ./src

    # 方案 B:使用本地漏洞数据库同步工具
    # 定期从外网同步 NVD 数据到内网服务器
    # 内网项目指向本地数据库服务器

    内网环境注意事项:

    • 漏洞数据库需要定期更新(建议每周一次)
    • 离线模式无法获取最新的漏洞信息
    • 考虑使用商业 SCA 工具,它们通常提供离线数据库支持

    Q4:扫描失败怎么办?

    A: 检查以下几点:

    • 网络连接是否正常
    • NVD API Key 是否有效
    • 磁盘空间是否充足
    • Java 版本是否符合要求

    详细排查步骤:

    1. 检查网络连接

    # 测试 NVD 服务是否可达
    curl -I https://nvd.nist.gov/

    # 测试 API Key 是否有效
    curl -H "apiKey: YOUR_API_KEY" \\
    https://services.nvd.nist.gov/rest/json/cves/2.0

    2. 验证 NVD API Key

    # API Key 格式应为 40 字符的十六进制字符串
    # 示例:abcd1234-5678-90ef-ghij-klmnopqrstuv

    # 检查环境变量
    echo $NVD_API_KEY

    # 如果 API Key 失效,重新申请:
    # https://nvd.nist.gov/developers/request-an-api-key

    3. 检查磁盘空间

    # Linux/Mac
    df -h

    # Windows
    dir

    # Dependency-Check 需要的空间:
    # – 数据库缓存:~2GB
    # – 临时文件:~1GB
    # – 报告文件:~100MB
    # 总计建议:至少 5GB 可用空间

    4. 验证 Java 版本

    # 检查 Java 版本
    java -version

    # Dependency-Check 要求:
    # – 最低:Java 8
    # – 推荐:Java 11 或更高
    # – 注意:Java 17+ 可能需要额外配置

    # 如果版本不符合要求,安装正确的 Java 版本

    5. 查看详细日志

    # 启用调试日志
    dependency-check.sh \\
    –scan ./src \\
    –out ./reports \\
    –log debug \\
    –logFile dependency-check-debug.log

    # 查看日志文件
    tail -f dependency-check-debug.log

    常见错误及解决方案:

    错误信息原因解决方案
    Failed to connect to NVD 网络问题或 NVD 服务不可用 检查网络,稍后重试
    API rate limit exceeded 超过 NVD API 限制 申请 API Key 或等待重置
    OutOfMemoryError 内存不足 增加 JVM 内存 -Xmx4g
    Permission denied 文件权限问题 检查目录权限
    Unsupported class file version Java 版本不兼容 升级或降级 Java 版本

    增加 JVM 内存:

    # 命令行方式
    export MAVEN_OPTS="-Xmx4g -Xms2g"
    dependency-check.sh –scan ./src

    # Maven 插件方式
    <plugin>
    <groupId>org.owasp</groupId>
    <artifactId>dependency-check-maven</artifactId>
    <configuration>
    <jvmArgs>-Xmx4g -Xms2g</jvmArgs>
    </configuration>
    </plugin>

    # Docker 方式
    docker run –rm \\
    -e JAVA_OPTS="-Xmx4g -Xms2g" \\
    -v $(pwd):/src \\
    owasp/dependency-check:latest \\
    –scan /src

    Q5:如何提升扫描速度?

    A: 可以通过以下方式优化扫描性能:

    1. 使用 NVD API Key

    # 无 API Key:每分钟 5 次请求
    # 有 API Key:每分钟 50 次请求(10倍提升)

    # 设置环境变量
    export NVD_API_KEY=your_api_key_here

    # 或在命令行中指定
    dependency-check.sh –nvdApiKey YOUR_API_KEY –scan ./src

    2. 持久化数据缓存

    # Docker 方式:映射数据目录
    docker run –rm \\
    -v $(pwd)/dc-data:/usr/share/dependency-check/data \\
    -v $(pwd):/src \\
    owasp/dependency-check:latest \\
    –scan /src

    # 命令行方式:指定数据目录
    dependency-check.sh \\
    –data /opt/dependency-check/data \\
    –scan ./src

    3. 禁用不必要的分析器

    # 只启用需要的分析器
    dependency-check.sh \\
    –disableRetireJS \\
    –disableNodeJS \\
    –disableNodeAudit \\
    –scan ./src

    # Maven 插件配置
    <configuration>
    <analyzers>
    <jarAnalyzer>false</jarAnalyzer>
    <npmAnalyzer>false</npmAnalyzer>
    <pythonAnalyzer>false</pythonAnalyzer>
    </analyzers>
    </configuration>

    4. 并行扫描(多模块项目)

    # 使用 GNU Parallel 并行扫描
    find . -name "pom.xml" -type f | parallel -j 4 \\
    'dependency-check.sh –project {/.} –scan {//} –out reports/{/.}'

    # Jenkins Pipeline 中使用并行 stage
    parallel(
    "module-a": { sh 'mvn -f module-a/pom.xml dependency-check:check' },
    "module-b": { sh 'mvn -f module-b/pom.xml dependency-check:check' },
    "module-c": { sh 'mvn -f module-c/pom.xml dependency-check:check' }
    )

    5. 优化扫描范围

    # 只扫描必要的目录
    dependency-check.sh \\
    –scan ./target/classes \\
    –scan ./target/lib \\
    –out ./reports

    # 排除不需要的目录
    dependency-check.sh \\
    –scan ./src \\
    –exclude ./src/test \\
    –exclude ./src/main/resources/static \\
    –out ./reports

    性能对比:

    优化措施扫描时间说明
    无优化(首次) 2-4 小时 需要下载完整数据库
    无优化(后续) 30-60 分钟 仅增量更新
    + NVD API Key 15-30 分钟 下载速度提升 10 倍
    + 数据缓存 5-10 分钟 跳过数据库更新
    + 禁用不必要分析器 3-5 分钟 减少分析时间
    + 并行扫描 1-3 分钟 利用多核 CPU

    Q6:如何集成到现有的 CI/CD 流程?

    A: 根据使用的 CI/CD 工具,选择相应的集成方式。

    GitHub Actions 集成:

    name: Security Scan

    on:
    push:
    branches: [ main, develop ]
    pull_request:
    branches: [ main ]
    schedule:
    cron: '0 2 * * *' # 每天凌晨 2 点

    jobs:
    dependency-check:
    runs-on: ubuntulatest

    steps:
    uses: actions/checkout@v3

    name: Set up JDK 11
    uses: actions/setupjava@v3
    with:
    java-version: '11'
    distribution: 'temurin'

    name: Run DependencyCheck
    uses: dependencycheck/DependencyCheck_Action@main
    with:
    project: 'my-project'
    path: '.'
    format: 'HTML'
    out: 'reports'
    args: >
    –nvdApiKey ${{ secrets.NVD_API_KEY }}
    –failOnCVSS 7
    –enableExperimental

    name: Upload Report
    uses: actions/uploadartifact@v3
    if: always()
    with:
    name: dependencycheckreport
    path: reports/

    GitLab CI 集成:

    stages:
    test
    security

    dependency-check:
    stage: security
    image: owasp/dependencycheck:latest
    script:
    dependencycheck.sh
    project "$CI_PROJECT_NAME"
    scan .
    out reports
    format HTML
    format JSON
    nvdApiKey $NVD_API_KEY
    failOnCVSS 7
    artifacts:
    paths:
    reports/
    when: always
    expire_in: 1 week
    only:
    main
    merge_requests

    Azure DevOps 集成:

    trigger:
    main

    pool:
    vmImage: 'ubuntu-latest'

    variables:
    NVD_API_KEY: $(NVD_API_KEY)

    steps:
    task: UseDotNet@2
    inputs:
    packageType: 'sdk'
    version: '6.x'

    script: |
    docker pull owasp/dependency-check:latest
    docker run –rm \\
    -v $(pwd):/src \\
    -v $(pwd)/reports:/report \\
    -e NVD_API_KEY=$NVD_API_KEY \\
    owasp/dependency-check:latest \\
    –scan /src \\
    –out /report \\
    –format ALL \\
    –failOnCVSS 7

    displayName: 'Run Dependency-Check'

    task: PublishBuildArtifacts@1
    inputs:
    PathtoPublish: 'reports'
    ArtifactName: 'dependency-check-reports'
    publishLocation: 'Container'
    condition: always()

    CircleCI 集成:

    version: 2.1

    orbs:
    dependency-check: dependencycheck/dependencycheck@1.0.0

    workflows:
    security-scan:
    jobs:
    dependency-check/scan:
    path: '.'
    project: 'my-project'
    format: 'ALL'
    failOnCVSS: 7
    nvdApiKey: $NVD_API_KEY
    filters:
    branches:
    only:
    main
    develop

    Q7:如何设置安全门禁?

    A: 根据项目安全要求,设置合适的 CVSS 阈值和阻断策略。

    基础门禁配置:

    # CVSS 阈值配置
    dependency-check.sh \\
    –failOnCVSS 7 \\
    –scan ./src \\
    –out ./reports

    # Maven 插件配置
    <plugin>
    <groupId>org.owasp</groupId>
    <artifactId>dependency-check-maven</artifactId>
    <configuration>
    <failBuildOnCVSS>7</failBuildOnCVSS>
    </configuration>
    </plugin>

    分级门禁策略:

    环境类型严重漏洞高危漏洞中危漏洞低危漏洞
    生产环境 阻断 阻断(>3个) 警告 警告
    预发布环境 阻断 警告 警告 忽略
    测试环境 警告 警告 忽略 忽略
    开发环境 忽略 忽略 忽略 忽略

    Jenkins Pipeline 门禁示例:

    pipeline {
    agent any

    environment {
    NVD_API_KEY = credentials('nvd-api-key')
    }

    stages {
    stage('Security Scan') {
    steps {
    script {
    def scanResult = sh(
    script: """
    dependency-check.sh \\
    –project "${env.JOB_NAME}" \\
    –scan ./src \\
    –out ./reports \\
    –format JSON \\
    –nvdApiKey ${NVD_API_KEY} \\
    –failOnCVSS 7
    """
    ,
    returnStatus: true
    )

    if (scanResult != 0) {
    // 解析报告,获取漏洞详情
    def report = readJSON file: 'reports/dependency-check-report.json'
    def criticalVulns = report.dependencies.findAll {
    it.vulnerabilities.any { it.cvssScore >= 9.0 }
    }

    if (criticalVulns) {
    error "发现 ${criticalVulns.size()} 个严重漏洞,构建失败!"
    }
    }
    }
    }
    }
    }

    post {
    always {
    publishHTML(target: [
    reportDir: 'reports',
    reportFiles: 'dependency-check-report.html',
    reportName: 'Dependency-Check Report'
    ])
    }
    }
    }

    GitHub Actions 门禁示例:

    name: Check for Critical Vulnerabilities
    run: |
    # 解析 JSON 报告
    CRITICAL_COUNT=$(jq '[.dependencies[].vulnerabilities[] | select(.cvssScore >= 9.0)] | length' reports/dependency-check-report.json)

    if [ $CRITICAL_COUNT gt 0 ]; then
    echo "❌ 发现 $CRITICAL_COUNT 个严重漏洞"
    exit 1
    else
    echo "✅ 未发现严重漏洞"
    fi

    Q8:如何处理大型项目的扫描?

    A: 大型项目需要特殊的优化策略来平衡扫描速度和准确性。

    策略一:增量扫描

    # 只扫描变更的模块
    git diff –name-only HEAD~1 HEAD | grep "pom.xml" | while read pom; do
    module_dir=$(dirname "$pom")
    dependency-check.sh \\
    –project "$(basename $module_dir)" \\
    –scan "$module_dir" \\
    –out "reports/$(basename $module_dir)"
    done

    策略二:分层扫描

    # 第一层:核心依赖(每次扫描)
    dependency-check.sh \\
    –project "core-deps" \\
    –scan ./core/target/lib \\
    –out ./reports/core

    # 第二层:业务模块(按需扫描)
    dependency-check.sh \\
    –project "business-modules" \\
    –scan ./modules/target/lib \\
    –out ./reports/modules

    # 第三层:测试依赖(低频扫描)
    dependency-check.sh \\
    –project "test-deps" \\
    –scan ./test/target/lib \\
    –out ./reports/test

    策略三:分布式扫描

    # 使用 Jenkins 分布式构建
    pipeline {
    agent none

    stages {
    stage('Parallel Security Scan') {
    parallel {
    stage('Module A') {
    agent { label 'agent-1' }
    steps {
    sh 'mvn -f module-a/pom.xml dependency-check:check'
    }
    }
    stage('Module B') {
    agent { label 'agent-2' }
    steps {
    sh 'mvn -f module-b/pom.xml dependency-check:check'
    }
    }
    stage('Module C') {
    agent { label 'agent-3' }
    steps {
    sh 'mvn -f module-c/pom.xml dependency-check:check'
    }
    }
    }
    }

    stage('Aggregate Results') {
    agent any
    steps {
    sh 'mvn dependency-check:aggregate'
    }
    }
    }
    }

    策略四:缓存优化

    # 使用构建缓存
    dependency-check.sh \\
    –data /opt/dependency-check/data \\
    –cache ./dependency-check-cache \\
    –scan ./src \\
    –out ./reports

    # Maven 插件缓存配置
    <plugin>
    <groupId>org.owasp</groupId>
    <artifactId>dependency-check-maven</artifactId>
    <configuration>
    <dataDirectory>/opt/dependency-check/data</dataDirectory>
    <cacheDirectory>${project.build.directory}/dependency-check-cache</cacheDirectory>
    </configuration>
    </plugin>

    大型项目扫描最佳实践:

    项目规模扫描策略频率耗时
    小型(<50 依赖) 全量扫描 每次 CI 1-3 分钟
    中型(50-200 依赖) 增量扫描 每次 CI 5-10 分钟
    大型(200-500 依赖) 分层扫描 每日/每周 15-30 分钟
    超大型(>500 依赖) 分布式扫描 每周 30-60 分钟

    结语

    写这篇文章的时候,我又想起了 Log4j2 那个晚上——整个技术圈都在熬夜排查漏洞,那种焦虑感至今记忆犹新。

    说到底,安全不是什么高大上的概念,就是别让你的项目成为下一个"定时炸弹"。Dependency-Check 这类工具,本质上就是帮你提前发现隐患,把风险控制在可接受范围内。

    几个关键点,再啰嗦一遍:

  • 别忽视依赖安全 —— 70% 以上的代码来自第三方,依赖安全就是应用安全
  • 工具要选对 —— Dependency-Check 是开源 SCA 的首选,功能完善、完全免费
  • 集成是关键 —— 把依赖扫描融进 CI/CD,自动化才是王道
  • 别贪大求全 —— 从基础配置开始,逐步完善安全门禁和流程
  • 文化比工具重要 —— 工具只是手段,建立安全文化才是根本
  • 接下来怎么做?

    别光看不练,选一个试点项目,按照本文案例配置 Dependency-Check。申请个 NVD API Key,优化扫描性能。建立团队的安全门禁策略和漏洞修复流程。逐步引入其他安全工具,构建完整的 DevSecOps 工具链。

    安全这场仗没有终点,但至少我们可以把防线往前推一推。希望本文能帮你在快速交付的同时,把安全这道门守好。


    参考资料

  • OWASP Dependency-Check 官方文档
  • OWASP Dependency-Check GitHub
  • NVD – National Vulnerability Database
  • CVSS v3.1 规范
  • OWASP Top 10
  • SonarQube Dependency-Check Plugin
  • Jenkins OWASP Dependency-Check Plugin
  • 赞(0)
    未经允许不得转载:171主机测评 » 打造 DevSecOps:以 Dependency-Check 为核心的安全实践指南
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址