Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 36 additions & 1 deletion evidence.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@

package swid

import "time"
import (
"errors"
"fmt"
"time"
)

// Evidence models a evidence-entry
type Evidence struct {
Expand Down Expand Up @@ -53,3 +57,34 @@ func (e *Evidence) AddProcess(p Process) error {

return nil
}

// Valid validates the Evidence receiver to ensure it has valid required fields
func (e Evidence) Valid() error {
if e.DeviceID == "" {
return errors.New("evidence device-id is empty")
}

if e.Date.IsZero() {
return errors.New("evidence date is zero")
}

// Validate Files if present
Copy link
Contributor

@yogeshbdeshpande yogeshbdeshpande Sep 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The correct implementation is to invoke Method Valid() on the File Object and let File Object, in the file file.go to implement the Valid Method that can be invoked from line 71. In the Valid for file, check for validity of Mandatory (like FsName) and check for presence of optional elements in the file object. If Optional Elements are present, then please check their validity!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure sir checking it @yogeshbdeshpande

if e.Files != nil {
for i, file := range *e.Files {
if file.FileSystemItem.FsName == "" {
return fmt.Errorf("evidence file[%d] fs-name is empty", i)
}
}
}

// Validate Processes if present
if e.Processes != nil {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for process, may be we are ok to check here as there is not much to check for validity of the process elements.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh ok sir

for i, process := range *e.Processes {
if process.ProcessName == "" {
return fmt.Errorf("evidence process[%d] process-name is empty", i)
}
}
}

return nil
}
168 changes: 168 additions & 0 deletions evidence_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,171 @@ func TestEvidence_Roundtrip(t *testing.T) {
assert.Equal(t, tv.Date.UTC(), actual.Date.UTC()) // compare as UTC
assert.Equal(t, tv.DeviceID, actual.DeviceID)
}

func TestEvidence_Valid_empty_device_id(t *testing.T) {
evidence := Evidence{
DeviceID: "",
Date: time.Now(),
}

err := evidence.Valid()

assert.EqualError(t, err, "evidence device-id is empty")
}

func TestEvidence_Valid_zero_date(t *testing.T) {
evidence := Evidence{
DeviceID: "test-device",
Date: time.Time{},
}

err := evidence.Valid()

assert.EqualError(t, err, "evidence date is zero")
}

func TestEvidence_Valid_simple_valid(t *testing.T) {
evidence := Evidence{
DeviceID: "test-device",
Date: time.Now(),
}

err := evidence.Valid()

assert.NoError(t, err)
}

func TestEvidence_Valid_with_valid_files(t *testing.T) {
files := Files{
File{
FileSystemItem: FileSystemItem{
FsName: "test.exe",
},
},
File{
FileSystemItem: FileSystemItem{
FsName: "config.ini",
},
},
}

evidence := Evidence{
DeviceID: "test-device",
Date: time.Now(),
ResourceCollection: ResourceCollection{
PathElements: PathElements{
Files: &files,
},
},
}

err := evidence.Valid()

assert.NoError(t, err)
}

func TestEvidence_Valid_with_invalid_files(t *testing.T) {
files := Files{
File{
FileSystemItem: FileSystemItem{
FsName: "test.exe",
},
},
File{
FileSystemItem: FileSystemItem{
FsName: "", // empty fs-name
},
},
}

evidence := Evidence{
DeviceID: "test-device",
Date: time.Now(),
ResourceCollection: ResourceCollection{
PathElements: PathElements{
Files: &files,
},
},
}

err := evidence.Valid()

assert.EqualError(t, err, "evidence file[1] fs-name is empty")
}

func TestEvidence_Valid_with_valid_processes(t *testing.T) {
processes := Processes{
Process{
ProcessName: "test.exe",
},
Process{
ProcessName: "service.exe",
},
}

evidence := Evidence{
DeviceID: "test-device",
Date: time.Now(),
ResourceCollection: ResourceCollection{
Processes: &processes,
},
}

err := evidence.Valid()

assert.NoError(t, err)
}

func TestEvidence_Valid_with_invalid_processes(t *testing.T) {
processes := Processes{
Process{
ProcessName: "test.exe",
},
Process{
ProcessName: "", // empty process name
},
}

evidence := Evidence{
DeviceID: "test-device",
Date: time.Now(),
ResourceCollection: ResourceCollection{
Processes: &processes,
},
}

err := evidence.Valid()

assert.EqualError(t, err, "evidence process[1] process-name is empty")
}

func TestEvidence_Valid_with_mixed_valid_resources(t *testing.T) {
files := Files{
File{
FileSystemItem: FileSystemItem{
FsName: "test.exe",
},
},
}

processes := Processes{
Process{
ProcessName: "service.exe",
},
}

evidence := Evidence{
DeviceID: "test-device",
Date: time.Now(),
ResourceCollection: ResourceCollection{
PathElements: PathElements{
Files: &files,
},
Processes: &processes,
},
}

err := evidence.Valid()

assert.NoError(t, err)
}
83 changes: 83 additions & 0 deletions example_validation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright 2020 Contributors to the Veraison project.
Copy link
Contributor

@yogeshbdeshpande yogeshbdeshpande Oct 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Copyright 2020 Contributors to the Veraison project.
// Copyright 2020-2025 Contributors to the Veraison project.

// SPDX-License-Identifier: Apache-2.0

package swid

import (
"fmt"
"time"

"github.com/google/uuid"
)

func ExampleTagID_Valid() {
// Valid string TagID
validStringTagID := TagID{val: "com.acme.product-v1.0.0"}
if err := validStringTagID.Valid(); err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Println("String TagID is valid")
}

// Valid UUID TagID
validUUIDTagID := TagID{val: uuid.MustParse("550e8400-e29b-41d4-a716-446655440000")}
if err := validUUIDTagID.Valid(); err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Println("UUID TagID is valid")
}

// Invalid empty string TagID
emptyStringTagID := TagID{val: ""}
if err := emptyStringTagID.Valid(); err != nil {
fmt.Printf("Error: %v\n", err)
}

// Invalid nil TagID
nilTagID := TagID{val: nil}
if err := nilTagID.Valid(); err != nil {
fmt.Printf("Error: %v\n", err)
}

// Output:
// String TagID is valid
// UUID TagID is valid
// Error: tag-id string value is empty
// Error: tag-id value is nil
}

func ExampleEvidence_Valid() {
// Valid Evidence
validEvidence := Evidence{
DeviceID: "device-001",
Date: time.Now(),
}
if err := validEvidence.Valid(); err != nil {
fmt.Printf("Error: %v\n", err)
} else {
fmt.Println("Evidence is valid")
}

// Invalid Evidence - empty DeviceID
invalidEvidence1 := Evidence{
DeviceID: "",
Date: time.Now(),
}
if err := invalidEvidence1.Valid(); err != nil {
fmt.Printf("Error: %v\n", err)
}

// Invalid Evidence - zero Date
invalidEvidence2 := Evidence{
DeviceID: "device-001",
Date: time.Time{},
}
if err := invalidEvidence2.Valid(); err != nil {
fmt.Printf("Error: %v\n", err)
}

// Output:
// Evidence is valid
// Error: evidence device-id is empty
// Error: evidence date is zero
}
22 changes: 22 additions & 0 deletions tagid.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,28 @@ func (t TagID) URI() string {
}
}

// Valid validates the TagID receiver to ensure it has a valid value
func (t TagID) Valid() error {
if t.val == nil {
return errors.New("tag-id value is nil")
}

switch v := t.val.(type) {
case string:
if v == "" {
return errors.New("tag-id string value is empty")
}
case uuid.UUID:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should call the Validate method on UUID..?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @yogeshbdeshpande, thank you for the review!

I investigated the UUID validation you mentioned. The github.com/google/uuid package (v1.3.0) doesn't provide a Validate() method. The available methods are:

  • String(), Version(), Variant(), etc.

UUIDs in TagID are already validated during creation via uuid.Parse() and uuid.FromBytes() in the NewTagIDFromUUID* functions. Since UUIDs can only enter a TagID through these validated paths, checking for uuid.Nil is the standard approach for this use case.

If you'd like additional validation (e.g., checking Version() or Variant()), I'm happy to add that. Could you clarify what specific validation you had in mind?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

@Sukuna0007Abhi Sukuna0007Abhi Sep 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks sir, @yogeshbdeshpande Sorry, You're absolutely right! Thank you for the link.

I was looking at v1.3.0 (our current version) which doesn't have Validate, but the newer versions do have uuid.Validate(s string) as a package function.

However, since we're dealing with an already-parsed uuid.UUID (not a string), we can't use uuid.Validate() directly. But I can enhance the validation to be more thorough. Would you like me to:

  1. Update to the latest uuid version, or
  2. Add additional validation using Version() and Variant() methods?

Thanks for catching this - great learning moment!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am looking into this further, will update you shortly!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, it is ok, to just check for the Variant here.

Please see:
https://github.com/veraison/corim/blob/main/comid/uuid.go#L35

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure sir @yogeshbdeshpande thank you for review and merging, thanks to all mentors also

if v == uuid.Nil {
return errors.New("tag-id UUID value is nil UUID")
}
default:
return fmt.Errorf("tag-id value must be string or uuid.UUID, got %T", v)
}

return nil
}

// MarshalXMLAttr encodes the TagID receiver as XML attribute
func (t TagID) MarshalXMLAttr(name xml.Name) (xml.Attr, error) {
return xml.Attr{Name: name, Value: t.String()}, nil
Expand Down
50 changes: 50 additions & 0 deletions tagid_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"encoding/xml"
"testing"

"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -236,3 +237,52 @@ func TestTagID_UnmarshalCBOR_empty_bytes(t *testing.T) {

assert.EqualError(t, err, expectedErr)
}

func TestTagID_Valid_nil_value(t *testing.T) {
tagID := TagID{val: nil}

err := tagID.Valid()

assert.EqualError(t, err, "tag-id value is nil")
}

func TestTagID_Valid_empty_string(t *testing.T) {
tagID := TagID{val: ""}

err := tagID.Valid()

assert.EqualError(t, err, "tag-id string value is empty")
}

func TestTagID_Valid_valid_string(t *testing.T) {
tagID := TagID{val: "com.acme.rrd-2013"}

err := tagID.Valid()

assert.NoError(t, err)
}

func TestTagID_Valid_nil_uuid(t *testing.T) {
tagID := TagID{val: uuid.Nil}

err := tagID.Valid()

assert.EqualError(t, err, "tag-id UUID value is nil UUID")
}

func TestTagID_Valid_valid_uuid(t *testing.T) {
validUUID := uuid.MustParse("00010001-0001-0001-0001-000100010001")
tagID := TagID{val: validUUID}

err := tagID.Valid()

assert.NoError(t, err)
}

func TestTagID_Valid_invalid_type(t *testing.T) {
tagID := TagID{val: 123}

err := tagID.Valid()

assert.EqualError(t, err, "tag-id value must be string or uuid.UUID, got int")
}