mirror of
https://github.com/slsa-framework/slsa-verifier.git
synced 2026-05-20 07:22:47 +00:00
* 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>
181 lines
4.0 KiB
Go
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
|
|
}
|