diff --git a/ocsf/mappers/protobuff_v3/enum.go b/ocsf/mappers/protobuff_v3/enum.go index c329a82..3697fbc 100644 --- a/ocsf/mappers/protobuff_v3/enum.go +++ b/ocsf/mappers/protobuff_v3/enum.go @@ -27,10 +27,11 @@ func (e *Enum) GetValue(name string) (*EnumValue, bool) { // Get enum values sorted by name (with UNKNOWN coming first) func (e *Enum) GetValues() []*EnumValue { - // Add UNKNOWN if not present - if !e.HasUnknown() { + // Add UNSPECIFIED if not present + if !e.HasUNSPECIFIED() { e.AddValue(&EnumValue{ - Name: "UNKNOWN", + Name: "UNSPECIFIED", + Value: 0, Comment: Comment{ "Type": "NON_OCSF_VALUE", }, @@ -41,7 +42,7 @@ func (e *Enum) GetValues() []*EnumValue { // Sort values by name (with UNKNOWN coming first) sort.Slice(values, func(i, j int) bool { - return valueSorter(values, i, j) + return values[i].Value < values[j].Value }) return values @@ -59,8 +60,8 @@ func (e *Enum) Marshal() string { values := e.GetValues() // Marshal values and add to content - for i, v := range values { - content = append(content, "\t"+v.Marshal(i)) + for _, v := range values { + content = append(content, "\t"+v.Marshal()) } // Close Enum @@ -85,31 +86,16 @@ func (e *Enum) GetPackage() string { return e.Package.GetFullName() } -// Enum has at least one value ending with UNKNOWN -func (e *Enum) HasUnknown() bool { +// Enum has at least one value ending with HasUNSPECIFIED +func (e *Enum) HasUNSPECIFIED() bool { for _, v := range e.values { - if strings.HasSuffix(strings.ToUpper(v.Name), "UNKNOWN") { + if v.Value == 0 { return true } } return false } -// Sorts EnumValues by name, with "UNKNOWN" coming first -func valueSorter(values []*EnumValue, i int, j int) bool { - - // if string ends with UNKNOWN, it should be first - if strings.HasSuffix(strings.ToUpper(values[i].Name), "UNKNOWN") { - return true - } - - if strings.HasSuffix(strings.ToUpper(values[j].Name), "UNKNOWN") { - return false - } - - return values[i].Name < values[j].Name -} - // ToEnumName converts a string to a valid Enum Name func ToEnumName(input string) string { diff --git a/ocsf/mappers/protobuff_v3/enumValue.go b/ocsf/mappers/protobuff_v3/enumValue.go index b9e22be..cbabbf9 100644 --- a/ocsf/mappers/protobuff_v3/enumValue.go +++ b/ocsf/mappers/protobuff_v3/enumValue.go @@ -7,12 +7,16 @@ import ( "github.com/iancoleman/strcase" ) -func (ev *EnumValue) Marshal(index int) string { +func (ev *EnumValue) Marshal() string { content := []string{} - content = append(content, ToEnumValueName(ev.enum.Name+" "+ev.Name)) + // NOTE: some OCSF Values have non-Zero UNKNOKWN values, while protos want a zero value unknown. + // class_uid * 100 + activity_id. -> These are OCSF values, and we still emit a UNSPECIFRIED=0 enum + // value + baseName := ev.enum.Name + " " + ev.Name + content = append(content, ToEnumValueName(baseName)) - content = append(content, fmt.Sprintf("= %d;", index)) + content = append(content, fmt.Sprintf("= %d;", ev.Value)) if len(ev.Comment) > 0 { content = append(content, "//") @@ -30,7 +34,7 @@ func ToEnumValueName(input string) string { value, exists := GetMapper().Cache.EnumValues.Get(input) if exists { - return fmt.Sprint(value) + return value.(string) } output := input diff --git a/ocsf/mappers/protobuff_v3/enum_test.go b/ocsf/mappers/protobuff_v3/enum_test.go index 3a9c1a6..408b7cc 100644 --- a/ocsf/mappers/protobuff_v3/enum_test.go +++ b/ocsf/mappers/protobuff_v3/enum_test.go @@ -7,67 +7,23 @@ import ( func TestValueSorter(t *testing.T) { values := []*EnumValue{ - {Name: "UNKNOWN"}, - {Name: "B"}, - {Name: "A"}, - {Name: "C"}, - {Name: "D"}, - {Name: "E"}, - {Name: "F"}, - {Name: "G"}, - {Name: "H"}, - {Name: "I"}, - {Name: "J"}, - {Name: "K"}, - {Name: "L"}, - {Name: "M"}, - {Name: "N"}, - {Name: "O"}, - {Name: "P"}, - {Name: "Q"}, - {Name: "R"}, - {Name: "S"}, - {Name: "T"}, - {Name: "U"}, - {Name: "V"}, - {Name: "W"}, - {Name: "X"}, - {Name: "Y"}, - {Name: "Z"}, + {Name: "UNSPECIFIED", Value: 0}, + {Name: "B", Value: 2}, + {Name: "A", Value: 1}, + {Name: "C", Value: 3}, + {Name: "D", Value: 4}, } sort.Slice(values, func(i, j int) bool { - return valueSorter(values, i, j) + return values[i].Value < values[j].Value }) expected := []string{ - "UNKNOWN", + "UNSPECIFIED", "A", "B", "C", "D", - "E", - "F", - "G", - "H", - "I", - "J", - "K", - "L", - "M", - "N", - "O", - "P", - "Q", - "R", - "S", - "T", - "U", - "V", - "W", - "X", - "Y", - "Z", } for i, v := range values { diff --git a/ocsf/mappers/protobuff_v3/mapper.go b/ocsf/mappers/protobuff_v3/mapper.go index 06165e5..9d63442 100644 --- a/ocsf/mappers/protobuff_v3/mapper.go +++ b/ocsf/mappers/protobuff_v3/mapper.go @@ -122,7 +122,15 @@ func (mapper *mapper) populateFieldsFromAttributes(message *Message, attributes field.Type = FIELD_TYPE_OBJECT } - if len(attr.Enum) > 0 { + if len(attr.Enum) > 0 && attr.Type == "string_t" { + // there are a couple string_t enums that should just be represented as strings + field.Type = FIELD_TYPE_PRIMITIVE + validKeys := make([]string, 0, len(attr.Enum)) + for aek := range attr.Enum { + validKeys = append(validKeys, aek) + } + field.Comment["AllowedValues"] = strings.Join(validKeys, ", ") + } else if len(attr.Enum) > 0 { field.Type = FIELD_TYPE_ENUM }