@effected/sbom 0.9.1 → 0.10.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/IdentityToken.js +9 -1
- package/InTotoStatement.js +10 -6
- package/NtiaReport.js +6 -3
- package/README.md +5 -5
- package/Sbom.js +6 -4
- package/SbomDocument.js +1 -1
- package/SbomMetadataSource.js +11 -8
- package/SigstoreBundle.js +2 -2
- package/SigstoreSigner.js +18 -17
- package/SlsaProvenance.js +14 -1
- package/index.d.ts +80 -39
- package/package.json +4 -4
package/IdentityToken.js
CHANGED
|
@@ -24,13 +24,21 @@ var IdentityTokenError = class extends Schema.TaggedError()("IdentityTokenError"
|
|
|
24
24
|
/** The token every double answers with unless a test says otherwise. */
|
|
25
25
|
const TEST_TOKEN = "test-identity-token";
|
|
26
26
|
/**
|
|
27
|
-
* A source of workload identity tokens
|
|
27
|
+
* A source of workload identity tokens (OIDC), the input `SigstoreSigner` needs
|
|
28
|
+
* to obtain a signing certificate.
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* In GitHub Actions, provide `ActionsIdentityToken.layer` from
|
|
32
|
+
* `@effected/github-actions`. A consumer that already holds a token uses
|
|
33
|
+
* {@link (IdentityToken:class).layerStatic}; tests use
|
|
34
|
+
* {@link (IdentityToken:class).layerTest}.
|
|
28
35
|
*
|
|
29
36
|
* @example
|
|
30
37
|
* ```ts
|
|
31
38
|
* import { IdentityToken, SigstoreSigner } from "@effected/sbom";
|
|
32
39
|
* import { Layer } from "effect";
|
|
33
40
|
*
|
|
41
|
+
* const token = process.env.OIDC_TOKEN ?? "";
|
|
34
42
|
* const layer = SigstoreSigner.layer.pipe(Layer.provide(IdentityToken.layerStatic(token)));
|
|
35
43
|
* ```
|
|
36
44
|
*
|
package/InTotoStatement.js
CHANGED
|
@@ -45,7 +45,7 @@ const parseResult = (value) => {
|
|
|
45
45
|
* A deliberate small duplication rather than a shared package: `@effected/github`
|
|
46
46
|
* types the same value structurally on its attestation surface, and dragging a
|
|
47
47
|
* package across that seam to share one branded string would cost more than the
|
|
48
|
-
* duplication does.
|
|
48
|
+
* duplication does.
|
|
49
49
|
*
|
|
50
50
|
* @public
|
|
51
51
|
*/
|
|
@@ -96,12 +96,16 @@ var InTotoSubject = class InTotoSubject extends Schema.Class("InTotoSubject")({
|
|
|
96
96
|
* @example
|
|
97
97
|
* ```ts
|
|
98
98
|
* import { InTotoStatement, Sha256Digest, SlsaProvenance } from "@effected/sbom";
|
|
99
|
+
* import { Effect } from "effect";
|
|
99
100
|
*
|
|
100
|
-
* const
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
101
|
+
* const program = Effect.gen(function* () {
|
|
102
|
+
* const digest = yield* Sha256Digest.parse("ab".repeat(32));
|
|
103
|
+
* return InTotoStatement.forSubject({
|
|
104
|
+
* name: "pkg:npm/%40scope/pkg@1.0.0",
|
|
105
|
+
* digest,
|
|
106
|
+
* predicateType: SlsaProvenance.predicateType,
|
|
107
|
+
* predicate: {}, // a SlsaProvenance, a BOM, or any predicate body
|
|
108
|
+
* });
|
|
105
109
|
* });
|
|
106
110
|
* ```
|
|
107
111
|
*
|
package/NtiaReport.js
CHANGED
|
@@ -102,10 +102,13 @@ const timestamp = (document) => {
|
|
|
102
102
|
*
|
|
103
103
|
* @example
|
|
104
104
|
* ```ts
|
|
105
|
-
* import { NtiaReport } from "@effected/sbom";
|
|
105
|
+
* import { Component, NtiaReport, Sbom } from "@effected/sbom";
|
|
106
106
|
*
|
|
107
|
-
* const
|
|
108
|
-
*
|
|
107
|
+
* const root = Component.make({ type: "library", name: "lib", version: "1.0.0" });
|
|
108
|
+
* const report = NtiaReport.of(Sbom.generate({ root, components: [] }));
|
|
109
|
+
*
|
|
110
|
+
* report.compliant; // => false
|
|
111
|
+
* report.missing; // => ["supplierName", "uniqueIdentifier", "sbomAuthor", "timestamp"]
|
|
109
112
|
* ```
|
|
110
113
|
*
|
|
111
114
|
* @public
|
package/README.md
CHANGED
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
|
|
8
8
|
Supply-chain artifacts for [Effect](https://effect.website) v4: a CycloneDX 1.6 SBOM, an NTIA minimum-elements report, in-toto statements, SLSA provenance, and Sigstore DSSE signing. `Sbom.generate` and `Sbom.toJson` are total, plain functions — assembling and serializing a document cannot fail, so the package's only error channel belongs to `Sbom.write`, the one member that touches a filesystem. Signing is a separate, deliberately walled-off capability: a consumer that only ever emits an SBOM never reaches `@sigstore/*` or its Fulcio/Rekor network calls.
|
|
9
9
|
|
|
10
|
-
> **Pre
|
|
11
|
-
>
|
|
12
|
-
> `1.0.0`
|
|
13
|
-
>
|
|
10
|
+
> **Pre-`1.0.0`.** This package is part of the `@effected/*` kit, built on stable
|
|
11
|
+
> Effect v4 (`effect` `^4.0.0`) and still in `0.x` development. Stable Effect
|
|
12
|
+
> makes a kit `1.0.0` possible, not automatic. To keep your `effect` and
|
|
13
|
+
> `@effect/*` versions on the line the kit is built and tested against, install
|
|
14
14
|
> [`@effected/pnpm-plugin-effect`](https://www.npmjs.com/package/@effected/pnpm-plugin-effect).
|
|
15
15
|
>
|
|
16
16
|
> **Stability: unstable.** This package's API surface is not yet considered
|
|
@@ -53,7 +53,7 @@ declare const pkg: Package;
|
|
|
53
53
|
const root = SbomMetadataSource.rootComponent(pkg);
|
|
54
54
|
const metadata = SbomMetadataSource.fromPackage(pkg, { timestamp: new Date().toISOString() });
|
|
55
55
|
const components = [
|
|
56
|
-
SbomMetadataSource.componentFor({ name: "effect", version: "4.0.0
|
|
56
|
+
SbomMetadataSource.componentFor({ name: "effect", version: "4.0.0", license: "MIT" }),
|
|
57
57
|
];
|
|
58
58
|
|
|
59
59
|
const document = Sbom.generate({ root, components, metadata });
|
package/Sbom.js
CHANGED
|
@@ -57,10 +57,11 @@ const write = Effect.fn("Sbom.write")(function* (document, path, options) {
|
|
|
57
57
|
*
|
|
58
58
|
* @example
|
|
59
59
|
* ```ts
|
|
60
|
-
* import { Sbom } from "@effected/sbom";
|
|
60
|
+
* import { Component, Sbom } from "@effected/sbom";
|
|
61
61
|
*
|
|
62
|
-
* const
|
|
63
|
-
* const
|
|
62
|
+
* const root = Component.make({ type: "application", name: "app", version: "1.0.0" });
|
|
63
|
+
* const document = Sbom.generate({ root, components: [] });
|
|
64
|
+
* const json = Sbom.toJson(document); // CycloneDX 1.6 JSON text
|
|
64
65
|
* ```
|
|
65
66
|
*
|
|
66
67
|
* @public
|
|
@@ -89,7 +90,8 @@ var Sbom = class {
|
|
|
89
90
|
* Write a document to `path` as canonical JSON.
|
|
90
91
|
*
|
|
91
92
|
* @remarks
|
|
92
|
-
* The one fallible member
|
|
93
|
+
* The one fallible member; it fails with {@link SbomWriteError} and requires
|
|
94
|
+
* `FileSystem` in `R`. It does not create parent directories — a caller
|
|
93
95
|
* that wants one creates it, so the failure mode stays "the path you gave me
|
|
94
96
|
* is not writable" rather than "something was created somewhere".
|
|
95
97
|
*/
|
package/SbomDocument.js
CHANGED
|
@@ -84,7 +84,7 @@ var Supplier = class extends Schema.Class("Supplier")({
|
|
|
84
84
|
* @remarks
|
|
85
85
|
* `bomRef` is spelled **`bom-ref`** in the emitted JSON; the rename happens in
|
|
86
86
|
* `Sbom.toJson`. Emitting `bomRef` produces a document that looks
|
|
87
|
-
* correct and validates wrong
|
|
87
|
+
* correct and validates wrong.
|
|
88
88
|
*
|
|
89
89
|
* @public
|
|
90
90
|
*/
|
package/SbomMetadataSource.js
CHANGED
|
@@ -108,11 +108,16 @@ const merge = (base, override) => {
|
|
|
108
108
|
*
|
|
109
109
|
* @example
|
|
110
110
|
* ```ts
|
|
111
|
-
* import { Sbom, SbomMetadataSource } from "@effected/sbom";
|
|
111
|
+
* import { Package, Sbom, SbomMetadataSource, Supplier } from "@effected/sbom";
|
|
112
|
+
* import { Effect } from "effect";
|
|
112
113
|
*
|
|
113
|
-
* const
|
|
114
|
-
*
|
|
115
|
-
*
|
|
114
|
+
* const program = Effect.gen(function* () {
|
|
115
|
+
* const pkg = yield* Package.decode({ name: "@acme/app", version: "1.0.0", license: "MIT" });
|
|
116
|
+
* const supplier = Supplier.make({ name: "Acme Inc." });
|
|
117
|
+
* const root = SbomMetadataSource.rootComponent(pkg, { supplier });
|
|
118
|
+
* const metadata = SbomMetadataSource.fromPackage(pkg, { supplier, timestamp: "2026-01-01T00:00:00Z" });
|
|
119
|
+
* return Sbom.generate({ root, components: [], metadata });
|
|
120
|
+
* });
|
|
116
121
|
* ```
|
|
117
122
|
*
|
|
118
123
|
* @public
|
|
@@ -184,10 +189,8 @@ var SbomMetadataSource = class {
|
|
|
184
189
|
* A copyright statement for a holder and a year, or a span of years.
|
|
185
190
|
*
|
|
186
191
|
* @remarks
|
|
187
|
-
* The year is an **argument
|
|
188
|
-
*
|
|
189
|
-
* purity a claim rather than a property; the ambient read belongs at the
|
|
190
|
-
* caller's edge.
|
|
192
|
+
* The year is an **argument**: nothing here reads the clock, so the output is
|
|
193
|
+
* deterministic. Read the ambient year at the caller's edge.
|
|
191
194
|
*/
|
|
192
195
|
static formatCopyright = formatCopyright;
|
|
193
196
|
/**
|
package/SigstoreBundle.js
CHANGED
|
@@ -24,8 +24,8 @@ const IN_TOTO_PAYLOAD_TYPE = "application/vnd.in-toto+json";
|
|
|
24
24
|
* @remarks
|
|
25
25
|
* `verificationMaterial` and `dsseEnvelope` are `unknown` because their shapes
|
|
26
26
|
* belong to the Sigstore protobuf specifications, and re-declaring them here
|
|
27
|
-
* would be a second, drifting copy of a wire format
|
|
28
|
-
* is opaque to everything that merely stores or forwards it.
|
|
27
|
+
* would be a second, drifting copy of a wire format this package does not own.
|
|
28
|
+
* The bundle is opaque to everything that merely stores or forwards it.
|
|
29
29
|
*
|
|
30
30
|
* `mediaType` is carried through from what the builder produced rather than
|
|
31
31
|
* asserted — the version is the producer's statement about the bundle, and a
|
package/SigstoreSigner.js
CHANGED
|
@@ -12,9 +12,7 @@ import { DSSEBundleBuilder, FulcioSigner, RekorWitness } from "@sigstore/sign";
|
|
|
12
12
|
* It lives here, not at the call site, because it is the **signing protocol's**
|
|
13
13
|
* requirement rather than the caller's knowledge — which is why
|
|
14
14
|
* {@link SigstoreSignerShape.sign} takes only a statement and asks the identity
|
|
15
|
-
* contract for a token.
|
|
16
|
-
* which reads simpler and forces every caller to learn a constant that is none
|
|
17
|
-
* of its business.
|
|
15
|
+
* contract for a token.
|
|
18
16
|
*
|
|
19
17
|
* @public
|
|
20
18
|
*/
|
|
@@ -51,14 +49,6 @@ var SigningError = class extends Schema.TaggedError()("SigningError", {
|
|
|
51
49
|
return `Failed to sign the statement (${this.kind})`;
|
|
52
50
|
}
|
|
53
51
|
};
|
|
54
|
-
/**
|
|
55
|
-
* Attribute a `@sigstore/sign` failure to a step.
|
|
56
|
-
*
|
|
57
|
-
* `InternalError` carries a `code`, which is a far better signal than the
|
|
58
|
-
* message text the predecessor scraped. An unrecognized failure is `bundle` —
|
|
59
|
-
* literally "the bundle did not get built" — rather than being guessed into a
|
|
60
|
-
* step it may not belong to.
|
|
61
|
-
*/
|
|
62
52
|
const kindOf = (cause) => {
|
|
63
53
|
const code = cause?.code;
|
|
64
54
|
if (typeof code !== "string") return "bundle";
|
|
@@ -113,17 +103,28 @@ const unstubbed = () => {
|
|
|
113
103
|
throw new Error("SigstoreSigner.makeTest: sign() was called but not stubbed — a fabricated bundle would be a signature-shaped lie. Pass a `sign` override, or drive the real builder through SigstoreSigner.layerWith({ signer, witnesses }).");
|
|
114
104
|
};
|
|
115
105
|
/**
|
|
116
|
-
* Sigstore
|
|
106
|
+
* Signs an in-toto statement into a Sigstore DSSE bundle, using a Fulcio
|
|
107
|
+
* certificate and a Rekor transparency-log entry.
|
|
108
|
+
*
|
|
109
|
+
* @remarks
|
|
110
|
+
* Fails with {@link SigningError}. The live {@link (SigstoreSigner:class).layer}
|
|
111
|
+
* requires {@link IdentityToken} in `R`.
|
|
117
112
|
*
|
|
118
113
|
* @example
|
|
119
114
|
* ```ts
|
|
115
|
+
* import type { InTotoStatement } from "@effected/sbom";
|
|
120
116
|
* import { IdentityToken, SigstoreSigner } from "@effected/sbom";
|
|
121
|
-
* import { Effect, Layer } from "effect";
|
|
117
|
+
* import { Effect, Layer, Redacted } from "effect";
|
|
118
|
+
*
|
|
119
|
+
* const sign = (statement: InTotoStatement) =>
|
|
120
|
+
* Effect.gen(function* () {
|
|
121
|
+
* const signer = yield* SigstoreSigner;
|
|
122
|
+
* return yield* signer.sign(statement);
|
|
123
|
+
* });
|
|
122
124
|
*
|
|
123
|
-
* const
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
* });
|
|
125
|
+
* const live = SigstoreSigner.layer.pipe(
|
|
126
|
+
* Layer.provide(IdentityToken.layerStatic(Redacted.make(process.env.OIDC_TOKEN ?? ""))),
|
|
127
|
+
* );
|
|
127
128
|
* ```
|
|
128
129
|
*
|
|
129
130
|
* @public
|
package/SlsaProvenance.js
CHANGED
|
@@ -90,7 +90,20 @@ const workflowPathOf = (input) => input.workflowRef.replace(`${input.repository}
|
|
|
90
90
|
* ```ts
|
|
91
91
|
* import { SlsaProvenance } from "@effected/sbom";
|
|
92
92
|
*
|
|
93
|
-
* const provenance = SlsaProvenance.forGitHubWorkflow(
|
|
93
|
+
* const provenance = SlsaProvenance.forGitHubWorkflow({
|
|
94
|
+
* serverUrl: "https://github.com",
|
|
95
|
+
* repository: "acme/app",
|
|
96
|
+
* ref: "refs/heads/main",
|
|
97
|
+
* sha: "0123456789abcdef0123456789abcdef01234567",
|
|
98
|
+
* eventName: "push",
|
|
99
|
+
* workflowRef: "acme/app/.github/workflows/release.yml@refs/heads/main",
|
|
100
|
+
* jobWorkflowRef: "acme/app/.github/workflows/release.yml@refs/heads/main",
|
|
101
|
+
* repositoryId: "1",
|
|
102
|
+
* repositoryOwnerId: "2",
|
|
103
|
+
* runnerEnvironment: "github-hosted",
|
|
104
|
+
* runId: "3",
|
|
105
|
+
* runAttempt: "1",
|
|
106
|
+
* });
|
|
94
107
|
* ```
|
|
95
108
|
*
|
|
96
109
|
* @public
|
package/index.d.ts
CHANGED
|
@@ -38,13 +38,21 @@ interface IdentityTokenShape {
|
|
|
38
38
|
}
|
|
39
39
|
declare const IdentityToken_base: Context.ServiceClass<IdentityToken, "@effected/sbom/IdentityToken", IdentityTokenShape>;
|
|
40
40
|
/**
|
|
41
|
-
* A source of workload identity tokens
|
|
41
|
+
* A source of workload identity tokens (OIDC), the input `SigstoreSigner` needs
|
|
42
|
+
* to obtain a signing certificate.
|
|
43
|
+
*
|
|
44
|
+
* @remarks
|
|
45
|
+
* In GitHub Actions, provide `ActionsIdentityToken.layer` from
|
|
46
|
+
* `@effected/github-actions`. A consumer that already holds a token uses
|
|
47
|
+
* {@link (IdentityToken:class).layerStatic}; tests use
|
|
48
|
+
* {@link (IdentityToken:class).layerTest}.
|
|
42
49
|
*
|
|
43
50
|
* @example
|
|
44
51
|
* ```ts
|
|
45
52
|
* import { IdentityToken, SigstoreSigner } from "@effected/sbom";
|
|
46
53
|
* import { Layer } from "effect";
|
|
47
54
|
*
|
|
55
|
+
* const token = process.env.OIDC_TOKEN ?? "";
|
|
48
56
|
* const layer = SigstoreSigner.layer.pipe(Layer.provide(IdentityToken.layerStatic(token)));
|
|
49
57
|
* ```
|
|
50
58
|
*
|
|
@@ -124,7 +132,7 @@ export declare class InvalidSha256DigestError extends InvalidSha256DigestError_b
|
|
|
124
132
|
* A deliberate small duplication rather than a shared package: `@effected/github`
|
|
125
133
|
* types the same value structurally on its attestation surface, and dragging a
|
|
126
134
|
* package across that seam to share one branded string would cost more than the
|
|
127
|
-
* duplication does.
|
|
135
|
+
* duplication does.
|
|
128
136
|
*
|
|
129
137
|
* @public
|
|
130
138
|
*/
|
|
@@ -169,7 +177,7 @@ export declare class InTotoSubject extends InTotoSubject_base {
|
|
|
169
177
|
/**
|
|
170
178
|
* Input to {@link (InTotoStatement:class).of}.
|
|
171
179
|
*
|
|
172
|
-
* @
|
|
180
|
+
* @privateRemarks
|
|
173
181
|
* The two predicate members are spelled out here and again on
|
|
174
182
|
* {@link InTotoSubjectInput} rather than shared through a base interface: an
|
|
175
183
|
* internal type named on a `@public` signature is a forgotten export, and a
|
|
@@ -227,12 +235,16 @@ declare const InTotoStatement_base: Schema.Class<InTotoStatement, Schema.Struct<
|
|
|
227
235
|
* @example
|
|
228
236
|
* ```ts
|
|
229
237
|
* import { InTotoStatement, Sha256Digest, SlsaProvenance } from "@effected/sbom";
|
|
238
|
+
* import { Effect } from "effect";
|
|
230
239
|
*
|
|
231
|
-
* const
|
|
232
|
-
*
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
240
|
+
* const program = Effect.gen(function* () {
|
|
241
|
+
* const digest = yield* Sha256Digest.parse("ab".repeat(32));
|
|
242
|
+
* return InTotoStatement.forSubject({
|
|
243
|
+
* name: "pkg:npm/%40scope/pkg@1.0.0",
|
|
244
|
+
* digest,
|
|
245
|
+
* predicateType: SlsaProvenance.predicateType,
|
|
246
|
+
* predicate: {}, // a SlsaProvenance, a BOM, or any predicate body
|
|
247
|
+
* });
|
|
236
248
|
* });
|
|
237
249
|
* ```
|
|
238
250
|
*
|
|
@@ -367,7 +379,7 @@ declare const Component_base: Schema.Class<Component, Schema.Struct<{
|
|
|
367
379
|
* @remarks
|
|
368
380
|
* `bomRef` is spelled **`bom-ref`** in the emitted JSON; the rename happens in
|
|
369
381
|
* `Sbom.toJson`. Emitting `bomRef` produces a document that looks
|
|
370
|
-
* correct and validates wrong
|
|
382
|
+
* correct and validates wrong.
|
|
371
383
|
*
|
|
372
384
|
* @public
|
|
373
385
|
*/
|
|
@@ -452,10 +464,13 @@ declare const NtiaReport_base: Schema.Class<NtiaReport, Schema.Struct<{
|
|
|
452
464
|
*
|
|
453
465
|
* @example
|
|
454
466
|
* ```ts
|
|
455
|
-
* import { NtiaReport } from "@effected/sbom";
|
|
467
|
+
* import { Component, NtiaReport, Sbom } from "@effected/sbom";
|
|
456
468
|
*
|
|
457
|
-
* const
|
|
458
|
-
*
|
|
469
|
+
* const root = Component.make({ type: "library", name: "lib", version: "1.0.0" });
|
|
470
|
+
* const report = NtiaReport.of(Sbom.generate({ root, components: [] }));
|
|
471
|
+
*
|
|
472
|
+
* report.compliant; // => false
|
|
473
|
+
* report.missing; // => ["supplierName", "uniqueIdentifier", "sbomAuthor", "timestamp"]
|
|
459
474
|
* ```
|
|
460
475
|
*
|
|
461
476
|
* @public
|
|
@@ -518,10 +533,11 @@ export declare class SbomWriteError extends SbomWriteError_base {
|
|
|
518
533
|
*
|
|
519
534
|
* @example
|
|
520
535
|
* ```ts
|
|
521
|
-
* import { Sbom } from "@effected/sbom";
|
|
536
|
+
* import { Component, Sbom } from "@effected/sbom";
|
|
522
537
|
*
|
|
523
|
-
* const
|
|
524
|
-
* const
|
|
538
|
+
* const root = Component.make({ type: "application", name: "app", version: "1.0.0" });
|
|
539
|
+
* const document = Sbom.generate({ root, components: [] });
|
|
540
|
+
* const json = Sbom.toJson(document); // CycloneDX 1.6 JSON text
|
|
525
541
|
* ```
|
|
526
542
|
*
|
|
527
543
|
* @public
|
|
@@ -550,7 +566,8 @@ export declare class Sbom {
|
|
|
550
566
|
* Write a document to `path` as canonical JSON.
|
|
551
567
|
*
|
|
552
568
|
* @remarks
|
|
553
|
-
* The one fallible member
|
|
569
|
+
* The one fallible member; it fails with {@link SbomWriteError} and requires
|
|
570
|
+
* `FileSystem` in `R`. It does not create parent directories — a caller
|
|
554
571
|
* that wants one creates it, so the failure mode stays "the path you gave me
|
|
555
572
|
* is not writable" rather than "something was created somewhere".
|
|
556
573
|
*/
|
|
@@ -615,7 +632,7 @@ interface ComponentInput {
|
|
|
615
632
|
interface CopyrightYears {
|
|
616
633
|
/** The first year of the range. Omit for a single-year statement. */
|
|
617
634
|
readonly startYear?: number | undefined;
|
|
618
|
-
/** The year the statement is current through
|
|
635
|
+
/** The year the statement is current through, supplied by the caller. */
|
|
619
636
|
readonly year: number;
|
|
620
637
|
}
|
|
621
638
|
/**
|
|
@@ -623,11 +640,16 @@ interface CopyrightYears {
|
|
|
623
640
|
*
|
|
624
641
|
* @example
|
|
625
642
|
* ```ts
|
|
626
|
-
* import { Sbom, SbomMetadataSource } from "@effected/sbom";
|
|
643
|
+
* import { Package, Sbom, SbomMetadataSource, Supplier } from "@effected/sbom";
|
|
644
|
+
* import { Effect } from "effect";
|
|
627
645
|
*
|
|
628
|
-
* const
|
|
629
|
-
*
|
|
630
|
-
*
|
|
646
|
+
* const program = Effect.gen(function* () {
|
|
647
|
+
* const pkg = yield* Package.decode({ name: "@acme/app", version: "1.0.0", license: "MIT" });
|
|
648
|
+
* const supplier = Supplier.make({ name: "Acme Inc." });
|
|
649
|
+
* const root = SbomMetadataSource.rootComponent(pkg, { supplier });
|
|
650
|
+
* const metadata = SbomMetadataSource.fromPackage(pkg, { supplier, timestamp: "2026-01-01T00:00:00Z" });
|
|
651
|
+
* return Sbom.generate({ root, components: [], metadata });
|
|
652
|
+
* });
|
|
631
653
|
* ```
|
|
632
654
|
*
|
|
633
655
|
* @public
|
|
@@ -699,10 +721,8 @@ export declare class SbomMetadataSource {
|
|
|
699
721
|
* A copyright statement for a holder and a year, or a span of years.
|
|
700
722
|
*
|
|
701
723
|
* @remarks
|
|
702
|
-
* The year is an **argument
|
|
703
|
-
*
|
|
704
|
-
* purity a claim rather than a property; the ambient read belongs at the
|
|
705
|
-
* caller's edge.
|
|
724
|
+
* The year is an **argument**: nothing here reads the clock, so the output is
|
|
725
|
+
* deterministic. Read the ambient year at the caller's edge.
|
|
706
726
|
*/
|
|
707
727
|
static readonly formatCopyright: (holder: string, years: CopyrightYears) => string;
|
|
708
728
|
/**
|
|
@@ -749,8 +769,8 @@ declare const SigstoreBundle_base: Schema.Class<SigstoreBundle, Schema.Struct<{
|
|
|
749
769
|
* @remarks
|
|
750
770
|
* `verificationMaterial` and `dsseEnvelope` are `unknown` because their shapes
|
|
751
771
|
* belong to the Sigstore protobuf specifications, and re-declaring them here
|
|
752
|
-
* would be a second, drifting copy of a wire format
|
|
753
|
-
* is opaque to everything that merely stores or forwards it.
|
|
772
|
+
* would be a second, drifting copy of a wire format this package does not own.
|
|
773
|
+
* The bundle is opaque to everything that merely stores or forwards it.
|
|
754
774
|
*
|
|
755
775
|
* `mediaType` is carried through from what the builder produced rather than
|
|
756
776
|
* asserted — the version is the producer's statement about the bundle, and a
|
|
@@ -770,9 +790,7 @@ export declare class SigstoreBundle extends SigstoreBundle_base {}
|
|
|
770
790
|
* It lives here, not at the call site, because it is the **signing protocol's**
|
|
771
791
|
* requirement rather than the caller's knowledge — which is why
|
|
772
792
|
* {@link SigstoreSignerShape.sign} takes only a statement and asks the identity
|
|
773
|
-
* contract for a token.
|
|
774
|
-
* which reads simpler and forces every caller to learn a constant that is none
|
|
775
|
-
* of its business.
|
|
793
|
+
* contract for a token.
|
|
776
794
|
*
|
|
777
795
|
* @public
|
|
778
796
|
*/
|
|
@@ -848,17 +866,28 @@ interface SigstoreSignerShape {
|
|
|
848
866
|
}
|
|
849
867
|
declare const SigstoreSigner_base: Context.ServiceClass<SigstoreSigner, "@effected/sbom/SigstoreSigner", SigstoreSignerShape>;
|
|
850
868
|
/**
|
|
851
|
-
* Sigstore
|
|
869
|
+
* Signs an in-toto statement into a Sigstore DSSE bundle, using a Fulcio
|
|
870
|
+
* certificate and a Rekor transparency-log entry.
|
|
871
|
+
*
|
|
872
|
+
* @remarks
|
|
873
|
+
* Fails with {@link SigningError}. The live {@link (SigstoreSigner:class).layer}
|
|
874
|
+
* requires {@link IdentityToken} in `R`.
|
|
852
875
|
*
|
|
853
876
|
* @example
|
|
854
877
|
* ```ts
|
|
878
|
+
* import type { InTotoStatement } from "@effected/sbom";
|
|
855
879
|
* import { IdentityToken, SigstoreSigner } from "@effected/sbom";
|
|
856
|
-
* import { Effect, Layer } from "effect";
|
|
880
|
+
* import { Effect, Layer, Redacted } from "effect";
|
|
857
881
|
*
|
|
858
|
-
* const
|
|
859
|
-
*
|
|
860
|
-
*
|
|
861
|
-
*
|
|
882
|
+
* const sign = (statement: InTotoStatement) =>
|
|
883
|
+
* Effect.gen(function* () {
|
|
884
|
+
* const signer = yield* SigstoreSigner;
|
|
885
|
+
* return yield* signer.sign(statement);
|
|
886
|
+
* });
|
|
887
|
+
*
|
|
888
|
+
* const live = SigstoreSigner.layer.pipe(
|
|
889
|
+
* Layer.provide(IdentityToken.layerStatic(Redacted.make(process.env.OIDC_TOKEN ?? ""))),
|
|
890
|
+
* );
|
|
862
891
|
* ```
|
|
863
892
|
*
|
|
864
893
|
* @public
|
|
@@ -970,8 +999,7 @@ export declare class SlsaRunDetails extends SlsaRunDetails_base {}
|
|
|
970
999
|
*
|
|
971
1000
|
* @remarks
|
|
972
1001
|
* A plain input record, **not** a service. There is nothing to swap and no IO
|
|
973
|
-
* to invert — it is the argument to a data constructor
|
|
974
|
-
* for one would repeat a mistake this program already reversed once.
|
|
1002
|
+
* to invert — it is the argument to a data constructor.
|
|
975
1003
|
*
|
|
976
1004
|
* Fields are camelCase here and re-spelled to the platform's claim names where
|
|
977
1005
|
* the predicate demands it, so a caller reads its own vocabulary and the
|
|
@@ -1018,7 +1046,20 @@ declare const SlsaProvenance_base: Schema.Class<SlsaProvenance, Schema.Struct<{
|
|
|
1018
1046
|
* ```ts
|
|
1019
1047
|
* import { SlsaProvenance } from "@effected/sbom";
|
|
1020
1048
|
*
|
|
1021
|
-
* const provenance = SlsaProvenance.forGitHubWorkflow(
|
|
1049
|
+
* const provenance = SlsaProvenance.forGitHubWorkflow({
|
|
1050
|
+
* serverUrl: "https://github.com",
|
|
1051
|
+
* repository: "acme/app",
|
|
1052
|
+
* ref: "refs/heads/main",
|
|
1053
|
+
* sha: "0123456789abcdef0123456789abcdef01234567",
|
|
1054
|
+
* eventName: "push",
|
|
1055
|
+
* workflowRef: "acme/app/.github/workflows/release.yml@refs/heads/main",
|
|
1056
|
+
* jobWorkflowRef: "acme/app/.github/workflows/release.yml@refs/heads/main",
|
|
1057
|
+
* repositoryId: "1",
|
|
1058
|
+
* repositoryOwnerId: "2",
|
|
1059
|
+
* runnerEnvironment: "github-hosted",
|
|
1060
|
+
* runId: "3",
|
|
1061
|
+
* runAttempt: "1",
|
|
1062
|
+
* });
|
|
1022
1063
|
* ```
|
|
1023
1064
|
*
|
|
1024
1065
|
* @public
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effected/sbom",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "CycloneDX 1.6 SBOM construction, SLSA provenance, NTIA validation and Sigstore signing as typed services.",
|
|
6
6
|
"keywords": [
|
|
@@ -39,13 +39,13 @@
|
|
|
39
39
|
"./package.json": "./package.json"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@effected/package-json": "^0.
|
|
43
|
-
"@effected/spdx": "^0.
|
|
42
|
+
"@effected/package-json": "^0.20.0",
|
|
43
|
+
"@effected/spdx": "^0.11.0",
|
|
44
44
|
"@sigstore/bundle": "^5.0.0",
|
|
45
45
|
"@sigstore/sign": "^5.0.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"effect": "4.0.0
|
|
48
|
+
"effect": "^4.0.0"
|
|
49
49
|
},
|
|
50
50
|
"engines": {
|
|
51
51
|
"node": ">=24.11.0"
|