如何用jq按模式提取子串并重构CycloneDX格式依赖数据?
适配Debian与Alpine的CycloneDX组件purl重构及字段更新jq命令
需要处理Syft工具生成的Debian(bullseye-slim)和Alpine Docker镜像的CycloneDX格式JSON数据,实现以下转换逻辑:
- 从组件purl的
upstream参数提取源名称(srcName)和源版本(srcVersion):- 若
upstream值包含@,拆分@前后内容分别作为srcName、srcVersion(需解码URL编码的%40为@) - 若
upstream值不含@,则srcName为upstream值,srcVersion沿用组件原version字段
- 若
- 提取到有效srcName时:
- Debian组件更新
name为srcName、version为srcVersion;Alpine组件保留原name和version - 重构purl:保留
pkg:开头的包管理器前缀(如pkg:deb/debian/、pkg:apk/alpine/),替换为srcName@srcVersion?arch=source
- Debian组件更新
- 未提取到upstream参数的组件,保持原数据不变
原始输入JSON
{ "bomFormat": "CycloneDX", "components": [{ "name": "bsdutils", "version": "1:2.36.1-8+deb11u1", "purl": "pkg:deb/debian/bsdutils@1:2.36.1-8+deb11u1?arch=amd64&upstream=util-linux%402.36.1-8+deb11u1&distro=debian-11", "properties": [{ "name": "syft:metadata:source", "value": "util-linux" }, { "name": "syft:metadata:sourceVersion", "value": "2.36.1-8+deb11u1" } ] }, { "name": "curl", "version": "7.74.0-1.3+deb11u7", "purl": "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11" }, { "name": "libaudit-common", "version": "1:3.0-2", "purl": "pkg:deb/debian/libaudit-common@1:3.0-2?arch=all&upstream=audit&distro=debian-11", "properties": [{ "name": "syft:metadata:source", "value": "audit" } ] }, { "name": "busybox-binsh", "version": "1.35.0-r29", "purl": "pkg:apk/alpine/busybox-binsh@1.35.0-r29?arch=x86_64&upstream=busybox&distro=alpine-3.17.2", "properties": [{ "name": "syft:metadata:originPackage", "value": "busybox" } ] } ] }
目标输出JSON
{ "bomFormat": "CycloneDX", "components": [{ "name": "util-linux", "version": "2.36.1-8+deb11u1", "purl": "pkg:deb/debian/util-linux@2.36.1-8+deb11u1?arch=source", "properties": [{ "name": "syft:metadata:source", "value": "util-linux" }, { "name": "syft:metadata:sourceVersion", "value": "2.36.1-8+deb11u1" } ] }, { "name": "curl", "version": "7.74.0-1.3+deb11u7", "purl": "pkg:deb/debian/curl@7.74.0-1.3+deb11u7?arch=amd64&distro=debian-11" }, { "name": "audit", "version": "1:3.0-2", "purl": "pkg:deb/debian/audit@1:3.0-2?arch=source", "properties": [{ "name": "syft:metadata:source", "value": "audit" } ] }, { "name": "busybox-binsh", "version": "1.35.0-r29", "purl": "pkg:apk/alpine/busybox@1.35.0-r29?arch=source", "properties": [{ "name": "syft:metadata:originPackage", "value": "busybox" } ] } ] }
通用jq命令
.components[] |= ( # 提取并解码purl中的upstream参数 (.purl | capture("\\?.*upstream=(?<upstream>[^&]+)") | .upstream | @uri) as $upstream # 拆分upstream为源名称和版本 | ($upstream | split("@") as $parts | { srcName: $parts[0], srcVersion: if $parts | length > 1 then $parts[1] else .version end }) as $srcInfo # 提取purl的包管理器前缀(如pkg:deb/debian/) | (.purl | capture("(?<prefix>pkg:[^/]+/[^/]+/)")) as $prefix # 生成新的purl | let newPurl = "\($prefix.prefix)\($srcInfo.srcName)@\($srcInfo.srcVersion)?arch=source" # 按组件类型更新字段 | if .purl | startswith("pkg:deb/") then .name = $srcInfo.srcName | .version = $srcInfo.srcVersion | .purl = newPurl elif .purl | startswith("pkg:apk/") then .purl = newPurl else . end // . # 无upstream参数时返回原组件 )
命令逻辑说明
- 解析upstream参数:通过正则捕获purl中
upstream=后的内容,用@uri解码URL编码字符 - 拆分源信息:将upstream按
@拆分,自动处理带版本和不带版本的两种情况 - 提取前缀:保留purl的包管理器标识部分,确保重构后的purl格式合规
- 差异化更新:针对Debian和Alpine组件设置不同的字段更新规则,无upstream参数的组件保持原样
内容的提问来源于stack exchange,提问作者EricBDev
相关产品推荐
相关产品推荐

