Skip to content

Commit 6a8c0d9

Browse files
feat(ci):create sbom generate and attest functions (#643)
1 parent 0059692 commit 6a8c0d9

1 file changed

Lines changed: 81 additions & 0 deletions

File tree

.dagger/publishimage.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,29 @@ func (m *HarborCli) PublishImageAndSign(
4040
}
4141

4242
for _, addr := range imageAddrs {
43+
// Generate SBOM (SPDX JSON) for the published image
44+
sbom := m.GenerateSBOM(
45+
ctx,
46+
addr,
47+
registryUsername,
48+
registryPassword,
49+
);
50+
51+
// Attest SBOM to the image using Cosign in-toto attestation
52+
if _, err := m.AttestSBOM(
53+
ctx,
54+
sbom,
55+
addr,
56+
registryUsername,
57+
registryPassword,
58+
githubToken,
59+
actionsIdTokenRequestUrl,
60+
actionsIdTokenRequestToken,
61+
); err != nil {
62+
return "", fmt.Errorf("failed to attest SBOM for image %s: %w", addr, err)
63+
}
64+
fmt.Printf("Attested SBOM for image: %s\n", addr)
65+
4366
_, err = m.Sign(
4467
ctx,
4568
githubToken,
@@ -264,3 +287,61 @@ func getVersion(tags []string) string {
264287
}
265288
return "latest"
266289
}
290+
291+
// GenerateSBOM uses Syft to create an SPDX JSON SBOM for a given image and returns it as a file
292+
func (m *HarborCli) GenerateSBOM(
293+
ctx context.Context,
294+
imageAddr string,
295+
registryUsername string,
296+
registryPassword *dagger.Secret,
297+
) *dagger.File {
298+
syftCtr := dag.Container().
299+
From("anchore/syft:latest").
300+
WithSecretVariable("SYFT_REGISTRY_AUTH_PASSWORD", registryPassword).
301+
WithEnvVariable("SYFT_REGISTRY_AUTH_USERNAME", registryUsername).
302+
// Output SPDX JSON to a known path
303+
WithExec([]string{"syft", imageAddr, "-o", "spdx-json=/sbom.spdx.json"})
304+
305+
return syftCtr.File("/sbom.spdx.json")
306+
}
307+
308+
// AttestSBOM attaches an in-toto attestation (SBOM predicate) to the image using Cosign
309+
func (m *HarborCli) AttestSBOM(
310+
ctx context.Context,
311+
sbomFile *dagger.File,
312+
imageAddr string,
313+
registryUsername string,
314+
registryPassword *dagger.Secret,
315+
// +optional
316+
githubToken *dagger.Secret,
317+
// +optional
318+
actionsIdTokenRequestUrl *dagger.Secret,
319+
// +optional
320+
actionsIdTokenRequestToken *dagger.Secret,
321+
) (string, error) {
322+
cosignCtr := dag.Container().
323+
From("cgr.dev/chainguard/cosign").
324+
WithMountedFile("/sbom.spdx.json", sbomFile).
325+
WithSecretVariable("REGISTRY_PASSWORD", registryPassword)
326+
327+
// If githubToken is provided, configure OIDC for keyless signing
328+
if githubToken != nil {
329+
if actionsIdTokenRequestUrl == nil || actionsIdTokenRequestToken == nil {
330+
return "", fmt.Errorf("actionsIdTokenRequestUrl (exist=%v) and actionsIdTokenRequestToken (exist=%t) must be provided when githubToken is provided", actionsIdTokenRequestUrl != nil, actionsIdTokenRequestToken != nil)
331+
}
332+
cosignCtr = cosignCtr.
333+
WithSecretVariable("GITHUB_TOKEN", githubToken).
334+
WithSecretVariable("ACTIONS_ID_TOKEN_REQUEST_URL", actionsIdTokenRequestUrl).
335+
WithSecretVariable("ACTIONS_ID_TOKEN_REQUEST_TOKEN", actionsIdTokenRequestToken)
336+
}
337+
338+
// Use cosign attest to create in-toto attestation with SPDX JSON predicate
339+
// The registry password is already available as REGISTRY_PASSWORD env var
340+
return cosignCtr.WithExec([]string{
341+
"sh", "-c",
342+
fmt.Sprintf(
343+
"cosign attest --yes --type spdxjson --predicate /sbom.spdx.json --registry-username %s --registry-password $REGISTRY_PASSWORD %s --timeout 1m",
344+
registryUsername, imageAddr,
345+
),
346+
}).Stdout(ctx)
347+
}

0 commit comments

Comments
 (0)