Skip to content
This repository has been archived by the owner on Apr 9, 2024. It is now read-only.

Commit

Permalink
QC
Browse files Browse the repository at this point in the history
  • Loading branch information
rbehjati committed Sep 14, 2022
1 parent 89e58e8 commit 8366177
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 27 deletions.
4 changes: 3 additions & 1 deletion experimental/auth-logic/wrappers/provenance_build_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,13 @@ type simplifiedProvenance struct {
// by emitting the authorization logic statement.
func (pbw ProvenanceBuildWrapper) EmitStatement() (UnattributedStatement, error) {
// Unmarshal a provenance struct from the JSON file.
provenance, err := amber.ParseProvenanceFile(pbw.ProvenanceFilePath)
validatedProvenance, err := amber.ParseProvenanceFile(pbw.ProvenanceFilePath)
if err != nil {
return UnattributedStatement{}, fmt.Errorf("provenance build wrapper couldn't parse provenance file: %v", err)
}

provenance := validatedProvenance.GetProvenance()

// TODO(#69): Set the verifier as a field in pbw, and use that here.
verifier := verify.AmberProvenanceMetadataVerifier{}
if err := verifier.Verify(pbw.ProvenanceFilePath); err != nil {
Expand Down
19 changes: 6 additions & 13 deletions experimental/auth-logic/wrappers/provenance_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,12 @@ type ProvenanceWrapper struct{ FilePath string }
// EmitStatement implements the wrapper interface for ProvenanceWrapper
// by emitting the authorization logic statement.
func (p ProvenanceWrapper) EmitStatement() (UnattributedStatement, error) {
provenance, err := amber.ParseProvenanceFile(p.FilePath)
validatedProvenance, err := amber.ParseProvenanceFile(p.FilePath)
if err != nil {
return UnattributedStatement{}, fmt.Errorf("provenance wrapper couldn't prase provenance file: %v", err)
}

if len(provenance.Subject) != 1 {
return UnattributedStatement{}, fmt.Errorf("provenance file missing subject")
}

provenance := validatedProvenance.GetProvenance()
sanitizedAppName := SanitizeName(provenance.Subject[0].Name)
expectedHash, hashOk := provenance.Subject[0].Digest["sha256"]

Expand All @@ -64,14 +61,10 @@ func (p ProvenanceWrapper) EmitStatement() (UnattributedStatement, error) {
// application it is about. This is useful for generating principal names,
// for example.
func GetAppNameFromProvenance(provenanceFilePath string) (string, error) {
provenance, provenanceErr := amber.ParseProvenanceFile(provenanceFilePath)
if provenanceErr != nil {
return "", provenanceErr
}

if len(provenance.Subject) != 1 {
return "", fmt.Errorf("provenance file missing subject")
validatedProvenance, err := amber.ParseProvenanceFile(provenanceFilePath)
if err != nil {
return "", fmt.Errorf("provenance wrapper couldn't prase provenance file: %v", err)
}

return provenance.Subject[0].Name, nil
return validatedProvenance.GetProvenance().Subject[0].Name, nil
}
9 changes: 3 additions & 6 deletions internal/common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,8 @@ func LoadBuildConfigFromFile(path string) (*BuildConfig, error) {
}

// LoadBuildConfigFromProvenance loads build configuration from a SLSA Provenance object.
func LoadBuildConfigFromProvenance(statement *intoto.Statement) (*BuildConfig, error) {
if len(statement.Subject) != 1 {
return nil, fmt.Errorf("the provenance statement must have exactly one Subject, got %d", len(statement.Subject))
}

func LoadBuildConfigFromProvenance(provenance *amber.ValidatedProvenance) (*BuildConfig, error) {
statement := provenance.GetProvenance()
predicate := statement.Predicate.(slsa.ProvenancePredicate)
if len(predicate.Materials) != 2 {
return nil, fmt.Errorf("the provenance must have exactly two Materials, got %d", len(predicate.Materials))
Expand Down Expand Up @@ -328,7 +325,7 @@ func (b *BuildConfig) VerifyBinarySha256Hash(expectedBinarySha256Hash string) er
}

if binarySha256Hash != expectedBinarySha256Hash {
return fmt.Errorf("the hash of the generated binary does not match the expected SHA256 hash; got %s, want %v",
return fmt.Errorf("the hash of the generated binary does not match the expected SHA256 hash; got %s, want %s",
binarySha256Hash, expectedBinarySha256Hash)
}

Expand Down
5 changes: 3 additions & 2 deletions internal/verifier/verifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (verifier *ReproducibleProvenanceVerifier) Verify(provenanceFilePath string
}

// The provenance is valid, therefore `expectedBinaryHash` is guaranteed to be non-empty.
expectedBinaryHash := provenance.Subject[0].Digest["sha256"]
expectedBinaryHash := provenance.GetProvenance().Subject[0].Digest["sha256"]

if err := buildConfig.VerifyBinarySha256Hash(expectedBinaryHash); err != nil {
return fmt.Errorf("failed to verify the hash of the built binary: %v", err)
Expand All @@ -88,13 +88,14 @@ type AmberProvenanceMetadataVerifier struct {
// AmberProvenanceMetadataVerifier instance. Returns an error if any of the
// values is not as expected. Otherwise returns nil, indicating success.
// TODO(#69): Check metadata against the expected values.
// TODO(#126): Refactor and separate verification logic from the logic for reading the file.
func (verifier *AmberProvenanceMetadataVerifier) Verify(provenanceFilePath string) error {
provenance, err := amber.ParseProvenanceFile(provenanceFilePath)
if err != nil {
return fmt.Errorf("couldn't load the provenance file from %s: %v", provenanceFilePath, err)
}

predicate := provenance.Predicate.(slsa.ProvenancePredicate)
predicate := provenance.GetProvenance().Predicate.(slsa.ProvenancePredicate)

if predicate.BuildType != amber.AmberBuildTypeV1 {
return fmt.Errorf("incorrect BuildType: got %s, want %v", predicate.BuildType, amber.AmberBuildTypeV1)
Expand Down
2 changes: 2 additions & 0 deletions internal/verifier/verifier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func TestReproducibleProvenanceVerifier_validProvenance(t *testing.T) {
}
}

// TODO(#126): Update the test once Verify is refactored.
func TestReproducibleProvenanceVerifier_invalidHash(t *testing.T) {
// The path to provenance is specified relative to the root of the repo, so we need to go one level up.
// Get the current directory before that to restore the path at the end of the test.
Expand All @@ -60,6 +61,7 @@ func TestReproducibleProvenanceVerifier_invalidHash(t *testing.T) {
}
}

// TODO(#126): Update the test once Verify is refactored.
func TestReproducibleProvenanceVerifier_badCommand(t *testing.T) {
// The path to provenance is specified relative to the root of the repo, so we need to go one level up.
// Get the current directory before that to restore the path at the end of the test.
Expand Down
35 changes: 31 additions & 4 deletions pkg/amber/provenance.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,33 @@ type BuildConfig struct {
OutputPath string `json:"outputPath"`
}

// ValidatedProvenance wraps an intoto.Statement representing a valid SLSA provenance statement.
// A provenance statement is valid if it contains a single subject, with a SHA256 hash.
type ValidatedProvenance struct {
// The field is private so that invalid instances cannot be created.
provenance intoto.Statement
}

// GetProvenance returns a partial copy of the provenance statement wrapped in this instance.
// The partial copy guarantees that the validity condition will not be violated.
func (p *ValidatedProvenance) GetProvenance() intoto.Statement {
subject := intoto.Subject{
Name: p.provenance.Subject[0].Name,
Digest: slsa.DigestSet{"sha256": p.provenance.Subject[0].Digest["sha256"]},
}

statementHeader := intoto.StatementHeader{
Type: p.provenance.Type,
PredicateType: p.provenance.PredicateType,
Subject: []intoto.Subject{subject},
}

return intoto.Statement{
StatementHeader: statementHeader,
Predicate: p.provenance.Predicate,
}
}

func validateSLSAProvenanceJSON(provenanceFile []byte) error {
schemaFile, err := os.ReadFile(SchemaPath)
if err != nil {
Expand Down Expand Up @@ -74,7 +101,7 @@ func validateSLSAProvenanceJSON(provenanceFile []byte) error {

// ParseProvenanceFile reads a JSON file from a given path, validates it against the Amber
// buildType schema, and parses it into an instance of intoto.Statement.
func ParseProvenanceFile(path string) (*intoto.Statement, error) {
func ParseProvenanceFile(path string) (*ValidatedProvenance, error) {
statementBytes, readErr := os.ReadFile(path)
if readErr != nil {
return nil, fmt.Errorf("could not read the provenance file: %v", readErr)
Expand All @@ -89,8 +116,8 @@ func ParseProvenanceFile(path string) (*intoto.Statement, error) {
return nil, fmt.Errorf("could not unmarshal the provenance file:\n%v", err)
}

if len(statement.Subject) == 0 || statement.Subject[0].Digest["sha256"] == "" {
return nil, fmt.Errorf("at least one subject must be present, and it must have a sha256 digest")
if len(statement.Subject) != 1 || statement.Subject[0].Digest["sha256"] == "" {
return nil, fmt.Errorf("the provenance must have exactly one subject with a sha256 digest")
}

// statement.Predicate is now just a map, we have to parse it into an instance of slsa.ProvenancePredicate
Expand Down Expand Up @@ -119,5 +146,5 @@ func ParseProvenanceFile(path string) (*intoto.Statement, error) {
predicate.BuildConfig = buildConfig
statement.Predicate = predicate

return &statement, nil
return &ValidatedProvenance{provenance: statement}, nil
}
3 changes: 2 additions & 1 deletion pkg/amber/provenance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,11 @@ func TestExampleProvenance(t *testing.T) {
testutil.Chdir(t, "../../")

// Parses the provenance and validates it against the schema.
provenance, err := ParseProvenanceFile(provenanceExamplePath)
validatedProvenance, err := ParseProvenanceFile(provenanceExamplePath)
if err != nil {
t.Fatalf("Failed to parse example provenance: %v", err)
}
provenance := validatedProvenance.GetProvenance()

predicate := provenance.Predicate.(slsa.ProvenancePredicate)
buildConfig := predicate.BuildConfig.(BuildConfig)
Expand Down

0 comments on commit 8366177

Please sign in to comment.