File: sbom.go

package info (click to toggle)
singularity-container 4.1.5%2Bds4-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 43,876 kB
  • sloc: asm: 14,840; sh: 3,190; ansic: 1,751; awk: 414; makefile: 413; python: 99
file content (114 lines) | stat: -rw-r--r-- 3,636 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package sbom

import (
	"context"
	"encoding/json"
	"fmt"
	"path"
	"strings"

	intoto "github.com/in-toto/in-toto-golang/in_toto"
	"github.com/moby/buildkit/client/llb"
	"github.com/moby/buildkit/client/llb/sourceresolver"
	gatewaypb "github.com/moby/buildkit/frontend/gateway/pb"
	"github.com/moby/buildkit/solver/result"
	ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/pkg/errors"
)

const (
	CoreSBOMName    = "sbom"
	ExtraSBOMPrefix = CoreSBOMName + "-"

	srcDir = "/run/src/"
	outDir = "/run/out/"
)

// Scanner is a function type for scanning the contents of a state and
// returning a new attestation and state representing the scan results.
//
// A scanner is designed a scan a single state, however, additional states can
// also be attached, for attaching additional information, such as scans of
// build-contexts or multi-stage builds. Handling these separately allows the
// scanner to optionally ignore these or to mark them as such in the
// attestation.
type Scanner func(ctx context.Context, name string, ref llb.State, extras map[string]llb.State, opts ...llb.ConstraintsOpt) (result.Attestation[*llb.State], error)

func CreateSBOMScanner(ctx context.Context, resolver sourceresolver.MetaResolver, scanner string, resolveOpt sourceresolver.Opt) (Scanner, error) {
	if scanner == "" {
		return nil, nil
	}

	imr := sourceresolver.NewImageMetaResolver(resolver)
	scanner, _, dt, err := imr.ResolveImageConfig(ctx, scanner, resolveOpt)
	if err != nil {
		return nil, err
	}

	var cfg ocispecs.Image
	if err := json.Unmarshal(dt, &cfg); err != nil {
		return nil, err
	}

	var args []string
	args = append(args, cfg.Config.Entrypoint...)
	args = append(args, cfg.Config.Cmd...)
	if len(args) == 0 {
		return nil, errors.Errorf("scanner %s does not have cmd", scanner)
	}

	return func(ctx context.Context, name string, ref llb.State, extras map[string]llb.State, opts ...llb.ConstraintsOpt) (result.Attestation[*llb.State], error) {
		var env []string
		env = append(env, cfg.Config.Env...)
		env = append(env, "BUILDKIT_SCAN_DESTINATION="+outDir)
		env = append(env, "BUILDKIT_SCAN_SOURCE="+path.Join(srcDir, "core", CoreSBOMName))
		if len(extras) > 0 {
			env = append(env, "BUILDKIT_SCAN_SOURCE_EXTRAS="+path.Join(srcDir, "extras/"))
		}

		runOpts := []llb.RunOption{
			llb.WithCustomName(fmt.Sprintf("[%s] generating sbom using %s", name, scanner)),
		}
		for _, opt := range opts {
			runOpts = append(runOpts, opt)
		}
		runOpts = append(runOpts, llb.Dir(cfg.Config.WorkingDir))
		runOpts = append(runOpts, llb.Args(args))
		for _, e := range env {
			k, v, _ := strings.Cut(e, "=")
			runOpts = append(runOpts, llb.AddEnv(k, v))
		}

		runscan := llb.Image(scanner).Run(runOpts...)
		runscan.AddMount("/tmp", llb.Scratch(), llb.Tmpfs())

		runscan.AddMount(path.Join(srcDir, "core", CoreSBOMName), ref, llb.Readonly)
		for k, extra := range extras {
			runscan.AddMount(path.Join(srcDir, "extras", ExtraSBOMPrefix+k), extra, llb.Readonly)
		}

		stsbom := runscan.AddMount(outDir, llb.Scratch())
		return result.Attestation[*llb.State]{
			Kind: gatewaypb.AttestationKindBundle,
			Ref:  &stsbom,
			Metadata: map[string][]byte{
				result.AttestationReasonKey: []byte(result.AttestationReasonSBOM),
				result.AttestationSBOMCore:  []byte(CoreSBOMName),
			},
			InToto: result.InTotoAttestation{
				PredicateType: intoto.PredicateSPDX,
			},
		}, nil
	}, nil
}

func HasSBOM[T comparable](res *result.Result[T]) bool {
	for _, as := range res.Attestations {
		for _, a := range as {
			if a.InToto.PredicateType == intoto.PredicateSPDX {
				return true
			}
		}
	}
	return false
}