@intentius/chant 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/audit/catalog.d.ts +45 -5
- package/dist/audit/catalog.d.ts.map +1 -1
- package/dist/audit/fetch.d.ts.map +1 -1
- package/dist/audit/proof.d.ts +3 -0
- package/dist/audit/proof.d.ts.map +1 -1
- package/dist/audit/report-model.d.ts +4 -1
- package/dist/audit/report-model.d.ts.map +1 -1
- package/dist/audit/report.d.ts +3 -0
- package/dist/audit/report.d.ts.map +1 -1
- package/dist/audit/rules-doc.d.ts +8 -2
- package/dist/audit/rules-doc.d.ts.map +1 -1
- package/dist/cli/commands/audit.d.ts.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/lexicon-rolling-upgrade.d.ts +37 -0
- package/dist/cli/commands/lexicon-rolling-upgrade.d.ts.map +1 -0
- package/dist/cli/commands/lexicon-surface-diff.d.ts +48 -0
- package/dist/cli/commands/lexicon-surface-diff.d.ts.map +1 -0
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/pinned-upgrade.d.ts +41 -0
- package/dist/cli/commands/pinned-upgrade.d.ts.map +1 -0
- package/dist/cli/handlers/build.d.ts.map +1 -1
- package/dist/cli/handlers/components.d.ts +39 -0
- package/dist/cli/handlers/components.d.ts.map +1 -0
- package/dist/cli/handlers/dev.d.ts +3 -0
- package/dist/cli/handlers/dev.d.ts.map +1 -1
- package/dist/cli/handlers/graph.d.ts +4 -2
- package/dist/cli/handlers/graph.d.ts.map +1 -1
- package/dist/cli/handlers/misc.d.ts.map +1 -1
- package/dist/cli/handlers/run.d.ts +49 -0
- package/dist/cli/handlers/run.d.ts.map +1 -1
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/registry.d.ts +35 -0
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/codegen/fetch.d.ts +22 -0
- package/dist/codegen/fetch.d.ts.map +1 -1
- package/dist/codegen/lexicon-regen.d.ts +110 -0
- package/dist/codegen/lexicon-regen.d.ts.map +1 -0
- package/dist/codegen/pinned-upgrade.d.ts +153 -0
- package/dist/codegen/pinned-upgrade.d.ts.map +1 -0
- package/dist/codegen/rolling-upgrade.d.ts +138 -0
- package/dist/codegen/rolling-upgrade.d.ts.map +1 -0
- package/dist/codegen/surface-snapshot.d.ts +173 -0
- package/dist/codegen/surface-snapshot.d.ts.map +1 -0
- package/dist/codegen/version-bump.d.ts +21 -0
- package/dist/codegen/version-bump.d.ts.map +1 -0
- package/dist/components/auto-release.d.ts +93 -0
- package/dist/components/auto-release.d.ts.map +1 -0
- package/dist/components/builders.d.ts +37 -0
- package/dist/components/builders.d.ts.map +1 -0
- package/dist/components/capability-plugin-loader.d.ts +111 -0
- package/dist/components/capability-plugin-loader.d.ts.map +1 -0
- package/dist/components/capability-plugin-schema.d.ts +24 -0
- package/dist/components/capability-plugin-schema.d.ts.map +1 -0
- package/dist/components/capability-plugin.d.ts +85 -0
- package/dist/components/capability-plugin.d.ts.map +1 -0
- package/dist/components/capability.d.ts +89 -0
- package/dist/components/capability.d.ts.map +1 -0
- package/dist/components/cli-support.d.ts +204 -0
- package/dist/components/cli-support.d.ts.map +1 -0
- package/dist/components/component.d.ts +179 -0
- package/dist/components/component.d.ts.map +1 -0
- package/dist/components/config-defaults.d.ts +41 -0
- package/dist/components/config-defaults.d.ts.map +1 -0
- package/dist/components/discover.d.ts +60 -0
- package/dist/components/discover.d.ts.map +1 -0
- package/dist/components/driver-output.d.ts +20 -0
- package/dist/components/driver-output.d.ts.map +1 -0
- package/dist/components/driver.d.ts +209 -0
- package/dist/components/driver.d.ts.map +1 -0
- package/dist/components/index.d.ts +22 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/manifest-persistence.d.ts +79 -0
- package/dist/components/manifest-persistence.d.ts.map +1 -0
- package/dist/components/pilots/alb-ecs.pilot.d.ts +27 -0
- package/dist/components/pilots/alb-ecs.pilot.d.ts.map +1 -0
- package/dist/components/pilots/dynamodb.pilot.d.ts +22 -0
- package/dist/components/pilots/dynamodb.pilot.d.ts.map +1 -0
- package/dist/components/pilots/jar-emr.pilot.d.ts +37 -0
- package/dist/components/pilots/jar-emr.pilot.d.ts.map +1 -0
- package/dist/components/pilots/lambda.pilot.d.ts +35 -0
- package/dist/components/pilots/lambda.pilot.d.ts.map +1 -0
- package/dist/components/pilots/neo4j-fanout.pilot.d.ts +17 -0
- package/dist/components/pilots/neo4j-fanout.pilot.d.ts.map +1 -0
- package/dist/components/pilots/project.d.ts +13 -0
- package/dist/components/pilots/project.d.ts.map +1 -0
- package/dist/components/presets/ecs-fargate.d.ts +59 -0
- package/dist/components/presets/ecs-fargate.d.ts.map +1 -0
- package/dist/components/presets/index.d.ts +29 -0
- package/dist/components/presets/index.d.ts.map +1 -0
- package/dist/components/presets/lambda.d.ts +35 -0
- package/dist/components/presets/lambda.d.ts.map +1 -0
- package/dist/components/presets/single-host-compose.d.ts +43 -0
- package/dist/components/presets/single-host-compose.d.ts.map +1 -0
- package/dist/components/registry.d.ts +29 -0
- package/dist/components/registry.d.ts.map +1 -0
- package/dist/components/starter-plugin.d.ts +51 -0
- package/dist/components/starter-plugin.d.ts.map +1 -0
- package/dist/components/temporal-codegen-loader.d.ts +27 -0
- package/dist/components/temporal-codegen-loader.d.ts.map +1 -0
- package/dist/components/verbs/__tests__/mock-cloud-executor.d.ts +35 -0
- package/dist/components/verbs/__tests__/mock-cloud-executor.d.ts.map +1 -0
- package/dist/components/verbs/__tests__/mock-process-runner.d.ts +45 -0
- package/dist/components/verbs/__tests__/mock-process-runner.d.ts.map +1 -0
- package/dist/components/verbs/__tests__/mock-sbom-generator.d.ts +33 -0
- package/dist/components/verbs/__tests__/mock-sbom-generator.d.ts.map +1 -0
- package/dist/components/verbs/bom-writer.d.ts +155 -0
- package/dist/components/verbs/bom-writer.d.ts.map +1 -0
- package/dist/components/verbs/build-archive.d.ts +205 -0
- package/dist/components/verbs/build-archive.d.ts.map +1 -0
- package/dist/components/verbs/build.d.ts +150 -0
- package/dist/components/verbs/build.d.ts.map +1 -0
- package/dist/components/verbs/cloud-executor.d.ts +88 -0
- package/dist/components/verbs/cloud-executor.d.ts.map +1 -0
- package/dist/components/verbs/component-bom.d.ts +103 -0
- package/dist/components/verbs/component-bom.d.ts.map +1 -0
- package/dist/components/verbs/index.d.ts +49 -0
- package/dist/components/verbs/index.d.ts.map +1 -0
- package/dist/components/verbs/license-policy.d.ts +56 -0
- package/dist/components/verbs/license-policy.d.ts.map +1 -0
- package/dist/components/verbs/lockfile-sbom-generator.d.ts +81 -0
- package/dist/components/verbs/lockfile-sbom-generator.d.ts.map +1 -0
- package/dist/components/verbs/process-runner.d.ts +64 -0
- package/dist/components/verbs/process-runner.d.ts.map +1 -0
- package/dist/components/verbs/reproducibility.d.ts +80 -0
- package/dist/components/verbs/reproducibility.d.ts.map +1 -0
- package/dist/components/verbs/sbom-generator.d.ts +157 -0
- package/dist/components/verbs/sbom-generator.d.ts.map +1 -0
- package/dist/components/verbs/sbom.d.ts +79 -0
- package/dist/components/verbs/sbom.d.ts.map +1 -0
- package/dist/components/verbs/shell.d.ts +35 -0
- package/dist/components/verbs/shell.d.ts.map +1 -0
- package/dist/components/verbs/sign.d.ts +222 -0
- package/dist/components/verbs/sign.d.ts.map +1 -0
- package/dist/components/verbs/stub.d.ts +18 -0
- package/dist/components/verbs/stub.d.ts.map +1 -0
- package/dist/components/verbs/tool-sbom-generator.d.ts +51 -0
- package/dist/components/verbs/tool-sbom-generator.d.ts.map +1 -0
- package/dist/components/verbs/verify.d.ts +106 -0
- package/dist/components/verbs/verify.d.ts.map +1 -0
- package/dist/components/verbs/vex.d.ts +65 -0
- package/dist/components/verbs/vex.d.ts.map +1 -0
- package/dist/components/verbs/vuln-gate.d.ts +91 -0
- package/dist/components/verbs/vuln-gate.d.ts.map +1 -0
- package/dist/components/verbs/vuln-scan.d.ts +116 -0
- package/dist/components/verbs/vuln-scan.d.ts.map +1 -0
- package/dist/components/verbs/wait-verify.d.ts +85 -0
- package/dist/components/verbs/wait-verify.d.ts.map +1 -0
- package/dist/config.d.ts +205 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/lexicon.d.ts +78 -0
- package/dist/lexicon.d.ts.map +1 -1
- package/dist/lifecycle/build-ledger-store.d.ts +121 -0
- package/dist/lifecycle/build-ledger-store.d.ts.map +1 -0
- package/dist/lifecycle/build-ledger.d.ts +225 -0
- package/dist/lifecycle/build-ledger.d.ts.map +1 -0
- package/dist/lifecycle/git.d.ts +84 -0
- package/dist/lifecycle/git.d.ts.map +1 -1
- package/dist/lifecycle/index.d.ts +5 -0
- package/dist/lifecycle/index.d.ts.map +1 -1
- package/dist/lifecycle/oras-referrer-lookup.d.ts +41 -0
- package/dist/lifecycle/oras-referrer-lookup.d.ts.map +1 -0
- package/dist/lifecycle/release-ledger.d.ts +122 -0
- package/dist/lifecycle/release-ledger.d.ts.map +1 -0
- package/dist/lifecycle/status.d.ts +150 -0
- package/dist/lifecycle/status.d.ts.map +1 -0
- package/dist/lint/component-checks.d.ts +95 -0
- package/dist/lint/component-checks.d.ts.map +1 -0
- package/dist/lint/engine.d.ts +23 -0
- package/dist/lint/engine.d.ts.map +1 -1
- package/dist/lint/presets/strict.json +8 -1
- package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.d.ts +10 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.d.ts +3 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.d.ts +3 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.d.ts.map +1 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.d.ts +4 -0
- package/dist/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp001-publishes-never-applies.d.ts +20 -0
- package/dist/lint/rules/comp/comp001-publishes-never-applies.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp002-dangling-wiring-ref.d.ts +28 -0
- package/dist/lint/rules/comp/comp002-dangling-wiring-ref.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts +39 -0
- package/dist/lint/rules/comp/comp003-mutating-no-rollback.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts +37 -0
- package/dist/lint/rules/comp/comp004-gate-needs-temporal.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp005-capability-kind-is-noun.d.ts +43 -0
- package/dist/lint/rules/comp/comp005-capability-kind-is-noun.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp006-shell-needs-reason.d.ts +22 -0
- package/dist/lint/rules/comp/comp006-shell-needs-reason.d.ts.map +1 -0
- package/dist/lint/rules/comp/comp007-composition-sprawl.d.ts +33 -0
- package/dist/lint/rules/comp/comp007-composition-sprawl.d.ts.map +1 -0
- package/dist/lint/rules/comp/index.d.ts +18 -0
- package/dist/lint/rules/comp/index.d.ts.map +1 -0
- package/dist/lint/rules/comp/support.d.ts +71 -0
- package/dist/lint/rules/comp/support.d.ts.map +1 -0
- package/dist/lsp/lexicon-providers.d.ts.map +1 -1
- package/dist/op/activity-registry.d.ts +11 -6
- package/dist/op/activity-registry.d.ts.map +1 -1
- package/dist/op/activity-runtime.d.ts +24 -0
- package/dist/op/activity-runtime.d.ts.map +1 -0
- package/dist/op/builders.d.ts +56 -0
- package/dist/op/builders.d.ts.map +1 -1
- package/dist/op/index.d.ts +2 -1
- package/dist/op/index.d.ts.map +1 -1
- package/dist/ownership.d.ts +20 -14
- package/dist/ownership.d.ts.map +1 -1
- package/package.json +14 -1
- package/src/audit/catalog.test.ts +74 -22
- package/src/audit/catalog.ts +74 -471
- package/src/audit/fetch.test.ts +112 -8
- package/src/audit/fetch.ts +53 -8
- package/src/audit/proof.ts +4 -2
- package/src/audit/report-html.test.ts +14 -7
- package/src/audit/report-model.ts +20 -9
- package/src/audit/report.test.ts +36 -13
- package/src/audit/report.ts +2 -0
- package/src/audit/rules-doc.test.ts +8 -6
- package/src/audit/rules-doc.ts +13 -5
- package/src/cli/commands/audit.ts +23 -18
- package/src/cli/commands/component-lint.test.ts +158 -0
- package/src/cli/commands/doctor.test.ts +116 -2
- package/src/cli/commands/doctor.ts +46 -0
- package/src/cli/commands/lexicon-rolling-upgrade.ts +198 -0
- package/src/cli/commands/lexicon-surface-diff.test.ts +104 -0
- package/src/cli/commands/lexicon-surface-diff.ts +185 -0
- package/src/cli/commands/lint.ts +142 -1
- package/src/cli/commands/pinned-upgrade.ts +204 -0
- package/src/cli/handlers/build.ts +58 -1
- package/src/cli/handlers/components.test.ts +504 -0
- package/src/cli/handlers/components.ts +404 -0
- package/src/cli/handlers/dev.ts +60 -1
- package/src/cli/handlers/graph.ts +47 -2
- package/src/cli/handlers/misc.ts +45 -1
- package/src/cli/handlers/run.test.ts +967 -3
- package/src/cli/handlers/run.ts +618 -11
- package/src/cli/main.test.ts +66 -0
- package/src/cli/main.ts +105 -2
- package/src/cli/registry.ts +35 -0
- package/src/codegen/fetch.test.ts +84 -3
- package/src/codegen/fetch.ts +76 -2
- package/src/codegen/lexicon-regen.test.ts +298 -0
- package/src/codegen/lexicon-regen.ts +545 -0
- package/src/codegen/pinned-upgrade.test.ts +399 -0
- package/src/codegen/pinned-upgrade.ts +423 -0
- package/src/codegen/rolling-upgrade.test.ts +333 -0
- package/src/codegen/rolling-upgrade.ts +320 -0
- package/src/codegen/surface-snapshot.test.ts +461 -0
- package/src/codegen/surface-snapshot.ts +546 -0
- package/src/codegen/version-bump.test.ts +47 -0
- package/src/codegen/version-bump.ts +39 -0
- package/src/components/README.md +130 -0
- package/src/components/SPRAWL-VALIDATION.md +181 -0
- package/src/components/__fixtures__/alb-ecs-service.json +51 -0
- package/src/components/__fixtures__/dynamodb-infra.json +32 -0
- package/src/components/__fixtures__/emr-job-consumer.json +26 -0
- package/src/components/__fixtures__/jar-lib-producer.json +16 -0
- package/src/components/__fixtures__/lambda-image-processor.json +28 -0
- package/src/components/__fixtures__/neo4j-fanout.json +34 -0
- package/src/components/__fixtures__/single-host-compose.json +29 -0
- package/src/components/auto-release.test.ts +217 -0
- package/src/components/auto-release.ts +216 -0
- package/src/components/builders.test.ts +45 -0
- package/src/components/builders.ts +71 -0
- package/src/components/capability-plugin-loader.ts +218 -0
- package/src/components/capability-plugin-schema.ts +56 -0
- package/src/components/capability-plugin.test.ts +235 -0
- package/src/components/capability-plugin.ts +108 -0
- package/src/components/capability.ts +117 -0
- package/src/components/cli-support.test.ts +579 -0
- package/src/components/cli-support.ts +481 -0
- package/src/components/component-schema.test.ts +265 -0
- package/src/components/component.schema.json +235 -0
- package/src/components/component.test.ts +251 -0
- package/src/components/component.ts +255 -0
- package/src/components/config-defaults.test.ts +263 -0
- package/src/components/config-defaults.ts +145 -0
- package/src/components/discover.test.ts +352 -0
- package/src/components/discover.ts +167 -0
- package/src/components/driver-output.ts +71 -0
- package/src/components/driver.test.ts +638 -0
- package/src/components/driver.ts +690 -0
- package/src/components/index.ts +113 -0
- package/src/components/manifest-persistence.test.ts +152 -0
- package/src/components/manifest-persistence.ts +164 -0
- package/src/components/pilots/README.md +33 -0
- package/src/components/pilots/alb-ecs.pilot.ts +63 -0
- package/src/components/pilots/dynamodb.pilot.ts +59 -0
- package/src/components/pilots/jar-emr.pilot.ts +69 -0
- package/src/components/pilots/lambda.pilot.ts +55 -0
- package/src/components/pilots/neo4j-fanout.pilot.ts +92 -0
- package/src/components/pilots/pilots-lint-clean.test.ts +78 -0
- package/src/components/pilots/pilots.test.ts +126 -0
- package/src/components/pilots/project.ts +13 -0
- package/src/components/presets/comp007-reduction.test.ts +122 -0
- package/src/components/presets/ecs-fargate.test.ts +84 -0
- package/src/components/presets/ecs-fargate.ts +103 -0
- package/src/components/presets/index.ts +32 -0
- package/src/components/presets/lambda.test.ts +35 -0
- package/src/components/presets/lambda.ts +53 -0
- package/src/components/presets/single-host-compose.test.ts +65 -0
- package/src/components/presets/single-host-compose.ts +64 -0
- package/src/components/registry.test.ts +112 -0
- package/src/components/registry.ts +37 -0
- package/src/components/starter-plugin.ts +105 -0
- package/src/components/temporal-codegen-loader.ts +41 -0
- package/src/components/verbs/__fixtures__/package-lock.json +45 -0
- package/src/components/verbs/__fixtures__/pom.xml +37 -0
- package/src/components/verbs/__fixtures__/schemas/cyclonedx-1.5.schema.json +3800 -0
- package/src/components/verbs/__fixtures__/schemas/cyclonedx-spdx-license.schema.json +621 -0
- package/src/components/verbs/__fixtures__/schemas/jsf-0.82.schema.json +240 -0
- package/src/components/verbs/__fixtures__/schemas/spdx-2.3.schema.json +740 -0
- package/src/components/verbs/__tests__/mock-cloud-executor.ts +102 -0
- package/src/components/verbs/__tests__/mock-process-runner.ts +79 -0
- package/src/components/verbs/__tests__/mock-sbom-generator.ts +85 -0
- package/src/components/verbs/bom-writer.test.ts +331 -0
- package/src/components/verbs/bom-writer.ts +386 -0
- package/src/components/verbs/build-archive.test.ts +251 -0
- package/src/components/verbs/build-archive.ts +296 -0
- package/src/components/verbs/build.test.ts +222 -0
- package/src/components/verbs/build.ts +252 -0
- package/src/components/verbs/cloud-executor.ts +181 -0
- package/src/components/verbs/component-bom.test.ts +219 -0
- package/src/components/verbs/component-bom.ts +189 -0
- package/src/components/verbs/index.ts +49 -0
- package/src/components/verbs/license-policy.ts +115 -0
- package/src/components/verbs/lockfile-sbom-generator.test.ts +195 -0
- package/src/components/verbs/lockfile-sbom-generator.ts +236 -0
- package/src/components/verbs/process-runner.test.ts +79 -0
- package/src/components/verbs/process-runner.ts +113 -0
- package/src/components/verbs/reproducibility.test.ts +24 -0
- package/src/components/verbs/reproducibility.ts +100 -0
- package/src/components/verbs/sbom-generator.test.ts +96 -0
- package/src/components/verbs/sbom-generator.ts +209 -0
- package/src/components/verbs/sbom.test.ts +184 -0
- package/src/components/verbs/sbom.ts +152 -0
- package/src/components/verbs/shell.test.ts +36 -0
- package/src/components/verbs/shell.ts +51 -0
- package/src/components/verbs/sign.test.ts +210 -0
- package/src/components/verbs/sign.ts +350 -0
- package/src/components/verbs/stub.ts +34 -0
- package/src/components/verbs/tool-sbom-generator.test.ts +194 -0
- package/src/components/verbs/tool-sbom-generator.ts +192 -0
- package/src/components/verbs/verify.test.ts +159 -0
- package/src/components/verbs/verify.ts +179 -0
- package/src/components/verbs/vex.ts +182 -0
- package/src/components/verbs/vuln-gate.test.ts +201 -0
- package/src/components/verbs/vuln-gate.ts +170 -0
- package/src/components/verbs/vuln-scan.test.ts +145 -0
- package/src/components/verbs/vuln-scan.ts +266 -0
- package/src/components/verbs/wait-verify.test.ts +111 -0
- package/src/components/verbs/wait-verify.ts +171 -0
- package/src/config.test.ts +74 -1
- package/src/config.ts +219 -0
- package/src/index.ts +32 -0
- package/src/lexicon.ts +88 -0
- package/src/lifecycle/build-ledger-store.test.ts +217 -0
- package/src/lifecycle/build-ledger-store.ts +165 -0
- package/src/lifecycle/build-ledger.test.ts +349 -0
- package/src/lifecycle/build-ledger.ts +305 -0
- package/src/lifecycle/git.test.ts +118 -0
- package/src/lifecycle/git.ts +149 -14
- package/src/lifecycle/index.ts +5 -0
- package/src/lifecycle/oras-referrer-lookup.test.ts +116 -0
- package/src/lifecycle/oras-referrer-lookup.ts +110 -0
- package/src/lifecycle/release-ledger.test.ts +209 -0
- package/src/lifecycle/release-ledger.ts +185 -0
- package/src/lifecycle/status.test.ts +330 -0
- package/src/lifecycle/status.ts +278 -0
- package/src/lint/component-checks.ts +148 -0
- package/src/lint/config.test.ts +21 -0
- package/src/lint/engine.ts +11 -2
- package/src/lint/presets/relaxed.json +8 -1
- package/src/lint/presets/strict.json +8 -1
- package/src/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-a.component.ts +9 -0
- package/src/lint/rules/__fixtures__/comp/comp000-discovery-error/broken-b.component.ts +9 -0
- package/src/lint/rules/__fixtures__/comp/comp001/fail/orphan-lib.component.ts +11 -0
- package/src/lint/rules/__fixtures__/comp/comp001/pass/emr-job.component.ts +13 -0
- package/src/lint/rules/__fixtures__/comp/comp001/pass/jar-lib.component.ts +11 -0
- package/src/lint/rules/__fixtures__/comp/comp002/fail/search-service.component.ts +27 -0
- package/src/lint/rules/__fixtures__/comp/comp002/pass/jar-lib.component.ts +10 -0
- package/src/lint/rules/__fixtures__/comp/comp002/pass/search-service.component.ts +23 -0
- package/src/lint/rules/__fixtures__/comp/comp003/fail/migrator.component.ts +10 -0
- package/src/lint/rules/__fixtures__/comp/comp003/pass/migrator.component.ts +19 -0
- package/src/lint/rules/__fixtures__/comp/comp003/pass/orders-table.component.ts +10 -0
- package/src/lint/rules/__fixtures__/comp/comp004/fail/neo4j-cluster.component.ts +20 -0
- package/src/lint/rules/__fixtures__/comp/comp004/pass/neo4j-cluster.component.ts +23 -0
- package/src/lint/rules/__fixtures__/comp/comp004/pass/orders-table.component.ts +10 -0
- package/src/lint/rules/__fixtures__/comp/comp005/fail/search-service.component.ts +14 -0
- package/src/lint/rules/__fixtures__/comp/comp005/pass/search-service.component.ts +18 -0
- package/src/lint/rules/__fixtures__/comp/comp006/fail/legacy-tool.component.ts +10 -0
- package/src/lint/rules/__fixtures__/comp/comp006/pass/legacy-tool.component.ts +14 -0
- package/src/lint/rules/__fixtures__/comp/comp007/fail/inventory-table.component.ts +13 -0
- package/src/lint/rules/__fixtures__/comp/comp007/fail/orders-table.component.ts +13 -0
- package/src/lint/rules/__fixtures__/comp/comp007/pass/orders-table.component.ts +12 -0
- package/src/lint/rules/__fixtures__/comp/comp007/pass/search-service.component.ts +18 -0
- package/src/lint/rules/comp/comp.test.ts +378 -0
- package/src/lint/rules/comp/comp001-publishes-never-applies.ts +78 -0
- package/src/lint/rules/comp/comp002-dangling-wiring-ref.ts +94 -0
- package/src/lint/rules/comp/comp003-mutating-no-rollback.ts +115 -0
- package/src/lint/rules/comp/comp004-gate-needs-temporal.ts +66 -0
- package/src/lint/rules/comp/comp005-capability-kind-is-noun.ts +113 -0
- package/src/lint/rules/comp/comp006-shell-needs-reason.ts +55 -0
- package/src/lint/rules/comp/comp007-composition-sprawl.ts +71 -0
- package/src/lint/rules/comp/index.ts +37 -0
- package/src/lint/rules/comp/support.ts +173 -0
- package/src/lsp/lexicon-providers.test.ts +55 -0
- package/src/lsp/lexicon-providers.ts +13 -3
- package/src/op/activity-registry.test.ts +38 -0
- package/src/op/activity-registry.ts +31 -12
- package/src/op/activity-runtime.ts +73 -0
- package/src/op/builders.ts +92 -0
- package/src/op/index.ts +3 -1
- package/src/ownership.test.ts +24 -26
- package/src/ownership.ts +26 -38
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
buildLedgerEntries,
|
|
4
|
+
findReferrer,
|
|
5
|
+
noopReferrerLookup,
|
|
6
|
+
artifactReproducibilitySummary,
|
|
7
|
+
componentBomSummary,
|
|
8
|
+
type Referrer,
|
|
9
|
+
type ReferrerLookup,
|
|
10
|
+
} from "./build-ledger";
|
|
11
|
+
import {
|
|
12
|
+
createBuildArchiveManifest,
|
|
13
|
+
addArchiveEntry,
|
|
14
|
+
findSbomForSubject,
|
|
15
|
+
} from "../components/verbs/build-archive";
|
|
16
|
+
|
|
17
|
+
describe("build-ledger", () => {
|
|
18
|
+
describe("buildLedgerEntries", () => {
|
|
19
|
+
test("returns one entry per image-kind manifest entry", async () => {
|
|
20
|
+
let manifest = createBuildArchiveManifest("search-service", { now: () => new Date("2026-01-01T00:00:00.000Z") });
|
|
21
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:image1" });
|
|
22
|
+
manifest = addArchiveEntry(manifest, { kind: "template", path: "search.template.json", digest: "sha256:tmpl1" });
|
|
23
|
+
|
|
24
|
+
const entries = await buildLedgerEntries(manifest);
|
|
25
|
+
expect(entries).toHaveLength(1);
|
|
26
|
+
expect(entries[0]).toMatchObject({
|
|
27
|
+
component: "search-service",
|
|
28
|
+
path: "image.tar",
|
|
29
|
+
digest: "sha256:image1",
|
|
30
|
+
manifestDigest: manifest.manifestDigest,
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("noop lookup (default) attaches no referrers", async () => {
|
|
35
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
36
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:image1" });
|
|
37
|
+
const entries = await buildLedgerEntries(manifest);
|
|
38
|
+
expect(entries[0].referrers).toEqual([]);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("injected lookup attaches referrers keyed by digest", async () => {
|
|
42
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
43
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:image1" });
|
|
44
|
+
|
|
45
|
+
const sbom: Referrer = { kind: "sbom", mediaType: "application/vnd.cyclonedx+json", digest: "sha256:sbom1" };
|
|
46
|
+
const provenance: Referrer = { kind: "provenance", mediaType: "application/vnd.in-toto+json", digest: "sha256:prov1" };
|
|
47
|
+
const lookup: ReferrerLookup = {
|
|
48
|
+
async discover(digest) {
|
|
49
|
+
if (digest !== "sha256:image1") return [];
|
|
50
|
+
return [sbom, provenance];
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const entries = await buildLedgerEntries(manifest, lookup);
|
|
55
|
+
expect(entries[0].referrers).toEqual([sbom, provenance]);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("multiple image entries each get their own referrer lookup", async () => {
|
|
59
|
+
let manifest = createBuildArchiveManifest("multi-image");
|
|
60
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "a.tar", digest: "sha256:a" });
|
|
61
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "b.tar", digest: "sha256:b" });
|
|
62
|
+
|
|
63
|
+
const lookup: ReferrerLookup = {
|
|
64
|
+
async discover(digest) {
|
|
65
|
+
return [{ kind: "signature", mediaType: "application/vnd.dev.cosign.simplesigning.v1+json", digest: `${digest}-sig` }];
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const entries = await buildLedgerEntries(manifest, lookup);
|
|
70
|
+
expect(entries.map((e) => e.path).sort()).toEqual(["a.tar", "b.tar"]);
|
|
71
|
+
const byDigest = new Map(entries.map((e) => [e.digest, e]));
|
|
72
|
+
expect(byDigest.get("sha256:a")?.referrers[0].digest).toBe("sha256:a-sig");
|
|
73
|
+
expect(byDigest.get("sha256:b")?.referrers[0].digest).toBe("sha256:b-sig");
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("no image entries -> no build ledger entries", async () => {
|
|
77
|
+
let manifest = createBuildArchiveManifest("infra-only");
|
|
78
|
+
manifest = addArchiveEntry(manifest, { kind: "template", path: "t.json", digest: "sha256:t" });
|
|
79
|
+
const entries = await buildLedgerEntries(manifest);
|
|
80
|
+
expect(entries).toEqual([]);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe("findReferrer", () => {
|
|
85
|
+
test("finds a referrer by kind", () => {
|
|
86
|
+
const referrers: Referrer[] = [
|
|
87
|
+
{ kind: "sbom", mediaType: "x", digest: "sha256:1" },
|
|
88
|
+
{ kind: "signature", mediaType: "y", digest: "sha256:2" },
|
|
89
|
+
];
|
|
90
|
+
expect(findReferrer(referrers, "signature")?.digest).toBe("sha256:2");
|
|
91
|
+
expect(findReferrer(referrers, "provenance")).toBeUndefined();
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe("noopReferrerLookup", () => {
|
|
96
|
+
test("always returns an empty list", async () => {
|
|
97
|
+
expect(await noopReferrerLookup.discover("sha256:anything")).toEqual([]);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("sbom summary (#606) — archive-carried vs referrer-projected", () => {
|
|
102
|
+
test("no sbom entry and no sbom referrer -> sbom is undefined (component opted out / no SBOM generated)", async () => {
|
|
103
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
104
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:image1" });
|
|
105
|
+
const entries = await buildLedgerEntries(manifest);
|
|
106
|
+
expect(entries[0].sbom).toBeUndefined();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("archive-carried sbom entry is surfaced with format/package count/generator, source 'archive'", async () => {
|
|
110
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
111
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:image1" });
|
|
112
|
+
manifest = addArchiveEntry(manifest, {
|
|
113
|
+
kind: "sbom",
|
|
114
|
+
path: "image.tar.sbom.json",
|
|
115
|
+
digest: "sha256:sbomdoc",
|
|
116
|
+
mediaType: "application/spdx+json",
|
|
117
|
+
subjectDigest: "sha256:image1",
|
|
118
|
+
packageCount: 17,
|
|
119
|
+
generator: "syft",
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const entries = await buildLedgerEntries(manifest);
|
|
123
|
+
expect(entries[0].sbom).toEqual({
|
|
124
|
+
mediaType: "application/spdx+json",
|
|
125
|
+
packageCount: 17,
|
|
126
|
+
generator: "syft",
|
|
127
|
+
source: "archive",
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("referrer-projected sbom is surfaced when no archive-carried entry exists, source 'referrer'", async () => {
|
|
132
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
133
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:image1" });
|
|
134
|
+
|
|
135
|
+
const lookup: ReferrerLookup = {
|
|
136
|
+
async discover(digest) {
|
|
137
|
+
if (digest !== "sha256:image1") return [];
|
|
138
|
+
return [{ kind: "sbom", mediaType: "application/vnd.cyclonedx+json", digest: "sha256:sbomref" }];
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const entries = await buildLedgerEntries(manifest, lookup);
|
|
143
|
+
expect(entries[0].sbom).toEqual({
|
|
144
|
+
mediaType: "application/vnd.cyclonedx+json",
|
|
145
|
+
source: "referrer",
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("archive-carried sbom is preferred over a referrer-projected one when both exist", async () => {
|
|
150
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
151
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:image1" });
|
|
152
|
+
manifest = addArchiveEntry(manifest, {
|
|
153
|
+
kind: "sbom",
|
|
154
|
+
path: "image.tar.sbom.json",
|
|
155
|
+
digest: "sha256:sbomdoc",
|
|
156
|
+
mediaType: "application/spdx+json",
|
|
157
|
+
subjectDigest: "sha256:image1",
|
|
158
|
+
packageCount: 9,
|
|
159
|
+
generator: "buildkit",
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const lookup: ReferrerLookup = {
|
|
163
|
+
async discover() {
|
|
164
|
+
return [{ kind: "sbom", mediaType: "application/vnd.cyclonedx+json", digest: "sha256:sbomref" }];
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
const entries = await buildLedgerEntries(manifest, lookup);
|
|
169
|
+
expect(entries[0].sbom).toEqual({
|
|
170
|
+
mediaType: "application/spdx+json",
|
|
171
|
+
packageCount: 9,
|
|
172
|
+
generator: "buildkit",
|
|
173
|
+
source: "archive",
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test("format-agnostic: CycloneDX archive-carried sbom is surfaced the same way SPDX is", async () => {
|
|
178
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
179
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:image1" });
|
|
180
|
+
manifest = addArchiveEntry(manifest, {
|
|
181
|
+
kind: "sbom",
|
|
182
|
+
path: "image.tar.sbom.json",
|
|
183
|
+
digest: "sha256:sbomdoc",
|
|
184
|
+
mediaType: "application/vnd.cyclonedx+json",
|
|
185
|
+
subjectDigest: "sha256:image1",
|
|
186
|
+
packageCount: 5,
|
|
187
|
+
generator: "syft",
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
const entries = await buildLedgerEntries(manifest);
|
|
191
|
+
expect(entries[0].sbom?.mediaType).toBe("application/vnd.cyclonedx+json");
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("non-image artifact (jar/zip, no image entry) still carries its sbom in the manifest, findable independent of buildLedgerEntries (which is image-scoped)", async () => {
|
|
195
|
+
let manifest = createBuildArchiveManifest("jar-lib");
|
|
196
|
+
manifest = addArchiveEntry(manifest, { kind: "asset", path: "lib.jar", digest: "sha256:jar1" });
|
|
197
|
+
manifest = addArchiveEntry(manifest, {
|
|
198
|
+
kind: "sbom",
|
|
199
|
+
path: "lib.jar.sbom.json",
|
|
200
|
+
digest: "sha256:sbomdoc",
|
|
201
|
+
mediaType: "application/spdx+json",
|
|
202
|
+
subjectDigest: "sha256:jar1",
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// buildLedgerEntries only enumerates image-kind entries (the build
|
|
206
|
+
// ledger's historical scope, #568) — a jar's SBOM is still readable
|
|
207
|
+
// straight from the archive via findSbomForSubject, the same
|
|
208
|
+
// artifact-type-agnostic accessor ./build-ledger.ts uses internally.
|
|
209
|
+
const entries = await buildLedgerEntries(manifest);
|
|
210
|
+
expect(entries).toEqual([]);
|
|
211
|
+
expect(findSbomForSubject(manifest, "sha256:jar1")).toMatchObject({ mediaType: "application/spdx+json" });
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("image entries surface their own reproducibility record (#614)", async () => {
|
|
215
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
216
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:image1" });
|
|
217
|
+
const entries = await buildLedgerEntries(manifest);
|
|
218
|
+
expect(entries[0].reproducibility).toEqual({ basis: "best-effort" });
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
describe("artifactReproducibilitySummary (#614)", () => {
|
|
223
|
+
test("reports every image/template/asset entry's own reproducibility, honestly per artifact type", () => {
|
|
224
|
+
let manifest = createBuildArchiveManifest("search-service");
|
|
225
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:img1" });
|
|
226
|
+
manifest = addArchiveEntry(manifest, { kind: "template", path: "search.template.json", digest: "sha256:tmpl1" });
|
|
227
|
+
manifest = addArchiveEntry(manifest, { kind: "asset", path: "lib.jar", digest: "sha256:jar1" });
|
|
228
|
+
|
|
229
|
+
const summary = artifactReproducibilitySummary(manifest);
|
|
230
|
+
const byPath = new Map(summary.map((s) => [s.path, s]));
|
|
231
|
+
expect(byPath.get("image.tar")?.reproducibility).toEqual({ basis: "best-effort" });
|
|
232
|
+
expect(byPath.get("search.template.json")?.reproducibility).toEqual({
|
|
233
|
+
basis: "deterministic-synthesis",
|
|
234
|
+
verifyBy: "re-synth",
|
|
235
|
+
});
|
|
236
|
+
expect(byPath.get("lib.jar")?.reproducibility).toEqual({ basis: "best-effort" });
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test("excludes sbom-kind entries — they describe another artifact, they aren't one themselves", () => {
|
|
240
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
241
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:img1" });
|
|
242
|
+
manifest = addArchiveEntry(manifest, {
|
|
243
|
+
kind: "sbom",
|
|
244
|
+
path: "image.tar.sbom.json",
|
|
245
|
+
digest: "sha256:sbom1",
|
|
246
|
+
subjectDigest: "sha256:img1",
|
|
247
|
+
});
|
|
248
|
+
const summary = artifactReproducibilitySummary(manifest);
|
|
249
|
+
expect(summary.map((s) => s.path)).toEqual(["image.tar"]);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test("surfaces the recorded provenance sourceRef when present", () => {
|
|
253
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
254
|
+
manifest = addArchiveEntry(manifest, {
|
|
255
|
+
kind: "template",
|
|
256
|
+
path: "t.json",
|
|
257
|
+
digest: "sha256:t1",
|
|
258
|
+
provenance: { sourceRef: "abc123", artifactDigest: "sha256:t1" },
|
|
259
|
+
});
|
|
260
|
+
const summary = artifactReproducibilitySummary(manifest);
|
|
261
|
+
expect(summary[0]!.sourceRef).toBe("abc123");
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
test("no artifact entries -> empty summary", () => {
|
|
265
|
+
const manifest = createBuildArchiveManifest("empty");
|
|
266
|
+
expect(artifactReproducibilitySummary(manifest)).toEqual([]);
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
describe("componentBomSummary (#614)", () => {
|
|
271
|
+
test("undefined when the manifest carries no BOM at all", () => {
|
|
272
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
273
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:img1" });
|
|
274
|
+
expect(componentBomSummary(manifest)).toBeUndefined();
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
test("single leaf: isAssembly is false — a 1:1 component BOM", () => {
|
|
278
|
+
let manifest = createBuildArchiveManifest("infra-only");
|
|
279
|
+
manifest = addArchiveEntry(manifest, { kind: "template", path: "t.json", digest: "sha256:t1" });
|
|
280
|
+
manifest = addArchiveEntry(manifest, {
|
|
281
|
+
kind: "sbom",
|
|
282
|
+
bomKind: "config",
|
|
283
|
+
path: "t.json.config-bom.json",
|
|
284
|
+
digest: "sha256:sbom1",
|
|
285
|
+
subjectDigest: "sha256:t1",
|
|
286
|
+
mediaType: "application/spdx+json",
|
|
287
|
+
packageCount: 3,
|
|
288
|
+
generator: "chant-config-bom-extractor",
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
const summary = componentBomSummary(manifest);
|
|
292
|
+
expect(summary?.isAssembly).toBe(false);
|
|
293
|
+
expect(summary?.leaves).toHaveLength(1);
|
|
294
|
+
expect(summary?.leaves[0]).toEqual({
|
|
295
|
+
path: "t.json.config-bom.json",
|
|
296
|
+
bomKind: "config",
|
|
297
|
+
subjectDigest: "sha256:t1",
|
|
298
|
+
mediaType: "application/spdx+json",
|
|
299
|
+
packageCount: 3,
|
|
300
|
+
generator: "chant-config-bom-extractor",
|
|
301
|
+
});
|
|
302
|
+
expect(summary?.totalPackageCount).toBe(3);
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
test("multi leaf (software SBOM + config-BOM): isAssembly is true, totalPackageCount sums both leaves", () => {
|
|
306
|
+
let manifest = createBuildArchiveManifest("search-service");
|
|
307
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "image.tar", digest: "sha256:img1" });
|
|
308
|
+
manifest = addArchiveEntry(manifest, { kind: "template", path: "search.template.json", digest: "sha256:tmpl1" });
|
|
309
|
+
manifest = addArchiveEntry(manifest, {
|
|
310
|
+
kind: "sbom",
|
|
311
|
+
bomKind: "software",
|
|
312
|
+
path: "image.tar.sbom.json",
|
|
313
|
+
digest: "sha256:sbomsoft",
|
|
314
|
+
subjectDigest: "sha256:img1",
|
|
315
|
+
mediaType: "application/spdx+json",
|
|
316
|
+
packageCount: 17,
|
|
317
|
+
generator: "syft",
|
|
318
|
+
});
|
|
319
|
+
manifest = addArchiveEntry(manifest, {
|
|
320
|
+
kind: "sbom",
|
|
321
|
+
bomKind: "config",
|
|
322
|
+
path: "search.template.json.config-bom.json",
|
|
323
|
+
digest: "sha256:sbomconfig",
|
|
324
|
+
subjectDigest: "sha256:tmpl1",
|
|
325
|
+
mediaType: "application/spdx+json",
|
|
326
|
+
packageCount: 5,
|
|
327
|
+
generator: "chant-config-bom-extractor",
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
const summary = componentBomSummary(manifest);
|
|
331
|
+
expect(summary?.isAssembly).toBe(true);
|
|
332
|
+
expect(summary?.leaves).toHaveLength(2);
|
|
333
|
+
expect(summary?.totalPackageCount).toBe(22);
|
|
334
|
+
expect(summary?.leaves.map((l) => l.bomKind).sort()).toEqual(["config", "software"]);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
test("bomKind defaults to 'software' when a leaf entry omits it, matching BuildArchiveEntry's own default", () => {
|
|
338
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
339
|
+
manifest = addArchiveEntry(manifest, {
|
|
340
|
+
kind: "sbom",
|
|
341
|
+
path: "a.sbom.json",
|
|
342
|
+
digest: "sha256:s1",
|
|
343
|
+
subjectDigest: "sha256:img1",
|
|
344
|
+
});
|
|
345
|
+
const summary = componentBomSummary(manifest);
|
|
346
|
+
expect(summary?.leaves[0]?.bomKind).toBe("software");
|
|
347
|
+
});
|
|
348
|
+
});
|
|
349
|
+
});
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build ledger — the "what's been built" half of build & deploy observability
|
|
3
|
+
* (#568, epic #551 "Build & deploy observability" / "4. Build archive +
|
|
4
|
+
* deferred publish"; SBOM consume/surface added in #606).
|
|
5
|
+
*
|
|
6
|
+
* The `BuildArchive` manifest (../components/verbs/build-archive.ts) is
|
|
7
|
+
* itself the build ledger: it is content-addressed by digest and enumerates
|
|
8
|
+
* every image/template/asset/sbom a build produced. What #568 adds on top is
|
|
9
|
+
* discovery of an image's **referrers** — the SBOM, SLSA provenance, and
|
|
10
|
+
* signature attached to the same digest by tooling like `oras discover
|
|
11
|
+
* <digest>` / `cosign tree` (epic #551's phrasing).
|
|
12
|
+
*
|
|
13
|
+
* This module only *surfaces and consumes* SBOM/provenance/signature
|
|
14
|
+
* evidence — generation is ../components/verbs/sbom.ts's `generate-sbom`
|
|
15
|
+
* capability (#606; SLSA provenance + signing remain out of scope, noted as
|
|
16
|
+
* fast-follow siblings). Two sources feed the same `sbom` summary below, and
|
|
17
|
+
* a caller gets *both* without special-casing which one exists:
|
|
18
|
+
*
|
|
19
|
+
* - **archive-carried** — an `sbom`-kind entry in the same
|
|
20
|
+
* `BuildArchiveManifest`, written directly by `generate-sbom`. This is
|
|
21
|
+
* the **universal home**: it works for every artifact type (image, jar,
|
|
22
|
+
* zip) and every publish backend, including the registry-less
|
|
23
|
+
* `load-image-on-host` path, with no registry/network access required to
|
|
24
|
+
* read it back.
|
|
25
|
+
* - **referrer-projected** — an OCI referrer of `kind: "sbom"` discovered
|
|
26
|
+
* via `ReferrerLookup`, the registry-side convenience for image
|
|
27
|
+
* artifacts (`oras discover <digest>`/`cosign tree`) that #568 already
|
|
28
|
+
* modeled. Not every artifact type has this projection (only images
|
|
29
|
+
* published to a registry do), and not every environment can query it
|
|
30
|
+
* (`ReferrerLookup` defaults to `noopReferrerLookup`, no network).
|
|
31
|
+
*
|
|
32
|
+
* `buildLedgerEntries` prefers the archive-carried copy when both exist
|
|
33
|
+
* (same digest either way, since both trace back to the same
|
|
34
|
+
* `generate-sbom` run — the archive is the source of truth; the referrer is
|
|
35
|
+
* a projection of it) and falls back to the referrer when only that is
|
|
36
|
+
* available (e.g. a manifest read that predates #606, or a third-party tool
|
|
37
|
+
* attached an SBOM directly to the registry outside chant's own build). The
|
|
38
|
+
* `ReferrerLookup` interface remains injectable for exactly the reason
|
|
39
|
+
* `CloudExecutor` is injectable elsewhere in ../components/verbs: no test and
|
|
40
|
+
* no default implementation shells out to a real `oras`/`cosign`/registry
|
|
41
|
+
* call.
|
|
42
|
+
*
|
|
43
|
+
* **#614 additions.** Two more things get surfaced per manifest, both
|
|
44
|
+
* derived from data the manifest already carries (no new generation, no
|
|
45
|
+
* re-parsing of BOM bytes):
|
|
46
|
+
*
|
|
47
|
+
* - Each `BuildLedgerEntry` (still one per image, matching this module's
|
|
48
|
+
* existing scope) now carries that image's own `reproducibility` — read
|
|
49
|
+
* straight off its `BuildArchiveEntry`, never inferred from a sibling
|
|
50
|
+
* artifact (see ../components/verbs/reproducibility.ts's module doc for
|
|
51
|
+
* why a blanket flag would misrepresent a multi-artifact component).
|
|
52
|
+
* `artifactReproducibilitySummary` below reports the same per-artifact
|
|
53
|
+
* honesty for *every* artifact entry in a manifest (image, template,
|
|
54
|
+
* asset alike) — the full per-artifact picture #614 asks for, not just
|
|
55
|
+
* the image-scoped slice `buildLedgerEntries` has always covered.
|
|
56
|
+
* - `componentBomSummary` reports the component-level BOM aggregation
|
|
57
|
+
* picture (../components/verbs/component-bom.ts): how many leaf BOMs
|
|
58
|
+
* (software SBOM + IaC config-BOM) the manifest carries, each one's
|
|
59
|
+
* `bomKind`/format/package count, and whether they'd compose into a
|
|
60
|
+
* single-artifact (1:1) or real multi-artifact assembly. This is a
|
|
61
|
+
* summary read off the manifest's existing `sbom`-kind entries — it does
|
|
62
|
+
* not regenerate or re-validate the aggregate document (that stays
|
|
63
|
+
* `aggregateComponentBom`'s job, given each leaf's `BomPackage[]`, which
|
|
64
|
+
* the manifest alone does not carry).
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
import type { BuildArchiveManifest, BuildArchiveEntry } from "../components/verbs/build-archive";
|
|
68
|
+
import { findSbomForSubject, imageEntries, sbomEntries, artifactEntries } from "../components/verbs/build-archive";
|
|
69
|
+
import type { ArtifactReproducibility } from "../components/verbs/reproducibility";
|
|
70
|
+
|
|
71
|
+
/** The kind of referrer artifact attached to an image digest (mirrors the OCI referrers API / `oras discover` output). */
|
|
72
|
+
export type ReferrerKind = "sbom" | "provenance" | "signature";
|
|
73
|
+
|
|
74
|
+
/** One referrer attached to an image digest. */
|
|
75
|
+
export interface Referrer {
|
|
76
|
+
kind: ReferrerKind;
|
|
77
|
+
/** Media type of the referrer artifact itself (e.g. `application/vnd.cyclonedx+json`, `application/vnd.in-toto+json`). */
|
|
78
|
+
mediaType: string;
|
|
79
|
+
/** Content-addressed digest of the referrer artifact (`sha256:...`) — distinct from the subject digest it refers to. */
|
|
80
|
+
digest: string;
|
|
81
|
+
/** Where to fetch the referrer's content, when known (a registry blob ref, a file path, ...). Opaque to this module. */
|
|
82
|
+
location?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Injectable lookup for an image digest's referrers — the seam a real
|
|
87
|
+
* `oras`/`cosign`/registry-API implementation plugs into. Defaults to
|
|
88
|
+
* `NoopReferrerLookup` (below) so build ledger queries degrade to "no
|
|
89
|
+
* referrers known" rather than requiring network/registry access, matching
|
|
90
|
+
* how ../components/verbs/cloud-executor.ts's real implementations are
|
|
91
|
+
* optional and mockable.
|
|
92
|
+
*/
|
|
93
|
+
export interface ReferrerLookup {
|
|
94
|
+
/** List every referrer attached to `digest`, or `[]` if none / lookup unsupported. */
|
|
95
|
+
discover(digest: string): Promise<Referrer[]>;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Default `ReferrerLookup`: reports no referrers. A real registry/`oras`-backed implementation is out of scope here (belongs with #564's signing follow-up) — this keeps build-ledger queries safe to run with no cloud/network access. */
|
|
99
|
+
export const noopReferrerLookup: ReferrerLookup = {
|
|
100
|
+
async discover() {
|
|
101
|
+
return [];
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Where a build-ledger entry's `sbom` summary was read from — surfaced so
|
|
107
|
+
* `chant components status` can report SBOM *source*, not just presence
|
|
108
|
+
* (#606). "avoid drift": whichever source answers, it traces back to the
|
|
109
|
+
* same `generate-sbom` run for a given digest, since promote-by-digest never
|
|
110
|
+
* rebuilds — see docs/components/build-archive.mdx.
|
|
111
|
+
*/
|
|
112
|
+
export type SbomSource = "archive" | "referrer";
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* The SBOM summary `chant components status` surfaces for one build-ledger
|
|
116
|
+
* entry (#606): format, package count, and where it was read from — never
|
|
117
|
+
* the raw SBOM bytes (chant's job is to generate/store/surface, not to
|
|
118
|
+
* interpret SBOM content; that is the scan-result/VEX policy-gate
|
|
119
|
+
* fast-follow, out of scope here).
|
|
120
|
+
*/
|
|
121
|
+
export interface SbomSummary {
|
|
122
|
+
/** Media type of the SBOM document (`application/spdx+json` or `application/vnd.cyclonedx+json`) — the format-agnostic key, never a hardcoded format. */
|
|
123
|
+
mediaType: string;
|
|
124
|
+
/** Number of packages/components enumerated, when known. */
|
|
125
|
+
packageCount?: number;
|
|
126
|
+
/** Which tool produced it (e.g. "syft", "buildkit"), when known. */
|
|
127
|
+
generator?: string;
|
|
128
|
+
/** Whether this summary was read from the archive-carried entry or a registry-discovered OCI referrer. */
|
|
129
|
+
source: SbomSource;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** One build-ledger entry: an archive's image entry plus whatever referrers are attached to its digest. */
|
|
133
|
+
export interface BuildLedgerEntry {
|
|
134
|
+
/** Component the archive was built for. */
|
|
135
|
+
component: string;
|
|
136
|
+
/** Archive-relative path of the image entry (see `BuildArchiveEntry.path`). */
|
|
137
|
+
path: string;
|
|
138
|
+
/** Content-addressed digest of the image (`sha256:...`) — the join key with the release ledger. */
|
|
139
|
+
digest: string;
|
|
140
|
+
/** When the archive manifest was assembled. */
|
|
141
|
+
createdAt: string;
|
|
142
|
+
/** The manifest's own aggregate digest — traces a promoted image digest back to the exact manifest that produced it. */
|
|
143
|
+
manifestDigest: string;
|
|
144
|
+
/** SBOM/provenance/signature referrers attached to this image's digest, if any were discoverable. */
|
|
145
|
+
referrers: Referrer[];
|
|
146
|
+
/**
|
|
147
|
+
* SBOM summary for this digest, read from whichever source is available —
|
|
148
|
+
* the archive-carried `sbom`-kind manifest entry (preferred; works for
|
|
149
|
+
* every artifact type and every publish backend, registry or not) or a
|
|
150
|
+
* registry-discovered `sbom`-kind referrer (image artifacts only, when
|
|
151
|
+
* `lookup` can reach a registry). `undefined` when neither source has one
|
|
152
|
+
* — including when the component opted out of `generate-sbom` entirely.
|
|
153
|
+
*/
|
|
154
|
+
sbom?: SbomSummary;
|
|
155
|
+
/**
|
|
156
|
+
* This image's own honest, per-artifact reproducibility record (#614) —
|
|
157
|
+
* read from its `BuildArchiveEntry.reproducibility`. An `image` entry
|
|
158
|
+
* defaults to `{ basis: "best-effort" }` (see
|
|
159
|
+
* ../components/verbs/reproducibility.ts's `defaultReproducibility`)
|
|
160
|
+
* absent a real reproducible-build attestation; never inferred from a
|
|
161
|
+
* sibling `template`/`asset` entry's basis.
|
|
162
|
+
*/
|
|
163
|
+
reproducibility?: ArtifactReproducibility;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Build the ledger view for one archive manifest: every `image`-kind entry,
|
|
168
|
+
* each paired with its OCI referrers via `lookup` and its SBOM summary (read
|
|
169
|
+
* from the archive-carried entry first, the referrer projection otherwise —
|
|
170
|
+
* see this module's doc comment). "What's built" becomes "what's built, from
|
|
171
|
+
* which commit, by what, when" once a release record (./release-ledger.ts)
|
|
172
|
+
* is joined onto this by `digest` — this function only produces the build
|
|
173
|
+
* side of that join.
|
|
174
|
+
*/
|
|
175
|
+
export async function buildLedgerEntries(
|
|
176
|
+
manifest: BuildArchiveManifest,
|
|
177
|
+
lookup: ReferrerLookup = noopReferrerLookup,
|
|
178
|
+
): Promise<BuildLedgerEntry[]> {
|
|
179
|
+
const images = imageEntries(manifest);
|
|
180
|
+
const entries: BuildLedgerEntry[] = [];
|
|
181
|
+
for (const image of images) {
|
|
182
|
+
const referrers = await lookup.discover(image.digest);
|
|
183
|
+
|
|
184
|
+
const archiveSbom = findSbomForSubject(manifest, image.digest);
|
|
185
|
+
const referrerSbom = findReferrer(referrers, "sbom");
|
|
186
|
+
const sbom: SbomSummary | undefined = archiveSbom
|
|
187
|
+
? {
|
|
188
|
+
mediaType: archiveSbom.mediaType ?? "application/octet-stream",
|
|
189
|
+
packageCount: archiveSbom.packageCount,
|
|
190
|
+
generator: archiveSbom.generator,
|
|
191
|
+
source: "archive",
|
|
192
|
+
}
|
|
193
|
+
: referrerSbom
|
|
194
|
+
? { mediaType: referrerSbom.mediaType, source: "referrer" }
|
|
195
|
+
: undefined;
|
|
196
|
+
|
|
197
|
+
entries.push({
|
|
198
|
+
component: manifest.component,
|
|
199
|
+
path: image.path,
|
|
200
|
+
digest: image.digest,
|
|
201
|
+
createdAt: manifest.createdAt,
|
|
202
|
+
manifestDigest: manifest.manifestDigest,
|
|
203
|
+
referrers,
|
|
204
|
+
sbom,
|
|
205
|
+
reproducibility: image.reproducibility,
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
return entries;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Find the referrer of a given kind for a digest, if any (a convenience for "does this build have a signature/SBOM/provenance"). */
|
|
212
|
+
export function findReferrer(referrers: Referrer[], kind: ReferrerKind): Referrer | undefined {
|
|
213
|
+
return referrers.find((r) => r.kind === kind);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// ── #614: per-artifact reproducibility + component BOM summary ─────────────
|
|
217
|
+
|
|
218
|
+
/** One artifact's reproducibility, named for display (`chant components status`) rather than just keyed by digest. */
|
|
219
|
+
export interface ArtifactReproducibilitySummary {
|
|
220
|
+
/** Archive-relative path of the artifact (`BuildArchiveEntry.path`). */
|
|
221
|
+
path: string;
|
|
222
|
+
/** Which kind of artifact this is. */
|
|
223
|
+
kind: BuildArchiveEntry["kind"];
|
|
224
|
+
/** Content-addressed digest of the artifact. */
|
|
225
|
+
digest: string;
|
|
226
|
+
/** The artifact's own reproducibility record, when one is recorded (absent only for `sbom`-kind entries, which describe another artifact rather than being one). */
|
|
227
|
+
reproducibility?: ArtifactReproducibility;
|
|
228
|
+
/** Source ref/commit that produced this artifact, when recorded (#614's provenance link). */
|
|
229
|
+
sourceRef?: string;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Report every artifact entry's (`image`/`template`/`asset`) own
|
|
234
|
+
* reproducibility + provenance, honestly per-artifact — the full picture
|
|
235
|
+
* #614 asks for, wider than `buildLedgerEntries`'s image-only scope. Order
|
|
236
|
+
* matches `artifactEntries`' manifest insertion order; sort by `path`
|
|
237
|
+
* yourself for a stable display order (mirroring
|
|
238
|
+
* `computeManifestDigest`'s own path-sort convention).
|
|
239
|
+
*/
|
|
240
|
+
export function artifactReproducibilitySummary(manifest: BuildArchiveManifest): ArtifactReproducibilitySummary[] {
|
|
241
|
+
return artifactEntries(manifest).map((entry) => ({
|
|
242
|
+
path: entry.path,
|
|
243
|
+
kind: entry.kind,
|
|
244
|
+
digest: entry.digest,
|
|
245
|
+
reproducibility: entry.reproducibility,
|
|
246
|
+
sourceRef: entry.provenance?.sourceRef,
|
|
247
|
+
}));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** One leaf BOM in a component's aggregation picture — the summary form `componentBomSummary` reports, without regenerating or re-validating the aggregate document itself. */
|
|
251
|
+
export interface ComponentBomLeafSummary {
|
|
252
|
+
/** Archive-relative path of the leaf BOM document. */
|
|
253
|
+
path: string;
|
|
254
|
+
/** Software SBOM or IaC config-BOM (#613's `bomKind`, defaulting to `"software"` the same way `BuildArchiveEntry.bomKind` does). */
|
|
255
|
+
bomKind: "software" | "config";
|
|
256
|
+
/** Digest of the artifact this leaf describes (`subjectDigest`), when recorded. */
|
|
257
|
+
subjectDigest?: string;
|
|
258
|
+
/** Media type of the leaf document (format-agnostic key, per #613's convention). */
|
|
259
|
+
mediaType: string;
|
|
260
|
+
/** Package/component count this leaf enumerates, when known. */
|
|
261
|
+
packageCount?: number;
|
|
262
|
+
/** Which tool produced this leaf, when known. */
|
|
263
|
+
generator?: string;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Summary of a component's BOM aggregation picture (#614,
|
|
268
|
+
* ../components/verbs/component-bom.ts): every leaf BOM the manifest
|
|
269
|
+
* carries, whether they compose 1:1 (a single-artifact component — the leaf
|
|
270
|
+
* BOM *is* the component BOM, structurally) or as a real multi-artifact
|
|
271
|
+
* assembly (2+ leaves), and the combined package count across every leaf.
|
|
272
|
+
*/
|
|
273
|
+
export interface ComponentBomSummary {
|
|
274
|
+
/** Every leaf BOM (software SBOM + IaC config-BOM) the manifest carries. */
|
|
275
|
+
leaves: ComponentBomLeafSummary[];
|
|
276
|
+
/** Sum of every leaf's `packageCount` (0 for a leaf with an unknown count). */
|
|
277
|
+
totalPackageCount: number;
|
|
278
|
+
/** `false` when there are 0 or 1 leaves (nothing to assemble, or a 1:1 component BOM); `true` when there are 2+ leaves (a real multi-artifact assembly, per #614's "single-artifact = 1:1, multi-artifact = a real assembly"). */
|
|
279
|
+
isAssembly: boolean;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Build the component BOM summary for one manifest, straight from its
|
|
284
|
+
* existing `sbom`-kind entries — no regeneration, no re-parsing of BOM
|
|
285
|
+
* bytes. `undefined` when the manifest carries no BOM at all (a component
|
|
286
|
+
* that composed neither `generate-sbom` nor `extract-config-bom` — skipping
|
|
287
|
+
* is structural, matching every other BOM consumer's convention in this
|
|
288
|
+
* codebase).
|
|
289
|
+
*/
|
|
290
|
+
export function componentBomSummary(manifest: BuildArchiveManifest): ComponentBomSummary | undefined {
|
|
291
|
+
const boms = sbomEntries(manifest);
|
|
292
|
+
if (boms.length === 0) return undefined;
|
|
293
|
+
|
|
294
|
+
const leaves: ComponentBomLeafSummary[] = boms.map((entry) => ({
|
|
295
|
+
path: entry.path,
|
|
296
|
+
bomKind: entry.bomKind ?? "software",
|
|
297
|
+
subjectDigest: entry.subjectDigest,
|
|
298
|
+
mediaType: entry.mediaType ?? "application/octet-stream",
|
|
299
|
+
packageCount: entry.packageCount,
|
|
300
|
+
generator: entry.generator,
|
|
301
|
+
}));
|
|
302
|
+
const totalPackageCount = leaves.reduce((sum, l) => sum + (l.packageCount ?? 0), 0);
|
|
303
|
+
|
|
304
|
+
return { leaves, totalPackageCount, isAssembly: leaves.length > 1 };
|
|
305
|
+
}
|