ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

pnpm sbom SPDX 时间戳修复:`creationInfo.created` 整秒化改造与严格合规实践

pnpm sbom SPDX 时间戳修复:`creationInfo.created` 整秒化改造与严格合规实践 pnpm sbom SPDX 时间戳修复creationInfo.created整秒化改造与严格合规实践【免费下载链接】pnpmFast, disk space efficient package manager项目地址: https://gitcode.com/gh_mirrors/pn/pnpm导读本文围绕 pnpm 仓库中一项针对软件物料清单SBOM生成的合规性修复展开pnpm sbom --sbom-format spdx此前在文档头写入携带小数秒的时间戳如2026-09-08T10:38:21.123Z被严格遵循 SPDX 规范的消费方拒绝修复后统一输出整秒 UTC 时间如2026-09-08T10:38:21Z。读完本文你将掌握 pnpm 的 SPDX SBOM 生成架构、creationInfo时间戳的格式约束与校验方式以及如何在 Rust 与 TypeScript 两套实现中落地这一修复并配套测试。变更背景为什么小数秒时间戳会被拒绝本次变更记录在 .changeset/sbom-spdx-timestamp-seconds.md 中其核心内容是pnpm sbom --sbom-format spdxnow writescreationInfo.createdwith whole seconds, such as2026-09-08T10:38:21Z. The timestamp carried fractional seconds, which strict SPDX consumers rejected.要点拆解命令pnpm sbom --sbom-format spdx即要求生成 SPDX 格式的 SBOM 文档字段SPDX 文档头document header中的creationInfo.created用于记录文档的创建时间问题旧实现产出的时间戳带有小数秒fractional seconds例如2026-09-08T10:38:21.123Z后果部分严格校验的 SPDX 消费方strict SPDX consumers会直接拒绝这类文档。需要说明的是SPDX 2.3 规范对created字段要求的时间格式是整秒的 UTC 时间戳YYYY-MM-DDThh:mm:ssZ不允许出现小数秒部分。这就是“严格消费方拒绝”的根源——小数秒超出了规范允许的字符模式。SPDX 序列化实现整秒时间戳的落地位置pnpm 主仓库的 SBOM 生成逻辑位于 Rust 实现中pnpm目录下的 cratesSPDX 序列化代码在 pnpm/crates/cli/src/cli_args/sbom/spdx.rs。其中生成creationInfo.created的关键一行spdx.rs 第 35 行let timestamp chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true);参数含义chrono::Utc::now()获取当前 UTC 时刻to_rfc3339_opts按 RFC 3339 格式输出时间戳chrono::SecondsFormat::Secs关键参数——将精度限制到秒丢弃小数秒部分第二个参数true使用Z作为 UTC 时区标记而非00:00偏移写法。随后该时间戳被写入 SPDX 文档 JSON 的creationInfo对象spdx.rs 第 44-47 行creationInfo: { created: timestamp, creators: [Tool: pnpm], },由 spdx.rs 的serialize_spdx函数整体负责把内存中的 SBOM 结果SbomResult序列化为 SPDX 2.3 JSON 文档文档以spdxVersion: SPDX-2.3、dataLicense: CC0-1.0开头包含根包SPDXRef-RootPackage与每个依赖包SPDXRef-Package-name-version-index并生成去重后的DEPENDS_ON关系边。从源码结构可以推断在修复前这里使用的是允许小数秒的格式例如to_rfc3339_opts(chrono::SecondsFormat::AutoSi, ...)或直接to_rfc3339()修复后切换为Secs从而在序列化源头就保证了整秒输出。测试验证用形状断言锁定时间戳格式仓库为本次修复配套了专门的测试位于 pnpm/crates/cli/tests/suite/sbom/metadata.rs 的sbom_spdx_creation_info_whole_seconds测试metadata.rs 第 148-161 行#[test] fn sbom_spdx_creation_info_whole_seconds() { let tmp copy_fixture(simple-sbom); let parsed run_sbom_json(tmp.path(), spdx, []); let created parsed[creationInfo][created].as_str().expect(created string); let shape: String created .chars() .map(|character| if character.is_ascii_digit() { d } else { character }) .collect(); assert_eq!( shape, dddd-dd-ddTdd:dd:ddZ, SPDX 2.3 (6.9) requires whole-second UTC timestamps (YYYY-MM-DDThh:mm:ssZ), got {created}, ); }这个测试非常巧妙地使用了形状断言shape assertion由于created是运行时时间戳无法断言具体数值于是把所有 ASCII 数字替换为d再把其余字符原样保留最终必须精确匹配dddd-dd-ddTdd:dd:ddZ这一形状。也就是说年份必须是 4 位数字、月日时分秒均为 2 位数字分隔符必须是-、T、:末尾必须是Z小数秒部分. 额外数字一旦存在形状立刻变成dddd-dd-ddTdd:dd:dd.dddZ断言即失败。测试消息中的 SPDX 2.3 (6.9) 指向 SPDX 2.3 规范的 6.9 节该节规定了created字段的合法时间格式。同文件中的另一个测试sbom_spdx_creation_infometadata.rs 第 132-146 行则确认creationInfo存在、created为字符串且creators中包含pnpm工具标识。从 TS 移植到 Rust新旧实现对照值得关注的是Rust 实现是 pnpm 从 TypeScript 代码库移植而来的。在 pnpm/crates/cli/src/cli_args/sbom.rs 的模块注释中明确写着pacquet sbom— generate a Software Bill of Materials. Ports pnpmssbomcommand (pnpm11/deps/compliance/commands/src/sbom/sbom.ts).TypeScript 侧的 SPDX 序列化在 pnpm11/deps/compliance/sbom/src/serializeSpdx.ts其creationInfo生成方式serializeSpdx.ts 第 142-147 行creationInfo: { created: ${new Date().toISOString().split(.)[0]}Z, creators: [ Tool: pnpm, ], },这里用toISOString()得到YYYY-MM-DDTHH:mm:ss.sssZ后以.切分丢弃毫秒部分再补回Z得到YYYY-MM-DDTHH:mm:ssZ。这与 Rust 侧SecondsFormat::Secs实现的是同一语义去除小数秒、强制整秒 UTC 输出。TS 侧的对应测试位于 pnpm11/deps/compliance/sbom/test/serializeSpdx.test.ts其中第 56 行断言expect(parsed.creationInfo.created).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/)正则^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$与 Rust 侧的形状断言完全等价两套实现用各自语言的原生方式锁定了同一格式规范。深入pnpm sbom参数体系与整体流程要理解这次修复所处的上下文有必要了解pnpm sbom命令的整体设计。命令入口与参数定义位于 pnpm/crates/cli/src/cli_args/sbom.rs通过 clap 定义了两组参数文档级参数--sbom-*参数说明默认值--sbom-format输出格式spdx或cyclonedx必填无--sbom-type根包组件类型library或applicationlibrary--sbom-spec-versionCycloneDX 规范版本1.5/1.6/1.7仅对 cyclonedx 有效1.7--sbom-authorsSBOM 作者逗号分隔写入 CycloneDXmetadata.authors无--sbom-supplierSBOM 供应商名CycloneDXmetadata.supplier无其中--sbom-format是必填的如果省略命令直接失败对应测试 sbom.rs 的sbom_missing_format_fails。依赖范围参数参数说明--prod/-P只包含生产依赖--dev/-D只包含开发依赖--no-optional/--optional排除 / 包含可选依赖互相覆盖--exclude-peers排除 peer 依赖及其子树另有--lockfile-only只读锁文件、跳过 store、--out写入文件而非 stdout支持%s与%v占位符、--split为每个匹配的 workspace 包单独生成一份 SBOM等行为参数。命令的完整执行流程sbom.rs 的run方法大致为加载并校验锁文件通过select_importer_ids选出要描述的项目支持--filter选择器根据--split/--out模板决定是合并生成单份 SBOM还是逐项目拆分调用collect_components从锁文件可选地从 store 补充元数据收集组件与依赖关系得到SbomResult按--sbom-format分派到 CycloneDXserialize_cyclonedx或 SPDXserialize_spdx序列化器输出到 stdout 或模板化文件路径。本次修复影响的就是第 5 步中 SPDX 序列化器对creationInfo.created的格式化。SPDX 文档结构速览从 spdx.rs 可以看出pnpm 产出的 SPDX 2.3 文档包含顶层字段spdxVersionSPDX-2.3、dataLicenseCC0-1.0、SPDXIDSPDXRef-DOCUMENT、name根包名、documentNamespace基于根包名、版本与 UUID 生成、creationInfo、packages、relationshipspackages根包SPDXRef-RootPackage与每个依赖组件组件 ID 为SPDXRef-Package-sanitized-name-sanitized-version-index包含purl外部引用、许可字段缺失时为NOASSERTION以及可选的文件校验和由 SRI integrity 字符串转换而来支持 SHA1/SHA256/SHA384/SHA512/MD5relationshipsDESCRIBES与去重后的DEPENDS_ON边。同系列修复SBOM 生成的其他合规性改进.changeset目录下还有多个与本主题同属 SBOM 生成合规性的变更记录可以作为同一功能演进脉络的参考.changeset/sbom-blank-authors.md当 manifest 的 author 字段为空或全空白时pnpm sbom不再输出作者字段.changeset/sbom-exclude-uninstallable-platform-packages.md不再为当前平台无法安装的可选平台相关依赖如其他操作系统的rolldown/binding-*原生变体输出组件因为这类包无法解析出许可证信息--lockfile-only仍描述完整锁文件图平台无关.changeset/sbom-missing-importers.md当锁文件缺少所选项目的条目时以ERR_PNPM_SBOM_MISSING_IMPORTERS明确失败替代原先“静默漏报依赖”或直接崩溃的行为。实操建议如何在项目中使用与验证要在自己的项目中复现并验证本次修复可以在包含package.json与pnpm-lock.yaml的项目目录执行pnpm sbom --sbom-format spdx --lockfile-only检查输出 JSON 的creationInfo.created字段应形如2026-09-08T10:38:21Z——即YYYY-MM-DDTHH:mm:ssZ的整秒 UTC 格式不含小数秒用严格遵循 SPDX 的校验工具如 SPDX 官方工具链或 CI 中的 SBOM 校验步骤验证文档可被接受在 CI 流水线中加入正则断言如/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/防止回归。小结本次变更是一个典型的“小改动、强约束”合规修复通过把 SPDXcreationInfo.created的时间戳精度从秒小数秒收敛为整秒 UTCpnpm 使生成物符合 SPDX 2.3 规范对时间字段的要求从而被严格消费方接受。仓库同时在 Rustspdx.rs与 TypeScriptserializeSpdx.ts两条实现线上落地并分别以形状断言metadata.rs与正则断言serializeSpdx.test.ts锁定了输出格式为后续 SBOM 相关改动提供了可回归的基线。【免费下载链接】pnpmFast, disk space efficient package manager项目地址: https://gitcode.com/gh_mirrors/pn/pnpm创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表