@intentius/chant 0.13.1 → 0.15.1
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/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 +59 -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/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 +99 -0
- package/src/op/index.ts +3 -1
- package/src/ownership.test.ts +24 -26
- package/src/ownership.ts +26 -38
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native JSON BOM writer (#613, epic #551 follow-up to #606). Pure
|
|
3
|
+
* TypeScript, no external binaries (no `syft`/`buildx --sbom`/`cyclonedx-maven`/
|
|
4
|
+
* `oras`/`cosign`), no network — every document below is assembled entirely
|
|
5
|
+
* from in-memory data and `JSON.stringify`.
|
|
6
|
+
*
|
|
7
|
+
* This module is the shared serialization core both real `SbomGenerator`
|
|
8
|
+
* backends in this directory build on: the software leaf
|
|
9
|
+
* (./lockfile-sbom-generator.ts, lockfile/manifest-derived package lists) and
|
|
10
|
+
* the IaC leaf (./config-bom.ts, synthesized-template-derived component
|
|
11
|
+
* lists). Neither backend hand-rolls its own SPDX/CycloneDX shape — both
|
|
12
|
+
* call `writeSpdx`/`writeCycloneDx` here so the two document standards have
|
|
13
|
+
* exactly one writer each in the whole codebase.
|
|
14
|
+
*
|
|
15
|
+
* **Why hand-write the JSON instead of a library.** No SPDX/CycloneDX SDK is
|
|
16
|
+
* a dependency of this package (matching ./cloud-executor.ts's stance on the
|
|
17
|
+
* AWS SDK: shell out or hand-roll rather than add a heavy dependency for a
|
|
18
|
+
* narrow need). Both standards' JSON forms are simple enough — a handful of
|
|
19
|
+
* required top-level fields plus an array of package/component records — that
|
|
20
|
+
* a ~100-line writer is more auditable than a dependency. Structural
|
|
21
|
+
* correctness (required fields, enum values) is verified in
|
|
22
|
+
* ./bom-writer.test.ts against each standard's own published JSON Schema
|
|
23
|
+
* (vendored as devDep test fixtures, the same convention #553 used for the
|
|
24
|
+
* Component contract schema — see ./__fixtures__/schemas/).
|
|
25
|
+
*
|
|
26
|
+
* **Format decisions, recorded for reviewers:**
|
|
27
|
+
* - SPDX **2.3** (not 2.2 or 3.0): 2.3 is what BuildKit's native `--sbom`
|
|
28
|
+
* attestation emits (referenced in ./sbom-generator.ts's module doc), so
|
|
29
|
+
* matching that version keeps a future BuildKit-backed `forImage` and this
|
|
30
|
+
* lockfile-backed backend speaking the same SPDX dialect. SPDX 3.0 is a
|
|
31
|
+
* substantially different (JSON-LD/RDF-shaped) model not yet the de facto
|
|
32
|
+
* tooling default as of this writing.
|
|
33
|
+
* - CycloneDX **1.5** (not 1.4 or 1.6): 1.5 is the version most broadly
|
|
34
|
+
* supported by consumer tooling (Dependency-Track, `cyclonedx-cli`) at
|
|
35
|
+
* time of writing and has a stable published JSON Schema; 1.6 adds fields
|
|
36
|
+
* this writer doesn't need yet.
|
|
37
|
+
* - `documentNamespace`/`serialNumber` are deterministic, derived from the
|
|
38
|
+
* subject's digest/path rather than a random UUID, so two BOM generations
|
|
39
|
+
* of byte-identical inputs produce byte-identical documents — the same
|
|
40
|
+
* content-addressing property ./build-archive.ts's `contentDigest`
|
|
41
|
+
* depends on for its "identical inputs -> identical manifest digest"
|
|
42
|
+
* guarantee (see ./sbom.test.ts's existing test of that property).
|
|
43
|
+
*
|
|
44
|
+
* **Assembly (#614).** `BomInput.subDocuments`, when present, projects each
|
|
45
|
+
* sub-document as a real nested unit rather than flattening its packages into
|
|
46
|
+
* the root's own list — CycloneDX's `metadata.component.components` +
|
|
47
|
+
* `compositions` (`aggregate: "incomplete"`, since a component BOM only
|
|
48
|
+
* composes the leaf BOMs it was given, never a closed-world claim over
|
|
49
|
+
* everything the artifact might contain), SPDX's `hasFiles`-style `CONTAINS`
|
|
50
|
+
* relationship from the root package to each sub-document's own root package
|
|
51
|
+
* plus that sub-document's own `DEPENDS_ON` edges. This is what
|
|
52
|
+
* ./component-bom.ts's `aggregateComponentBom` uses to compose a component's
|
|
53
|
+
* leaf BOMs (a software SBOM plus an IaC config-BOM) into one document
|
|
54
|
+
* without flattening away which packages came from which leaf — still one
|
|
55
|
+
* writer per standard, extended rather than duplicated.
|
|
56
|
+
*/
|
|
57
|
+
|
|
58
|
+
import { SBOM_MEDIA_TYPES, type SbomFormat } from "./sbom-generator";
|
|
59
|
+
|
|
60
|
+
// ── shared package/component model ──────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* One dependency/component the BOM enumerates — the standard-agnostic unit
|
|
64
|
+
* both `writeSpdx` and `writeCycloneDx` project into their own per-format
|
|
65
|
+
* package/component record shape. A generator backend (lockfile parser,
|
|
66
|
+
* config-BOM template walker) builds a `BomPackage[]` once and hands it to
|
|
67
|
+
* whichever writer the requested `format` selects.
|
|
68
|
+
*/
|
|
69
|
+
export interface BomPackage {
|
|
70
|
+
/** Package/component name (npm package name, Maven artifactId, or — for a config-BOM — a resource's logical name/type). */
|
|
71
|
+
name: string;
|
|
72
|
+
/** Version string, when known. Omitted for resources with no version concept (e.g. a CFN resource). */
|
|
73
|
+
version?: string;
|
|
74
|
+
/** Package manager / ecosystem this entry came from (e.g. "npm", "maven"), surfaced in SPDX's `externalRefs`/CycloneDX's `purl` when derivable. */
|
|
75
|
+
type?: "npm" | "maven" | "config" | "external-reference";
|
|
76
|
+
/** Package URL (purl), when derivable — e.g. `pkg:npm/left-pad@1.3.0`. */
|
|
77
|
+
purl?: string;
|
|
78
|
+
/** SPDX download location, when known. Defaults to `"NOASSERTION"` per the SPDX spec's requirement that every package declare one. */
|
|
79
|
+
downloadLocation?: string;
|
|
80
|
+
/** Names of other `BomPackage.name` entries this one depends on — projected into SPDX's `relationships` (`DEPENDS_ON`) and CycloneDX's `dependencies`. */
|
|
81
|
+
dependsOn?: string[];
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* One nested BOM unit an assembly (#614) composes into its root document —
|
|
86
|
+
* projected as a real nested `components` entry in CycloneDX and a
|
|
87
|
+
* `CONTAINS`-linked sub-package group in SPDX, never flattened into the
|
|
88
|
+
* root's own package list. Typically one leaf BOM (a software SBOM or an IaC
|
|
89
|
+
* config-BOM) a component produced, see ./component-bom.ts.
|
|
90
|
+
*/
|
|
91
|
+
export interface BomSubDocument {
|
|
92
|
+
/** Human-readable name for this sub-document's own subject (e.g. an artifact's archive path). */
|
|
93
|
+
subjectName: string;
|
|
94
|
+
/** Version of this sub-document's subject, when known. */
|
|
95
|
+
subjectVersion?: string;
|
|
96
|
+
/** Stable identity for this sub-document's subject — typically the artifact digest it describes, so the assembly's `CONTAINS`/nested-component edge is traceable back to a real `BuildArchiveEntry.digest`. */
|
|
97
|
+
subjectId: string;
|
|
98
|
+
/** Every package/component this sub-document enumerates. */
|
|
99
|
+
packages: BomPackage[];
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Input to both writers: the subject the BOM describes plus its enumerated packages/components. */
|
|
103
|
+
export interface BomInput {
|
|
104
|
+
/** Human-readable name for the BOM's subject (a package name, a template/stack name). */
|
|
105
|
+
subjectName: string;
|
|
106
|
+
/** Version of the subject itself, when known. */
|
|
107
|
+
subjectVersion?: string;
|
|
108
|
+
/** Stable identity for the subject, used to derive deterministic namespace/serial IDs — typically a digest or an archive-relative path. */
|
|
109
|
+
subjectId: string;
|
|
110
|
+
/** Every package/component the BOM enumerates. */
|
|
111
|
+
packages: BomPackage[];
|
|
112
|
+
/** Name of the tool that produced this document (surfaced in SPDX's `creationInfo.creators` / CycloneDX's `metadata.tools`). */
|
|
113
|
+
generator: string;
|
|
114
|
+
/**
|
|
115
|
+
* Nested BOM units to compose into this document as an assembly (#614) —
|
|
116
|
+
* present only for a component-level aggregation BOM (see
|
|
117
|
+
* ./component-bom.ts). When omitted, `writeSpdx`/`writeCycloneDx` behave
|
|
118
|
+
* exactly as before #614: a flat single-subject document.
|
|
119
|
+
*/
|
|
120
|
+
subDocuments?: BomSubDocument[];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Deterministic, dependency-free short hash used to derive stable SPDX document namespaces / CycloneDX serial numbers from `BomInput.subjectId`. Not a security hash — just stable and collision-unlikely enough for a namespace suffix. */
|
|
124
|
+
function stableId(input: string): string {
|
|
125
|
+
let hash = 0;
|
|
126
|
+
for (let i = 0; i < input.length; i++) {
|
|
127
|
+
hash = (Math.imul(hash, 31) + input.charCodeAt(i)) | 0;
|
|
128
|
+
}
|
|
129
|
+
return Math.abs(hash).toString(16).padStart(8, "0");
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/** SPDX identifiers may only contain letters, digits, `.`, and `-` (SPDX-2.3 §2.2). */
|
|
133
|
+
function spdxSafeId(name: string): string {
|
|
134
|
+
return name.replace(/[^a-zA-Z0-9.-]/g, "-");
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ── SPDX-2.3 JSON ────────────────────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Build one SPDX package record + its `DEPENDS_ON` relationship edges for a
|
|
141
|
+
* flat package list, with SPDX IDs namespaced under `idPrefix` so a
|
|
142
|
+
* sub-document's packages (#614 assembly) never collide with the root's own
|
|
143
|
+
* or another sub-document's, even when two leaf BOMs happen to enumerate a
|
|
144
|
+
* same-named package.
|
|
145
|
+
*/
|
|
146
|
+
function spdxPackagesAndDeps(
|
|
147
|
+
packages: BomPackage[],
|
|
148
|
+
idPrefix: string,
|
|
149
|
+
): { packages: unknown[]; relationships: Array<{ spdxElementId: string; relationshipType: string; relatedSpdxElement: string }> } {
|
|
150
|
+
const spdxId = (name: string) => `SPDXRef-Package-${idPrefix}${spdxSafeId(name)}`;
|
|
151
|
+
const pkgs = packages.map((pkg) => ({
|
|
152
|
+
name: pkg.name,
|
|
153
|
+
SPDXID: spdxId(pkg.name),
|
|
154
|
+
versionInfo: pkg.version ?? "NOASSERTION",
|
|
155
|
+
downloadLocation: pkg.downloadLocation ?? "NOASSERTION",
|
|
156
|
+
filesAnalyzed: false,
|
|
157
|
+
licenseConcluded: "NOASSERTION",
|
|
158
|
+
licenseDeclared: "NOASSERTION",
|
|
159
|
+
copyrightText: "NOASSERTION",
|
|
160
|
+
...(pkg.purl
|
|
161
|
+
? {
|
|
162
|
+
externalRefs: [
|
|
163
|
+
{ referenceCategory: "PACKAGE-MANAGER", referenceType: "purl", referenceLocator: pkg.purl },
|
|
164
|
+
],
|
|
165
|
+
}
|
|
166
|
+
: {}),
|
|
167
|
+
}));
|
|
168
|
+
const relationships = packages.flatMap((pkg) =>
|
|
169
|
+
(pkg.dependsOn ?? []).map((dep) => ({
|
|
170
|
+
spdxElementId: spdxId(pkg.name),
|
|
171
|
+
relationshipType: "DEPENDS_ON",
|
|
172
|
+
relatedSpdxElement: spdxId(dep),
|
|
173
|
+
})),
|
|
174
|
+
);
|
|
175
|
+
return { packages: pkgs, relationships };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Native SPDX-2.3 JSON writer. Emits the required top-level fields
|
|
180
|
+
* (`spdxVersion`, `dataLicense`, `SPDXID`, `name`, `documentNamespace`,
|
|
181
|
+
* `creationInfo`, `packages`) plus a `relationships` array recording the
|
|
182
|
+
* document's `DESCRIBES` edge to the subject package and each package's
|
|
183
|
+
* declared `DEPENDS_ON` edges — the "valid relationships section where
|
|
184
|
+
* applicable" #613 calls for.
|
|
185
|
+
*
|
|
186
|
+
* When `input.subDocuments` is present (#614 assembly), each sub-document's
|
|
187
|
+
* own root package is emitted alongside the top-level subject, linked from
|
|
188
|
+
* the top-level subject via a `CONTAINS` relationship, with that
|
|
189
|
+
* sub-document's own packages and `DEPENDS_ON` edges nested under an
|
|
190
|
+
* SPDX-ID namespace unique to it (`SPDXRef-Package-sub<n>-...`) so packages
|
|
191
|
+
* from different leaf BOMs never collide even if same-named.
|
|
192
|
+
*/
|
|
193
|
+
export function writeSpdx(input: BomInput, now: () => Date = () => new Date()): string {
|
|
194
|
+
const rootId = "SPDXRef-DOCUMENT";
|
|
195
|
+
const subjectSpdxId = `SPDXRef-Package-${spdxSafeId(input.subjectName)}`;
|
|
196
|
+
|
|
197
|
+
const { packages, relationships: packageRelationships } = spdxPackagesAndDeps(input.packages, "");
|
|
198
|
+
|
|
199
|
+
const relationships: Array<{ spdxElementId: string; relationshipType: string; relatedSpdxElement: string }> = [
|
|
200
|
+
{ spdxElementId: rootId, relationshipType: "DESCRIBES", relatedSpdxElement: subjectSpdxId },
|
|
201
|
+
...packageRelationships,
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
const subDocumentPackages: unknown[] = [];
|
|
205
|
+
(input.subDocuments ?? []).forEach((sub, i) => {
|
|
206
|
+
const subRootId = `SPDXRef-Package-sub${i}-${spdxSafeId(sub.subjectName)}`;
|
|
207
|
+
subDocumentPackages.push({
|
|
208
|
+
name: sub.subjectName,
|
|
209
|
+
SPDXID: subRootId,
|
|
210
|
+
versionInfo: sub.subjectVersion ?? "NOASSERTION",
|
|
211
|
+
downloadLocation: "NOASSERTION",
|
|
212
|
+
filesAnalyzed: false,
|
|
213
|
+
licenseConcluded: "NOASSERTION",
|
|
214
|
+
licenseDeclared: "NOASSERTION",
|
|
215
|
+
copyrightText: "NOASSERTION",
|
|
216
|
+
comment: `subjectId: ${sub.subjectId}`,
|
|
217
|
+
});
|
|
218
|
+
relationships.push({
|
|
219
|
+
spdxElementId: subjectSpdxId,
|
|
220
|
+
relationshipType: "CONTAINS",
|
|
221
|
+
relatedSpdxElement: subRootId,
|
|
222
|
+
});
|
|
223
|
+
const { packages: subPkgs, relationships: subRels } = spdxPackagesAndDeps(sub.packages, `sub${i}-`);
|
|
224
|
+
subDocumentPackages.push(...subPkgs);
|
|
225
|
+
relationships.push(
|
|
226
|
+
// CONTAINS (not DEPENDS_ON): membership in this leaf BOM, not a
|
|
227
|
+
// literal dependency edge — the leaf's own DEPENDS_ON edges (from
|
|
228
|
+
// BomPackage.dependsOn) are `subRels`, pushed separately below.
|
|
229
|
+
...subPkgs.map((p) => ({
|
|
230
|
+
spdxElementId: subRootId,
|
|
231
|
+
relationshipType: "CONTAINS",
|
|
232
|
+
relatedSpdxElement: (p as { SPDXID: string }).SPDXID,
|
|
233
|
+
})),
|
|
234
|
+
...subRels,
|
|
235
|
+
);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
const document = {
|
|
239
|
+
spdxVersion: "SPDX-2.3",
|
|
240
|
+
dataLicense: "CC0-1.0",
|
|
241
|
+
SPDXID: rootId,
|
|
242
|
+
name: input.subjectName,
|
|
243
|
+
documentNamespace: `https://chant.dev/spdxdocs/${spdxSafeId(input.subjectName)}-${stableId(input.subjectId)}`,
|
|
244
|
+
creationInfo: {
|
|
245
|
+
created: now().toISOString(),
|
|
246
|
+
creators: [`Tool: ${input.generator}`],
|
|
247
|
+
},
|
|
248
|
+
packages: [
|
|
249
|
+
{
|
|
250
|
+
name: input.subjectName,
|
|
251
|
+
SPDXID: subjectSpdxId,
|
|
252
|
+
versionInfo: input.subjectVersion ?? "NOASSERTION",
|
|
253
|
+
downloadLocation: "NOASSERTION",
|
|
254
|
+
filesAnalyzed: false,
|
|
255
|
+
licenseConcluded: "NOASSERTION",
|
|
256
|
+
licenseDeclared: "NOASSERTION",
|
|
257
|
+
copyrightText: "NOASSERTION",
|
|
258
|
+
},
|
|
259
|
+
...packages,
|
|
260
|
+
...subDocumentPackages,
|
|
261
|
+
],
|
|
262
|
+
relationships,
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
return JSON.stringify(document, null, 2);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ── CycloneDX-1.5 JSON ───────────────────────────────────────────────────────
|
|
269
|
+
|
|
270
|
+
/** Project a flat package list into CycloneDX `components`, with `bom-ref`s namespaced under `refPrefix` so a sub-document's components (#614 assembly) never collide with the root's own or another sub-document's. */
|
|
271
|
+
function cycloneDxComponents(packages: BomPackage[], refPrefix: string): Array<Record<string, unknown>> {
|
|
272
|
+
return packages.map((pkg) => ({
|
|
273
|
+
type: pkg.type === "config" ? "application" : "library",
|
|
274
|
+
"bom-ref": `${refPrefix}${pkg.name}`,
|
|
275
|
+
name: pkg.name,
|
|
276
|
+
...(pkg.version ? { version: pkg.version } : {}),
|
|
277
|
+
...(pkg.purl ? { purl: pkg.purl } : {}),
|
|
278
|
+
}));
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** Project a flat package list's `dependsOn` edges into CycloneDX `dependencies` entries, `bom-ref`s namespaced the same way `cycloneDxComponents` namespaces its components. */
|
|
282
|
+
function cycloneDxDependencies(packages: BomPackage[], refPrefix: string): Array<{ ref: string; dependsOn: string[] }> {
|
|
283
|
+
return packages
|
|
284
|
+
.filter((p) => (p.dependsOn?.length ?? 0) > 0)
|
|
285
|
+
.map((p) => ({ ref: `${refPrefix}${p.name}`, dependsOn: p.dependsOn!.map((d) => `${refPrefix}${d}`) }));
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Native CycloneDX-1.5 JSON writer. Emits the required top-level fields
|
|
290
|
+
* (`bomFormat`, `specVersion`, `serialNumber`, `version`, `metadata`,
|
|
291
|
+
* `components`) plus a `dependencies` array recording the root subject's
|
|
292
|
+
* direct dependencies and each component's own declared dependencies.
|
|
293
|
+
*
|
|
294
|
+
* When `input.subDocuments` is present (#614 assembly), each sub-document is
|
|
295
|
+
* projected as a real nested component under `metadata.component.components`
|
|
296
|
+
* (CycloneDX's own "hierarchical representation of component assemblies,
|
|
297
|
+
* similar to system -> subsystem -> parts" per its schema doc) — never
|
|
298
|
+
* flattened into the root's own `components` array — with a `compositions`
|
|
299
|
+
* entry recording the aggregate as `"incomplete"` (this document composes
|
|
300
|
+
* exactly the leaf BOMs it was given, not a closed-world claim over
|
|
301
|
+
* everything the artifact might contain) and a root `dependencies` edge from
|
|
302
|
+
* the top-level subject to each sub-document's root.
|
|
303
|
+
*/
|
|
304
|
+
export function writeCycloneDx(input: BomInput, now: () => Date = () => new Date()): string {
|
|
305
|
+
const rootRef = `subject:${input.subjectName}`;
|
|
306
|
+
|
|
307
|
+
const components = cycloneDxComponents(input.packages, "");
|
|
308
|
+
|
|
309
|
+
const subDocuments = input.subDocuments ?? [];
|
|
310
|
+
const subComponents = subDocuments.map((sub, i) => {
|
|
311
|
+
const refPrefix = `sub${i}:`;
|
|
312
|
+
const subRef = `${refPrefix}subject:${sub.subjectName}`;
|
|
313
|
+
return {
|
|
314
|
+
type: "application",
|
|
315
|
+
"bom-ref": subRef,
|
|
316
|
+
name: sub.subjectName,
|
|
317
|
+
...(sub.subjectVersion ? { version: sub.subjectVersion } : {}),
|
|
318
|
+
properties: [{ name: "chant:subjectId", value: sub.subjectId }],
|
|
319
|
+
components: cycloneDxComponents(sub.packages, refPrefix),
|
|
320
|
+
};
|
|
321
|
+
});
|
|
322
|
+
const subDependencies = subDocuments.flatMap((sub, i) => {
|
|
323
|
+
const refPrefix = `sub${i}:`;
|
|
324
|
+
const subRef = `${refPrefix}subject:${sub.subjectName}`;
|
|
325
|
+
return [
|
|
326
|
+
{ ref: subRef, dependsOn: sub.packages.map((p) => `${refPrefix}${p.name}`) },
|
|
327
|
+
...cycloneDxDependencies(sub.packages, refPrefix),
|
|
328
|
+
];
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// Root subject depends on its own flat packages plus, when this is an
|
|
332
|
+
// assembly (#614), each sub-document's own root — one entry, not two, so
|
|
333
|
+
// the root ref never appears twice in `dependencies`.
|
|
334
|
+
const rootDependsOn = [...input.packages.map((p) => p.name), ...subComponents.map((c) => c["bom-ref"] as string)];
|
|
335
|
+
const dependencies = [
|
|
336
|
+
{ ref: rootRef, dependsOn: rootDependsOn },
|
|
337
|
+
...cycloneDxDependencies(input.packages, ""),
|
|
338
|
+
...subDependencies,
|
|
339
|
+
];
|
|
340
|
+
|
|
341
|
+
const compositions =
|
|
342
|
+
subDocuments.length > 0
|
|
343
|
+
? [{ aggregate: "incomplete" as const, assemblies: [rootRef, ...subComponents.map((c) => c["bom-ref"] as string)] }]
|
|
344
|
+
: undefined;
|
|
345
|
+
|
|
346
|
+
const document = {
|
|
347
|
+
bomFormat: "CycloneDX",
|
|
348
|
+
specVersion: "1.5",
|
|
349
|
+
serialNumber: `urn:uuid:${toUuidLike(stableId(input.subjectId) + stableId(input.subjectName))}`,
|
|
350
|
+
version: 1,
|
|
351
|
+
metadata: {
|
|
352
|
+
timestamp: now().toISOString(),
|
|
353
|
+
tools: [{ name: input.generator }],
|
|
354
|
+
component: {
|
|
355
|
+
type: "application",
|
|
356
|
+
"bom-ref": rootRef,
|
|
357
|
+
name: input.subjectName,
|
|
358
|
+
...(input.subjectVersion ? { version: input.subjectVersion } : {}),
|
|
359
|
+
...(subComponents.length > 0 ? { components: subComponents } : {}),
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
components,
|
|
363
|
+
dependencies,
|
|
364
|
+
...(compositions ? { compositions } : {}),
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
return JSON.stringify(document, null, 2);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** Format an 8-hex-char stable id into a UUID-shaped (but not random/RFC-4122-compliant) string, since CycloneDX only requires `serialNumber` to match `urn:uuid:<uuid-form>` syntactically, not to be a real random UUID — determinism matters more here than entropy. */
|
|
371
|
+
function toUuidLike(seed: string): string {
|
|
372
|
+
const padded = (seed + seed + seed + seed).slice(0, 32);
|
|
373
|
+
return `${padded.slice(0, 8)}-${padded.slice(8, 12)}-4${padded.slice(13, 16)}-8${padded.slice(17, 20)}-${padded.slice(20, 32)}`;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ── format dispatch ──────────────────────────────────────────────────────────
|
|
377
|
+
|
|
378
|
+
/** Serialize `input` in the requested `format` and wrap it as the project-wide `{ format, mediaType, bytes }` doc shape (./sbom-generator.ts's `SbomDocument`, minus `generator`/`packageCount` which the caller fills in). */
|
|
379
|
+
export function writeBom(
|
|
380
|
+
format: SbomFormat,
|
|
381
|
+
input: BomInput,
|
|
382
|
+
now?: () => Date,
|
|
383
|
+
): { format: SbomFormat; mediaType: string; bytes: string } {
|
|
384
|
+
const bytes = format === "spdx" ? writeSpdx(input, now) : writeCycloneDx(input, now);
|
|
385
|
+
return { format, mediaType: SBOM_MEDIA_TYPES[format], bytes };
|
|
386
|
+
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
addArchiveEntry,
|
|
4
|
+
archiveRelativePath,
|
|
5
|
+
artifactEntries,
|
|
6
|
+
assetEntries,
|
|
7
|
+
computeManifestDigest,
|
|
8
|
+
createBuildArchiveManifest,
|
|
9
|
+
findArchiveEntry,
|
|
10
|
+
findSbomForSubject,
|
|
11
|
+
imageEntries,
|
|
12
|
+
sbomEntries,
|
|
13
|
+
} from "./build-archive";
|
|
14
|
+
|
|
15
|
+
describe("BuildArchive manifest (#564)", () => {
|
|
16
|
+
it("starts empty and content-addressed", () => {
|
|
17
|
+
const manifest = createBuildArchiveManifest("search-service");
|
|
18
|
+
expect(manifest.version).toBe(1);
|
|
19
|
+
expect(manifest.component).toBe("search-service");
|
|
20
|
+
expect(manifest.contents).toEqual([]);
|
|
21
|
+
expect(manifest.manifestDigest).toMatch(/^sha256:/);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("adding an entry changes the manifest digest and records the entry", () => {
|
|
25
|
+
const empty = createBuildArchiveManifest("search-service");
|
|
26
|
+
const withImage = addArchiveEntry(empty, {
|
|
27
|
+
kind: "image",
|
|
28
|
+
path: "archive/search.tar",
|
|
29
|
+
digest: "sha256:abc123",
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expect(withImage.manifestDigest).not.toBe(empty.manifestDigest);
|
|
33
|
+
expect(withImage.contents).toHaveLength(1);
|
|
34
|
+
expect(findArchiveEntry(withImage, "archive/search.tar")).toMatchObject({
|
|
35
|
+
kind: "image",
|
|
36
|
+
digest: "sha256:abc123",
|
|
37
|
+
mediaType: "application/vnd.oci.image.layout.v1.tar",
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("is content-addressed: two manifests built from the same entries produce the same digest, regardless of createdAt or insertion order", () => {
|
|
42
|
+
const a = addArchiveEntry(
|
|
43
|
+
addArchiveEntry(createBuildArchiveManifest("svc", { now: () => new Date("2024-01-01") }), {
|
|
44
|
+
kind: "image",
|
|
45
|
+
path: "archive/x.tar",
|
|
46
|
+
digest: "sha256:111",
|
|
47
|
+
}),
|
|
48
|
+
{ kind: "template", path: "x.template.json", digest: "sha256:222" },
|
|
49
|
+
);
|
|
50
|
+
const b = addArchiveEntry(
|
|
51
|
+
addArchiveEntry(createBuildArchiveManifest("svc", { now: () => new Date("2099-12-31") }), {
|
|
52
|
+
kind: "template",
|
|
53
|
+
path: "x.template.json",
|
|
54
|
+
digest: "sha256:222",
|
|
55
|
+
}),
|
|
56
|
+
{ kind: "image", path: "archive/x.tar", digest: "sha256:111" },
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
expect(a.manifestDigest).toBe(b.manifestDigest);
|
|
60
|
+
expect(a.createdAt).not.toBe(b.createdAt);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("changed content changes the digest", () => {
|
|
64
|
+
const base = createBuildArchiveManifest("svc");
|
|
65
|
+
const v1 = addArchiveEntry(base, { kind: "image", path: "archive/x.tar", digest: "sha256:111" });
|
|
66
|
+
const v2 = addArchiveEntry(base, { kind: "image", path: "archive/x.tar", digest: "sha256:999" });
|
|
67
|
+
expect(v1.manifestDigest).not.toBe(v2.manifestDigest);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it("replaces an entry at the same path rather than duplicating it", () => {
|
|
71
|
+
const base = createBuildArchiveManifest("svc");
|
|
72
|
+
const v1 = addArchiveEntry(base, { kind: "image", path: "archive/x.tar", digest: "sha256:111" });
|
|
73
|
+
const v2 = addArchiveEntry(v1, { kind: "image", path: "archive/x.tar", digest: "sha256:222" });
|
|
74
|
+
expect(v2.contents).toHaveLength(1);
|
|
75
|
+
expect(v2.contents[0]!.digest).toBe("sha256:222");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("is immutable: adding an entry never mutates the input manifest", () => {
|
|
79
|
+
const base = createBuildArchiveManifest("svc");
|
|
80
|
+
const before = JSON.stringify(base);
|
|
81
|
+
addArchiveEntry(base, { kind: "asset", path: "lib.jar", digest: "sha256:333" });
|
|
82
|
+
expect(JSON.stringify(base)).toBe(before);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("computeManifestDigest matches what addArchiveEntry produces incrementally", () => {
|
|
86
|
+
const entries = [
|
|
87
|
+
{ kind: "image" as const, path: "archive/x.tar", digest: "sha256:111" },
|
|
88
|
+
{ kind: "template" as const, path: "x.template.json", digest: "sha256:222" },
|
|
89
|
+
];
|
|
90
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
91
|
+
for (const entry of entries) manifest = addArchiveEntry(manifest, entry);
|
|
92
|
+
expect(manifest.manifestDigest).toBe(computeManifestDigest(manifest.contents));
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("imageEntries filters to image-kind entries only", () => {
|
|
96
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
97
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "archive/a.tar", digest: "sha256:1" });
|
|
98
|
+
manifest = addArchiveEntry(manifest, { kind: "template", path: "a.template.json", digest: "sha256:2" });
|
|
99
|
+
manifest = addArchiveEntry(manifest, { kind: "asset", path: "a.jar", digest: "sha256:3" });
|
|
100
|
+
expect(imageEntries(manifest).map((e) => e.path)).toEqual(["archive/a.tar"]);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("archiveRelativePath strips the archive: wiring prefix and passes plain paths through unchanged", () => {
|
|
104
|
+
expect(archiveRelativePath("archive:search.template.json")).toBe("search.template.json");
|
|
105
|
+
expect(archiveRelativePath("archive/search.tar")).toBe("archive/search.tar");
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe("sbom entries (#606)", () => {
|
|
109
|
+
it("sbomEntries filters to sbom-kind entries only", () => {
|
|
110
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
111
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "archive/a.tar", digest: "sha256:1" });
|
|
112
|
+
manifest = addArchiveEntry(manifest, {
|
|
113
|
+
kind: "sbom",
|
|
114
|
+
path: "archive/a.tar.sbom.json",
|
|
115
|
+
digest: "sha256:sbom1",
|
|
116
|
+
mediaType: "application/spdx+json",
|
|
117
|
+
subjectDigest: "sha256:1",
|
|
118
|
+
});
|
|
119
|
+
expect(sbomEntries(manifest).map((e) => e.path)).toEqual(["archive/a.tar.sbom.json"]);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("findSbomForSubject finds the sbom entry attached to a given subject digest", () => {
|
|
123
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
124
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "archive/a.tar", digest: "sha256:1" });
|
|
125
|
+
manifest = addArchiveEntry(manifest, {
|
|
126
|
+
kind: "sbom",
|
|
127
|
+
path: "archive/a.tar.sbom.json",
|
|
128
|
+
digest: "sha256:sbom1",
|
|
129
|
+
mediaType: "application/vnd.cyclonedx+json",
|
|
130
|
+
subjectDigest: "sha256:1",
|
|
131
|
+
});
|
|
132
|
+
expect(findSbomForSubject(manifest, "sha256:1")).toMatchObject({
|
|
133
|
+
path: "archive/a.tar.sbom.json",
|
|
134
|
+
mediaType: "application/vnd.cyclonedx+json",
|
|
135
|
+
});
|
|
136
|
+
expect(findSbomForSubject(manifest, "sha256:nonexistent")).toBeUndefined();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("sbom entry mediaType is never hardcoded to one format — carries whatever the generator produced", () => {
|
|
140
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
141
|
+
manifest = addArchiveEntry(manifest, {
|
|
142
|
+
kind: "sbom",
|
|
143
|
+
path: "a.sbom.json",
|
|
144
|
+
digest: "sha256:s1",
|
|
145
|
+
mediaType: "application/spdx+json",
|
|
146
|
+
subjectDigest: "sha256:1",
|
|
147
|
+
});
|
|
148
|
+
manifest = addArchiveEntry(manifest, {
|
|
149
|
+
kind: "sbom",
|
|
150
|
+
path: "b.sbom.json",
|
|
151
|
+
digest: "sha256:s2",
|
|
152
|
+
mediaType: "application/vnd.cyclonedx+json",
|
|
153
|
+
subjectDigest: "sha256:2",
|
|
154
|
+
});
|
|
155
|
+
const mediaTypes = sbomEntries(manifest).map((e) => e.mediaType).sort();
|
|
156
|
+
expect(mediaTypes).toEqual(["application/spdx+json", "application/vnd.cyclonedx+json"]);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe("per-artifact reproducibility (#614)", () => {
|
|
161
|
+
it("a template entry defaults to deterministic-synthesis when the caller supplies none", () => {
|
|
162
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
163
|
+
manifest = addArchiveEntry(manifest, { kind: "template", path: "x.template.json", digest: "sha256:1" });
|
|
164
|
+
expect(findArchiveEntry(manifest, "x.template.json")!.reproducibility).toEqual({
|
|
165
|
+
basis: "deterministic-synthesis",
|
|
166
|
+
verifyBy: "re-synth",
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("an image entry defaults to best-effort", () => {
|
|
171
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
172
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "a.tar", digest: "sha256:1" });
|
|
173
|
+
expect(findArchiveEntry(manifest, "a.tar")!.reproducibility).toEqual({ basis: "best-effort" });
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it("an asset entry defaults to best-effort", () => {
|
|
177
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
178
|
+
manifest = addArchiveEntry(manifest, { kind: "asset", path: "lib.jar", digest: "sha256:1" });
|
|
179
|
+
expect(findArchiveEntry(manifest, "lib.jar")!.reproducibility).toEqual({ basis: "best-effort" });
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it("an sbom entry gets no reproducibility default — it describes another artifact, not one itself", () => {
|
|
183
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
184
|
+
manifest = addArchiveEntry(manifest, {
|
|
185
|
+
kind: "sbom",
|
|
186
|
+
path: "a.sbom.json",
|
|
187
|
+
digest: "sha256:1",
|
|
188
|
+
subjectDigest: "sha256:img",
|
|
189
|
+
});
|
|
190
|
+
expect(findArchiveEntry(manifest, "a.sbom.json")!.reproducibility).toBeUndefined();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it("an explicit reproducibility always overrides the kind-appropriate default", () => {
|
|
194
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
195
|
+
manifest = addArchiveEntry(manifest, {
|
|
196
|
+
kind: "image",
|
|
197
|
+
path: "a.tar",
|
|
198
|
+
digest: "sha256:1",
|
|
199
|
+
reproducibility: { basis: "deterministic-synthesis", verifyBy: "re-synth" },
|
|
200
|
+
});
|
|
201
|
+
expect(findArchiveEntry(manifest, "a.tar")!.reproducibility).toEqual({
|
|
202
|
+
basis: "deterministic-synthesis",
|
|
203
|
+
verifyBy: "re-synth",
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("reproducibility is honest per artifact — a template and an image in the same manifest carry different bases", () => {
|
|
208
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
209
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "a.tar", digest: "sha256:1" });
|
|
210
|
+
manifest = addArchiveEntry(manifest, { kind: "template", path: "a.template.json", digest: "sha256:2" });
|
|
211
|
+
expect(findArchiveEntry(manifest, "a.tar")!.reproducibility!.basis).toBe("best-effort");
|
|
212
|
+
expect(findArchiveEntry(manifest, "a.template.json")!.reproducibility!.basis).toBe("deterministic-synthesis");
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it("reproducibility/provenance never perturb manifestDigest — recording them later doesn't change archive identity", () => {
|
|
216
|
+
const base = createBuildArchiveManifest("svc");
|
|
217
|
+
const withDefault = addArchiveEntry(base, { kind: "image", path: "a.tar", digest: "sha256:1" });
|
|
218
|
+
const withExplicit = addArchiveEntry(base, {
|
|
219
|
+
kind: "image",
|
|
220
|
+
path: "a.tar",
|
|
221
|
+
digest: "sha256:1",
|
|
222
|
+
reproducibility: { basis: "deterministic-synthesis", verifyBy: "re-synth" },
|
|
223
|
+
provenance: { sourceRef: "abc123", artifactDigest: "sha256:1" },
|
|
224
|
+
});
|
|
225
|
+
expect(withDefault.manifestDigest).toBe(withExplicit.manifestDigest);
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
describe("artifact accessors (#614)", () => {
|
|
230
|
+
it("assetEntries filters to asset-kind entries only", () => {
|
|
231
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
232
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "a.tar", digest: "sha256:1" });
|
|
233
|
+
manifest = addArchiveEntry(manifest, { kind: "asset", path: "lib.jar", digest: "sha256:2" });
|
|
234
|
+
expect(assetEntries(manifest).map((e) => e.path)).toEqual(["lib.jar"]);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it("artifactEntries returns image/template/asset entries but excludes sbom entries", () => {
|
|
238
|
+
let manifest = createBuildArchiveManifest("svc");
|
|
239
|
+
manifest = addArchiveEntry(manifest, { kind: "image", path: "a.tar", digest: "sha256:1" });
|
|
240
|
+
manifest = addArchiveEntry(manifest, { kind: "template", path: "a.template.json", digest: "sha256:2" });
|
|
241
|
+
manifest = addArchiveEntry(manifest, { kind: "asset", path: "lib.jar", digest: "sha256:3" });
|
|
242
|
+
manifest = addArchiveEntry(manifest, {
|
|
243
|
+
kind: "sbom",
|
|
244
|
+
path: "a.tar.sbom.json",
|
|
245
|
+
digest: "sha256:4",
|
|
246
|
+
subjectDigest: "sha256:1",
|
|
247
|
+
});
|
|
248
|
+
expect(artifactEntries(manifest).map((e) => e.path).sort()).toEqual(["a.tar", "a.template.json", "lib.jar"]);
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
});
|