-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
Copy pathsbom.go
118 lines (102 loc) · 3.66 KB
/
sbom.go
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
115
116
117
118
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, params map[string]string) (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/"))
}
for k, v := range params {
env = append(env, "BUILDKIT_SCAN_"+k+"="+v)
}
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.AttestationKind_Bundle,
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
}