很多用GitHub Actions集成Fortify做代码安全扫描的开发者,都会遇到一个头疼的问题——每次代码提交或者PR的时候,Fortify都会对整个代码库进行完整扫描。如果项目不大,这个问题可能不明显,但如果是有几千个文件的大型项目,全库扫描可能需要十几分钟甚至更久,严重拖慢CI流程的效率,让开发者等待很久才能拿到安全扫描结果。更关键的是,如果这次变更只改了几个核心文件,剩下的几千个未修改的代码其实没有新的安全风险,全库扫描完全是冗余的,浪费了CI服务器的资源和Fortify的扫描成本。
一、问题由来:全库扫描的痛点
全库扫描的核心问题在于没有区分变更和未变更的代码,只要触发CI就对所有文件做一次安全检查。对于小型项目,这个开销可以忽略;但对于中大型项目,全库扫描的劣势就会被放大:一是CI任务超时风险高,很多公司会给CI任务设置时间限制,全库扫描很容易超时,导致PR无法合并;二是资源浪费,CI服务器的CPU、内存需要处理大量未变更的代码,增加云服务成本;三是开发者体验差,每次提交都要等十几分钟甚至更久,降低开发效率。
二、解决方案:用changed-files实现增量扫描
要解决这个问题,核心思路很简单:只对本次提交变更的代码文件做安全扫描,跳过所有未修改的文件。实现这个思路的关键工具是changed-files,它可以在GitHub Actions中对比当前提交和基准分支(比如main分支),精准找出所有被修改、新增的代码文件。
2.1 核心原理:精准定位变更文件
changed-files工具的工作逻辑是:在GitHub Actions的运行环境中,获取当前提交的SHA值,再对比基准分支的最新SHA值,执行Git的diff命令,筛选出所有状态为modified、added的文件,过滤掉删除的、文档类的非代码文件,最终输出一个可直接使用的变更文件列表。这个过程完全由自动化完成,不需要人工手动整理,精准度很高。
2.2 具体实现:GitHub Actions配置示例
这里我们用GitHub Actions的YAML配置来实现完整的增量Fortify扫描,技术栈为GitHub Actions Workflow配置,示例带有详细注释,方便理解和修改:
name: Fortify Incremental Code Scan
# 触发条件:主分支推送、PR到主分支时运行
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
incremental-scan:
runs-on: ubuntu-latest
steps:
# Step1: 拉取完整代码,必须设置fetch-depth为0
# 原因:默认fetch-depth是1,只能获取最近一次提交,无法对比更早的变更
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 0
# Step2: 获取变更的代码文件列表
- name: Get Changed Code Files
id: changed-files
uses: tj-actions/changed-files@v45
with:
# 只包含的代码文件类型,根据项目语言修改(比如Java就用java,Python用py)
files: |
**/*.java
**/*.js
**/*.ts
**/*.py
# 排除不需要扫描的文件/目录,比如测试代码、依赖包、文档
files_ignore: |
**/test/**
**/tests/**
**/node_modules/**
**/*.log
**/*.md
**/*.json
# Step3: 安装Fortify CLI,用于运行安全扫描
- name: Install Fortify CLI
run: |
sudo apt update -y
sudo apt install -y fortify-cli
# Step4: 运行增量Fortify扫描,仅针对变更代码
- name: Run Incremental Fortify Scan
env:
# Fortify授权令牌,需在GitHub Settings->Secrets中配置
FORTIFY_TOKEN: ${{ secrets.FORTIFY_TOKEN }}
run: |
# 获取变更文件列表,转成空格分隔的字符串
CHANGED_FILES="${{ steps.changed-files.outputs.all_changed_files }}"
# 如果有变更文件,才运行扫描,无变更则跳过
if [ -n "$CHANGED_FILES" ]; then
# 执行Fortify扫描,指定变更文件,输出结果文件
fortify scan -f $CHANGED_FILES --output fortify-incremental.fpr
echo "增量扫描完成,结果文件:fortify-incremental.fpr"
else
echo "无代码文件变更,跳过安全扫描"
fi
这个示例的核心细节需要注意:fetch-depth必须设为0,否则changed-files无法获取足够的历史提交来对比;files和files_ignore的规则必须匹配项目的代码结构,比如前端项目要加上**/*.tsx,后端项目要排除**/target/**(Maven的产物目录);Fortify令牌要存放在GitHub Secrets中,不能硬编码在配置里,保证安全。
三、应用场景
这个增量扫描方案非常适合以下场景:
- 大型多模块项目:比如有5000+代码文件的企业级应用,每次提交通常只修改几到几十个文件,增量扫描能把扫描时间从15分钟缩短到2分钟以内。
- CI流程有时间限制的团队:公司规定CI任务必须在10分钟内完成,全库扫描经常超时,增量扫描能轻松满足时间要求。
- 高频提交的团队:每天有数十次提交或PR,增量扫描能大幅降低CI服务器的负载,减少云资源成本。
- PR需快速审核的场景:开发者提交PR后,希望快速得到安全扫描结果,不需要等待全库扫描,提升审核效率。 但这个方案并不适合所有场景:如果项目是少于100个文件的小型项目,全库扫描本身只需1-2分钟,增量的优势不明显;如果有合规要求需要定期全库安全基线检查,还是需要配合全库扫描作为补充。
四、技术优缺点
4.1 优点
- 速度提升显著:对比全库扫描,增量扫描时间减少80%以上,甚至90%,开发者能快速获取结果,不打断开发节奏。
- 资源消耗降低:CI服务器的CPU、内存使用量减少,Fortify的扫描次数或文件数(如果是按这个收费)也会减少,降低成本。
- 精准性更高:只扫描变更的代码,能精准定位本次提交引入的新漏洞,不会因为全库扫描的大量旧文件分散注意力。
- 配置灵活:changed-files的过滤规则可以根据项目类型、结构自由调整,适配不同技术栈的项目。
4.2 缺点
- 依赖工具准确性:如果fetch-depth没设置为0,或者过滤规则写错,changed-files会输出错误的变更文件,导致漏扫或扫到无关文件。
- 无法替代全库扫描:对于合规要求、年度安全评估的场景,增量扫描不能覆盖整个代码库,需要额外的全库扫描作为补充。
- 规则维护成本:随着项目迭代,过滤规则(files和files_ignore)可能需要调整,比如新增了一种代码文件类型,需要手动更新配置。
五、注意事项
- 必须显式设置fetch-depth为0:很多开发者用actions/checkout时会忽略这个配置,默认的fetch-depth=1会导致changed-files只能对比最近一次提交,无法正确获取基准分支的变更,必须设置fetch-depth:0才能获取完整的历史记录。
- 正确配置基准分支对比:在pull_request场景下,changed-files默认会对比PR的目标分支(比如main),不需要额外修改,但若项目使用特殊分支作为开发分支,需确认对比的分支是否正确。
- 过滤规则要严谨:比如不要把核心代码文件的类型写错,比如把**.ts写成**.js,会遗漏TypeScript代码;还要排除第三方依赖目录,比如**/venv/(Python虚拟环境)、/vendor/**(PHP依赖),这些是不需要扫描的。
- 处理删除文件:changed-files会输出被删除的文件,Fortify扫描时会自动跳过不存在的文件,所以不用额外处理,但最好在规则中加上files_ignore排除测试文件等不必要的文件。
- Fortify命令的兼容性:不同版本的Fortify CLI的参数可能有差异,比如高版本可能用--file参数替代-f,配置时要参考Fortify官方文档,确保命令正确。
六、文章总结
通过GitHub Actions集成changed-files工具,我们可以轻松实现Fortify的增量代码安全扫描,完美解决全库扫描耗时久、资源浪费的问题。这个方案的核心是精准定位变更代码,只扫描有风险的部分,既保证了安全检查的效果,又大幅提升了CI流程的效率。对于大型项目、高频提交的团队来说,这个优化能明显提升开发者体验,降低CI成本。只要注意fetch-depth、过滤规则、分支对比等细节,就能快速上手这个方案,适配不同技术栈的项目。
评论
围绕“GitHub Actions集成Fortify后每次提交都重扫全库?利用changed-files实现增量推送扫描”参与讨论