diff --git a/go.mod b/go.mod index 1bf7e13e97..8c2d5d4653 100644 --- a/go.mod +++ b/go.mod @@ -76,6 +76,7 @@ require ( github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/in-toto/attestation v1.0.2 github.com/jellydator/ttlcache/v3 v3.2.0 // indirect github.com/klauspost/compress v1.17.7 // indirect github.com/klauspost/cpuid/v2 v2.2.7 // indirect diff --git a/go.sum b/go.sum index 7d4385dabe..4b297610d1 100644 --- a/go.sum +++ b/go.sum @@ -140,6 +140,8 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M= github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/in-toto/attestation v1.0.2 h1:ICqV41bfaDC3ixVUzAtFxFu+Dy56EPcjiIrJQe+4LVM= +github.com/in-toto/attestation v1.0.2/go.mod h1:3uRayZSKuCHDDZOxLm5UfYulqqd1L1NdzYvxX/jyZEM= github.com/jellydator/ttlcache/v3 v3.2.0 h1:6lqVJ8X3ZaUwvzENqPAobDsXNExfUJd61u++uW8a3LE= github.com/jellydator/ttlcache/v3 v3.2.0/go.mod h1:hi7MGFdMAwZna5n2tuvh63DvFLzVKySzCVW6+0gA2n4= github.com/jmhodges/clock v1.2.0 h1:eq4kys+NI0PLngzaHEe7AmPT90XMGIEySD1JfV1PDIs= diff --git a/src/BUILD.plz b/src/BUILD.plz index 549c304a57..0f1703d8b6 100644 --- a/src/BUILD.plz +++ b/src/BUILD.plz @@ -10,6 +10,7 @@ go_binary( deps = [ "///third_party/go/github.com_thought-machine_go-flags//:go-flags", "///third_party/go/go.uber.org_automaxprocs//maxprocs", + "//src/attestor", "//src/assets", "//src/build", "//src/cache", diff --git a/src/attestor/BUILD b/src/attestor/BUILD new file mode 100644 index 0000000000..2cc34699d7 --- /dev/null +++ b/src/attestor/BUILD @@ -0,0 +1,9 @@ +go_library( + name = "attestor", + srcs = ["attestor.go"], + visibility = ["PUBLIC"], + deps = [ + "//src/cli", + "//src/core", + ], +) diff --git a/src/attestor/attestor.go b/src/attestor/attestor.go new file mode 100644 index 0000000000..b83cc58332 --- /dev/null +++ b/src/attestor/attestor.go @@ -0,0 +1,118 @@ +package attestor + +import ( + "encoding/hex" + "encoding/json" + + prov "github.com/in-toto/attestation/go/predicates/provenance/v1" + attestation "github.com/in-toto/attestation/go/v1" + // v1 "github.com/in-toto/attestation/go/v1" + "google.golang.org/protobuf/types/known/structpb" + + "github.com/thought-machine/please/src/cli" + "github.com/thought-machine/please/src/core" +) + +const ( + Name = "please-build" + Type = "https://slsa.dev/provenance/v1.0" + BuildType = "https://please.build/buildtypes/build@v0.1" + DefaultBuilderId = "https://please.build/please-build@v0.1" +) + + + +type Provenance struct { + PbProvenance prov.Provenance + subjects []*attestation.ResourceDescriptor +} + +func New() *Provenance { + return &Provenance{} +} + +func (p *Provenance) Name() string { + return Name +} + +func (p *Provenance) Type() string { + return Type +} + +func (p *Provenance) Attest(targets, preTargets []core.BuildLabel, state *core.BuildState, config *core.Configuration, arch cli.Arch) error { + builder := prov.Builder{} + metadata := prov.BuildMetadata{} + p.PbProvenance.BuildDefinition = &prov.BuildDefinition{} + p.PbProvenance.RunDetails = &prov.RunDetails{Builder: &builder, Metadata: &metadata} + + p.PbProvenance.BuildDefinition.BuildType = BuildType + p.PbProvenance.RunDetails.Builder.Id = DefaultBuilderId + + // Internal Parameters + internalParam := make(map[string]interface{}) + internalParam["version"] = config.Please.Version.VersionString() + + var err error + p.PbProvenance.BuildDefinition.InternalParameters, err = structpb.NewStruct(internalParam) + if err != nil { + return err + } + + // External Parameters + externalParam := make(map[string]interface{}) + + targetNames := make([]interface{}, 0) + for _, v := range targets { + targetNames = append(targetNames, v.String()) + } + externalParam["targets"] = targetNames + + p.PbProvenance.BuildDefinition.ExternalParameters, err = structpb.NewStruct(externalParam) + if err != nil { + return err + } + + // Resolved Dependencies + + + // Run Details + + + // Subjects + p.subjects, err = p.Subjects(targets, state) + if err != nil { + return err + } + + return nil +} + +func (p *Provenance) MarshalJSON() ([]byte, error) { + return json.Marshal(&p.PbProvenance) +} + +func (p *Provenance) Subjects(targets []core.BuildLabel, state *core.BuildState) ([]*attestation.ResourceDescriptor, error) { + subjects := []*attestation.ResourceDescriptor{} + + for _, label := range targets { + p := state.SyncParsePackage(label) + outputs := p.Target(label.Name).FullOutputs() + + for _, outputItem := range outputs { + hash, err := state.PathHasher.Hash(outputItem, false, false, false) + if err != nil { + return nil, err + } + + subject := &attestation.ResourceDescriptor{} + subject.Name = outputItem + subject.Digest = map[string]string{ + state.PathHasher.AlgoName(): hex.EncodeToString(hash), + } + + subjects = append(subjects, subject) + } + + } + return subjects, nil +} \ No newline at end of file diff --git a/src/plz/plz.go b/src/plz/plz.go index 4f83d00f37..2d10c6567f 100644 --- a/src/plz/plz.go +++ b/src/plz/plz.go @@ -7,6 +7,7 @@ import ( "github.com/peterebden/go-cli-init/v5/flags" + "github.com/thought-machine/please/src/attestor" "github.com/thought-machine/please/src/build" "github.com/thought-machine/please/src/cli" "github.com/thought-machine/please/src/cli/logging" @@ -89,6 +90,25 @@ func Run(targets, preTargets []core.BuildLabel, state *core.BuildState, config * log.Info("Total remote RPC data in: %d out: %d", in, out) } state.CloseResults() + + prov := attestor.New() + err := prov.Attest(targets, preTargets, state, config, arch) + if err != nil { + log.Errorf("%v", err) + } + + provenanceJson, err := prov.MarshalJSON() + if err != nil { + log.Errorf("%v", err) + } + + log.Infof("%s", provenanceJson) + + // TODO: Provenance implementation + // - Sign provenance + // - Get config for output location + // - Write to output file + metrics.Push(config) } diff --git a/third_party/go/BUILD b/third_party/go/BUILD index 5f5d8d86a5..a5bac9e866 100644 --- a/third_party/go/BUILD +++ b/third_party/go/BUILD @@ -686,3 +686,9 @@ go_repo( version = "v1.2.0", licences = ["Apache-2.0"], ) + +go_repo( + module = "github.com/in-toto/attestation", + version = "v1.0.2", + licences = ["Apache-2.0"], +)