audit-go/internal/audit/api/schema_validation_test.go
Mathias Koehrer (EXT) 84c49f2690 Merged PR 889470: feat: Add more validation to proto schema
Made the initiator.email optional and added a new validation.
Added a new regex pattern to string fields to prevent them from consisting only of whitespace.

Security-concept-update-needed: false

JIRA Work Item: [STACKITRMA-677](https://jira.schwarz/browse/STACKITRMA-677)
2025-11-27 14:52:27 +00:00

184 lines
5.9 KiB
Go

package api
import (
"testing"
"buf.build/go/protovalidate"
"github.com/stretchr/testify/assert"
auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1"
pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common"
)
func Test_RoutableAuditEvent(t *testing.T) {
validator, err := protovalidate.New()
assert.NoError(t, err)
newEvent := func() auditV1.RoutableAuditEvent {
return auditV1.RoutableAuditEvent{
OperationName: "stackit.resource-manager.v1.organizations.create",
Visibility: auditV1.Visibility_VISIBILITY_PUBLIC,
ObjectIdentifier: &auditV1.ObjectIdentifier{
Identifier: "14f7aa86-77ba-4d77-a091-a2cf3395a221",
Type: string(pkgAuditCommon.ObjectTypeProject),
},
Data: &auditV1.RoutableAuditEvent_UnencryptedData{UnencryptedData: &auditV1.UnencryptedData{
Data: []byte("data"),
ProtobufType: "audit.v1.AuditLogEntry",
}}}
}
t.Run("valid event", func(t *testing.T) {
event := newEvent()
err := validator.Validate(&event)
assert.NoError(t, err)
})
t.Run("empty operation name", func(t *testing.T) {
event := newEvent()
event.OperationName = ""
err := validator.Validate(&event)
assert.EqualError(t, err, "validation error:\n - operation_name: value is required [required]")
})
t.Run("invalid operation name", func(t *testing.T) {
event := newEvent()
event.OperationName = "stackit.resource-manager.v1.INVALID.organizations.create"
err := validator.Validate(&event)
assert.EqualError(t, err, "validation error:\n - operation_name: value does not match regex pattern `^stackit\\.[a-z0-9-]+\\.(?:v[0-9]+\\.)?(?:[a-z0-9-.]+\\.)?[a-z0-9-]+$` [string.pattern]")
})
t.Run("visibility invalid", func(t *testing.T) {
event := newEvent()
event.Visibility = -1
err := validator.Validate(&event)
assert.EqualError(t, err, "validation error:\n - visibility: value must be one of the defined enum values [enum.defined_only]")
})
t.Run("visibility unspecified", func(t *testing.T) {
event := newEvent()
event.Visibility = auditV1.Visibility_VISIBILITY_UNSPECIFIED
err := validator.Validate(&event)
assert.EqualError(t, err, "validation error:\n - visibility: value is required [required]")
})
t.Run("object identifier nil", func(t *testing.T) {
event := newEvent()
event.ObjectIdentifier = nil
err := validator.Validate(&event)
assert.EqualError(t, err, "validation error:\n - object_identifier: value is required [required]")
})
t.Run("object identifier id empty", func(t *testing.T) {
event := newEvent()
event.ObjectIdentifier.Identifier = ""
err := validator.Validate(&event)
assert.EqualError(t, err, "validation error:\n - object_identifier.identifier: value is required [required]")
})
t.Run("object identifier id not uuid", func(t *testing.T) {
event := newEvent()
event.ObjectIdentifier.Identifier = "invalid"
err := validator.Validate(&event)
assert.EqualError(t, err, "validation error:\n - object_identifier.identifier: value must be a valid UUID [string.uuid]")
})
t.Run("object identifier type empty", func(t *testing.T) {
event := newEvent()
event.ObjectIdentifier.Type = ""
err := validator.Validate(&event)
assert.EqualError(t, err, "validation error:\n - object_identifier.type: value is required [required]")
})
t.Run("data nil", func(t *testing.T) {
event := newEvent()
event.Data = nil
err := validator.Validate(&event)
assert.EqualError(t, err, "validation error:\n - data: exactly one field is required in oneof [required]")
})
t.Run("data empty", func(t *testing.T) {
event := newEvent()
event.Data = &auditV1.RoutableAuditEvent_UnencryptedData{UnencryptedData: &auditV1.UnencryptedData{
Data: []byte{},
ProtobufType: "audit.v1.AuditLogEntry",
}}
err := validator.Validate(&event)
assert.EqualError(t, err, "validation error:\n - unencrypted_data.data: value is required [required]")
})
t.Run("data protobuf type empty", func(t *testing.T) {
event := newEvent()
event.Data = &auditV1.RoutableAuditEvent_UnencryptedData{UnencryptedData: &auditV1.UnencryptedData{
Data: []byte("data"),
ProtobufType: "",
}}
err := validator.Validate(&event)
assert.EqualError(t, err, "validation error:\n - unencrypted_data.protobuf_type: value is required [required]")
})
}
func Test_AuthenticationInfo(t *testing.T) {
validator, err := protovalidate.New()
assert.NoError(t, err)
email := "x@x.x"
newEvent := func() auditV1.AuthenticationInfo {
return auditV1.AuthenticationInfo{
PrincipalId: "1234567890",
PrincipalEmail: &email,
ServiceAccountName: nil,
ServiceAccountDelegationInfo: nil,
}
}
t.Run("valid event", func(t *testing.T) {
event := newEvent()
err := validator.Validate(&event)
assert.NoError(t, err)
})
t.Run("valid event without email", func(t *testing.T) {
event := newEvent()
event.PrincipalEmail = nil
err := validator.Validate(&event)
assert.NoError(t, err)
})
t.Run("principal id contains only whitespace", func(t *testing.T) {
event := newEvent()
event.PrincipalId = " "
err := validator.Validate(&event)
assert.EqualError(t, err, "validation error:\n - principal_id: value does not match regex pattern `.*\\S.*` [string.pattern]")
})
t.Run("principal email contains only whitespace", func(t *testing.T) {
event := newEvent()
whitespaceEmail := " "
event.PrincipalEmail = &whitespaceEmail
err := validator.Validate(&event)
assert.EqualError(t, err, "validation error:\n - principal_email: value must be a valid email address [string.email]")
})
t.Run("missing host in email", func(t *testing.T) {
event := newEvent()
invalidEmail := "@test.com"
event.PrincipalEmail = &invalidEmail
err := validator.Validate(&event)
assert.EqualError(t, err, "validation error:\n - principal_email: value must be a valid email address [string.email]")
})
}