Files
slsa-verifier/experimental/rest/service.go
Shunsuke Suzuki 74fd528309 fix: fix the Go package version to v2 (#373)
* fix: fix the package version to v2

```
git ls-files | grep ".go$" | xargs -n 1 gsed -i "s|github.com/slsa-framework/slsa-verifier|github.com/slsa-framework/slsa-verifier/v2|g"
```

Signed-off-by: Shunsuke Suzuki <suzuki.shunsuke.1989@gmail.com>

* fix: fix the package version to v2

Signed-off-by: Shunsuke Suzuki <suzuki.shunsuke.1989@gmail.com>

* test: fix source

Signed-off-by: Shunsuke Suzuki <suzuki.shunsuke.1989@gmail.com>

Signed-off-by: Shunsuke Suzuki <suzuki.shunsuke.1989@gmail.com>
2022-12-01 18:49:39 -08:00

181 lines
4.0 KiB
Go

package rest
import (
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"github.com/slsa-framework/slsa-verifier/v2/options"
"github.com/slsa-framework/slsa-verifier/v2/verifiers"
)
var errInvalid = errors.New("invalid")
type v1Query struct {
// Compulsory fields.
Source string `json:"source"`
ArtifactHash string `json:"artifactHash"`
DsseEnvelope string `json:"provenanceContent"`
// Optional fields.
BuilderID *string `json:"builderID"`
Tag *string `json:"tag"`
Branch *string `json:"branch"`
VersionedTag *string `json:"versionedTag"`
PrintProvenance *bool `json:"printProvenance"`
}
type validation string
var (
validationSuccess = validation("success")
validationFailure = validation("failure")
)
type v1Result struct {
Version uint `json:"version"`
Error *string `json:"error,omitempty"`
Validation validation `json:"validation"`
BuilderID string `json:"builderID"`
IntotoStatement *string `json:"provenanceContent,omitempty"`
}
func VerifyHandlerV1(w http.ResponseWriter, r *http.Request) {
if r == nil {
http.Error(w, "empty request", http.StatusInternalServerError)
return
}
results := verifyHandlerV1(r)
encoder := json.NewEncoder(w)
if err := encoder.Encode(results); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func toStringPtr(e error) *string {
if e != nil {
s := e.Error()
return &s
}
return nil
}
func v1ResultNew() *v1Result {
return &v1Result{
Version: 1,
Error: nil,
Validation: validationFailure,
IntotoStatement: nil,
}
}
func (r *v1Result) withError(e error) *v1Result {
r.Error = toStringPtr(e)
return r
}
func (r *v1Result) withValidation(v validation) *v1Result {
r.Validation = v
return r
}
func (r *v1Result) withBuilderID(id string) *v1Result {
r.BuilderID = id
return r
}
func (r *v1Result) withIntotoStatement(c []byte) *v1Result {
b := base64.StdEncoding.EncodeToString(c)
r.IntotoStatement = &b
return r
}
func verifyHandlerV1(r *http.Request) *v1Result {
results := v1ResultNew()
body, err := io.ReadAll(r.Body)
if err != nil {
return results.withError(err)
}
r.Body.Close()
// Create a query.
query, err := queryFromString(body)
if err != nil {
return results.withError(err)
}
// Validate it.
if err := query.validate(); err != nil {
return results.withError(err)
}
// Run the verification.
provenanceOpts := &options.ProvenanceOpts{
ExpectedSourceURI: query.Source,
ExpectedBranch: query.Branch,
ExpectedDigest: query.ArtifactHash,
ExpectedVersionedTag: query.VersionedTag,
ExpectedTag: query.Tag,
}
builderOpts := &options.BuilderOpts{
ExpectedID: query.BuilderID,
}
ctx := context.Background()
p, builderID, err := verifiers.VerifyArtifact(ctx, []byte(query.DsseEnvelope),
query.ArtifactHash, provenanceOpts, builderOpts)
if err != nil {
return results.withError(err)
}
if query.PrintProvenance != nil && *query.PrintProvenance {
results = results.withIntotoStatement(p)
}
return results.withBuilderID(builderID.String()).withValidation(validationSuccess)
}
func queryFromString(content []byte) (*v1Query, error) {
var query v1Query
err := json.Unmarshal(content, &query)
if err != nil {
return nil, err
}
env, err := base64.StdEncoding.DecodeString(query.DsseEnvelope)
if err != nil {
return nil, fmt.Errorf("%w: decoding payload", errInvalid)
}
query.DsseEnvelope = string(env)
return &query, nil
}
func (q *v1Query) validate() error {
if q.Source == "" {
return fmt.Errorf("%w: empty source", errInvalid)
}
if q.ArtifactHash == "" {
return fmt.Errorf("%w: empty artifactHash", errInvalid)
}
if q.DsseEnvelope == "" {
return fmt.Errorf("%w: empty dsseEnvelope", errInvalid)
}
if q.Tag != nil && q.VersionedTag != nil {
return fmt.Errorf("%w: tag and versionedTag are mutually exclusive", errInvalid)
}
// BuilderID is optional, so not additional validation needed.
return nil
}