From e187a4c11292c12fce8071f5be746f38166f4e66 Mon Sep 17 00:00:00 2001 From: "Adolfo Garcia Veytia (puerco)" Date: Sun, 9 Jul 2023 21:01:03 -0600 Subject: [PATCH] Implement changes in openvex/spec#25 This commit implements the changes to the spec done to update OpenVEX to the published version of the minimum elements for VEX document. These changes were proposed to the spec in: https://github.com/openvex/spec/pull/25 Specifically, this commit introduces the following changes: Document - version is now an integer - Added last_updated to record changes to the doc - author is now recommended to be a machine-readable identifier - role is now optional Statement - @id takes an IRI to identify the statement and make it referenceable - version an integer field, accounts for changes in the statement. Defaults to zero, and may be omitted when so. - last_updated added to the statement - supplier an identifier of the supplier of the document Signed-off-by: Adolfo Garcia Veytia (puerco) --- pkg/vex/product.go | 15 +++++++++++++-- pkg/vex/statement.go | 7 +++++++ pkg/vex/vex.go | 14 +++++++++----- pkg/vex/vex_test.go | 2 +- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/pkg/vex/product.go b/pkg/vex/product.go index d34a659..dc02835 100644 --- a/pkg/vex/product.go +++ b/pkg/vex/product.go @@ -15,9 +15,20 @@ package vex // must an IRI describe two different pieces of software or used to describe // a range of software. type Component struct { - ID string `json:"@id,omitempty"` - Hashes map[Algorithm]Hash `json:"hashes,omitempty"` + // ID is an IRI identifying the component. It is optional as the component + // can also be identified using hashes or software identifiers. + ID string `json:"@id,omitempty"` + + // Hashes is a map of hashes to identify the component using cryptographic + // hashes. + Hashes map[Algorithm]Hash `json:"hashes,omitempty"` + + // Identifiers is a list of software identifiers that describe the component. Identifiers map[IdentifierType]string `json:"identifiers,omitempty"` + + // Supplier is an optional machine-readable identifier for the supplier of + // the component. Valid examples include email address or IRIs. + Supplier string `json:"supplier,omitempty"` } // Product abstracts the VEX product into a struct that can identify sofware diff --git a/pkg/vex/statement.go b/pkg/vex/statement.go index 7ec981c..253aa5b 100644 --- a/pkg/vex/statement.go +++ b/pkg/vex/statement.go @@ -16,6 +16,10 @@ import ( // [vul_id] for one or more [product_id]s. A VEX Statement exists within a VEX // Document. type Statement struct { + // ID is an optional identifier for the statement. It takes an IRI and must + // be unique for each statement in the document. + ID string `json:"@id,omitempty"` + // [vul_id] SHOULD use existing and well known identifiers, for example: // CVE, the Global Security Database (GSD), or a supplier’s vulnerability // tracking system. It is expected that vulnerability identification systems @@ -29,6 +33,9 @@ type Statement struct { // was known to be true. Timestamp *time.Time `json:"timestamp,omitempty"` + // LastUpdated records the time when the statement last had a modification + LastUpdated *time.Time `json:"last_updated,omitempty"` + // Product // Product details MUST specify what Status applies to. // Product details MUST include [product_id] and MAY include [subcomponent_id]. diff --git a/pkg/vex/vex.go b/pkg/vex/vex.go index 79ba280..f716df4 100644 --- a/pkg/vex/vex.go +++ b/pkg/vex/vex.go @@ -34,7 +34,7 @@ const ( DefaultAuthor = "Unknown Author" // DefaultRole is the default value for a document's AuthorRole field. - DefaultRole = "Document Creator" + DefaultRole = "" // Context is the URL of the json-ld context definition Context = "https://openvex.dev/ns" @@ -70,15 +70,19 @@ type Metadata struct { Author string `json:"author"` // AuthorRole describes the role of the document Author. - AuthorRole string `json:"role"` + AuthorRole string `json:"role,omitempty"` // Timestamp defines the time at which the document was issued. Timestamp *time.Time `json:"timestamp"` + // LastUpdated marks the time when the document had its last update. When the + // document changes both version and this field should be updated. + LastUpdated *time.Time `json:"last_updated,omitempty"` + // Version is the document version. It must be incremented when any content // within the VEX document changes, including any VEX statements included within // the VEX document. - Version string `json:"version"` + Version int `json:"version"` // Tooling expresses how the VEX document and contained VEX statements were // generated. It's optional. It may specify tools or automated processes used in @@ -104,7 +108,7 @@ func New() VEX { Context: Context, Author: DefaultAuthor, AuthorRole: DefaultRole, - Version: "1", + Version: 1, Timestamp: &now, }, Statements: []Statement{}, @@ -344,7 +348,7 @@ func (vexDoc *VEX) CanonicalHash() (string, error) { cString := fmt.Sprintf("%d", vexDoc.Timestamp.Unix()) // 2. Document version - cString += fmt.Sprintf(":%s", vexDoc.Version) + cString += fmt.Sprintf(":%d", vexDoc.Version) // 3. Author identity cString += fmt.Sprintf(":%s", vexDoc.Author) diff --git a/pkg/vex/vex_test.go b/pkg/vex/vex_test.go index 2f64996..99b705d 100644 --- a/pkg/vex/vex_test.go +++ b/pkg/vex/vex_test.go @@ -124,7 +124,7 @@ func genTestDoc(t *testing.T) VEX { Author: "John Doe", AuthorRole: "VEX Writer Extraordinaire", Timestamp: &ts, - Version: "1", + Version: 1, Tooling: "OpenVEX", Supplier: "Chainguard Inc", },