Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Question] What is the canonical way to unmarshal "https://witness.testifysec.com/attestation-collection/v0.1" #376

Open
xNok opened this issue Oct 16, 2024 · 1 comment

Comments

@xNok
Copy link

xNok commented Oct 16, 2024

I have started using Witness to produce attestations for builds, and now I would like to be able to parse those attestations to extract relevant metadata to display in other systems.

I didn't really know where to start so I looked into in-toto spec and landed on:

Parsing the attestation.Collection feels very tedious, and I hope there is a better way.

// parse the manifest envelope
	var envelope dsse.Envelope
	err := json.Unmarshal(att, &envelope)
	if err != nil {
		return fmt.Errorf("failed to artefact: %w", err)
	}

	if envelope.PayloadType != "application/vnd.in-toto+json" {
		return fmt.Errorf("cant't decode payload type %s", envelope.PayloadType)
	}

	var statement intoto.Statement
	err = protojson.Unmarshal(envelope.Payload, &statement)
	if err != nil {
		return fmt.Errorf("failed to unmarshal statement payload: %w", err)
	}

	if statement.PredicateType != attestation.CollectionType {
		return fmt.Errorf("expected '%s' type, got %s", attestation.CollectionType, statement.PredicateType)
	}

	jsonPredicate, err := statement.Predicate.MarshalJSON()
	if err != nil {
		return fmt.Errorf("failed to remarshal predicate: %w", err)
	}

	var collection attestation.Collection
	err = json.Unmarshal(jsonPredicate, &collection)
	if err != nil {
		return fmt.Errorf("failed to collection predicate: %w", err)
	}

Even at that point, I was not able to directly parse individual attestation because of the following error:

failed to collection predicate: attestation not found: https://witness.dev/attestations/gitlab/v0.1

This seems to be caused by the attestationsByType map being empty.

All this leads me to deliver that this is not the intended way to parse attestation with the library.

@xNok
Copy link
Author

xNok commented Oct 23, 2024

I found the answer in the end, two catches:

  • I shouldn't have used the code from in-toto/attestation it makes things so much harder
  • We need to import the entire library so the attestator are properly registered
_ "github.com/in-toto/go-witness"

So it can be done in four steps:

env := dsse.Envelope{}
	if err := json.Unmarshal(data, &env); err != nil {
		return errors.Join(ErrFailedToProcessInTotoAttestation, err)
	}

	statement := intoto.Statement{}
	if err := json.Unmarshal(env.Payload, &statement); err != nil {
		return errors.Join(ErrFailedToProcessInTotoAttestation, err)
	}

	collection := attestation.Collection{}
	if err := json.Unmarshal(statement.Predicate, &collection); err != nil {
		return errors.Join(ErrFailedToProcessInTotoAttestation, err)
	}

	var attestationProcessingErrors error
	for _, att := range collection.Attestations {
		val, ok := h.AttestationHandlers[att.Type]
		if !ok {
			continue
		}

		err := val.Handle(att.Attestation, result)
		if err != nil {
			attestationProcessingErrors = errors.Join(attestationProcessingErrors, fmt.Errorf("error processing attestation %s: %w", att.Type, err))
		}
	}

I do think the lib should provide simple methods to parse the attestation collection. I haven't yet looked at the verification, but I was hoping to get both (verification and metadata) out of a simple interface.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant