最近做了 Black Duck 与 Jenkins 的集成,目标是给测试和开发提供定制化、定时的对开发代码仓库的进行源码以及许可信息扫描。
为什么要做源码扫描
在产品开发中经常需要引入一些开源组件,但这些开源的代码的漏洞和许可信息等问题会给产品带来安全以及商业风险,因此在发布产品前需要对这些开源组件的漏洞和许可信息进行扫描。
Black Duck(黑鸭)是一款可以对源代码进行扫描、审计和代码管理的软件工具,能够搜索安全的开源代码,检测产品的开源代码使用情况和风险情况。
同类型的工具还有 JFrog Xray
但是,如果不能及时的进行代码扫描,在产品发布快要发布甚至发布后才进行扫描,这时候发现问题再去解决就会变得非常被动,因此团队需要尽早发现并解决问题安全以及许可信息等问题,因此将 Black Duck 与 CI 工具集成进行每月、每周、甚至每日构建就变得十分重要。
Black Duck 手动执行一般步骤
- 手动下载指定 Git 仓库及分支代码
- 去掉不相关的代码(也可以通过 Black Duck 参数去指定要扫描的特定文件或文件夹)
- 手动执行 Black Duck 扫描命令
- 扫描成功后,结果传到内部 Black Duck 网站供相关人员进行审查
Black Duck 与 Jenkins 的集成目标
- 一个流水线支持定制化仓库的代码下载
- 给开发和测试提供简单的、可随时可以执行源码扫描的界面
- 支持定期自动扫描,以及与其他 Jenkins 任务联动执行
Black Duck 参数介绍
代码语言:javascript复制--blackduck.url # 你的 Black Duck 网址
--blackduck.username # 你的登录用户
--blackduck.api.token # 你的登录用户 Token
--detect.project.name # Black Duck 下面的项目
--detect.project.version.name # 项目版本号
--detect.source.path # 要扫描的代码目录
--logging.level.com.synopsys.integration # 扫描日志级别
--blackduck.trust.cert=TRUE # 是否信任 socket (SSL)
--detect.blackduck.signature.scanner.snippet.matching # 扫描片段模式
更多其他参数可以参照官方的 CI 集成文档 Synopsys Detect for Jenkins https://synopsys.atlassian.net/wiki/spaces/INTDOCS/pages/71106939/Synopsys Detect for Jenkins
Black Duck 配置
首先,安装 Black Duck 插件 Synopsys Detect 到 Jenkins
然后,配置 Synopsys Detect 插件
- Jenkins -> Confiruration(系统配置)
- Black Duck URL:公司内部的 Black Duck 网址,例如:https://yourcompany.blackducksoftware.com
- Black Duck credentials:注意要选择 credentials 类型为 Secret text, Secret 填写你用户的 Token
- 配置完成后点击 Test connections to Black Duck,显示 Connection successful 表示配置成功。
Black Duck 流水线任务效果
Black Duck 流水线代码
代码语言:javascript复制pipeline{
agent {
node {
label 'black-duck'
customWorkspace "/agent/workspace/blackduck"
}
}
parameters {
choice(
name: 'VERSION',
choices: ['MVSURE_v1.1', 'MVSURE_v1.2', 'MVSURE_v2.2'],
description: 'Which version do you want scan on black duck? MVSURE_v1.1, MVSURE_v1.2 or others?')
choice(
name: 'REPO',
choices: ['blog-server', 'blog-client', 'blog-docker'],
description: 'Which repository code does above VERSION belong to?')
string(
name: 'BRANCH',
defaultValue: 'develop',
description: 'Which branch does above VERSION belong to?')
choice(
name: 'SNIPPET-MODES',
choices: ['SNIPPET_MATCHING', 'SNIPPET_MATCHING_ONLY', 'FULL_SNIPPET_MATCHING', 'FULL_SNIPPET_MATCHING_ONLY', 'NONE'],
description: 'What snippet scan mode do you want to choose?')
}
environment {
ROBOT = credentials("d1cbab74-823d-41aa-abb7-858485121212")
hub_detect = 'https://blackducksoftware.github.io/hub-detect/hub-detect.sh'
blackduck_url = 'https://yourcompany.blackducksoftware.com'
blackduck_user = 'robot@yourcompany.com'
detect_project = 'GITHUB'
detect_project_version = '${VERSION}'
detect_source_path = '${WORKSPACE}/${REPO}/src'
}
# 只保留最近十次 Jenkins 执行结果
options {buildDiscarder(logRotator(numToKeepStr:'10'))}
# 定时触发可以在这里添加
stages {
stage("git clone"){ # 参数化 git clone 代码过程
steps{
sh '''
if [ -d ${REPO} ]; then
rm -rf ${REPO}
fi
git clone -b ${BRANCH} --depth 1 https://$ROBOT_USR:"$ROBOT_PSW"@git.yourcompany.com/scm/github/${REPO}.git
'''
}
}
stage("black duck scan"){ # 参数化 Black Duck 所用到的参数值
steps {
withCredentials([string(credentialsId: 'robot-black-duck-scan', variable: 'TOKEN')]) { # 用 withCredentials 来获得 Token
synopsys_detect 'bash <(curl -s ${hub_detect}) --blackduck.url=${blackduck_url} --blackduck.username=${blackduck_user} --blackduck.api.token=${TOKEN} --detect.project.name=${detect_project} --detect.project.version.name=${detect_project_version} --detect.source.path=${detect_source_path} --logging.level.com.synopsys.integration=debug --blackduck.trust.cert=TRUE --detect.blackduck.signature.scanner.snippet.matching=${SNIPPET-MODES}'
}
}
}
}
post {
# 不论结果任何都给执行者发送邮件通知
always {
script {
def email = load "vars/email.groovy"
wrap([$class: 'BuildUser']) {
def user = env.BUILD_USER_ID
email.build(currentBuild.result, "${user}")
}
}
}
success {
echo "success, cleanup blackduck workspace"
cleanWs()
}
}
}