diff --git a/api/types/events/api.go b/api/types/events/api.go index 4aeb2df0963b..7be3f12718c2 100644 --- a/api/types/events/api.go +++ b/api/types/events/api.go @@ -69,6 +69,14 @@ type AuditEvent interface { GetClusterName() string // SetClusterName sets the name of the teleport cluster on the event. SetClusterName(string) + + // TrimToMaxSize returns a copy of the event trimmed to a smaller + // size. The desired size may not be achievable so callers + // should always check the size of the returned event before + // using it. Generally fields that are unique to the event + // will be trimmed, other fields are not currently modified + // (ie *Metadata messages). + TrimToMaxSize(maxSizeBytes int) AuditEvent } // Emitter emits audit events. diff --git a/api/types/events/events.go b/api/types/events/events.go index a3b99878a7b5..dd0ca0ecf8d1 100644 --- a/api/types/events/events.go +++ b/api/types/events/events.go @@ -21,15 +21,66 @@ import ( "encoding/hex" "encoding/json" + "github.com/gogo/protobuf/types" "github.com/gravitational/trace" "google.golang.org/protobuf/types/known/structpb" "google.golang.org/protobuf/types/known/timestamppb" auditlogpb "github.com/gravitational/teleport/api/gen/proto/go/teleport/auditlog/v1" + "github.com/gravitational/teleport/api/types/wrappers" "github.com/gravitational/teleport/api/utils" ) -func trimN(s string, n int) string { +// ToUnstructured converts the event stored in the AuditEvent interface +// to unstructured. +// If the event is a session print event, it is converted to a plugins printEvent struct +// which is then converted to structpb.Struct. Otherwise the event is marshaled directly. +func ToUnstructured(evt AuditEvent) (*auditlogpb.EventUnstructured, error) { + payload, err := json.Marshal(evt) + if err != nil { + return nil, trace.Wrap(err) + } + id := computeEventID(evt, payload) + + str := &structpb.Struct{} + if err := str.UnmarshalJSON(payload); err != nil { + return nil, trace.Wrap(err) + } + + // If the event is a session print event, convert it to a printEvent struct + // to include the `data` field in the JSON. + if p, ok := evt.(*SessionPrint); ok { + const printEventDataKey = "data" + // append the `data` field to the unstructured event + str.Fields[printEventDataKey], err = structpb.NewValue(p.Data) + if err != nil { + return nil, trace.Wrap(err) + } + } + + return &auditlogpb.EventUnstructured{ + Type: evt.GetType(), + Index: evt.GetIndex(), + Time: timestamppb.New(evt.GetTime()), + Id: id, + Unstructured: str, + }, nil +} + +// computeEventID computes the ID of the event. If the event already has an ID, it is returned. +// Otherwise, the event is marshaled to JSON and the SHA256 hash of the JSON is returned. +func computeEventID(evt AuditEvent, payload []byte) string { + id := evt.GetID() + if id != "" { + return id + } + + hash := sha256.Sum256(payload) + return hex.EncodeToString(hash[:]) +} + +// trimStr trims a string to a given length. +func trimStr(s string, n int) string { // Starting at 2 to leave room for quotes at the begging and end. charCount := 2 for i, r := range s { @@ -45,94 +96,315 @@ func trimN(s string, n int) string { return s } -func maxSizePerField(maxLength, customFields int) int { +// maxSizePerField returns the maximum size each field can be when trimming. +func maxSizePerField(maxSize, customFields int) int { if customFields == 0 { - return maxLength + return maxSize } - return maxLength / customFields + return maxSize / customFields } -// TrimToMaxSize trims the DatabaseSessionQuery message content. The maxSize is used to calculate -// per-field max size where only user input message fields DatabaseQuery and DatabaseQueryParameters are taken into -// account. -func (m *DatabaseSessionQuery) TrimToMaxSize(maxSize int) AuditEvent { - size := m.Size() - if size <= maxSize { - return m - } +// adjustedMaxSize returns the maximum size to trim an event to after +// accounting for the size of the message without custom fields that +// will be trimmed. +func adjustedMaxSize(e AuditEvent, maxSize int) int { + // Use 10% max size ballast + message size without custom fields. + sizeBallast := maxSize/10 + e.Size() + return maxSize - sizeBallast +} - out := utils.CloneProtoMsg(m) - out.DatabaseQuery = "" - out.DatabaseQueryParameters = nil +// nonEmptyStrs returns the number of non-empty strings. +func nonEmptyStrs(s ...string) int { + nonEmptyStrs := 0 + for _, s := range s { + if s != "" { + nonEmptyStrs++ + } + } + return nonEmptyStrs +} - // Use 10% max size ballast + message size without custom fields. - sizeBallast := maxSize/10 + out.Size() - maxSize -= sizeBallast +// nonEmptyStrsInSlice returns the number of non-empty elements in a +// slice of strings. +func nonEmptyStrsInSlice[T ~string](s ...[]T) int { + nonEmptyStrs := 0 + for _, s := range s { + if len(s) != 0 { + nonEmptyStrs += len(s) + } + } + return nonEmptyStrs +} - // Check how many custom fields are set. - customFieldsCount := 0 - if m.DatabaseQuery != "" { - customFieldsCount++ +// trimStrSlice trims each element in a slice of strings to a given +// length. +func trimStrSlice[T ~string](strs []T, maxSize int) []T { + if len(strs) == 0 { + return nil } - for range m.DatabaseQueryParameters { - customFieldsCount++ + trimmed := make([]T, len(strs)) + for i, v := range strs { + trimmed[i] = T(trimStr(string(v), maxSize)) } + return trimmed +} - maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) +func (m *Status) nonEmptyStrs() int { + return nonEmptyStrs(m.Error, m.UserMessage) +} + +func (m *Status) trimToMaxSize(maxSize int) Status { + var out Status + out.Error = trimStr(m.Error, maxSize) + out.UserMessage = trimStr(m.UserMessage, maxSize) + return out +} + +func (m *Struct) nonEmptyStrs() int { + var toTrim int + for _, v := range m.Fields { + toTrim++ - out.DatabaseQuery = trimN(m.DatabaseQuery, maxFieldsSize) - if m.DatabaseQueryParameters != nil { - out.DatabaseQueryParameters = make([]string, len(m.DatabaseQueryParameters)) + if v != nil { + if v.GetStringValue() != "" { + toTrim++ + continue + } + if l := v.GetListValue(); l != nil { + for _, lv := range l.Values { + if lv.GetStringValue() != "" { + toTrim++ + } + } + } + } } - for i, v := range m.DatabaseQueryParameters { - out.DatabaseQueryParameters[i] = trimN(v, maxFieldsSize) + + return toTrim +} + +func (m *Struct) trimToMaxSize(maxSize int) *Struct { + var out Struct + for k, v := range m.Fields { + delete(out.Fields, k) + trimmedKey := trimStr(k, maxSize) + + if v != nil { + if strVal := v.GetStringValue(); strVal != "" { + trimmedVal := trimStr(strVal, maxSize) + out.Fields[trimmedKey] = &types.Value{ + Kind: &types.Value_StringValue{ + StringValue: trimmedVal, + }, + } + } else if l := v.GetListValue(); l != nil { + for i, lv := range l.Values { + if strVal := lv.GetStringValue(); strVal != "" { + trimmedVal := trimStr(strVal, maxSize) + l.Values[i] = &types.Value{ + Kind: &types.Value_StringValue{ + StringValue: trimmedVal, + }, + } + } + } + } + } } + return &out +} + +func (m *CommandMetadata) nonEmptyStrs() int { + return nonEmptyStrs(m.Command, m.Error) +} + +func (m *CommandMetadata) trimToMaxSize(maxSize int) CommandMetadata { + var out CommandMetadata + out.Command = trimStr(m.Command, maxSize) + out.Error = trimStr(m.Error, maxSize) return out } +// nonEmptyStrsInMap returns the number of non-empty keys and values in +// a map of strings to strings. +func nonEmptyStrsInMap(m map[string]string) int { + var toTrim int + for k, v := range m { + if k != "" { + toTrim++ + } + if v != "" { + toTrim++ + } + } + return toTrim +} + +// trimMap trims each key and value in a map of strings to strings +// to a given length. +func trimMap(m map[string]string, maxSize int) map[string]string { + for k, v := range m { + delete(m, k) + trimmedKey := trimStr(k, maxSize) + m[trimmedKey] = trimStr(v, maxSize) + } + return m +} + +// nonEmptyTraits returns the number of non-empty keys and values in +// some traits. +func nonEmptyTraits(traits wrappers.Traits) int { + var toTrim int + for k, vals := range traits { + if k != "" { + toTrim++ + } + for _, v := range vals { + if v != "" { + toTrim++ + } + } + } + return toTrim +} + +// trimTraits trims each key and value in some traits to a given length. +func trimTraits(traits wrappers.Traits, maxSize int) wrappers.Traits { + for k, vals := range traits { + delete(traits, k) + trimmedKey := trimStr(k, maxSize) + traits[trimmedKey] = trimStrSlice(vals, maxSize) + } + return traits +} + +// TrimToMaxSize returns the SessionPrint event unmodified because there +// are no string fields to trim. +func (m *SessionPrint) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + // TrimToMaxSize trims the SessionStart event to the given maximum size. // Currently assumes that the largest field will be InitialCommand and tries to // trim that. -func (e *SessionStart) TrimToMaxSize(maxSize int) AuditEvent { - size := e.Size() +func (m *SessionStart) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.InitialCommand = nil + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrsInSlice(m.InitialCommand) + maxFieldSize := maxSizePerField(maxSize, customFieldsCount) + + out.InitialCommand = trimStrSlice(m.InitialCommand, maxFieldSize) + + return out +} + +func (m *SessionEnd) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() if size <= maxSize { - return e + return m } - out := utils.CloneProtoMsg(e) + out := utils.CloneProtoMsg(m) out.InitialCommand = nil - // Use 10% max size ballast + message size without InitialCommand - sizeBallast := maxSize/10 + out.Size() - maxSize -= sizeBallast + maxSize = adjustedMaxSize(out, maxSize) + + // Check how many custom fields are set. + customFieldsCount := nonEmptyStrsInSlice(m.InitialCommand) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.InitialCommand = trimStrSlice(m.InitialCommand, maxFieldsSize) + + return out +} + +func (m *SessionUpload) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *SessionJoin) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *SessionLeave) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *SessionData) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *ClientDisconnect) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Reason = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.Reason) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) - maxFieldSize := maxSizePerField(maxSize, len(e.InitialCommand)) + out.Reason = trimStr(m.Reason, maxFieldsSize) - out.InitialCommand = make([]string, len(e.InitialCommand)) - for i, c := range e.InitialCommand { - out.InitialCommand[i] = trimN(c, maxFieldSize) + return out +} + +// TrimToMaxSize trims the DatabaseSessionQuery message content. The maxSize is used to calculate +// per-field max size where only user input message fields DatabaseQuery and DatabaseQueryParameters are taken into +// account. +func (m *DatabaseSessionQuery) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m } + out := utils.CloneProtoMsg(m) + out.DatabaseQuery = "" + out.DatabaseQueryParameters = nil + + maxSize = adjustedMaxSize(out, maxSize) + + // Check how many custom fields are set. + customFieldsCount := nonEmptyStrs(m.DatabaseQuery) + nonEmptyStrsInSlice(m.DatabaseQueryParameters) + + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.DatabaseQuery = trimStr(m.DatabaseQuery, maxFieldsSize) + out.DatabaseQueryParameters = trimStrSlice(m.DatabaseQueryParameters, maxFieldsSize) + return out } // TrimToMaxSize trims the Exec event to the given maximum size. // Currently assumes that the largest field will be Command and tries to trim // that. -func (e *Exec) TrimToMaxSize(maxSize int) AuditEvent { - size := e.Size() +func (m *Exec) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() if size <= maxSize { - return e + return m } - out := utils.CloneProtoMsg(e) - out.Command = "" + out := utils.CloneProtoMsg(m) + out.CommandMetadata = CommandMetadata{} + + maxSize = adjustedMaxSize(out, maxSize) - // Use 10% max size ballast + message size without Command - sizeBallast := maxSize/10 + out.Size() - maxSize -= sizeBallast + customFieldsCount := m.CommandMetadata.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) - out.Command = trimN(e.Command, maxSize) + out.CommandMetadata = m.CommandMetadata.trimToMaxSize(maxFieldsSize) return out } @@ -142,75 +414,1569 @@ func (e *Exec) TrimToMaxSize(maxSize int) AuditEvent { // craft a request that creates error messages too large to be handled by the // underlying storage and thus cause the events to be omitted entirely. See // teleport-private#172. -func (e *UserLogin) TrimToMaxSize(maxSize int) AuditEvent { - size := e.Size() +func (m *UserLogin) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() if size <= maxSize { - return e + return m } - out := utils.CloneProtoMsg(e) - out.Status.Error = "" - out.Status.UserMessage = "" - - // Use 10% max size ballast + message size without Error and UserMessage - sizeBallast := maxSize/10 + out.Size() - maxSize -= sizeBallast + out := utils.CloneProtoMsg(m) + out.Status = Status{} - maxFieldSize := maxSizePerField(maxSize, 2) + maxSize = adjustedMaxSize(out, maxSize) + customFieldsCount := m.Status.nonEmptyStrs() + maxFieldSize := maxSizePerField(maxSize, customFieldsCount) - out.Status.Error = trimN(e.Status.Error, maxFieldSize) - out.Status.UserMessage = trimN(e.Status.UserMessage, maxFieldSize) + out.Status = m.Status.trimToMaxSize(maxFieldSize) return out } -// ToUnstructured converts the event stored in the AuditEvent interface -// to unstructured. -// If the event is a session print event, it is converted to a plugins printEvent struct -// which is then converted to structpb.Struct. Otherwise the event is marshaled directly. -func ToUnstructured(evt AuditEvent) (*auditlogpb.EventUnstructured, error) { - payload, err := json.Marshal(evt) - if err != nil { - return nil, trace.Wrap(err) - } - id := computeEventID(evt, payload) - if err != nil { - return nil, trace.Wrap(err) - } +func (m *UserDelete) TrimToMaxSize(maxSize int) AuditEvent { + return m +} - str := &structpb.Struct{} - if err := str.UnmarshalJSON(payload); err != nil { - return nil, trace.Wrap(err) +func (m *UserCreate) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m } - // If the event is a session print event, convert it to a printEvent struct - // to include the `data` field in the JSON. - if p, ok := evt.(*SessionPrint); ok { - const printEventDataKey = "data" - // append the `data` field to the unstructured event - str.Fields[printEventDataKey], err = structpb.NewValue(p.Data) - if err != nil { - return nil, trace.Wrap(err) - } + out := utils.CloneProtoMsg(m) + out.Roles = nil + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrsInSlice(m.Roles) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Roles = trimStrSlice(m.Roles, maxFieldsSize) + + return out +} + +func (m *UserPasswordChange) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *AccessRequestCreate) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m } - return &auditlogpb.EventUnstructured{ - Type: evt.GetType(), - Index: evt.GetIndex(), - Time: timestamppb.New(evt.GetTime()), - Id: id, - Unstructured: str, - }, nil + out := utils.CloneProtoMsg(m) + out.Roles = nil + out.Reason = "" + out.Annotations = nil + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrsInSlice(m.Roles) + + nonEmptyStrs(m.Reason) + + m.Annotations.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Roles = trimStrSlice(m.Roles, maxFieldsSize) + out.Reason = trimStr(m.Reason, maxFieldsSize) + out.Annotations = m.Annotations.trimToMaxSize(maxFieldsSize) + + return out } -// computeEventID computes the ID of the event. If the event already has an ID, it is returned. -// Otherwise, the event is marshaled to JSON and the SHA256 hash of the JSON is returned. -func computeEventID(evt AuditEvent, payload []byte) string { - id := evt.GetID() - if id != "" { - return id +func (m *AccessRequestResourceSearch) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m } - hash := sha256.Sum256(payload) - return hex.EncodeToString(hash[:]) + out := utils.CloneProtoMsg(m) + out.SearchAsRoles = nil + out.Labels = nil + out.PredicateExpression = "" + out.SearchKeywords = nil + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrsInSlice(m.SearchAsRoles, m.SearchKeywords) + + nonEmptyStrsInMap(m.Labels) + + nonEmptyStrs(m.PredicateExpression) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.SearchAsRoles = trimStrSlice(m.SearchAsRoles, maxFieldsSize) + out.Labels = trimMap(m.Labels, maxFieldsSize) + out.PredicateExpression = trimStr(m.PredicateExpression, maxFieldsSize) + out.SearchKeywords = trimStrSlice(m.SearchKeywords, maxFieldsSize) + + return out +} + +func (m *BillingCardCreate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *BillingCardDelete) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *BillingInformationUpdate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *UserTokenCreate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *Subsystem) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Name = "" + out.Error = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.Name, m.Error) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Name = trimStr(m.Name, maxFieldsSize) + out.Error = trimStr(m.Error, maxFieldsSize) + + return out +} + +func (m *X11Forward) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *PortForward) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *AuthAttempt) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *SCP) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Path = "" + out.CommandMetadata = CommandMetadata{} + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.Path) + m.CommandMetadata.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Path = trimStr(m.Path, maxFieldsSize) + out.CommandMetadata = m.CommandMetadata.trimToMaxSize(maxFieldsSize) + + return out +} + +func (m *Resize) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *SessionCommand) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Path = "" + out.Argv = nil + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.Path) + nonEmptyStrsInSlice(m.Argv) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Path = trimStr(m.Path, maxFieldsSize) + out.Argv = trimStrSlice(m.Argv, maxFieldsSize) + + return out +} + +func (m *SessionDisk) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Path = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.Path) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Path = trimStr(m.Path, maxFieldsSize) + + return out +} + +func (m *SessionNetwork) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *RoleCreate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *RoleDelete) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *TrustedClusterCreate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *TrustedClusterDelete) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *TrustedClusterTokenCreate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *ProvisionTokenCreate) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Roles = nil + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrsInSlice(m.Roles) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Roles = trimStrSlice(m.Roles, maxFieldsSize) + + return out +} + +func (m *GithubConnectorCreate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *GithubConnectorDelete) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *OIDCConnectorCreate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *OIDCConnectorDelete) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *SAMLConnectorCreate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *SAMLConnectorDelete) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *SessionReject) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Reason = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.Reason) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Reason = trimStr(m.Reason, maxFieldsSize) + + return out +} + +func (m *AppSessionStart) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *AppSessionEnd) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *AppSessionChunk) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *AppSessionRequest) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Path = "" + out.RawQuery = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.Path, m.RawQuery) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Path = trimStr(m.Path, maxFieldsSize) + out.RawQuery = trimStr(m.RawQuery, maxFieldsSize) + + return out +} + +func (m *AppSessionDynamoDBRequest) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Path = "" + out.RawQuery = "" + out.Target = "" + out.Body = nil + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.Path, m.RawQuery, m.Target) + m.Body.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Path = trimStr(m.Path, maxFieldsSize) + out.RawQuery = trimStr(m.RawQuery, maxFieldsSize) + out.Target = trimStr(m.Target, maxFieldsSize) + out.Body = m.Body.trimToMaxSize(maxFieldsSize) + + return out +} + +func (m *AppCreate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *AppUpdate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *AppDelete) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *DatabaseCreate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *DatabaseUpdate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *DatabaseDelete) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *DatabaseSessionStart) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Status = Status{} + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := m.Status.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Status = m.Status.trimToMaxSize(maxFieldsSize) + + return out +} + +func (m *DatabaseSessionEnd) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *DatabaseSessionMalformedPacket) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Payload = nil + + maxSize = adjustedMaxSize(out, maxSize) + + var customFieldsCount int + if len(m.Payload) != 0 { + customFieldsCount++ + } + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Payload = []byte(trimStr(string(m.Payload), maxFieldsSize)) + + return out +} + +func (m *PostgresParse) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.StatementName = "" + out.Query = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.StatementName, m.Query) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.StatementName = trimStr(m.StatementName, maxFieldsSize) + out.Query = trimStr(m.Query, maxFieldsSize) + + return out +} + +func (m *PostgresBind) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.StatementName = "" + out.PortalName = "" + out.Parameters = nil + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.StatementName, m.PortalName) + nonEmptyStrsInSlice(m.Parameters) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.StatementName = trimStr(m.StatementName, maxFieldsSize) + out.PortalName = trimStr(m.PortalName, maxFieldsSize) + out.Parameters = trimStrSlice(m.Parameters, maxFieldsSize) + + return out +} + +func (m *PostgresExecute) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.PortalName = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.PortalName) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.PortalName = trimStr(m.PortalName, maxFieldsSize) + + return out +} + +func (m *PostgresClose) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.StatementName = "" + out.PortalName = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.StatementName, m.PortalName) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.StatementName = trimStr(m.StatementName, maxFieldsSize) + out.PortalName = trimStr(m.PortalName, maxFieldsSize) + + return out +} + +func (m *PostgresFunctionCall) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.FunctionArgs = nil + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrsInSlice(m.FunctionArgs) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.FunctionArgs = trimStrSlice(m.FunctionArgs, maxFieldsSize) + + return out +} + +func (m *MySQLStatementPrepare) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Query = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.Query) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Query = trimStr(m.Query, maxFieldsSize) + + return out +} + +func (m *MySQLStatementExecute) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Parameters = nil + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrsInSlice(m.Parameters) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Parameters = trimStrSlice(m.Parameters, maxFieldsSize) + + return out +} + +func (m *MySQLStatementSendLongData) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *MySQLStatementClose) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *MySQLStatementReset) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *MySQLStatementFetch) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *MySQLStatementBulkExecute) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Parameters = nil + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrsInSlice(m.Parameters) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Parameters = trimStrSlice(m.Parameters, maxFieldsSize) + + return out +} + +func (m *MySQLInitDB) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.SchemaName = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.SchemaName) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.SchemaName = trimStr(m.SchemaName, maxFieldsSize) + + return out +} + +func (m *MySQLCreateDB) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.SchemaName = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.SchemaName) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.SchemaName = trimStr(m.SchemaName, maxFieldsSize) + + return out +} + +func (m *MySQLDropDB) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.SchemaName = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.SchemaName) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.SchemaName = trimStr(m.SchemaName, maxFieldsSize) + + return out +} + +func (m *MySQLShutDown) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *MySQLProcessKill) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *MySQLDebug) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *MySQLRefresh) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Subcommand = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.Subcommand) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Subcommand = trimStr(m.Subcommand, maxFieldsSize) + + return out +} + +func (m *SQLServerRPCRequest) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Procname = "" + out.Parameters = nil + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.Procname) + nonEmptyStrsInSlice(m.Parameters) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Procname = trimStr(m.Procname, maxFieldsSize) + out.Parameters = trimStrSlice(m.Parameters, maxFieldsSize) + + return out +} + +func (m *ElasticsearchRequest) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Path = "" + out.RawQuery = "" + out.Body = nil + out.Headers = nil + out.Target = "" + out.Query = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.Path, m.RawQuery, m.Target, m.Query) + nonEmptyTraits(m.Headers) + if len(m.Body) != 0 { + customFieldsCount++ + } + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Path = trimStr(m.Path, maxFieldsSize) + out.RawQuery = trimStr(m.RawQuery, maxFieldsSize) + out.Body = []byte(trimStr(string(m.Body), maxFieldsSize)) + out.Headers = trimTraits(m.Headers, maxFieldsSize) + out.Target = trimStr(m.Target, maxFieldsSize) + out.Query = trimStr(m.Query, maxFieldsSize) + + return out +} + +func (m *OpenSearchRequest) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Path = "" + out.RawQuery = "" + out.Body = nil + out.Headers = nil + out.Target = "" + out.Query = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.Path, m.RawQuery, m.Target, m.Query) + nonEmptyTraits(m.Headers) + if len(m.Body) != 0 { + customFieldsCount++ + } + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Path = trimStr(m.Path, maxFieldsSize) + out.RawQuery = trimStr(m.RawQuery, maxFieldsSize) + out.Body = []byte(trimStr(string(m.Body), maxFieldsSize)) + out.Headers = trimTraits(m.Headers, maxFieldsSize) + out.Target = trimStr(m.Target, maxFieldsSize) + out.Query = trimStr(m.Query, maxFieldsSize) + + return out +} + +func (m *DynamoDBRequest) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Path = "" + out.RawQuery = "" + out.Body = nil + out.Target = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.Path, m.RawQuery, m.Target) + m.Body.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Path = trimStr(m.Path, maxFieldsSize) + out.RawQuery = trimStr(m.RawQuery, maxFieldsSize) + out.Body = m.Body.trimToMaxSize(maxFieldsSize) + out.Target = trimStr(m.Target, maxFieldsSize) + + return out +} + +func (m *KubeRequest) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.RequestPath = "" + out.ResourceAPIGroup = "" + out.ResourceNamespace = "" + out.ResourceName = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.RequestPath, m.ResourceAPIGroup, m.ResourceNamespace, m.ResourceName) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.RequestPath = trimStr(m.RequestPath, maxFieldsSize) + out.ResourceAPIGroup = trimStr(m.ResourceAPIGroup, maxFieldsSize) + out.ResourceNamespace = trimStr(m.ResourceNamespace, maxFieldsSize) + out.ResourceName = trimStr(m.ResourceName, maxFieldsSize) + + return out +} + +func (m *MFADeviceAdd) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *MFADeviceDelete) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *DeviceEvent) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Status = &Status{} + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := m.Status.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + status := m.Status.trimToMaxSize(maxFieldsSize) + out.Status = &status + + return out +} + +func (m *DeviceEvent2) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Status = Status{} + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := m.Status.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Status = m.Status.trimToMaxSize(maxFieldsSize) + + return out +} + +func (m *LockCreate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *LockDelete) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *RecoveryCodeGenerate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *RecoveryCodeUsed) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Status = Status{} + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := m.Status.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Status = m.Status.trimToMaxSize(maxFieldsSize) + + return out +} + +func (m *WindowsDesktopSessionStart) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Status = Status{} + out.Domain = "" + out.WindowsUser = "" + out.DesktopLabels = nil + out.DesktopName = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := m.Status.nonEmptyStrs() + + nonEmptyStrs(m.Domain, m.WindowsUser, m.DesktopName) + + nonEmptyStrsInMap(m.DesktopLabels) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Status = m.Status.trimToMaxSize(maxFieldsSize) + out.Domain = trimStr(m.Domain, maxFieldsSize) + out.WindowsUser = trimStr(m.WindowsUser, maxFieldsSize) + out.DesktopLabels = trimMap(m.DesktopLabels, maxFieldsSize) + out.DesktopName = trimStr(m.DesktopName, maxFieldsSize) + + return out +} + +func (m *WindowsDesktopSessionEnd) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Domain = "" + out.WindowsUser = "" + out.DesktopLabels = nil + out.DesktopName = "" + out.Participants = nil + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.Domain, m.WindowsUser, m.DesktopName) + + nonEmptyStrsInMap(m.DesktopLabels) + + nonEmptyStrsInSlice(m.Participants) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Domain = trimStr(m.Domain, maxFieldsSize) + out.WindowsUser = trimStr(m.WindowsUser, maxFieldsSize) + out.DesktopLabels = trimMap(m.DesktopLabels, maxFieldsSize) + out.DesktopName = trimStr(m.DesktopName, maxFieldsSize) + out.Participants = trimStrSlice(m.Participants, maxFieldsSize) + + return out +} + +func (m *DesktopClipboardSend) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *DesktopClipboardReceive) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *SessionConnect) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *AccessRequestDelete) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *CertificateCreate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *DesktopRecording) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Message = nil + + maxSize = adjustedMaxSize(out, maxSize) + + var customFieldsCount int + if len(m.Message) != 0 { + customFieldsCount++ + } + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Message = []byte(trimStr(string(m.Message), maxFieldsSize)) + + return out +} + +func (m *RenewableCertificateGenerationMismatch) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *SFTP) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.WorkingDirectory = "" + out.Path = "" + out.TargetPath = "" + out.Error = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.WorkingDirectory, m.Path, m.TargetPath, m.Error) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.WorkingDirectory = trimStr(m.WorkingDirectory, maxFieldsSize) + out.Path = trimStr(m.Path, maxFieldsSize) + out.TargetPath = trimStr(m.TargetPath, maxFieldsSize) + out.Error = trimStr(m.Error, maxFieldsSize) + + return out +} + +func (m *UpgradeWindowStartUpdate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *SessionRecordingAccess) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *SSMRun) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Status = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.Status) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Status = trimStr(m.Status, maxFieldsSize) + + return out +} + +func (m *KubernetesClusterCreate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *KubernetesClusterUpdate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *KubernetesClusterDelete) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *DesktopSharedDirectoryStart) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.DirectoryName = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.DirectoryName) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.DirectoryName = trimStr(m.DirectoryName, maxFieldsSize) + + return out +} + +func (m *DesktopSharedDirectoryRead) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.DirectoryName = "" + out.Path = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.DirectoryName, m.Path) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.DirectoryName = trimStr(m.DirectoryName, maxFieldsSize) + out.Path = trimStr(m.Path, maxFieldsSize) + + return out +} + +func (m *DesktopSharedDirectoryWrite) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.DirectoryName = "" + out.Path = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.DirectoryName, m.Path) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.DirectoryName = trimStr(m.DirectoryName, maxFieldsSize) + out.Path = trimStr(m.Path, maxFieldsSize) + + return out +} + +func (m *BotJoin) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Attributes = nil + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := m.Attributes.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Attributes = m.Attributes.trimToMaxSize(maxFieldsSize) + + return out +} + +func (m *InstanceJoin) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Status = Status{} + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := m.Status.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Status = m.Status.trimToMaxSize(maxFieldsSize) + + return out +} + +func (m *LoginRuleCreate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *LoginRuleDelete) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *SAMLIdPAuthAttempt) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Status = Status{} + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := m.Status.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Status = m.Status.trimToMaxSize(maxFieldsSize) + + return out +} + +func (m *SAMLIdPServiceProviderCreate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *SAMLIdPServiceProviderUpdate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *SAMLIdPServiceProviderDelete) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *SAMLIdPServiceProviderDeleteAll) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *OktaResourcesUpdate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *OktaSyncFailure) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Status = Status{} + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := m.Status.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Status = m.Status.trimToMaxSize(maxFieldsSize) + + return out +} + +func (m *OktaAssignmentResult) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Status = Status{} + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := m.Status.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Status = m.Status.trimToMaxSize(maxFieldsSize) + + return out +} + +func (m *AccessListCreate) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Status = Status{} + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := m.Status.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Status = m.Status.trimToMaxSize(maxFieldsSize) + + return out +} + +func (m *AccessListUpdate) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Status = Status{} + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := m.Status.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Status = m.Status.trimToMaxSize(maxFieldsSize) + + return out +} + +func (m *AccessListDelete) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Status = Status{} + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := m.Status.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Status = m.Status.trimToMaxSize(maxFieldsSize) + + return out +} + +func (m *AccessListReview) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Status = Status{} + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := m.Status.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Status = m.Status.trimToMaxSize(maxFieldsSize) + + return out +} + +func (m *AccessListMemberCreate) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Status = Status{} + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := m.Status.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Status = m.Status.trimToMaxSize(maxFieldsSize) + + return out +} + +func (m *AccessListMemberUpdate) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Status = Status{} + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := m.Status.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Status = m.Status.trimToMaxSize(maxFieldsSize) + + return out +} + +func (m *AccessListMemberDelete) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Status = Status{} + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := m.Status.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Status = m.Status.trimToMaxSize(maxFieldsSize) + + return out +} + +func (m *AccessListMemberDeleteAllForAccessList) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Status = Status{} + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := m.Status.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Status = m.Status.trimToMaxSize(maxFieldsSize) + + return out +} + +func (m *AuditQueryRun) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Status = Status{} + out.Name = "" + out.Query = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := m.Status.nonEmptyStrs() + nonEmptyStrs(m.Name, m.Query) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Status = m.Status.trimToMaxSize(maxFieldsSize) + out.Name = trimStr(m.Name, maxFieldsSize) + out.Query = trimStr(m.Query, maxFieldsSize) + + return out +} + +func (m *SecurityReportRun) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Status = Status{} + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := m.Status.nonEmptyStrs() + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Status = m.Status.trimToMaxSize(maxFieldsSize) + + return out +} + +func (m *ExternalAuditStorageEnable) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *ExternalAuditStorageDisable) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *Unknown) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Data = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.Data) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Data = trimStr(m.Data, maxFieldsSize) + + return out +} + +func (m *CassandraBatch) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Keyspace = "" + out.BatchType = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.Keyspace, m.BatchType) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Keyspace = trimStr(m.Keyspace, maxFieldsSize) + out.BatchType = trimStr(m.BatchType, maxFieldsSize) + + return out +} + +func (m *CassandraRegister) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.EventTypes = nil + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrsInSlice(m.EventTypes) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.EventTypes = trimStrSlice(m.EventTypes, maxFieldsSize) + + return out +} + +func (m *CassandraPrepare) TrimToMaxSize(maxSize int) AuditEvent { + size := m.Size() + if size <= maxSize { + return m + } + + out := utils.CloneProtoMsg(m) + out.Query = "" + out.Keyspace = "" + + maxSize = adjustedMaxSize(out, maxSize) + + customFieldsCount := nonEmptyStrs(m.Query, m.Keyspace) + maxFieldsSize := maxSizePerField(maxSize, customFieldsCount) + + out.Query = trimStr(m.Query, maxFieldsSize) + out.Keyspace = trimStr(m.Keyspace, maxFieldsSize) + + return out +} + +func (m *CassandraExecute) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *DiscoveryConfigCreate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *DiscoveryConfigUpdate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *DiscoveryConfigDelete) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *DiscoveryConfigDeleteAll) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *IntegrationCreate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *IntegrationUpdate) TrimToMaxSize(maxSize int) AuditEvent { + return m +} + +func (m *IntegrationDelete) TrimToMaxSize(maxSize int) AuditEvent { + return m } diff --git a/api/types/events/events_test.go b/api/types/events/events_test.go index 8c0a9bb3da9a..b76550fc60c3 100644 --- a/api/types/events/events_test.go +++ b/api/types/events/events_test.go @@ -139,7 +139,7 @@ func TestTrimToMaxSize(t *testing.T) { } } -func TestTrimN(t *testing.T) { +func TestTrimStr(t *testing.T) { tests := []struct { have string want string @@ -153,6 +153,6 @@ func TestTrimN(t *testing.T) { const maxLen = 20 for _, test := range tests { - require.Equal(t, trimN(test.have, maxLen), test.want) + require.Equal(t, test.want, trimStr(test.have, maxLen)) } } diff --git a/lib/events/athena/athena.go b/lib/events/athena/athena.go index 984f6d5d67b9..b292abab05d5 100644 --- a/lib/events/athena/athena.go +++ b/lib/events/athena/athena.go @@ -641,7 +641,3 @@ func newAthenaMetrics(cfg athenaMetricsConfig) (*athenaMetrics, error) { m.consumerNumberOfErrorsFromSQSCollect, )) } - -type trimmableEvent interface { - TrimToMaxSize(int) apievents.AuditEvent -} diff --git a/lib/events/athena/publisher.go b/lib/events/athena/publisher.go index ef53e464476b..f33c373bf878 100644 --- a/lib/events/athena/publisher.go +++ b/lib/events/athena/publisher.go @@ -222,14 +222,12 @@ func (p *publisher) EmitAuditEvent(ctx context.Context, in apievents.AuditEvent) // event may need to be trimmed again on the querier side, but this is an // attempt to preserve as much of the event as possible in case we add the // ability to query very large events in the future. - if t, ok := in.(trimmableEvent); ok { - prevSize := in.Size() - // Trim to 3/4 the max size because base64 has 33% overhead. - // The TrimToMaxSize implementations have a 10% buffer already. - in = t.TrimToMaxSize(maxS3BasedSize - maxS3BasedSize/4) - if in.Size() != prevSize { - events.MetricStoredTrimmedEvents.Inc() - } + prevSize := in.Size() + // Trim to 3/4 the max size because base64 has 33% overhead. + // The TrimToMaxSize implementations have a 10% buffer already. + in = in.TrimToMaxSize(maxS3BasedSize - maxS3BasedSize/4) + if in.Size() != prevSize { + events.MetricStoredTrimmedEvents.Inc() } oneOf, err := apievents.ToOneOf(in) diff --git a/lib/events/athena/querier.go b/lib/events/athena/querier.go index a52ed020a14c..2ef22e424ab9 100644 --- a/lib/events/athena/querier.go +++ b/lib/events/athena/querier.go @@ -721,8 +721,18 @@ func (rb *responseBuilder) appendUntilSizeLimit(resultResp *athena.GetQueryResul } // A single event is larger than the max page size - the best we can // do is try to trim it. - if t, ok := event.(trimmableEvent); ok { - event = t.TrimToMaxSize(events.MaxEventBytesInResponse) + event = event.TrimToMaxSize(events.MaxEventBytesInResponse) + + // Check to make sure the trimmed event is small enough. + fields, err = events.ToEventFields(event) + if err != nil { + return false, trace.Wrap(err) + } + marshalledEvent, err := utils.FastMarshal(&fields) + if err != nil { + return false, trace.Wrap(err, "failed to marshal event, %s", eventData) + } + if len(marshalledEvent)+rb.totalSize <= events.MaxEventBytesInResponse { events.MetricQueriedTrimmedEvents.Inc() // Exact rb.totalSize doesn't really matter since the response is // already size limited. @@ -730,6 +740,7 @@ func (rb *responseBuilder) appendUntilSizeLimit(resultResp *athena.GetQueryResul rb.output = append(rb.output, event) return true, nil } + // Failed to trim the event to size. The only options are to return // a response with 0 events, skip this event, or return an error. // diff --git a/lib/events/dynamic.go b/lib/events/dynamic.go index 087090481313..788ca2700dd5 100644 --- a/lib/events/dynamic.go +++ b/lib/events/dynamic.go @@ -251,6 +251,8 @@ func FromEventFields(fields EventFields) (events.AuditEvent, error) { e = &events.WindowsDesktopSessionStart{} case WindowsDesktopSessionEndEvent: e = &events.WindowsDesktopSessionEnd{} + case DesktopRecordingEvent: + e = &events.DesktopRecording{} case DesktopClipboardSendEvent: e = &events.DesktopClipboardSend{} case DesktopClipboardReceiveEvent: diff --git a/lib/events/dynamoevents/dynamoevents.go b/lib/events/dynamoevents/dynamoevents.go index 90de12e0af27..efb4c6e5a883 100644 --- a/lib/events/dynamoevents/dynamoevents.go +++ b/lib/events/dynamoevents/dynamoevents.go @@ -420,11 +420,11 @@ func isAWSValidationError(err error) bool { } func trimEventSize(event apievents.AuditEvent) (apievents.AuditEvent, bool) { - m, ok := event.(messageSizeTrimmer) - if !ok { - return nil, false + trimmedEvent := event.TrimToMaxSize(maxItemSize) + if trimmedEvent.Size() >= maxItemSize { + return trimmedEvent, false } - return m.TrimToMaxSize(maxItemSize), true + return trimmedEvent, true } // putAuditEventContextKey represents context keys of putAuditEvent. @@ -503,10 +503,6 @@ func (l *Log) createPutItem(sessionID string, in apievents.AuditEvent) (*dynamod return input, nil } -type messageSizeTrimmer interface { - TrimToMaxSize(int) apievents.AuditEvent -} - func (l *Log) setExpiry(e *event) { if l.RetentionPeriod.Value() == 0 { return diff --git a/lib/events/events_test.go b/lib/events/events_test.go index f9df7b4e5423..71ce27164fd2 100644 --- a/lib/events/events_test.go +++ b/lib/events/events_test.go @@ -20,10 +20,16 @@ import ( "encoding/json" "fmt" "reflect" + "strings" "testing" "time" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/protoadapt" + "google.golang.org/protobuf/reflect/protoreflect" + "google.golang.org/protobuf/runtime/protoiface" apievents "github.com/gravitational/teleport/api/types/events" "github.com/gravitational/teleport/lib/utils" @@ -133,6 +139,7 @@ var eventsMap = map[string]apievents.AuditEvent{ PrivilegeTokenCreateEvent: &apievents.UserTokenCreate{}, WindowsDesktopSessionStartEvent: &apievents.WindowsDesktopSessionStart{}, WindowsDesktopSessionEndEvent: &apievents.WindowsDesktopSessionEnd{}, + DesktopRecordingEvent: &apievents.DesktopRecording{}, DesktopClipboardSendEvent: &apievents.DesktopClipboardSend{}, DesktopClipboardReceiveEvent: &apievents.DesktopClipboardReceive{}, SessionConnectEvent: &apievents.SessionConnect{}, @@ -956,3 +963,146 @@ func TestEvents(t *testing.T) { }) } } + +func TestTrimToMaxSize(t *testing.T) { + t.Parallel() + + for eventName, eventMsg := range eventsMap { + t.Run(eventName, func(t *testing.T) { + // clone the message to avoid modifying the original in the global map + event := proto.Clone(toV2Proto(t, eventMsg)) + setProtoFields(event) + + auditEvent := protoadapt.MessageV1Of(event).(apievents.AuditEvent) + size := auditEvent.Size() + maxSize := int(float32(size) * 0.9) + + trimmedAuditEvent := auditEvent.TrimToMaxSize(maxSize) + if trimmedAuditEvent.Size() == auditEvent.Size() { + t.Skipf("skipping %s, event does not have any fields to trim", eventName) + } + trimmedEvent := toV2Proto(t, trimmedAuditEvent) + + require.NotEqual(t, auditEvent, trimmedEvent) + require.LessOrEqual(t, trimmedAuditEvent.Size(), maxSize) + if trimmedAuditEvent.Size() != maxSize { + t.Logf("original event: %s\ntrimmed event: %s", protojson.Format(event), protojson.Format(trimmedEvent)) + } + + // ensure Metadata hasn't been trimmed + require.Equal(t, auditEvent.GetID(), trimmedAuditEvent.GetID()) + require.Equal(t, auditEvent.GetCode(), trimmedAuditEvent.GetCode()) + require.Equal(t, auditEvent.GetType(), trimmedAuditEvent.GetType()) + require.Equal(t, auditEvent.GetClusterName(), trimmedAuditEvent.GetClusterName()) + }) + } +} + +type testingVal interface { + Helper() + require.TestingT +} + +func setProtoFields(msg proto.Message) { + m := msg.ProtoReflect() + fields := m.Descriptor().Fields() + + for i := 0; i < fields.Len(); i++ { + fd := fields.Get(i) + if m.Has(fd) { + continue + } + + if fd.IsList() { + // Handle repeated fields + listValue := m.Mutable(fd).List() + if fd.Kind() == protoreflect.MessageKind { + listMsg := listValue.AppendMutable().Message() + setProtoFields(listMsg.Interface()) + } else { + listValue.Append(getDefaultValue(m, fd)) + } + continue + } + + switch fd.Kind() { + case protoreflect.MessageKind: + if fd.IsMap() { + // Handle map values + mapValue := m.Mutable(fd).Map() + keyDesc := fd.MapKey() + valueDesc := fd.MapValue() + + keyVal := getDefaultValue(m, keyDesc).MapKey() + var valueVal protoreflect.Value + + if valueDesc.Kind() == protoreflect.MessageKind { + valueMsg := mapValue.NewValue().Message() + setProtoFields(valueMsg.Interface()) + valueVal = protoreflect.ValueOfMessage(valueMsg) + } else { + valueVal = getDefaultValue(m, valueDesc) + } + + mapValue.Set(keyVal, valueVal) + } else { + // Handle singular message fields + nestedMsg := m.Mutable(fd).Message() + setProtoFields(nestedMsg.Interface()) + } + default: + m.Set(fd, getDefaultValue(m, fd)) + } + } +} + +const metadataString = "some metadata" + +var ( + eventString = strings.Repeat("umai", 170) +) + +func getDefaultValue(m protoreflect.Message, fd protoreflect.FieldDescriptor) protoreflect.Value { + strVal := metadataString + msgName := string(m.Descriptor().Name()) + // set shorter strings for metadata fields which won't be trimmed + if msgName == "CommandMetadata" || !strings.Contains(msgName, "Metadata") { + strVal = eventString + } + + switch fd.Kind() { + case protoreflect.BoolKind: + return protoreflect.ValueOfBool(true) + case protoreflect.Int32Kind, protoreflect.Int64Kind: + return protoreflect.ValueOfInt64(6) + case protoreflect.Uint32Kind, protoreflect.Uint64Kind: + return protoreflect.ValueOfUint64(7) + case protoreflect.FloatKind, protoreflect.DoubleKind: + return protoreflect.ValueOfFloat64(3.14) + case protoreflect.StringKind: + return protoreflect.ValueOfString(strVal) + case protoreflect.BytesKind: + return protoreflect.ValueOfBytes([]byte(strVal)) + case protoreflect.EnumKind: + enumValues := fd.Enum().Values() + if enumValues.Len() > 0 { + return protoreflect.ValueOfEnum(enumValues.Get(0).Number()) + } + case protoreflect.MessageKind: + // Handle singular message fields + nestedMsg := m.NewField(fd).Message() + setProtoFields(nestedMsg.Interface()) + return protoreflect.ValueOfMessage(nestedMsg) + default: + panic(fmt.Sprintf("unhandled field kind: %s", fd.Kind())) + } + return protoreflect.Value{} // This should never happen +} + +func toV2Proto(t testingVal, e apievents.AuditEvent) protoreflect.ProtoMessage { + t.Helper() + + pm, ok := e.(protoiface.MessageV1) + require.True(t, ok) + return protoadapt.MessageV2Of(pm) +} diff --git a/lib/events/filelog.go b/lib/events/filelog.go index 5033b4af66aa..1b6dd2100f7e 100644 --- a/lib/events/filelog.go +++ b/lib/events/filelog.go @@ -158,18 +158,11 @@ func (l *FileLog) EmitAuditEvent(ctx context.Context, event apievents.AuditEvent } if len(line) > l.MaxScanTokenSize { - switch { - case canReduceMessageSize(event): - line, err = l.trimSizeAndMarshal(event) - if err != nil { - return trace.Wrap(err) - } - MetricStoredTrimmedEvents.Inc() - default: - fields := log.Fields{"event_type": event.GetType(), "event_size": len(line)} - l.WithFields(fields).Warnf("Got a event that exceeded max allowed size.") - return trace.BadParameter("event size %v exceeds max entry size %v", len(line), l.MaxScanTokenSize) + line, err = l.trimSizeAndMarshal(event) + if err != nil { + return trace.Wrap(err) } + MetricStoredTrimmedEvents.Inc() } // log it to the main log file: @@ -177,17 +170,8 @@ func (l *FileLog) EmitAuditEvent(ctx context.Context, event apievents.AuditEvent return trace.ConvertSystemError(err) } -func canReduceMessageSize(event apievents.AuditEvent) bool { - _, ok := event.(messageSizeTrimmer) - return ok -} - func (l *FileLog) trimSizeAndMarshal(event apievents.AuditEvent) ([]byte, error) { - s, ok := event.(messageSizeTrimmer) - if !ok { - return nil, trace.BadParameter("invalid event type %T", event) - } - sEvent := s.TrimToMaxSize(l.MaxScanTokenSize) + sEvent := event.TrimToMaxSize(l.MaxScanTokenSize) line, err := utils.FastMarshal(sEvent) if err != nil { return nil, trace.Wrap(err) @@ -198,10 +182,6 @@ func (l *FileLog) trimSizeAndMarshal(event apievents.AuditEvent) ([]byte, error) return line, nil } -type messageSizeTrimmer interface { - TrimToMaxSize(int) apievents.AuditEvent -} - // SearchEvents is a flexible way to find events. // // Event types to filter can be specified and pagination is handled by an iterator key that allows diff --git a/lib/events/stream.go b/lib/events/stream.go index aa78605a72d7..f4586bd7c35a 100644 --- a/lib/events/stream.go +++ b/lib/events/stream.go @@ -397,10 +397,8 @@ func (s *ProtoStream) RecordEvent(ctx context.Context, pe apievents.PreparedSess event := pe.GetAuditEvent() messageSize := event.Size() if messageSize > MaxProtoMessageSizeBytes { - switch v := event.(type) { - case messageSizeTrimmer: - event = v.TrimToMaxSize(MaxProtoMessageSizeBytes) - default: + event = event.TrimToMaxSize(MaxProtoMessageSizeBytes) + if event.Size() > MaxProtoMessageSizeBytes { return trace.BadParameter("record size %v exceeds max message size of %v bytes", messageSize, MaxProtoMessageSizeBytes) } }