From e15fd609d44c7682fa4abb656df98390b59d043a Mon Sep 17 00:00:00 2001 From: Tiago Silva Date: Fri, 3 Jul 2026 13:17:55 +0100 Subject: [PATCH] session-search[31]: add needs_further_review filter and response field (#67754) * session-search[31]: replace needs_further_review bool with enum reason filter Replaces the optional bool needs_further_review filter on both SearchSessionSummariesParams and SearchSessionSummariesRequest with repeated NeedsReviewReason filter_needs_further_review_reasons, letting callers narrow results to sessions flagged for specific reasons rather than any-or-none. The access graph and public API protos are regenerated accordingly, and the enterprise service layer is updated to forward the new field. tctl recordings search replaces --[no-]needs-review with --review-reason, a Kingpin enum flag accepting individual reason names or all to match any flagged session. The valid values live in reviewReasonNames, a single map that drives both flag validation and proto enum conversion. A parity test asserts that reviewReasonNames contains exactly one entry per non-UNSPECIFIED NeedsReviewReason value, so adding a proto reason without wiring it into the CLI fails the test. * fix test * update docs --- .../sessionsearch/v1/session_search.pb.go | 111 ++++--- .../v1/session_search_protoopaque.pb.go | 188 +++++++----- .../sessionsearch/v1/session_search.proto | 9 + docs/pages/reference/cli/tctl.mdx | 1 + .../go/accessgraph/v1/session_search.pb.go | 167 +++++++---- .../v1/session_search_protoopaque.pb.go | 275 +++++++++++------- proto/accessgraph/v1/session_search.proto | 14 + tool/tctl/common/recordings/detail.go | 25 ++ tool/tctl/common/recordings/item.go | 8 +- tool/tctl/common/recordings_command.go | 64 ++++ tool/tctl/common/recordings_command_test.go | 13 + 11 files changed, 604 insertions(+), 271 deletions(-) diff --git a/api/gen/proto/go/teleport/sessionsearch/v1/session_search.pb.go b/api/gen/proto/go/teleport/sessionsearch/v1/session_search.pb.go index 53b73ad632f..d85edf3efa8 100644 --- a/api/gen/proto/go/teleport/sessionsearch/v1/session_search.pb.go +++ b/api/gen/proto/go/teleport/sessionsearch/v1/session_search.pb.go @@ -281,9 +281,12 @@ type SearchSessionSummariesRequest struct { // are present. SEARCH_MODE_UNSPECIFIED and SEARCH_MODE_HYBRID both execute // the full hybrid pipeline. SEARCH_MODE_KEYWORD_ONLY uses only full-text // matching; SEARCH_MODE_EMBEDDING_ONLY uses only vector similarity. - SearchMode SearchMode `protobuf:"varint,15,opt,name=search_mode,json=searchMode,proto3,enum=teleport.sessionsearch.v1.SearchMode" json:"search_mode,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + SearchMode SearchMode `protobuf:"varint,15,opt,name=search_mode,json=searchMode,proto3,enum=teleport.sessionsearch.v1.SearchMode" json:"search_mode,omitempty"` + // filter_needs_further_review_reasons restricts results to sessions that have + // at least one of the given review reasons. An empty list disables this filter. + FilterNeedsFurtherReviewReasons []v1.NeedsReviewReason `protobuf:"varint,16,rep,packed,name=filter_needs_further_review_reasons,json=filterNeedsFurtherReviewReasons,proto3,enum=teleport.summarizer.v1.NeedsReviewReason" json:"filter_needs_further_review_reasons,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SearchSessionSummariesRequest) Reset() { @@ -416,6 +419,13 @@ func (x *SearchSessionSummariesRequest) GetSearchMode() SearchMode { return SearchMode_SEARCH_MODE_UNSPECIFIED } +func (x *SearchSessionSummariesRequest) GetFilterNeedsFurtherReviewReasons() []v1.NeedsReviewReason { + if x != nil { + return x.FilterNeedsFurtherReviewReasons + } + return nil +} + func (x *SearchSessionSummariesRequest) SetStartTime(v *timestamppb.Timestamp) { x.StartTime = v } @@ -476,6 +486,10 @@ func (x *SearchSessionSummariesRequest) SetSearchMode(v SearchMode) { x.SearchMode = v } +func (x *SearchSessionSummariesRequest) SetFilterNeedsFurtherReviewReasons(v []v1.NeedsReviewReason) { + x.FilterNeedsFurtherReviewReasons = v +} + func (x *SearchSessionSummariesRequest) HasStartTime() bool { if x == nil { return false @@ -619,6 +633,9 @@ type SearchSessionSummariesRequest_builder struct { // the full hybrid pipeline. SEARCH_MODE_KEYWORD_ONLY uses only full-text // matching; SEARCH_MODE_EMBEDDING_ONLY uses only vector similarity. SearchMode SearchMode + // filter_needs_further_review_reasons restricts results to sessions that have + // at least one of the given review reasons. An empty list disables this filter. + FilterNeedsFurtherReviewReasons []v1.NeedsReviewReason } func (b0 SearchSessionSummariesRequest_builder) Build() *SearchSessionSummariesRequest { @@ -640,6 +657,7 @@ func (b0 SearchSessionSummariesRequest_builder) Build() *SearchSessionSummariesR x.MaxResults = b.MaxResults x.BatchToken = b.BatchToken x.SearchMode = b.SearchMode + x.FilterNeedsFurtherReviewReasons = b.FilterNeedsFurtherReviewReasons return m0 } @@ -1601,9 +1619,13 @@ type SessionSummary struct { // session_end is the timestamp when the session ended. SessionEnd *timestamppb.Timestamp `protobuf:"bytes,15,opt,name=session_end,json=sessionEnd,proto3" json:"session_end,omitempty"` // host_id is the unique identifier of the host where the session occurred. - HostId string `protobuf:"bytes,16,opt,name=host_id,json=hostId,proto3" json:"host_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + HostId string `protobuf:"bytes,16,opt,name=host_id,json=hostId,proto3" json:"host_id,omitempty"` + // needs_further_review_reasons contains the reasons why this session was + // flagged as requiring further human review by the summarizer. Empty when the + // session was not flagged. + NeedsFurtherReviewReasons []v1.NeedsReviewReason `protobuf:"varint,17,rep,packed,name=needs_further_review_reasons,json=needsFurtherReviewReasons,proto3,enum=teleport.summarizer.v1.NeedsReviewReason" json:"needs_further_review_reasons,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SessionSummary) Reset() { @@ -1743,6 +1765,13 @@ func (x *SessionSummary) GetHostId() string { return "" } +func (x *SessionSummary) GetNeedsFurtherReviewReasons() []v1.NeedsReviewReason { + if x != nil { + return x.NeedsFurtherReviewReasons + } + return nil +} + func (x *SessionSummary) SetSessionId(v string) { x.SessionId = v } @@ -1807,6 +1836,10 @@ func (x *SessionSummary) SetHostId(v string) { x.HostId = v } +func (x *SessionSummary) SetNeedsFurtherReviewReasons(v []v1.NeedsReviewReason) { + x.NeedsFurtherReviewReasons = v +} + func (x *SessionSummary) HasSessionStart() bool { if x == nil { return false @@ -1898,6 +1931,10 @@ type SessionSummary_builder struct { SessionEnd *timestamppb.Timestamp // host_id is the unique identifier of the host where the session occurred. HostId string + // needs_further_review_reasons contains the reasons why this session was + // flagged as requiring further human review by the summarizer. Empty when the + // session was not flagged. + NeedsFurtherReviewReasons []v1.NeedsReviewReason } func (b0 SessionSummary_builder) Build() *SessionSummary { @@ -1920,6 +1957,7 @@ func (b0 SessionSummary_builder) Build() *SessionSummary { x.Severity = b.Severity x.SessionEnd = b.SessionEnd x.HostId = b.HostId + x.NeedsFurtherReviewReasons = b.NeedsFurtherReviewReasons return m0 } @@ -2117,7 +2155,7 @@ var File_teleport_sessionsearch_v1_session_search_proto protoreflect.FileDescrip const file_teleport_sessionsearch_v1_session_search_proto_rawDesc = "" + "\n" + - ".teleport/sessionsearch/v1/session_search.proto\x12\x19teleport.sessionsearch.v1\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a'teleport/summarizer/v1/summarizer.proto\"\xb6\a\n" + + ".teleport/sessionsearch/v1/session_search.proto\x12\x19teleport.sessionsearch.v1\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a'teleport/summarizer/v1/summarizer.proto\"\xaf\b\n" + "\x1dSearchSessionSummariesRequest\x129\n" + "\n" + "start_time\x18\x01 \x01(\v2\x1a.google.protobuf.TimestampR\tstartTime\x125\n" + @@ -2139,7 +2177,8 @@ const file_teleport_sessionsearch_v1_session_search_proto_rawDesc = "" + "\vbatch_token\x18\x0e \x01(\tR\n" + "batchToken\x12F\n" + "\vsearch_mode\x18\x0f \x01(\x0e2%.teleport.sessionsearch.v1.SearchModeR\n" + - "searchMode\x1aA\n" + + "searchMode\x12w\n" + + "#filter_needs_further_review_reasons\x18\x10 \x03(\x0e2).teleport.summarizer.v1.NeedsReviewReasonR\x1ffilterNeedsFurtherReviewReasons\x1aA\n" + "\x13ResourceLabelsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\v\n" + @@ -2185,7 +2224,7 @@ const file_teleport_sessionsearch_v1_session_search_proto_rawDesc = "" + "\rBatchComplete\x12\x19\n" + "\bhas_more\x18\x01 \x01(\bR\ahasMore\x12(\n" + "\x10next_batch_token\x18\x02 \x01(\tR\x0enextBatchTokenB\t\n" + - "\apayload\"\xd6\x06\n" + + "\apayload\"\xc2\a\n" + "\x0eSessionSummary\x12\x1d\n" + "\n" + "session_id\x18\x01 \x01(\tR\tsessionId\x12\x12\n" + @@ -2208,7 +2247,8 @@ const file_teleport_sessionsearch_v1_session_search_proto_rawDesc = "" + "\bseverity\x18\x0e \x01(\x0e2!.teleport.summarizer.v1.RiskLevelR\bseverity\x12;\n" + "\vsession_end\x18\x0f \x01(\v2\x1a.google.protobuf.TimestampR\n" + "sessionEnd\x12\x17\n" + - "\ahost_id\x18\x10 \x01(\tR\x06hostId\x1aA\n" + + "\ahost_id\x18\x10 \x01(\tR\x06hostId\x12j\n" + + "\x1cneeds_further_review_reasons\x18\x11 \x03(\x0e2).teleport.summarizer.v1.NeedsReviewReasonR\x19needsFurtherReviewReasons\x1aA\n" + "\x13ResourceLabelsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x12\n" + @@ -2256,7 +2296,8 @@ var file_teleport_sessionsearch_v1_session_search_proto_goTypes = []any{ nil, // 15: teleport.sessionsearch.v1.SessionSummary.ResourceLabelsEntry (*timestamppb.Timestamp)(nil), // 16: google.protobuf.Timestamp (v1.RiskLevel)(0), // 17: teleport.summarizer.v1.RiskLevel - (*structpb.Struct)(nil), // 18: google.protobuf.Struct + (v1.NeedsReviewReason)(0), // 18: teleport.summarizer.v1.NeedsReviewReason + (*structpb.Struct)(nil), // 19: google.protobuf.Struct } var file_teleport_sessionsearch_v1_session_search_proto_depIdxs = []int32{ 16, // 0: teleport.sessionsearch.v1.SearchSessionSummariesRequest.start_time:type_name -> google.protobuf.Timestamp @@ -2265,29 +2306,31 @@ var file_teleport_sessionsearch_v1_session_search_proto_depIdxs = []int32{ 4, // 3: teleport.sessionsearch.v1.SearchSessionSummariesRequest.resource_properties:type_name -> teleport.sessionsearch.v1.ResourceProperties 17, // 4: teleport.sessionsearch.v1.SearchSessionSummariesRequest.severity:type_name -> teleport.summarizer.v1.RiskLevel 0, // 5: teleport.sessionsearch.v1.SearchSessionSummariesRequest.search_mode:type_name -> teleport.sessionsearch.v1.SearchMode - 5, // 6: teleport.sessionsearch.v1.ResourceProperties.ssh:type_name -> teleport.sessionsearch.v1.SSHProperties - 6, // 7: teleport.sessionsearch.v1.ResourceProperties.kubernetes:type_name -> teleport.sessionsearch.v1.KubernetesProperties - 7, // 8: teleport.sessionsearch.v1.ResourceProperties.database:type_name -> teleport.sessionsearch.v1.DatabaseProperties - 8, // 9: teleport.sessionsearch.v1.ResourceProperties.desktop:type_name -> teleport.sessionsearch.v1.DesktopProperties - 1, // 10: teleport.sessionsearch.v1.DesktopProperties.type:type_name -> teleport.sessionsearch.v1.DesktopType - 10, // 11: teleport.sessionsearch.v1.SearchSessionSummariesResponse.summary:type_name -> teleport.sessionsearch.v1.SessionSummary - 14, // 12: teleport.sessionsearch.v1.SearchSessionSummariesResponse.batch_complete:type_name -> teleport.sessionsearch.v1.SearchSessionSummariesResponse.BatchComplete - 16, // 13: teleport.sessionsearch.v1.SessionSummary.session_start:type_name -> google.protobuf.Timestamp - 18, // 14: teleport.sessionsearch.v1.SessionSummary.user_traits:type_name -> google.protobuf.Struct - 15, // 15: teleport.sessionsearch.v1.SessionSummary.resource_labels:type_name -> teleport.sessionsearch.v1.SessionSummary.ResourceLabelsEntry - 4, // 16: teleport.sessionsearch.v1.SessionSummary.resource_properties:type_name -> teleport.sessionsearch.v1.ResourceProperties - 17, // 17: teleport.sessionsearch.v1.SessionSummary.severity:type_name -> teleport.summarizer.v1.RiskLevel - 16, // 18: teleport.sessionsearch.v1.SessionSummary.session_end:type_name -> google.protobuf.Timestamp - 2, // 19: teleport.sessionsearch.v1.IsEnabledResponse.availability:type_name -> teleport.sessionsearch.v1.SessionSearchAvailability - 3, // 20: teleport.sessionsearch.v1.SessionSearchService.SearchSessionSummaries:input_type -> teleport.sessionsearch.v1.SearchSessionSummariesRequest - 11, // 21: teleport.sessionsearch.v1.SessionSearchService.IsEnabled:input_type -> teleport.sessionsearch.v1.IsEnabledRequest - 9, // 22: teleport.sessionsearch.v1.SessionSearchService.SearchSessionSummaries:output_type -> teleport.sessionsearch.v1.SearchSessionSummariesResponse - 12, // 23: teleport.sessionsearch.v1.SessionSearchService.IsEnabled:output_type -> teleport.sessionsearch.v1.IsEnabledResponse - 22, // [22:24] is the sub-list for method output_type - 20, // [20:22] is the sub-list for method input_type - 20, // [20:20] is the sub-list for extension type_name - 20, // [20:20] is the sub-list for extension extendee - 0, // [0:20] is the sub-list for field type_name + 18, // 6: teleport.sessionsearch.v1.SearchSessionSummariesRequest.filter_needs_further_review_reasons:type_name -> teleport.summarizer.v1.NeedsReviewReason + 5, // 7: teleport.sessionsearch.v1.ResourceProperties.ssh:type_name -> teleport.sessionsearch.v1.SSHProperties + 6, // 8: teleport.sessionsearch.v1.ResourceProperties.kubernetes:type_name -> teleport.sessionsearch.v1.KubernetesProperties + 7, // 9: teleport.sessionsearch.v1.ResourceProperties.database:type_name -> teleport.sessionsearch.v1.DatabaseProperties + 8, // 10: teleport.sessionsearch.v1.ResourceProperties.desktop:type_name -> teleport.sessionsearch.v1.DesktopProperties + 1, // 11: teleport.sessionsearch.v1.DesktopProperties.type:type_name -> teleport.sessionsearch.v1.DesktopType + 10, // 12: teleport.sessionsearch.v1.SearchSessionSummariesResponse.summary:type_name -> teleport.sessionsearch.v1.SessionSummary + 14, // 13: teleport.sessionsearch.v1.SearchSessionSummariesResponse.batch_complete:type_name -> teleport.sessionsearch.v1.SearchSessionSummariesResponse.BatchComplete + 16, // 14: teleport.sessionsearch.v1.SessionSummary.session_start:type_name -> google.protobuf.Timestamp + 19, // 15: teleport.sessionsearch.v1.SessionSummary.user_traits:type_name -> google.protobuf.Struct + 15, // 16: teleport.sessionsearch.v1.SessionSummary.resource_labels:type_name -> teleport.sessionsearch.v1.SessionSummary.ResourceLabelsEntry + 4, // 17: teleport.sessionsearch.v1.SessionSummary.resource_properties:type_name -> teleport.sessionsearch.v1.ResourceProperties + 17, // 18: teleport.sessionsearch.v1.SessionSummary.severity:type_name -> teleport.summarizer.v1.RiskLevel + 16, // 19: teleport.sessionsearch.v1.SessionSummary.session_end:type_name -> google.protobuf.Timestamp + 18, // 20: teleport.sessionsearch.v1.SessionSummary.needs_further_review_reasons:type_name -> teleport.summarizer.v1.NeedsReviewReason + 2, // 21: teleport.sessionsearch.v1.IsEnabledResponse.availability:type_name -> teleport.sessionsearch.v1.SessionSearchAvailability + 3, // 22: teleport.sessionsearch.v1.SessionSearchService.SearchSessionSummaries:input_type -> teleport.sessionsearch.v1.SearchSessionSummariesRequest + 11, // 23: teleport.sessionsearch.v1.SessionSearchService.IsEnabled:input_type -> teleport.sessionsearch.v1.IsEnabledRequest + 9, // 24: teleport.sessionsearch.v1.SessionSearchService.SearchSessionSummaries:output_type -> teleport.sessionsearch.v1.SearchSessionSummariesResponse + 12, // 25: teleport.sessionsearch.v1.SessionSearchService.IsEnabled:output_type -> teleport.sessionsearch.v1.IsEnabledResponse + 24, // [24:26] is the sub-list for method output_type + 22, // [22:24] is the sub-list for method input_type + 22, // [22:22] is the sub-list for extension type_name + 22, // [22:22] is the sub-list for extension extendee + 0, // [0:22] is the sub-list for field type_name } func init() { file_teleport_sessionsearch_v1_session_search_proto_init() } diff --git a/api/gen/proto/go/teleport/sessionsearch/v1/session_search_protoopaque.pb.go b/api/gen/proto/go/teleport/sessionsearch/v1/session_search_protoopaque.pb.go index e0cea84044f..d17f74a96be 100644 --- a/api/gen/proto/go/teleport/sessionsearch/v1/session_search_protoopaque.pb.go +++ b/api/gen/proto/go/teleport/sessionsearch/v1/session_search_protoopaque.pb.go @@ -218,26 +218,27 @@ func (x SessionSearchAvailability) Number() protoreflect.EnumNumber { // All filter fields are optional except start and end time; omitting a field disables // that filter. The server converts search_queries into chunks and generates embeddings internally. type SearchSessionSummariesRequest struct { - state protoimpl.MessageState `protogen:"opaque.v1"` - xxx_hidden_StartTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=start_time,json=startTime,proto3"` - xxx_hidden_EndTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=end_time,json=endTime,proto3"` - xxx_hidden_Kinds []string `protobuf:"bytes,3,rep,name=kinds,proto3"` - xxx_hidden_Username *string `protobuf:"bytes,4,opt,name=username,proto3,oneof"` - xxx_hidden_UserRoles []string `protobuf:"bytes,5,rep,name=user_roles,json=userRoles,proto3"` - xxx_hidden_AccessRequestIds []string `protobuf:"bytes,6,rep,name=access_request_ids,json=accessRequestIds,proto3"` - xxx_hidden_ResourceKind *string `protobuf:"bytes,7,opt,name=resource_kind,json=resourceKind,proto3,oneof"` - xxx_hidden_ResourceName *string `protobuf:"bytes,8,opt,name=resource_name,json=resourceName,proto3,oneof"` - xxx_hidden_ResourceLabels map[string]string `protobuf:"bytes,9,rep,name=resource_labels,json=resourceLabels,proto3" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - xxx_hidden_ResourceProperties *ResourceProperties `protobuf:"bytes,10,opt,name=resource_properties,json=resourceProperties,proto3"` - xxx_hidden_Severity v1.RiskLevel `protobuf:"varint,11,opt,name=severity,proto3,enum=teleport.summarizer.v1.RiskLevel,oneof"` - xxx_hidden_SearchQueries []string `protobuf:"bytes,12,rep,name=search_queries,json=searchQueries,proto3"` - xxx_hidden_MaxResults uint32 `protobuf:"varint,13,opt,name=max_results,json=maxResults,proto3"` - xxx_hidden_BatchToken string `protobuf:"bytes,14,opt,name=batch_token,json=batchToken,proto3"` - xxx_hidden_SearchMode SearchMode `protobuf:"varint,15,opt,name=search_mode,json=searchMode,proto3,enum=teleport.sessionsearch.v1.SearchMode"` - XXX_raceDetectHookData protoimpl.RaceDetectHookData - XXX_presence [1]uint32 - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"opaque.v1"` + xxx_hidden_StartTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=start_time,json=startTime,proto3"` + xxx_hidden_EndTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=end_time,json=endTime,proto3"` + xxx_hidden_Kinds []string `protobuf:"bytes,3,rep,name=kinds,proto3"` + xxx_hidden_Username *string `protobuf:"bytes,4,opt,name=username,proto3,oneof"` + xxx_hidden_UserRoles []string `protobuf:"bytes,5,rep,name=user_roles,json=userRoles,proto3"` + xxx_hidden_AccessRequestIds []string `protobuf:"bytes,6,rep,name=access_request_ids,json=accessRequestIds,proto3"` + xxx_hidden_ResourceKind *string `protobuf:"bytes,7,opt,name=resource_kind,json=resourceKind,proto3,oneof"` + xxx_hidden_ResourceName *string `protobuf:"bytes,8,opt,name=resource_name,json=resourceName,proto3,oneof"` + xxx_hidden_ResourceLabels map[string]string `protobuf:"bytes,9,rep,name=resource_labels,json=resourceLabels,proto3" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + xxx_hidden_ResourceProperties *ResourceProperties `protobuf:"bytes,10,opt,name=resource_properties,json=resourceProperties,proto3"` + xxx_hidden_Severity v1.RiskLevel `protobuf:"varint,11,opt,name=severity,proto3,enum=teleport.summarizer.v1.RiskLevel,oneof"` + xxx_hidden_SearchQueries []string `protobuf:"bytes,12,rep,name=search_queries,json=searchQueries,proto3"` + xxx_hidden_MaxResults uint32 `protobuf:"varint,13,opt,name=max_results,json=maxResults,proto3"` + xxx_hidden_BatchToken string `protobuf:"bytes,14,opt,name=batch_token,json=batchToken,proto3"` + xxx_hidden_SearchMode SearchMode `protobuf:"varint,15,opt,name=search_mode,json=searchMode,proto3,enum=teleport.sessionsearch.v1.SearchMode"` + xxx_hidden_FilterNeedsFurtherReviewReasons []v1.NeedsReviewReason `protobuf:"varint,16,rep,packed,name=filter_needs_further_review_reasons,json=filterNeedsFurtherReviewReasons,proto3,enum=teleport.summarizer.v1.NeedsReviewReason"` + XXX_raceDetectHookData protoimpl.RaceDetectHookData + XXX_presence [1]uint32 + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SearchSessionSummariesRequest) Reset() { @@ -381,6 +382,13 @@ func (x *SearchSessionSummariesRequest) GetSearchMode() SearchMode { return SearchMode_SEARCH_MODE_UNSPECIFIED } +func (x *SearchSessionSummariesRequest) GetFilterNeedsFurtherReviewReasons() []v1.NeedsReviewReason { + if x != nil { + return x.xxx_hidden_FilterNeedsFurtherReviewReasons + } + return nil +} + func (x *SearchSessionSummariesRequest) SetStartTime(v *timestamppb.Timestamp) { x.xxx_hidden_StartTime = v } @@ -395,7 +403,7 @@ func (x *SearchSessionSummariesRequest) SetKinds(v []string) { func (x *SearchSessionSummariesRequest) SetUsername(v string) { x.xxx_hidden_Username = &v - protoimpl.X.SetPresent(&(x.XXX_presence[0]), 3, 15) + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 3, 16) } func (x *SearchSessionSummariesRequest) SetUserRoles(v []string) { @@ -408,12 +416,12 @@ func (x *SearchSessionSummariesRequest) SetAccessRequestIds(v []string) { func (x *SearchSessionSummariesRequest) SetResourceKind(v string) { x.xxx_hidden_ResourceKind = &v - protoimpl.X.SetPresent(&(x.XXX_presence[0]), 6, 15) + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 6, 16) } func (x *SearchSessionSummariesRequest) SetResourceName(v string) { x.xxx_hidden_ResourceName = &v - protoimpl.X.SetPresent(&(x.XXX_presence[0]), 7, 15) + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 7, 16) } func (x *SearchSessionSummariesRequest) SetResourceLabels(v map[string]string) { @@ -426,7 +434,7 @@ func (x *SearchSessionSummariesRequest) SetResourceProperties(v *ResourcePropert func (x *SearchSessionSummariesRequest) SetSeverity(v v1.RiskLevel) { x.xxx_hidden_Severity = v - protoimpl.X.SetPresent(&(x.XXX_presence[0]), 10, 15) + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 10, 16) } func (x *SearchSessionSummariesRequest) SetSearchQueries(v []string) { @@ -445,6 +453,10 @@ func (x *SearchSessionSummariesRequest) SetSearchMode(v SearchMode) { x.xxx_hidden_SearchMode = v } +func (x *SearchSessionSummariesRequest) SetFilterNeedsFurtherReviewReasons(v []v1.NeedsReviewReason) { + x.xxx_hidden_FilterNeedsFurtherReviewReasons = v +} + func (x *SearchSessionSummariesRequest) HasStartTime() bool { if x == nil { return false @@ -592,6 +604,9 @@ type SearchSessionSummariesRequest_builder struct { // the full hybrid pipeline. SEARCH_MODE_KEYWORD_ONLY uses only full-text // matching; SEARCH_MODE_EMBEDDING_ONLY uses only vector similarity. SearchMode SearchMode + // filter_needs_further_review_reasons restricts results to sessions that have + // at least one of the given review reasons. An empty list disables this filter. + FilterNeedsFurtherReviewReasons []v1.NeedsReviewReason } func (b0 SearchSessionSummariesRequest_builder) Build() *SearchSessionSummariesRequest { @@ -602,29 +617,30 @@ func (b0 SearchSessionSummariesRequest_builder) Build() *SearchSessionSummariesR x.xxx_hidden_EndTime = b.EndTime x.xxx_hidden_Kinds = b.Kinds if b.Username != nil { - protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 3, 15) + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 3, 16) x.xxx_hidden_Username = b.Username } x.xxx_hidden_UserRoles = b.UserRoles x.xxx_hidden_AccessRequestIds = b.AccessRequestIds if b.ResourceKind != nil { - protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 6, 15) + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 6, 16) x.xxx_hidden_ResourceKind = b.ResourceKind } if b.ResourceName != nil { - protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 7, 15) + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 7, 16) x.xxx_hidden_ResourceName = b.ResourceName } x.xxx_hidden_ResourceLabels = b.ResourceLabels x.xxx_hidden_ResourceProperties = b.ResourceProperties if b.Severity != nil { - protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 10, 15) + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 10, 16) x.xxx_hidden_Severity = *b.Severity } x.xxx_hidden_SearchQueries = b.SearchQueries x.xxx_hidden_MaxResults = b.MaxResults x.xxx_hidden_BatchToken = b.BatchToken x.xxx_hidden_SearchMode = b.SearchMode + x.xxx_hidden_FilterNeedsFurtherReviewReasons = b.FilterNeedsFurtherReviewReasons return m0 } @@ -1588,25 +1604,26 @@ func (*searchSessionSummariesResponse_BatchComplete_) isSearchSessionSummariesRe // SessionSummary describes a single recorded session returned by the Auth // server. Only sessions the calling user is permitted to see are included. type SessionSummary struct { - state protoimpl.MessageState `protogen:"opaque.v1"` - xxx_hidden_SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3"` - xxx_hidden_Kind string `protobuf:"bytes,2,opt,name=kind,proto3"` - xxx_hidden_SessionStart *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=session_start,json=sessionStart,proto3"` - xxx_hidden_Username string `protobuf:"bytes,4,opt,name=username,proto3"` - xxx_hidden_UserTraits *structpb.Struct `protobuf:"bytes,5,opt,name=user_traits,json=userTraits,proto3"` - xxx_hidden_UserRoles []string `protobuf:"bytes,6,rep,name=user_roles,json=userRoles,proto3"` - xxx_hidden_AccessRequestIds []string `protobuf:"bytes,7,rep,name=access_request_ids,json=accessRequestIds,proto3"` - xxx_hidden_Participants []string `protobuf:"bytes,8,rep,name=participants,proto3"` - xxx_hidden_ResourceKind string `protobuf:"bytes,9,opt,name=resource_kind,json=resourceKind,proto3"` - xxx_hidden_ResourceLabels map[string]string `protobuf:"bytes,10,rep,name=resource_labels,json=resourceLabels,proto3" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - xxx_hidden_ResourceId string `protobuf:"bytes,11,opt,name=resource_id,json=resourceId,proto3"` - xxx_hidden_ResourceName string `protobuf:"bytes,12,opt,name=resource_name,json=resourceName,proto3"` - xxx_hidden_ResourceProperties *ResourceProperties `protobuf:"bytes,13,opt,name=resource_properties,json=resourceProperties,proto3"` - xxx_hidden_Severity v1.RiskLevel `protobuf:"varint,14,opt,name=severity,proto3,enum=teleport.summarizer.v1.RiskLevel"` - xxx_hidden_SessionEnd *timestamppb.Timestamp `protobuf:"bytes,15,opt,name=session_end,json=sessionEnd,proto3"` - xxx_hidden_HostId string `protobuf:"bytes,16,opt,name=host_id,json=hostId,proto3"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"opaque.v1"` + xxx_hidden_SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3"` + xxx_hidden_Kind string `protobuf:"bytes,2,opt,name=kind,proto3"` + xxx_hidden_SessionStart *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=session_start,json=sessionStart,proto3"` + xxx_hidden_Username string `protobuf:"bytes,4,opt,name=username,proto3"` + xxx_hidden_UserTraits *structpb.Struct `protobuf:"bytes,5,opt,name=user_traits,json=userTraits,proto3"` + xxx_hidden_UserRoles []string `protobuf:"bytes,6,rep,name=user_roles,json=userRoles,proto3"` + xxx_hidden_AccessRequestIds []string `protobuf:"bytes,7,rep,name=access_request_ids,json=accessRequestIds,proto3"` + xxx_hidden_Participants []string `protobuf:"bytes,8,rep,name=participants,proto3"` + xxx_hidden_ResourceKind string `protobuf:"bytes,9,opt,name=resource_kind,json=resourceKind,proto3"` + xxx_hidden_ResourceLabels map[string]string `protobuf:"bytes,10,rep,name=resource_labels,json=resourceLabels,proto3" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + xxx_hidden_ResourceId string `protobuf:"bytes,11,opt,name=resource_id,json=resourceId,proto3"` + xxx_hidden_ResourceName string `protobuf:"bytes,12,opt,name=resource_name,json=resourceName,proto3"` + xxx_hidden_ResourceProperties *ResourceProperties `protobuf:"bytes,13,opt,name=resource_properties,json=resourceProperties,proto3"` + xxx_hidden_Severity v1.RiskLevel `protobuf:"varint,14,opt,name=severity,proto3,enum=teleport.summarizer.v1.RiskLevel"` + xxx_hidden_SessionEnd *timestamppb.Timestamp `protobuf:"bytes,15,opt,name=session_end,json=sessionEnd,proto3"` + xxx_hidden_HostId string `protobuf:"bytes,16,opt,name=host_id,json=hostId,proto3"` + xxx_hidden_NeedsFurtherReviewReasons []v1.NeedsReviewReason `protobuf:"varint,17,rep,packed,name=needs_further_review_reasons,json=needsFurtherReviewReasons,proto3,enum=teleport.summarizer.v1.NeedsReviewReason"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SessionSummary) Reset() { @@ -1746,6 +1763,13 @@ func (x *SessionSummary) GetHostId() string { return "" } +func (x *SessionSummary) GetNeedsFurtherReviewReasons() []v1.NeedsReviewReason { + if x != nil { + return x.xxx_hidden_NeedsFurtherReviewReasons + } + return nil +} + func (x *SessionSummary) SetSessionId(v string) { x.xxx_hidden_SessionId = v } @@ -1810,6 +1834,10 @@ func (x *SessionSummary) SetHostId(v string) { x.xxx_hidden_HostId = v } +func (x *SessionSummary) SetNeedsFurtherReviewReasons(v []v1.NeedsReviewReason) { + x.xxx_hidden_NeedsFurtherReviewReasons = v +} + func (x *SessionSummary) HasSessionStart() bool { if x == nil { return false @@ -1901,6 +1929,10 @@ type SessionSummary_builder struct { SessionEnd *timestamppb.Timestamp // host_id is the unique identifier of the host where the session occurred. HostId string + // needs_further_review_reasons contains the reasons why this session was + // flagged as requiring further human review by the summarizer. Empty when the + // session was not flagged. + NeedsFurtherReviewReasons []v1.NeedsReviewReason } func (b0 SessionSummary_builder) Build() *SessionSummary { @@ -1923,6 +1955,7 @@ func (b0 SessionSummary_builder) Build() *SessionSummary { x.xxx_hidden_Severity = b.Severity x.xxx_hidden_SessionEnd = b.SessionEnd x.xxx_hidden_HostId = b.HostId + x.xxx_hidden_NeedsFurtherReviewReasons = b.NeedsFurtherReviewReasons return m0 } @@ -2113,7 +2146,7 @@ var File_teleport_sessionsearch_v1_session_search_proto protoreflect.FileDescrip const file_teleport_sessionsearch_v1_session_search_proto_rawDesc = "" + "\n" + - ".teleport/sessionsearch/v1/session_search.proto\x12\x19teleport.sessionsearch.v1\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a'teleport/summarizer/v1/summarizer.proto\"\xb6\a\n" + + ".teleport/sessionsearch/v1/session_search.proto\x12\x19teleport.sessionsearch.v1\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a'teleport/summarizer/v1/summarizer.proto\"\xaf\b\n" + "\x1dSearchSessionSummariesRequest\x129\n" + "\n" + "start_time\x18\x01 \x01(\v2\x1a.google.protobuf.TimestampR\tstartTime\x125\n" + @@ -2135,7 +2168,8 @@ const file_teleport_sessionsearch_v1_session_search_proto_rawDesc = "" + "\vbatch_token\x18\x0e \x01(\tR\n" + "batchToken\x12F\n" + "\vsearch_mode\x18\x0f \x01(\x0e2%.teleport.sessionsearch.v1.SearchModeR\n" + - "searchMode\x1aA\n" + + "searchMode\x12w\n" + + "#filter_needs_further_review_reasons\x18\x10 \x03(\x0e2).teleport.summarizer.v1.NeedsReviewReasonR\x1ffilterNeedsFurtherReviewReasons\x1aA\n" + "\x13ResourceLabelsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\v\n" + @@ -2181,7 +2215,7 @@ const file_teleport_sessionsearch_v1_session_search_proto_rawDesc = "" + "\rBatchComplete\x12\x19\n" + "\bhas_more\x18\x01 \x01(\bR\ahasMore\x12(\n" + "\x10next_batch_token\x18\x02 \x01(\tR\x0enextBatchTokenB\t\n" + - "\apayload\"\xd6\x06\n" + + "\apayload\"\xc2\a\n" + "\x0eSessionSummary\x12\x1d\n" + "\n" + "session_id\x18\x01 \x01(\tR\tsessionId\x12\x12\n" + @@ -2204,7 +2238,8 @@ const file_teleport_sessionsearch_v1_session_search_proto_rawDesc = "" + "\bseverity\x18\x0e \x01(\x0e2!.teleport.summarizer.v1.RiskLevelR\bseverity\x12;\n" + "\vsession_end\x18\x0f \x01(\v2\x1a.google.protobuf.TimestampR\n" + "sessionEnd\x12\x17\n" + - "\ahost_id\x18\x10 \x01(\tR\x06hostId\x1aA\n" + + "\ahost_id\x18\x10 \x01(\tR\x06hostId\x12j\n" + + "\x1cneeds_further_review_reasons\x18\x11 \x03(\x0e2).teleport.summarizer.v1.NeedsReviewReasonR\x19needsFurtherReviewReasons\x1aA\n" + "\x13ResourceLabelsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\x12\n" + @@ -2252,7 +2287,8 @@ var file_teleport_sessionsearch_v1_session_search_proto_goTypes = []any{ nil, // 15: teleport.sessionsearch.v1.SessionSummary.ResourceLabelsEntry (*timestamppb.Timestamp)(nil), // 16: google.protobuf.Timestamp (v1.RiskLevel)(0), // 17: teleport.summarizer.v1.RiskLevel - (*structpb.Struct)(nil), // 18: google.protobuf.Struct + (v1.NeedsReviewReason)(0), // 18: teleport.summarizer.v1.NeedsReviewReason + (*structpb.Struct)(nil), // 19: google.protobuf.Struct } var file_teleport_sessionsearch_v1_session_search_proto_depIdxs = []int32{ 16, // 0: teleport.sessionsearch.v1.SearchSessionSummariesRequest.start_time:type_name -> google.protobuf.Timestamp @@ -2261,29 +2297,31 @@ var file_teleport_sessionsearch_v1_session_search_proto_depIdxs = []int32{ 4, // 3: teleport.sessionsearch.v1.SearchSessionSummariesRequest.resource_properties:type_name -> teleport.sessionsearch.v1.ResourceProperties 17, // 4: teleport.sessionsearch.v1.SearchSessionSummariesRequest.severity:type_name -> teleport.summarizer.v1.RiskLevel 0, // 5: teleport.sessionsearch.v1.SearchSessionSummariesRequest.search_mode:type_name -> teleport.sessionsearch.v1.SearchMode - 5, // 6: teleport.sessionsearch.v1.ResourceProperties.ssh:type_name -> teleport.sessionsearch.v1.SSHProperties - 6, // 7: teleport.sessionsearch.v1.ResourceProperties.kubernetes:type_name -> teleport.sessionsearch.v1.KubernetesProperties - 7, // 8: teleport.sessionsearch.v1.ResourceProperties.database:type_name -> teleport.sessionsearch.v1.DatabaseProperties - 8, // 9: teleport.sessionsearch.v1.ResourceProperties.desktop:type_name -> teleport.sessionsearch.v1.DesktopProperties - 1, // 10: teleport.sessionsearch.v1.DesktopProperties.type:type_name -> teleport.sessionsearch.v1.DesktopType - 10, // 11: teleport.sessionsearch.v1.SearchSessionSummariesResponse.summary:type_name -> teleport.sessionsearch.v1.SessionSummary - 14, // 12: teleport.sessionsearch.v1.SearchSessionSummariesResponse.batch_complete:type_name -> teleport.sessionsearch.v1.SearchSessionSummariesResponse.BatchComplete - 16, // 13: teleport.sessionsearch.v1.SessionSummary.session_start:type_name -> google.protobuf.Timestamp - 18, // 14: teleport.sessionsearch.v1.SessionSummary.user_traits:type_name -> google.protobuf.Struct - 15, // 15: teleport.sessionsearch.v1.SessionSummary.resource_labels:type_name -> teleport.sessionsearch.v1.SessionSummary.ResourceLabelsEntry - 4, // 16: teleport.sessionsearch.v1.SessionSummary.resource_properties:type_name -> teleport.sessionsearch.v1.ResourceProperties - 17, // 17: teleport.sessionsearch.v1.SessionSummary.severity:type_name -> teleport.summarizer.v1.RiskLevel - 16, // 18: teleport.sessionsearch.v1.SessionSummary.session_end:type_name -> google.protobuf.Timestamp - 2, // 19: teleport.sessionsearch.v1.IsEnabledResponse.availability:type_name -> teleport.sessionsearch.v1.SessionSearchAvailability - 3, // 20: teleport.sessionsearch.v1.SessionSearchService.SearchSessionSummaries:input_type -> teleport.sessionsearch.v1.SearchSessionSummariesRequest - 11, // 21: teleport.sessionsearch.v1.SessionSearchService.IsEnabled:input_type -> teleport.sessionsearch.v1.IsEnabledRequest - 9, // 22: teleport.sessionsearch.v1.SessionSearchService.SearchSessionSummaries:output_type -> teleport.sessionsearch.v1.SearchSessionSummariesResponse - 12, // 23: teleport.sessionsearch.v1.SessionSearchService.IsEnabled:output_type -> teleport.sessionsearch.v1.IsEnabledResponse - 22, // [22:24] is the sub-list for method output_type - 20, // [20:22] is the sub-list for method input_type - 20, // [20:20] is the sub-list for extension type_name - 20, // [20:20] is the sub-list for extension extendee - 0, // [0:20] is the sub-list for field type_name + 18, // 6: teleport.sessionsearch.v1.SearchSessionSummariesRequest.filter_needs_further_review_reasons:type_name -> teleport.summarizer.v1.NeedsReviewReason + 5, // 7: teleport.sessionsearch.v1.ResourceProperties.ssh:type_name -> teleport.sessionsearch.v1.SSHProperties + 6, // 8: teleport.sessionsearch.v1.ResourceProperties.kubernetes:type_name -> teleport.sessionsearch.v1.KubernetesProperties + 7, // 9: teleport.sessionsearch.v1.ResourceProperties.database:type_name -> teleport.sessionsearch.v1.DatabaseProperties + 8, // 10: teleport.sessionsearch.v1.ResourceProperties.desktop:type_name -> teleport.sessionsearch.v1.DesktopProperties + 1, // 11: teleport.sessionsearch.v1.DesktopProperties.type:type_name -> teleport.sessionsearch.v1.DesktopType + 10, // 12: teleport.sessionsearch.v1.SearchSessionSummariesResponse.summary:type_name -> teleport.sessionsearch.v1.SessionSummary + 14, // 13: teleport.sessionsearch.v1.SearchSessionSummariesResponse.batch_complete:type_name -> teleport.sessionsearch.v1.SearchSessionSummariesResponse.BatchComplete + 16, // 14: teleport.sessionsearch.v1.SessionSummary.session_start:type_name -> google.protobuf.Timestamp + 19, // 15: teleport.sessionsearch.v1.SessionSummary.user_traits:type_name -> google.protobuf.Struct + 15, // 16: teleport.sessionsearch.v1.SessionSummary.resource_labels:type_name -> teleport.sessionsearch.v1.SessionSummary.ResourceLabelsEntry + 4, // 17: teleport.sessionsearch.v1.SessionSummary.resource_properties:type_name -> teleport.sessionsearch.v1.ResourceProperties + 17, // 18: teleport.sessionsearch.v1.SessionSummary.severity:type_name -> teleport.summarizer.v1.RiskLevel + 16, // 19: teleport.sessionsearch.v1.SessionSummary.session_end:type_name -> google.protobuf.Timestamp + 18, // 20: teleport.sessionsearch.v1.SessionSummary.needs_further_review_reasons:type_name -> teleport.summarizer.v1.NeedsReviewReason + 2, // 21: teleport.sessionsearch.v1.IsEnabledResponse.availability:type_name -> teleport.sessionsearch.v1.SessionSearchAvailability + 3, // 22: teleport.sessionsearch.v1.SessionSearchService.SearchSessionSummaries:input_type -> teleport.sessionsearch.v1.SearchSessionSummariesRequest + 11, // 23: teleport.sessionsearch.v1.SessionSearchService.IsEnabled:input_type -> teleport.sessionsearch.v1.IsEnabledRequest + 9, // 24: teleport.sessionsearch.v1.SessionSearchService.SearchSessionSummaries:output_type -> teleport.sessionsearch.v1.SearchSessionSummariesResponse + 12, // 25: teleport.sessionsearch.v1.SessionSearchService.IsEnabled:output_type -> teleport.sessionsearch.v1.IsEnabledResponse + 24, // [24:26] is the sub-list for method output_type + 22, // [22:24] is the sub-list for method input_type + 22, // [22:22] is the sub-list for extension type_name + 22, // [22:22] is the sub-list for extension extendee + 0, // [0:22] is the sub-list for field type_name } func init() { file_teleport_sessionsearch_v1_session_search_proto_init() } diff --git a/api/proto/teleport/sessionsearch/v1/session_search.proto b/api/proto/teleport/sessionsearch/v1/session_search.proto index d9db98d6b36..1a495f01988 100644 --- a/api/proto/teleport/sessionsearch/v1/session_search.proto +++ b/api/proto/teleport/sessionsearch/v1/session_search.proto @@ -169,6 +169,10 @@ message SearchSessionSummariesRequest { // the full hybrid pipeline. SEARCH_MODE_KEYWORD_ONLY uses only full-text // matching; SEARCH_MODE_EMBEDDING_ONLY uses only vector similarity. SearchMode search_mode = 15; + + // filter_needs_further_review_reasons restricts results to sessions that have + // at least one of the given review reasons. An empty list disables this filter. + repeated teleport.summarizer.v1.NeedsReviewReason filter_needs_further_review_reasons = 16; } // ResourceProperties holds session-kind-specific properties for a session. @@ -322,6 +326,11 @@ message SessionSummary { // host_id is the unique identifier of the host where the session occurred. string host_id = 16; + + // needs_further_review_reasons contains the reasons why this session was + // flagged as requiring further human review by the summarizer. Empty when the + // session was not flagged. + repeated teleport.summarizer.v1.NeedsReviewReason needs_further_review_reasons = 17; } // SessionSearchAvailability describes the availability state of the session diff --git a/docs/pages/reference/cli/tctl.mdx b/docs/pages/reference/cli/tctl.mdx index 19e68a6d9a8..73f53146968 100644 --- a/docs/pages/reference/cli/tctl.mdx +++ b/docs/pages/reference/cli/tctl.mdx @@ -2163,6 +2163,7 @@ Flags: |`--resource-kind`|*no default*|Filter by Teleport resource type (node, kube_cluster, db).| |`--resource-name`|*no default*|Filter by resource name.| |`--resume-token`|*no default*|Resume a previous JSON/YAML search from a truncated result set (token printed to stderr when results are truncated).| +|`--review-reason`|*no default* (any of (repeatable): `access_request_resource_mismatch`, `classifier_matched`, `command_analysis_failed`, `failed_to_fetch_access_request`, `output_not_fully_captured`, `too_large`, `any`)|Filter to sessions that have at least one of the given review reasons. Use 'any' to match any flagged session. Can be specified multiple times.| |`--role`|*no default*|Filter by role held during the session. Can be specified multiple times.| |`--search-mode`|`hybrid` (one of: `hybrid`, `keyword`, `embeddings`)|Search strategy to use when search queries are provided.| |`--server-addr`|*no default*|Filter SSH sessions by server address.| diff --git a/gen/proto/go/accessgraph/v1/session_search.pb.go b/gen/proto/go/accessgraph/v1/session_search.pb.go index bea5bc8324e..5ad5f75535a 100644 --- a/gen/proto/go/accessgraph/v1/session_search.pb.go +++ b/gen/proto/go/accessgraph/v1/session_search.pb.go @@ -476,9 +476,12 @@ type SearchSessionSummariesParams struct { // are present. SEARCH_MODE_UNSPECIFIED and SEARCH_MODE_HYBRID both execute // the full hybrid pipeline. SEARCH_MODE_KEYWORD_ONLY uses only full-text // matching; SEARCH_MODE_EMBEDDING_ONLY uses only vector similarity. - SearchMode SearchMode `protobuf:"varint,15,opt,name=search_mode,json=searchMode,proto3,enum=accessgraph.v1.SearchMode" json:"search_mode,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + SearchMode SearchMode `protobuf:"varint,15,opt,name=search_mode,json=searchMode,proto3,enum=accessgraph.v1.SearchMode" json:"search_mode,omitempty"` + // filter_needs_further_review_reasons restricts results to sessions that have + // at least one of the given review reasons. An empty list disables this filter. + FilterNeedsFurtherReviewReasons []v1.NeedsReviewReason `protobuf:"varint,16,rep,packed,name=filter_needs_further_review_reasons,json=filterNeedsFurtherReviewReasons,proto3,enum=teleport.summarizer.v1.NeedsReviewReason" json:"filter_needs_further_review_reasons,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SearchSessionSummariesParams) Reset() { @@ -611,6 +614,13 @@ func (x *SearchSessionSummariesParams) GetSearchMode() SearchMode { return SearchMode_SEARCH_MODE_UNSPECIFIED } +func (x *SearchSessionSummariesParams) GetFilterNeedsFurtherReviewReasons() []v1.NeedsReviewReason { + if x != nil { + return x.FilterNeedsFurtherReviewReasons + } + return nil +} + func (x *SearchSessionSummariesParams) SetStartTime(v *timestamppb.Timestamp) { x.StartTime = v } @@ -671,6 +681,10 @@ func (x *SearchSessionSummariesParams) SetSearchMode(v SearchMode) { x.SearchMode = v } +func (x *SearchSessionSummariesParams) SetFilterNeedsFurtherReviewReasons(v []v1.NeedsReviewReason) { + x.FilterNeedsFurtherReviewReasons = v +} + func (x *SearchSessionSummariesParams) HasStartTime() bool { if x == nil { return false @@ -803,6 +817,9 @@ type SearchSessionSummariesParams_builder struct { // the full hybrid pipeline. SEARCH_MODE_KEYWORD_ONLY uses only full-text // matching; SEARCH_MODE_EMBEDDING_ONLY uses only vector similarity. SearchMode SearchMode + // filter_needs_further_review_reasons restricts results to sessions that have + // at least one of the given review reasons. An empty list disables this filter. + FilterNeedsFurtherReviewReasons []v1.NeedsReviewReason } func (b0 SearchSessionSummariesParams_builder) Build() *SearchSessionSummariesParams { @@ -824,6 +841,7 @@ func (b0 SearchSessionSummariesParams_builder) Build() *SearchSessionSummariesPa x.MaxSummaries = b.MaxSummaries x.ResumeToken = b.ResumeToken x.SearchMode = b.SearchMode + x.FilterNeedsFurtherReviewReasons = b.FilterNeedsFurtherReviewReasons return m0 } @@ -2020,9 +2038,13 @@ type SessionSummary struct { // session_end is the timestamp when the session ended. SessionEnd *timestamppb.Timestamp `protobuf:"bytes,16,opt,name=session_end,json=sessionEnd,proto3" json:"session_end,omitempty"` // host_id is the unique identifier of the host where the session occurred. - HostId string `protobuf:"bytes,17,opt,name=host_id,json=hostId,proto3" json:"host_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + HostId string `protobuf:"bytes,17,opt,name=host_id,json=hostId,proto3" json:"host_id,omitempty"` + // needs_further_review_reasons contains the reasons why this session was + // flagged as requiring further human review by the summarizer. Empty when the + // session was not flagged. + NeedsFurtherReviewReasons []v1.NeedsReviewReason `protobuf:"varint,18,rep,packed,name=needs_further_review_reasons,json=needsFurtherReviewReasons,proto3,enum=teleport.summarizer.v1.NeedsReviewReason" json:"needs_further_review_reasons,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SessionSummary) Reset() { @@ -2169,6 +2191,13 @@ func (x *SessionSummary) GetHostId() string { return "" } +func (x *SessionSummary) GetNeedsFurtherReviewReasons() []v1.NeedsReviewReason { + if x != nil { + return x.NeedsFurtherReviewReasons + } + return nil +} + func (x *SessionSummary) SetSessionId(v string) { x.SessionId = v } @@ -2237,6 +2266,10 @@ func (x *SessionSummary) SetHostId(v string) { x.HostId = v } +func (x *SessionSummary) SetNeedsFurtherReviewReasons(v []v1.NeedsReviewReason) { + x.NeedsFurtherReviewReasons = v +} + func (x *SessionSummary) HasSessionStart() bool { if x == nil { return false @@ -2339,6 +2372,10 @@ type SessionSummary_builder struct { SessionEnd *timestamppb.Timestamp // host_id is the unique identifier of the host where the session occurred. HostId string + // needs_further_review_reasons contains the reasons why this session was + // flagged as requiring further human review by the summarizer. Empty when the + // session was not flagged. + NeedsFurtherReviewReasons []v1.NeedsReviewReason } func (b0 SessionSummary_builder) Build() *SessionSummary { @@ -2362,6 +2399,7 @@ func (b0 SessionSummary_builder) Build() *SessionSummary { x.SessionEndEvent = b.SessionEndEvent x.SessionEnd = b.SessionEnd x.HostId = b.HostId + x.NeedsFurtherReviewReasons = b.NeedsFurtherReviewReasons return m0 } @@ -2413,9 +2451,13 @@ type StoreSessionSummaryRequest struct { HostId string `protobuf:"bytes,17,opt,name=host_id,json=hostId,proto3" json:"host_id,omitempty"` // embeddings are the semantic vector embeddings of the session summary, // used for similarity-based search queries. - Embeddings []*EmbeddingChunk `protobuf:"bytes,18,rep,name=embeddings,proto3" json:"embeddings,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + Embeddings []*EmbeddingChunk `protobuf:"bytes,18,rep,name=embeddings,proto3" json:"embeddings,omitempty"` + // needs_further_review_reasons contains the reasons why this session needs + // further review, as determined by the summarizer. Empty when the session was + // not flagged. + NeedsFurtherReviewReasons []v1.NeedsReviewReason `protobuf:"varint,19,rep,packed,name=needs_further_review_reasons,json=needsFurtherReviewReasons,proto3,enum=teleport.summarizer.v1.NeedsReviewReason" json:"needs_further_review_reasons,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *StoreSessionSummaryRequest) Reset() { @@ -2569,6 +2611,13 @@ func (x *StoreSessionSummaryRequest) GetEmbeddings() []*EmbeddingChunk { return nil } +func (x *StoreSessionSummaryRequest) GetNeedsFurtherReviewReasons() []v1.NeedsReviewReason { + if x != nil { + return x.NeedsFurtherReviewReasons + } + return nil +} + func (x *StoreSessionSummaryRequest) SetSessionId(v string) { x.SessionId = v } @@ -2641,6 +2690,10 @@ func (x *StoreSessionSummaryRequest) SetEmbeddings(v []*EmbeddingChunk) { x.Embeddings = v } +func (x *StoreSessionSummaryRequest) SetNeedsFurtherReviewReasons(v []v1.NeedsReviewReason) { + x.NeedsFurtherReviewReasons = v +} + func (x *StoreSessionSummaryRequest) HasSessionStart() bool { if x == nil { return false @@ -2743,6 +2796,10 @@ type StoreSessionSummaryRequest_builder struct { // embeddings are the semantic vector embeddings of the session summary, // used for similarity-based search queries. Embeddings []*EmbeddingChunk + // needs_further_review_reasons contains the reasons why this session needs + // further review, as determined by the summarizer. Empty when the session was + // not flagged. + NeedsFurtherReviewReasons []v1.NeedsReviewReason } func (b0 StoreSessionSummaryRequest_builder) Build() *StoreSessionSummaryRequest { @@ -2767,6 +2824,7 @@ func (b0 StoreSessionSummaryRequest_builder) Build() *StoreSessionSummaryRequest x.SessionEndEvent = b.SessionEndEvent x.HostId = b.HostId x.Embeddings = b.Embeddings + x.NeedsFurtherReviewReasons = b.NeedsFurtherReviewReasons return m0 } @@ -3188,7 +3246,7 @@ const file_accessgraph_v1_session_search_proto_rawDesc = "" + "fetch_more\x18\x02 \x01(\v27.accessgraph.v1.SearchSessionSummariesRequest.FetchMoreH\x00R\tfetchMore\x1a0\n" + "\tFetchMore\x12#\n" + "\rmax_summaries\x18\x01 \x01(\rR\fmaxSummariesB\t\n" + - "\apayload\"\xa6\a\n" + + "\apayload\"\x9f\b\n" + "\x1cSearchSessionSummariesParams\x129\n" + "\n" + "start_time\x18\x01 \x01(\v2\x1a.google.protobuf.TimestampR\tstartTime\x125\n" + @@ -3208,7 +3266,8 @@ const file_accessgraph_v1_session_search_proto_rawDesc = "" + "\rmax_summaries\x18\r \x01(\rR\fmaxSummaries\x12!\n" + "\fresume_token\x18\x0e \x01(\tR\vresumeToken\x12;\n" + "\vsearch_mode\x18\x0f \x01(\x0e2\x1a.accessgraph.v1.SearchModeR\n" + - "searchMode\x1aA\n" + + "searchMode\x12w\n" + + "#filter_needs_further_review_reasons\x18\x10 \x03(\x0e2).teleport.summarizer.v1.NeedsReviewReasonR\x1ffilterNeedsFurtherReviewReasons\x1aA\n" + "\x13ResourceLabelsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\v\n" + @@ -3262,7 +3321,7 @@ const file_accessgraph_v1_session_search_proto_rawDesc = "" + "\apayload\"{\n" + "\x14SummaryAndCheckpoint\x128\n" + "\asummary\x18\x01 \x01(\v2\x1e.accessgraph.v1.SessionSummaryR\asummary\x12)\n" + - "\x10checkpoint_token\x18\x02 \x01(\tR\x0fcheckpointToken\"\x85\a\n" + + "\x10checkpoint_token\x18\x02 \x01(\tR\x0fcheckpointToken\"\xf1\a\n" + "\x0eSessionSummary\x12\x1d\n" + "\n" + "session_id\x18\x01 \x01(\tR\tsessionId\x12\x12\n" + @@ -3286,10 +3345,11 @@ const file_accessgraph_v1_session_search_proto_rawDesc = "" + "\x11session_end_event\x18\x0f \x01(\v2\x17.google.protobuf.StructR\x0fsessionEndEvent\x12;\n" + "\vsession_end\x18\x10 \x01(\v2\x1a.google.protobuf.TimestampR\n" + "sessionEnd\x12\x17\n" + - "\ahost_id\x18\x11 \x01(\tR\x06hostId\x1aA\n" + + "\ahost_id\x18\x11 \x01(\tR\x06hostId\x12j\n" + + "\x1cneeds_further_review_reasons\x18\x12 \x03(\x0e2).teleport.summarizer.v1.NeedsReviewReasonR\x19needsFurtherReviewReasons\x1aA\n" + "\x13ResourceLabelsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xdd\a\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xc9\b\n" + "\x1aStoreSessionSummaryRequest\x12\x1d\n" + "\n" + "session_id\x18\x01 \x01(\tR\tsessionId\x12\x12\n" + @@ -3316,7 +3376,8 @@ const file_accessgraph_v1_session_search_proto_rawDesc = "" + "\ahost_id\x18\x11 \x01(\tR\x06hostId\x12>\n" + "\n" + "embeddings\x18\x12 \x03(\v2\x1e.accessgraph.v1.EmbeddingChunkR\n" + - "embeddings\x1aA\n" + + "embeddings\x12j\n" + + "\x1cneeds_further_review_reasons\x18\x13 \x03(\x0e2).teleport.summarizer.v1.NeedsReviewReasonR\x19needsFurtherReviewReasons\x1aA\n" + "\x13ResourceLabelsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"~\n" + @@ -3381,7 +3442,8 @@ var file_accessgraph_v1_session_search_proto_goTypes = []any{ nil, // 23: accessgraph.v1.StoreSessionSummaryRequest.ResourceLabelsEntry (*timestamppb.Timestamp)(nil), // 24: google.protobuf.Timestamp (v1.RiskLevel)(0), // 25: teleport.summarizer.v1.RiskLevel - (*structpb.Struct)(nil), // 26: google.protobuf.Struct + (v1.NeedsReviewReason)(0), // 26: teleport.summarizer.v1.NeedsReviewReason + (*structpb.Struct)(nil), // 27: google.protobuf.Struct } var file_accessgraph_v1_session_search_proto_depIdxs = []int32{ 4, // 0: accessgraph.v1.SearchSessionSummariesRequest.search_params:type_name -> accessgraph.v1.SearchSessionSummariesParams @@ -3393,41 +3455,44 @@ var file_accessgraph_v1_session_search_proto_depIdxs = []int32{ 25, // 6: accessgraph.v1.SearchSessionSummariesParams.severity:type_name -> teleport.summarizer.v1.RiskLevel 5, // 7: accessgraph.v1.SearchSessionSummariesParams.search_queries:type_name -> accessgraph.v1.EmbeddedQuery 0, // 8: accessgraph.v1.SearchSessionSummariesParams.search_mode:type_name -> accessgraph.v1.SearchMode - 7, // 9: accessgraph.v1.ResourceProperties.ssh:type_name -> accessgraph.v1.SSHProperties - 8, // 10: accessgraph.v1.ResourceProperties.kubernetes:type_name -> accessgraph.v1.KubernetesProperties - 9, // 11: accessgraph.v1.ResourceProperties.database:type_name -> accessgraph.v1.DatabaseProperties - 10, // 12: accessgraph.v1.ResourceProperties.desktop:type_name -> accessgraph.v1.DesktopProperties - 1, // 13: accessgraph.v1.DesktopProperties.type:type_name -> accessgraph.v1.DesktopType - 12, // 14: accessgraph.v1.SearchSessionSummariesResponse.summary:type_name -> accessgraph.v1.SummaryAndCheckpoint - 21, // 15: accessgraph.v1.SearchSessionSummariesResponse.batch_complete:type_name -> accessgraph.v1.SearchSessionSummariesResponse.BatchComplete - 13, // 16: accessgraph.v1.SummaryAndCheckpoint.summary:type_name -> accessgraph.v1.SessionSummary - 24, // 17: accessgraph.v1.SessionSummary.session_start:type_name -> google.protobuf.Timestamp - 26, // 18: accessgraph.v1.SessionSummary.user_traits:type_name -> google.protobuf.Struct - 22, // 19: accessgraph.v1.SessionSummary.resource_labels:type_name -> accessgraph.v1.SessionSummary.ResourceLabelsEntry - 6, // 20: accessgraph.v1.SessionSummary.resource_properties:type_name -> accessgraph.v1.ResourceProperties - 25, // 21: accessgraph.v1.SessionSummary.severity:type_name -> teleport.summarizer.v1.RiskLevel - 26, // 22: accessgraph.v1.SessionSummary.session_end_event:type_name -> google.protobuf.Struct - 24, // 23: accessgraph.v1.SessionSummary.session_end:type_name -> google.protobuf.Timestamp - 24, // 24: accessgraph.v1.StoreSessionSummaryRequest.session_start:type_name -> google.protobuf.Timestamp - 24, // 25: accessgraph.v1.StoreSessionSummaryRequest.session_end:type_name -> google.protobuf.Timestamp - 26, // 26: accessgraph.v1.StoreSessionSummaryRequest.user_traits:type_name -> google.protobuf.Struct - 23, // 27: accessgraph.v1.StoreSessionSummaryRequest.resource_labels:type_name -> accessgraph.v1.StoreSessionSummaryRequest.ResourceLabelsEntry - 6, // 28: accessgraph.v1.StoreSessionSummaryRequest.resource_properties:type_name -> accessgraph.v1.ResourceProperties - 25, // 29: accessgraph.v1.StoreSessionSummaryRequest.severity:type_name -> teleport.summarizer.v1.RiskLevel - 26, // 30: accessgraph.v1.StoreSessionSummaryRequest.session_end_event:type_name -> google.protobuf.Struct - 15, // 31: accessgraph.v1.StoreSessionSummaryRequest.embeddings:type_name -> accessgraph.v1.EmbeddingChunk - 2, // 32: accessgraph.v1.IsSessionSearchEnabledResponse.availability:type_name -> accessgraph.v1.SessionSearchAvailability - 3, // 33: accessgraph.v1.SessionRecordingService.SearchSessionSummaries:input_type -> accessgraph.v1.SearchSessionSummariesRequest - 14, // 34: accessgraph.v1.SessionRecordingService.StoreSessionSummary:input_type -> accessgraph.v1.StoreSessionSummaryRequest - 17, // 35: accessgraph.v1.SessionRecordingService.IsSessionSearchEnabled:input_type -> accessgraph.v1.IsSessionSearchEnabledRequest - 11, // 36: accessgraph.v1.SessionRecordingService.SearchSessionSummaries:output_type -> accessgraph.v1.SearchSessionSummariesResponse - 16, // 37: accessgraph.v1.SessionRecordingService.StoreSessionSummary:output_type -> accessgraph.v1.StoreSessionSummaryResponse - 18, // 38: accessgraph.v1.SessionRecordingService.IsSessionSearchEnabled:output_type -> accessgraph.v1.IsSessionSearchEnabledResponse - 36, // [36:39] is the sub-list for method output_type - 33, // [33:36] is the sub-list for method input_type - 33, // [33:33] is the sub-list for extension type_name - 33, // [33:33] is the sub-list for extension extendee - 0, // [0:33] is the sub-list for field type_name + 26, // 9: accessgraph.v1.SearchSessionSummariesParams.filter_needs_further_review_reasons:type_name -> teleport.summarizer.v1.NeedsReviewReason + 7, // 10: accessgraph.v1.ResourceProperties.ssh:type_name -> accessgraph.v1.SSHProperties + 8, // 11: accessgraph.v1.ResourceProperties.kubernetes:type_name -> accessgraph.v1.KubernetesProperties + 9, // 12: accessgraph.v1.ResourceProperties.database:type_name -> accessgraph.v1.DatabaseProperties + 10, // 13: accessgraph.v1.ResourceProperties.desktop:type_name -> accessgraph.v1.DesktopProperties + 1, // 14: accessgraph.v1.DesktopProperties.type:type_name -> accessgraph.v1.DesktopType + 12, // 15: accessgraph.v1.SearchSessionSummariesResponse.summary:type_name -> accessgraph.v1.SummaryAndCheckpoint + 21, // 16: accessgraph.v1.SearchSessionSummariesResponse.batch_complete:type_name -> accessgraph.v1.SearchSessionSummariesResponse.BatchComplete + 13, // 17: accessgraph.v1.SummaryAndCheckpoint.summary:type_name -> accessgraph.v1.SessionSummary + 24, // 18: accessgraph.v1.SessionSummary.session_start:type_name -> google.protobuf.Timestamp + 27, // 19: accessgraph.v1.SessionSummary.user_traits:type_name -> google.protobuf.Struct + 22, // 20: accessgraph.v1.SessionSummary.resource_labels:type_name -> accessgraph.v1.SessionSummary.ResourceLabelsEntry + 6, // 21: accessgraph.v1.SessionSummary.resource_properties:type_name -> accessgraph.v1.ResourceProperties + 25, // 22: accessgraph.v1.SessionSummary.severity:type_name -> teleport.summarizer.v1.RiskLevel + 27, // 23: accessgraph.v1.SessionSummary.session_end_event:type_name -> google.protobuf.Struct + 24, // 24: accessgraph.v1.SessionSummary.session_end:type_name -> google.protobuf.Timestamp + 26, // 25: accessgraph.v1.SessionSummary.needs_further_review_reasons:type_name -> teleport.summarizer.v1.NeedsReviewReason + 24, // 26: accessgraph.v1.StoreSessionSummaryRequest.session_start:type_name -> google.protobuf.Timestamp + 24, // 27: accessgraph.v1.StoreSessionSummaryRequest.session_end:type_name -> google.protobuf.Timestamp + 27, // 28: accessgraph.v1.StoreSessionSummaryRequest.user_traits:type_name -> google.protobuf.Struct + 23, // 29: accessgraph.v1.StoreSessionSummaryRequest.resource_labels:type_name -> accessgraph.v1.StoreSessionSummaryRequest.ResourceLabelsEntry + 6, // 30: accessgraph.v1.StoreSessionSummaryRequest.resource_properties:type_name -> accessgraph.v1.ResourceProperties + 25, // 31: accessgraph.v1.StoreSessionSummaryRequest.severity:type_name -> teleport.summarizer.v1.RiskLevel + 27, // 32: accessgraph.v1.StoreSessionSummaryRequest.session_end_event:type_name -> google.protobuf.Struct + 15, // 33: accessgraph.v1.StoreSessionSummaryRequest.embeddings:type_name -> accessgraph.v1.EmbeddingChunk + 26, // 34: accessgraph.v1.StoreSessionSummaryRequest.needs_further_review_reasons:type_name -> teleport.summarizer.v1.NeedsReviewReason + 2, // 35: accessgraph.v1.IsSessionSearchEnabledResponse.availability:type_name -> accessgraph.v1.SessionSearchAvailability + 3, // 36: accessgraph.v1.SessionRecordingService.SearchSessionSummaries:input_type -> accessgraph.v1.SearchSessionSummariesRequest + 14, // 37: accessgraph.v1.SessionRecordingService.StoreSessionSummary:input_type -> accessgraph.v1.StoreSessionSummaryRequest + 17, // 38: accessgraph.v1.SessionRecordingService.IsSessionSearchEnabled:input_type -> accessgraph.v1.IsSessionSearchEnabledRequest + 11, // 39: accessgraph.v1.SessionRecordingService.SearchSessionSummaries:output_type -> accessgraph.v1.SearchSessionSummariesResponse + 16, // 40: accessgraph.v1.SessionRecordingService.StoreSessionSummary:output_type -> accessgraph.v1.StoreSessionSummaryResponse + 18, // 41: accessgraph.v1.SessionRecordingService.IsSessionSearchEnabled:output_type -> accessgraph.v1.IsSessionSearchEnabledResponse + 39, // [39:42] is the sub-list for method output_type + 36, // [36:39] is the sub-list for method input_type + 36, // [36:36] is the sub-list for extension type_name + 36, // [36:36] is the sub-list for extension extendee + 0, // [0:36] is the sub-list for field type_name } func init() { file_accessgraph_v1_session_search_proto_init() } diff --git a/gen/proto/go/accessgraph/v1/session_search_protoopaque.pb.go b/gen/proto/go/accessgraph/v1/session_search_protoopaque.pb.go index 8f556cd4acb..ddeb5addf3c 100644 --- a/gen/proto/go/accessgraph/v1/session_search_protoopaque.pb.go +++ b/gen/proto/go/accessgraph/v1/session_search_protoopaque.pb.go @@ -402,26 +402,27 @@ func (*searchSessionSummariesRequest_FetchMore_) isSearchSessionSummariesRequest // SearchSessionSummariesParams defines the filter criteria and batch settings // for a session summary search. type SearchSessionSummariesParams struct { - state protoimpl.MessageState `protogen:"opaque.v1"` - xxx_hidden_StartTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=start_time,json=startTime,proto3"` - xxx_hidden_EndTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=end_time,json=endTime,proto3"` - xxx_hidden_Kinds []string `protobuf:"bytes,3,rep,name=kinds,proto3"` - xxx_hidden_Username *string `protobuf:"bytes,4,opt,name=username,proto3,oneof"` - xxx_hidden_UserRoles []string `protobuf:"bytes,5,rep,name=user_roles,json=userRoles,proto3"` - xxx_hidden_AccessRequestIds []string `protobuf:"bytes,6,rep,name=access_request_ids,json=accessRequestIds,proto3"` - xxx_hidden_ResourceKind *string `protobuf:"bytes,7,opt,name=resource_kind,json=resourceKind,proto3,oneof"` - xxx_hidden_ResourceName *string `protobuf:"bytes,8,opt,name=resource_name,json=resourceName,proto3,oneof"` - xxx_hidden_ResourceLabels map[string]string `protobuf:"bytes,9,rep,name=resource_labels,json=resourceLabels,proto3" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - xxx_hidden_ResourceProperties *ResourceProperties `protobuf:"bytes,10,opt,name=resource_properties,json=resourceProperties,proto3"` - xxx_hidden_Severity v1.RiskLevel `protobuf:"varint,11,opt,name=severity,proto3,enum=teleport.summarizer.v1.RiskLevel"` - xxx_hidden_SearchQueries *[]*EmbeddedQuery `protobuf:"bytes,12,rep,name=search_queries,json=searchQueries,proto3"` - xxx_hidden_MaxSummaries uint32 `protobuf:"varint,13,opt,name=max_summaries,json=maxSummaries,proto3"` - xxx_hidden_ResumeToken string `protobuf:"bytes,14,opt,name=resume_token,json=resumeToken,proto3"` - xxx_hidden_SearchMode SearchMode `protobuf:"varint,15,opt,name=search_mode,json=searchMode,proto3,enum=accessgraph.v1.SearchMode"` - XXX_raceDetectHookData protoimpl.RaceDetectHookData - XXX_presence [1]uint32 - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"opaque.v1"` + xxx_hidden_StartTime *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=start_time,json=startTime,proto3"` + xxx_hidden_EndTime *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=end_time,json=endTime,proto3"` + xxx_hidden_Kinds []string `protobuf:"bytes,3,rep,name=kinds,proto3"` + xxx_hidden_Username *string `protobuf:"bytes,4,opt,name=username,proto3,oneof"` + xxx_hidden_UserRoles []string `protobuf:"bytes,5,rep,name=user_roles,json=userRoles,proto3"` + xxx_hidden_AccessRequestIds []string `protobuf:"bytes,6,rep,name=access_request_ids,json=accessRequestIds,proto3"` + xxx_hidden_ResourceKind *string `protobuf:"bytes,7,opt,name=resource_kind,json=resourceKind,proto3,oneof"` + xxx_hidden_ResourceName *string `protobuf:"bytes,8,opt,name=resource_name,json=resourceName,proto3,oneof"` + xxx_hidden_ResourceLabels map[string]string `protobuf:"bytes,9,rep,name=resource_labels,json=resourceLabels,proto3" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + xxx_hidden_ResourceProperties *ResourceProperties `protobuf:"bytes,10,opt,name=resource_properties,json=resourceProperties,proto3"` + xxx_hidden_Severity v1.RiskLevel `protobuf:"varint,11,opt,name=severity,proto3,enum=teleport.summarizer.v1.RiskLevel"` + xxx_hidden_SearchQueries *[]*EmbeddedQuery `protobuf:"bytes,12,rep,name=search_queries,json=searchQueries,proto3"` + xxx_hidden_MaxSummaries uint32 `protobuf:"varint,13,opt,name=max_summaries,json=maxSummaries,proto3"` + xxx_hidden_ResumeToken string `protobuf:"bytes,14,opt,name=resume_token,json=resumeToken,proto3"` + xxx_hidden_SearchMode SearchMode `protobuf:"varint,15,opt,name=search_mode,json=searchMode,proto3,enum=accessgraph.v1.SearchMode"` + xxx_hidden_FilterNeedsFurtherReviewReasons []v1.NeedsReviewReason `protobuf:"varint,16,rep,packed,name=filter_needs_further_review_reasons,json=filterNeedsFurtherReviewReasons,proto3,enum=teleport.summarizer.v1.NeedsReviewReason"` + XXX_raceDetectHookData protoimpl.RaceDetectHookData + XXX_presence [1]uint32 + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SearchSessionSummariesParams) Reset() { @@ -565,6 +566,13 @@ func (x *SearchSessionSummariesParams) GetSearchMode() SearchMode { return SearchMode_SEARCH_MODE_UNSPECIFIED } +func (x *SearchSessionSummariesParams) GetFilterNeedsFurtherReviewReasons() []v1.NeedsReviewReason { + if x != nil { + return x.xxx_hidden_FilterNeedsFurtherReviewReasons + } + return nil +} + func (x *SearchSessionSummariesParams) SetStartTime(v *timestamppb.Timestamp) { x.xxx_hidden_StartTime = v } @@ -579,7 +587,7 @@ func (x *SearchSessionSummariesParams) SetKinds(v []string) { func (x *SearchSessionSummariesParams) SetUsername(v string) { x.xxx_hidden_Username = &v - protoimpl.X.SetPresent(&(x.XXX_presence[0]), 3, 15) + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 3, 16) } func (x *SearchSessionSummariesParams) SetUserRoles(v []string) { @@ -592,12 +600,12 @@ func (x *SearchSessionSummariesParams) SetAccessRequestIds(v []string) { func (x *SearchSessionSummariesParams) SetResourceKind(v string) { x.xxx_hidden_ResourceKind = &v - protoimpl.X.SetPresent(&(x.XXX_presence[0]), 6, 15) + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 6, 16) } func (x *SearchSessionSummariesParams) SetResourceName(v string) { x.xxx_hidden_ResourceName = &v - protoimpl.X.SetPresent(&(x.XXX_presence[0]), 7, 15) + protoimpl.X.SetPresent(&(x.XXX_presence[0]), 7, 16) } func (x *SearchSessionSummariesParams) SetResourceLabels(v map[string]string) { @@ -628,6 +636,10 @@ func (x *SearchSessionSummariesParams) SetSearchMode(v SearchMode) { x.xxx_hidden_SearchMode = v } +func (x *SearchSessionSummariesParams) SetFilterNeedsFurtherReviewReasons(v []v1.NeedsReviewReason) { + x.xxx_hidden_FilterNeedsFurtherReviewReasons = v +} + func (x *SearchSessionSummariesParams) HasStartTime() bool { if x == nil { return false @@ -763,6 +775,9 @@ type SearchSessionSummariesParams_builder struct { // the full hybrid pipeline. SEARCH_MODE_KEYWORD_ONLY uses only full-text // matching; SEARCH_MODE_EMBEDDING_ONLY uses only vector similarity. SearchMode SearchMode + // filter_needs_further_review_reasons restricts results to sessions that have + // at least one of the given review reasons. An empty list disables this filter. + FilterNeedsFurtherReviewReasons []v1.NeedsReviewReason } func (b0 SearchSessionSummariesParams_builder) Build() *SearchSessionSummariesParams { @@ -773,17 +788,17 @@ func (b0 SearchSessionSummariesParams_builder) Build() *SearchSessionSummariesPa x.xxx_hidden_EndTime = b.EndTime x.xxx_hidden_Kinds = b.Kinds if b.Username != nil { - protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 3, 15) + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 3, 16) x.xxx_hidden_Username = b.Username } x.xxx_hidden_UserRoles = b.UserRoles x.xxx_hidden_AccessRequestIds = b.AccessRequestIds if b.ResourceKind != nil { - protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 6, 15) + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 6, 16) x.xxx_hidden_ResourceKind = b.ResourceKind } if b.ResourceName != nil { - protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 7, 15) + protoimpl.X.SetPresentNonAtomic(&(x.XXX_presence[0]), 7, 16) x.xxx_hidden_ResourceName = b.ResourceName } x.xxx_hidden_ResourceLabels = b.ResourceLabels @@ -793,6 +808,7 @@ func (b0 SearchSessionSummariesParams_builder) Build() *SearchSessionSummariesPa x.xxx_hidden_MaxSummaries = b.MaxSummaries x.xxx_hidden_ResumeToken = b.ResumeToken x.xxx_hidden_SearchMode = b.SearchMode + x.xxx_hidden_FilterNeedsFurtherReviewReasons = b.FilterNeedsFurtherReviewReasons return m0 } @@ -1977,26 +1993,27 @@ func (b0 SummaryAndCheckpoint_builder) Build() *SummaryAndCheckpoint { // SessionSummary contains the metadata and raw session-end event for a // single recorded session, as stored in the access graph. type SessionSummary struct { - state protoimpl.MessageState `protogen:"opaque.v1"` - xxx_hidden_SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3"` - xxx_hidden_Kind string `protobuf:"bytes,2,opt,name=kind,proto3"` - xxx_hidden_SessionStart *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=session_start,json=sessionStart,proto3"` - xxx_hidden_Username string `protobuf:"bytes,4,opt,name=username,proto3"` - xxx_hidden_UserTraits *structpb.Struct `protobuf:"bytes,5,opt,name=user_traits,json=userTraits,proto3"` - xxx_hidden_UserRoles []string `protobuf:"bytes,6,rep,name=user_roles,json=userRoles,proto3"` - xxx_hidden_AccessRequestIds []string `protobuf:"bytes,7,rep,name=access_request_ids,json=accessRequestIds,proto3"` - xxx_hidden_Participants []string `protobuf:"bytes,8,rep,name=participants,proto3"` - xxx_hidden_ResourceKind string `protobuf:"bytes,9,opt,name=resource_kind,json=resourceKind,proto3"` - xxx_hidden_ResourceLabels map[string]string `protobuf:"bytes,10,rep,name=resource_labels,json=resourceLabels,proto3" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - xxx_hidden_ResourceId string `protobuf:"bytes,11,opt,name=resource_id,json=resourceId,proto3"` - xxx_hidden_ResourceName string `protobuf:"bytes,12,opt,name=resource_name,json=resourceName,proto3"` - xxx_hidden_ResourceProperties *ResourceProperties `protobuf:"bytes,13,opt,name=resource_properties,json=resourceProperties,proto3"` - xxx_hidden_Severity v1.RiskLevel `protobuf:"varint,14,opt,name=severity,proto3,enum=teleport.summarizer.v1.RiskLevel"` - xxx_hidden_SessionEndEvent *structpb.Struct `protobuf:"bytes,15,opt,name=session_end_event,json=sessionEndEvent,proto3"` - xxx_hidden_SessionEnd *timestamppb.Timestamp `protobuf:"bytes,16,opt,name=session_end,json=sessionEnd,proto3"` - xxx_hidden_HostId string `protobuf:"bytes,17,opt,name=host_id,json=hostId,proto3"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"opaque.v1"` + xxx_hidden_SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3"` + xxx_hidden_Kind string `protobuf:"bytes,2,opt,name=kind,proto3"` + xxx_hidden_SessionStart *timestamppb.Timestamp `protobuf:"bytes,3,opt,name=session_start,json=sessionStart,proto3"` + xxx_hidden_Username string `protobuf:"bytes,4,opt,name=username,proto3"` + xxx_hidden_UserTraits *structpb.Struct `protobuf:"bytes,5,opt,name=user_traits,json=userTraits,proto3"` + xxx_hidden_UserRoles []string `protobuf:"bytes,6,rep,name=user_roles,json=userRoles,proto3"` + xxx_hidden_AccessRequestIds []string `protobuf:"bytes,7,rep,name=access_request_ids,json=accessRequestIds,proto3"` + xxx_hidden_Participants []string `protobuf:"bytes,8,rep,name=participants,proto3"` + xxx_hidden_ResourceKind string `protobuf:"bytes,9,opt,name=resource_kind,json=resourceKind,proto3"` + xxx_hidden_ResourceLabels map[string]string `protobuf:"bytes,10,rep,name=resource_labels,json=resourceLabels,proto3" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + xxx_hidden_ResourceId string `protobuf:"bytes,11,opt,name=resource_id,json=resourceId,proto3"` + xxx_hidden_ResourceName string `protobuf:"bytes,12,opt,name=resource_name,json=resourceName,proto3"` + xxx_hidden_ResourceProperties *ResourceProperties `protobuf:"bytes,13,opt,name=resource_properties,json=resourceProperties,proto3"` + xxx_hidden_Severity v1.RiskLevel `protobuf:"varint,14,opt,name=severity,proto3,enum=teleport.summarizer.v1.RiskLevel"` + xxx_hidden_SessionEndEvent *structpb.Struct `protobuf:"bytes,15,opt,name=session_end_event,json=sessionEndEvent,proto3"` + xxx_hidden_SessionEnd *timestamppb.Timestamp `protobuf:"bytes,16,opt,name=session_end,json=sessionEnd,proto3"` + xxx_hidden_HostId string `protobuf:"bytes,17,opt,name=host_id,json=hostId,proto3"` + xxx_hidden_NeedsFurtherReviewReasons []v1.NeedsReviewReason `protobuf:"varint,18,rep,packed,name=needs_further_review_reasons,json=needsFurtherReviewReasons,proto3,enum=teleport.summarizer.v1.NeedsReviewReason"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *SessionSummary) Reset() { @@ -2143,6 +2160,13 @@ func (x *SessionSummary) GetHostId() string { return "" } +func (x *SessionSummary) GetNeedsFurtherReviewReasons() []v1.NeedsReviewReason { + if x != nil { + return x.xxx_hidden_NeedsFurtherReviewReasons + } + return nil +} + func (x *SessionSummary) SetSessionId(v string) { x.xxx_hidden_SessionId = v } @@ -2211,6 +2235,10 @@ func (x *SessionSummary) SetHostId(v string) { x.xxx_hidden_HostId = v } +func (x *SessionSummary) SetNeedsFurtherReviewReasons(v []v1.NeedsReviewReason) { + x.xxx_hidden_NeedsFurtherReviewReasons = v +} + func (x *SessionSummary) HasSessionStart() bool { if x == nil { return false @@ -2313,6 +2341,10 @@ type SessionSummary_builder struct { SessionEnd *timestamppb.Timestamp // host_id is the unique identifier of the host where the session occurred. HostId string + // needs_further_review_reasons contains the reasons why this session was + // flagged as requiring further human review by the summarizer. Empty when the + // session was not flagged. + NeedsFurtherReviewReasons []v1.NeedsReviewReason } func (b0 SessionSummary_builder) Build() *SessionSummary { @@ -2336,6 +2368,7 @@ func (b0 SessionSummary_builder) Build() *SessionSummary { x.xxx_hidden_SessionEndEvent = b.SessionEndEvent x.xxx_hidden_SessionEnd = b.SessionEnd x.xxx_hidden_HostId = b.HostId + x.xxx_hidden_NeedsFurtherReviewReasons = b.NeedsFurtherReviewReasons return m0 } @@ -2343,27 +2376,28 @@ func (b0 SessionSummary_builder) Build() *SessionSummary { // its associated metadata and semantic embeddings. // Field order mirrors SessionSummary for easy mapping between the two. type StoreSessionSummaryRequest struct { - state protoimpl.MessageState `protogen:"opaque.v1"` - xxx_hidden_SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3"` - xxx_hidden_Kind string `protobuf:"bytes,2,opt,name=kind,proto3"` - xxx_hidden_Username string `protobuf:"bytes,3,opt,name=username,proto3"` - xxx_hidden_SessionStart *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=session_start,json=sessionStart,proto3"` - xxx_hidden_SessionEnd *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=session_end,json=sessionEnd,proto3"` - xxx_hidden_UserTraits *structpb.Struct `protobuf:"bytes,6,opt,name=user_traits,json=userTraits,proto3"` - xxx_hidden_UserRoles []string `protobuf:"bytes,7,rep,name=user_roles,json=userRoles,proto3"` - xxx_hidden_AccessRequestIds []string `protobuf:"bytes,8,rep,name=access_request_ids,json=accessRequestIds,proto3"` - xxx_hidden_Participants []string `protobuf:"bytes,9,rep,name=participants,proto3"` - xxx_hidden_ResourceKind string `protobuf:"bytes,10,opt,name=resource_kind,json=resourceKind,proto3"` - xxx_hidden_ResourceLabels map[string]string `protobuf:"bytes,11,rep,name=resource_labels,json=resourceLabels,proto3" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - xxx_hidden_ResourceId string `protobuf:"bytes,12,opt,name=resource_id,json=resourceId,proto3"` - xxx_hidden_ResourceName string `protobuf:"bytes,13,opt,name=resource_name,json=resourceName,proto3"` - xxx_hidden_ResourceProperties *ResourceProperties `protobuf:"bytes,14,opt,name=resource_properties,json=resourceProperties,proto3"` - xxx_hidden_Severity v1.RiskLevel `protobuf:"varint,15,opt,name=severity,proto3,enum=teleport.summarizer.v1.RiskLevel"` - xxx_hidden_SessionEndEvent *structpb.Struct `protobuf:"bytes,16,opt,name=session_end_event,json=sessionEndEvent,proto3"` - xxx_hidden_HostId string `protobuf:"bytes,17,opt,name=host_id,json=hostId,proto3"` - xxx_hidden_Embeddings *[]*EmbeddingChunk `protobuf:"bytes,18,rep,name=embeddings,proto3"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"opaque.v1"` + xxx_hidden_SessionId string `protobuf:"bytes,1,opt,name=session_id,json=sessionId,proto3"` + xxx_hidden_Kind string `protobuf:"bytes,2,opt,name=kind,proto3"` + xxx_hidden_Username string `protobuf:"bytes,3,opt,name=username,proto3"` + xxx_hidden_SessionStart *timestamppb.Timestamp `protobuf:"bytes,4,opt,name=session_start,json=sessionStart,proto3"` + xxx_hidden_SessionEnd *timestamppb.Timestamp `protobuf:"bytes,5,opt,name=session_end,json=sessionEnd,proto3"` + xxx_hidden_UserTraits *structpb.Struct `protobuf:"bytes,6,opt,name=user_traits,json=userTraits,proto3"` + xxx_hidden_UserRoles []string `protobuf:"bytes,7,rep,name=user_roles,json=userRoles,proto3"` + xxx_hidden_AccessRequestIds []string `protobuf:"bytes,8,rep,name=access_request_ids,json=accessRequestIds,proto3"` + xxx_hidden_Participants []string `protobuf:"bytes,9,rep,name=participants,proto3"` + xxx_hidden_ResourceKind string `protobuf:"bytes,10,opt,name=resource_kind,json=resourceKind,proto3"` + xxx_hidden_ResourceLabels map[string]string `protobuf:"bytes,11,rep,name=resource_labels,json=resourceLabels,proto3" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + xxx_hidden_ResourceId string `protobuf:"bytes,12,opt,name=resource_id,json=resourceId,proto3"` + xxx_hidden_ResourceName string `protobuf:"bytes,13,opt,name=resource_name,json=resourceName,proto3"` + xxx_hidden_ResourceProperties *ResourceProperties `protobuf:"bytes,14,opt,name=resource_properties,json=resourceProperties,proto3"` + xxx_hidden_Severity v1.RiskLevel `protobuf:"varint,15,opt,name=severity,proto3,enum=teleport.summarizer.v1.RiskLevel"` + xxx_hidden_SessionEndEvent *structpb.Struct `protobuf:"bytes,16,opt,name=session_end_event,json=sessionEndEvent,proto3"` + xxx_hidden_HostId string `protobuf:"bytes,17,opt,name=host_id,json=hostId,proto3"` + xxx_hidden_Embeddings *[]*EmbeddingChunk `protobuf:"bytes,18,rep,name=embeddings,proto3"` + xxx_hidden_NeedsFurtherReviewReasons []v1.NeedsReviewReason `protobuf:"varint,19,rep,packed,name=needs_further_review_reasons,json=needsFurtherReviewReasons,proto3,enum=teleport.summarizer.v1.NeedsReviewReason"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *StoreSessionSummaryRequest) Reset() { @@ -2519,6 +2553,13 @@ func (x *StoreSessionSummaryRequest) GetEmbeddings() []*EmbeddingChunk { return nil } +func (x *StoreSessionSummaryRequest) GetNeedsFurtherReviewReasons() []v1.NeedsReviewReason { + if x != nil { + return x.xxx_hidden_NeedsFurtherReviewReasons + } + return nil +} + func (x *StoreSessionSummaryRequest) SetSessionId(v string) { x.xxx_hidden_SessionId = v } @@ -2591,6 +2632,10 @@ func (x *StoreSessionSummaryRequest) SetEmbeddings(v []*EmbeddingChunk) { x.xxx_hidden_Embeddings = &v } +func (x *StoreSessionSummaryRequest) SetNeedsFurtherReviewReasons(v []v1.NeedsReviewReason) { + x.xxx_hidden_NeedsFurtherReviewReasons = v +} + func (x *StoreSessionSummaryRequest) HasSessionStart() bool { if x == nil { return false @@ -2693,6 +2738,10 @@ type StoreSessionSummaryRequest_builder struct { // embeddings are the semantic vector embeddings of the session summary, // used for similarity-based search queries. Embeddings []*EmbeddingChunk + // needs_further_review_reasons contains the reasons why this session needs + // further review, as determined by the summarizer. Empty when the session was + // not flagged. + NeedsFurtherReviewReasons []v1.NeedsReviewReason } func (b0 StoreSessionSummaryRequest_builder) Build() *StoreSessionSummaryRequest { @@ -2717,6 +2766,7 @@ func (b0 StoreSessionSummaryRequest_builder) Build() *StoreSessionSummaryRequest x.xxx_hidden_SessionEndEvent = b.SessionEndEvent x.xxx_hidden_HostId = b.HostId x.xxx_hidden_Embeddings = &b.Embeddings + x.xxx_hidden_NeedsFurtherReviewReasons = b.NeedsFurtherReviewReasons return m0 } @@ -3123,7 +3173,7 @@ const file_accessgraph_v1_session_search_proto_rawDesc = "" + "fetch_more\x18\x02 \x01(\v27.accessgraph.v1.SearchSessionSummariesRequest.FetchMoreH\x00R\tfetchMore\x1a0\n" + "\tFetchMore\x12#\n" + "\rmax_summaries\x18\x01 \x01(\rR\fmaxSummariesB\t\n" + - "\apayload\"\xa6\a\n" + + "\apayload\"\x9f\b\n" + "\x1cSearchSessionSummariesParams\x129\n" + "\n" + "start_time\x18\x01 \x01(\v2\x1a.google.protobuf.TimestampR\tstartTime\x125\n" + @@ -3143,7 +3193,8 @@ const file_accessgraph_v1_session_search_proto_rawDesc = "" + "\rmax_summaries\x18\r \x01(\rR\fmaxSummaries\x12!\n" + "\fresume_token\x18\x0e \x01(\tR\vresumeToken\x12;\n" + "\vsearch_mode\x18\x0f \x01(\x0e2\x1a.accessgraph.v1.SearchModeR\n" + - "searchMode\x1aA\n" + + "searchMode\x12w\n" + + "#filter_needs_further_review_reasons\x18\x10 \x03(\x0e2).teleport.summarizer.v1.NeedsReviewReasonR\x1ffilterNeedsFurtherReviewReasons\x1aA\n" + "\x13ResourceLabelsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B\v\n" + @@ -3197,7 +3248,7 @@ const file_accessgraph_v1_session_search_proto_rawDesc = "" + "\apayload\"{\n" + "\x14SummaryAndCheckpoint\x128\n" + "\asummary\x18\x01 \x01(\v2\x1e.accessgraph.v1.SessionSummaryR\asummary\x12)\n" + - "\x10checkpoint_token\x18\x02 \x01(\tR\x0fcheckpointToken\"\x85\a\n" + + "\x10checkpoint_token\x18\x02 \x01(\tR\x0fcheckpointToken\"\xf1\a\n" + "\x0eSessionSummary\x12\x1d\n" + "\n" + "session_id\x18\x01 \x01(\tR\tsessionId\x12\x12\n" + @@ -3221,10 +3272,11 @@ const file_accessgraph_v1_session_search_proto_rawDesc = "" + "\x11session_end_event\x18\x0f \x01(\v2\x17.google.protobuf.StructR\x0fsessionEndEvent\x12;\n" + "\vsession_end\x18\x10 \x01(\v2\x1a.google.protobuf.TimestampR\n" + "sessionEnd\x12\x17\n" + - "\ahost_id\x18\x11 \x01(\tR\x06hostId\x1aA\n" + + "\ahost_id\x18\x11 \x01(\tR\x06hostId\x12j\n" + + "\x1cneeds_further_review_reasons\x18\x12 \x03(\x0e2).teleport.summarizer.v1.NeedsReviewReasonR\x19needsFurtherReviewReasons\x1aA\n" + "\x13ResourceLabelsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xdd\a\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xc9\b\n" + "\x1aStoreSessionSummaryRequest\x12\x1d\n" + "\n" + "session_id\x18\x01 \x01(\tR\tsessionId\x12\x12\n" + @@ -3251,7 +3303,8 @@ const file_accessgraph_v1_session_search_proto_rawDesc = "" + "\ahost_id\x18\x11 \x01(\tR\x06hostId\x12>\n" + "\n" + "embeddings\x18\x12 \x03(\v2\x1e.accessgraph.v1.EmbeddingChunkR\n" + - "embeddings\x1aA\n" + + "embeddings\x12j\n" + + "\x1cneeds_further_review_reasons\x18\x13 \x03(\x0e2).teleport.summarizer.v1.NeedsReviewReasonR\x19needsFurtherReviewReasons\x1aA\n" + "\x13ResourceLabelsEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"~\n" + @@ -3316,7 +3369,8 @@ var file_accessgraph_v1_session_search_proto_goTypes = []any{ nil, // 23: accessgraph.v1.StoreSessionSummaryRequest.ResourceLabelsEntry (*timestamppb.Timestamp)(nil), // 24: google.protobuf.Timestamp (v1.RiskLevel)(0), // 25: teleport.summarizer.v1.RiskLevel - (*structpb.Struct)(nil), // 26: google.protobuf.Struct + (v1.NeedsReviewReason)(0), // 26: teleport.summarizer.v1.NeedsReviewReason + (*structpb.Struct)(nil), // 27: google.protobuf.Struct } var file_accessgraph_v1_session_search_proto_depIdxs = []int32{ 4, // 0: accessgraph.v1.SearchSessionSummariesRequest.search_params:type_name -> accessgraph.v1.SearchSessionSummariesParams @@ -3328,41 +3382,44 @@ var file_accessgraph_v1_session_search_proto_depIdxs = []int32{ 25, // 6: accessgraph.v1.SearchSessionSummariesParams.severity:type_name -> teleport.summarizer.v1.RiskLevel 5, // 7: accessgraph.v1.SearchSessionSummariesParams.search_queries:type_name -> accessgraph.v1.EmbeddedQuery 0, // 8: accessgraph.v1.SearchSessionSummariesParams.search_mode:type_name -> accessgraph.v1.SearchMode - 7, // 9: accessgraph.v1.ResourceProperties.ssh:type_name -> accessgraph.v1.SSHProperties - 8, // 10: accessgraph.v1.ResourceProperties.kubernetes:type_name -> accessgraph.v1.KubernetesProperties - 9, // 11: accessgraph.v1.ResourceProperties.database:type_name -> accessgraph.v1.DatabaseProperties - 10, // 12: accessgraph.v1.ResourceProperties.desktop:type_name -> accessgraph.v1.DesktopProperties - 1, // 13: accessgraph.v1.DesktopProperties.type:type_name -> accessgraph.v1.DesktopType - 12, // 14: accessgraph.v1.SearchSessionSummariesResponse.summary:type_name -> accessgraph.v1.SummaryAndCheckpoint - 21, // 15: accessgraph.v1.SearchSessionSummariesResponse.batch_complete:type_name -> accessgraph.v1.SearchSessionSummariesResponse.BatchComplete - 13, // 16: accessgraph.v1.SummaryAndCheckpoint.summary:type_name -> accessgraph.v1.SessionSummary - 24, // 17: accessgraph.v1.SessionSummary.session_start:type_name -> google.protobuf.Timestamp - 26, // 18: accessgraph.v1.SessionSummary.user_traits:type_name -> google.protobuf.Struct - 22, // 19: accessgraph.v1.SessionSummary.resource_labels:type_name -> accessgraph.v1.SessionSummary.ResourceLabelsEntry - 6, // 20: accessgraph.v1.SessionSummary.resource_properties:type_name -> accessgraph.v1.ResourceProperties - 25, // 21: accessgraph.v1.SessionSummary.severity:type_name -> teleport.summarizer.v1.RiskLevel - 26, // 22: accessgraph.v1.SessionSummary.session_end_event:type_name -> google.protobuf.Struct - 24, // 23: accessgraph.v1.SessionSummary.session_end:type_name -> google.protobuf.Timestamp - 24, // 24: accessgraph.v1.StoreSessionSummaryRequest.session_start:type_name -> google.protobuf.Timestamp - 24, // 25: accessgraph.v1.StoreSessionSummaryRequest.session_end:type_name -> google.protobuf.Timestamp - 26, // 26: accessgraph.v1.StoreSessionSummaryRequest.user_traits:type_name -> google.protobuf.Struct - 23, // 27: accessgraph.v1.StoreSessionSummaryRequest.resource_labels:type_name -> accessgraph.v1.StoreSessionSummaryRequest.ResourceLabelsEntry - 6, // 28: accessgraph.v1.StoreSessionSummaryRequest.resource_properties:type_name -> accessgraph.v1.ResourceProperties - 25, // 29: accessgraph.v1.StoreSessionSummaryRequest.severity:type_name -> teleport.summarizer.v1.RiskLevel - 26, // 30: accessgraph.v1.StoreSessionSummaryRequest.session_end_event:type_name -> google.protobuf.Struct - 15, // 31: accessgraph.v1.StoreSessionSummaryRequest.embeddings:type_name -> accessgraph.v1.EmbeddingChunk - 2, // 32: accessgraph.v1.IsSessionSearchEnabledResponse.availability:type_name -> accessgraph.v1.SessionSearchAvailability - 3, // 33: accessgraph.v1.SessionRecordingService.SearchSessionSummaries:input_type -> accessgraph.v1.SearchSessionSummariesRequest - 14, // 34: accessgraph.v1.SessionRecordingService.StoreSessionSummary:input_type -> accessgraph.v1.StoreSessionSummaryRequest - 17, // 35: accessgraph.v1.SessionRecordingService.IsSessionSearchEnabled:input_type -> accessgraph.v1.IsSessionSearchEnabledRequest - 11, // 36: accessgraph.v1.SessionRecordingService.SearchSessionSummaries:output_type -> accessgraph.v1.SearchSessionSummariesResponse - 16, // 37: accessgraph.v1.SessionRecordingService.StoreSessionSummary:output_type -> accessgraph.v1.StoreSessionSummaryResponse - 18, // 38: accessgraph.v1.SessionRecordingService.IsSessionSearchEnabled:output_type -> accessgraph.v1.IsSessionSearchEnabledResponse - 36, // [36:39] is the sub-list for method output_type - 33, // [33:36] is the sub-list for method input_type - 33, // [33:33] is the sub-list for extension type_name - 33, // [33:33] is the sub-list for extension extendee - 0, // [0:33] is the sub-list for field type_name + 26, // 9: accessgraph.v1.SearchSessionSummariesParams.filter_needs_further_review_reasons:type_name -> teleport.summarizer.v1.NeedsReviewReason + 7, // 10: accessgraph.v1.ResourceProperties.ssh:type_name -> accessgraph.v1.SSHProperties + 8, // 11: accessgraph.v1.ResourceProperties.kubernetes:type_name -> accessgraph.v1.KubernetesProperties + 9, // 12: accessgraph.v1.ResourceProperties.database:type_name -> accessgraph.v1.DatabaseProperties + 10, // 13: accessgraph.v1.ResourceProperties.desktop:type_name -> accessgraph.v1.DesktopProperties + 1, // 14: accessgraph.v1.DesktopProperties.type:type_name -> accessgraph.v1.DesktopType + 12, // 15: accessgraph.v1.SearchSessionSummariesResponse.summary:type_name -> accessgraph.v1.SummaryAndCheckpoint + 21, // 16: accessgraph.v1.SearchSessionSummariesResponse.batch_complete:type_name -> accessgraph.v1.SearchSessionSummariesResponse.BatchComplete + 13, // 17: accessgraph.v1.SummaryAndCheckpoint.summary:type_name -> accessgraph.v1.SessionSummary + 24, // 18: accessgraph.v1.SessionSummary.session_start:type_name -> google.protobuf.Timestamp + 27, // 19: accessgraph.v1.SessionSummary.user_traits:type_name -> google.protobuf.Struct + 22, // 20: accessgraph.v1.SessionSummary.resource_labels:type_name -> accessgraph.v1.SessionSummary.ResourceLabelsEntry + 6, // 21: accessgraph.v1.SessionSummary.resource_properties:type_name -> accessgraph.v1.ResourceProperties + 25, // 22: accessgraph.v1.SessionSummary.severity:type_name -> teleport.summarizer.v1.RiskLevel + 27, // 23: accessgraph.v1.SessionSummary.session_end_event:type_name -> google.protobuf.Struct + 24, // 24: accessgraph.v1.SessionSummary.session_end:type_name -> google.protobuf.Timestamp + 26, // 25: accessgraph.v1.SessionSummary.needs_further_review_reasons:type_name -> teleport.summarizer.v1.NeedsReviewReason + 24, // 26: accessgraph.v1.StoreSessionSummaryRequest.session_start:type_name -> google.protobuf.Timestamp + 24, // 27: accessgraph.v1.StoreSessionSummaryRequest.session_end:type_name -> google.protobuf.Timestamp + 27, // 28: accessgraph.v1.StoreSessionSummaryRequest.user_traits:type_name -> google.protobuf.Struct + 23, // 29: accessgraph.v1.StoreSessionSummaryRequest.resource_labels:type_name -> accessgraph.v1.StoreSessionSummaryRequest.ResourceLabelsEntry + 6, // 30: accessgraph.v1.StoreSessionSummaryRequest.resource_properties:type_name -> accessgraph.v1.ResourceProperties + 25, // 31: accessgraph.v1.StoreSessionSummaryRequest.severity:type_name -> teleport.summarizer.v1.RiskLevel + 27, // 32: accessgraph.v1.StoreSessionSummaryRequest.session_end_event:type_name -> google.protobuf.Struct + 15, // 33: accessgraph.v1.StoreSessionSummaryRequest.embeddings:type_name -> accessgraph.v1.EmbeddingChunk + 26, // 34: accessgraph.v1.StoreSessionSummaryRequest.needs_further_review_reasons:type_name -> teleport.summarizer.v1.NeedsReviewReason + 2, // 35: accessgraph.v1.IsSessionSearchEnabledResponse.availability:type_name -> accessgraph.v1.SessionSearchAvailability + 3, // 36: accessgraph.v1.SessionRecordingService.SearchSessionSummaries:input_type -> accessgraph.v1.SearchSessionSummariesRequest + 14, // 37: accessgraph.v1.SessionRecordingService.StoreSessionSummary:input_type -> accessgraph.v1.StoreSessionSummaryRequest + 17, // 38: accessgraph.v1.SessionRecordingService.IsSessionSearchEnabled:input_type -> accessgraph.v1.IsSessionSearchEnabledRequest + 11, // 39: accessgraph.v1.SessionRecordingService.SearchSessionSummaries:output_type -> accessgraph.v1.SearchSessionSummariesResponse + 16, // 40: accessgraph.v1.SessionRecordingService.StoreSessionSummary:output_type -> accessgraph.v1.StoreSessionSummaryResponse + 18, // 41: accessgraph.v1.SessionRecordingService.IsSessionSearchEnabled:output_type -> accessgraph.v1.IsSessionSearchEnabledResponse + 39, // [39:42] is the sub-list for method output_type + 36, // [36:39] is the sub-list for method input_type + 36, // [36:36] is the sub-list for extension type_name + 36, // [36:36] is the sub-list for extension extendee + 0, // [0:36] is the sub-list for field type_name } func init() { file_accessgraph_v1_session_search_proto_init() } diff --git a/proto/accessgraph/v1/session_search.proto b/proto/accessgraph/v1/session_search.proto index b2675ccafe4..95592329cd7 100644 --- a/proto/accessgraph/v1/session_search.proto +++ b/proto/accessgraph/v1/session_search.proto @@ -197,6 +197,10 @@ message SearchSessionSummariesParams { // the full hybrid pipeline. SEARCH_MODE_KEYWORD_ONLY uses only full-text // matching; SEARCH_MODE_EMBEDDING_ONLY uses only vector similarity. SearchMode search_mode = 15; + + // filter_needs_further_review_reasons restricts results to sessions that have + // at least one of the given review reasons. An empty list disables this filter. + repeated teleport.summarizer.v1.NeedsReviewReason filter_needs_further_review_reasons = 16; } // EmbeddedQuery pairs a free-text query with its pre-computed vector embedding, @@ -410,6 +414,11 @@ message SessionSummary { // host_id is the unique identifier of the host where the session occurred. string host_id = 17; + + // needs_further_review_reasons contains the reasons why this session was + // flagged as requiring further human review by the summarizer. Empty when the + // session was not flagged. + repeated teleport.summarizer.v1.NeedsReviewReason needs_further_review_reasons = 18; } // StoreSessionSummaryRequest is sent to store a session summary along with @@ -477,6 +486,11 @@ message StoreSessionSummaryRequest { // embeddings are the semantic vector embeddings of the session summary, // used for similarity-based search queries. repeated EmbeddingChunk embeddings = 18; + + // needs_further_review_reasons contains the reasons why this session needs + // further review, as determined by the summarizer. Empty when the session was + // not flagged. + repeated teleport.summarizer.v1.NeedsReviewReason needs_further_review_reasons = 19; } // EmbeddingChunk represents a semantic vector embedding for a text chunk from the diff --git a/tool/tctl/common/recordings/detail.go b/tool/tctl/common/recordings/detail.go index dc3e626b329..b9d66d576ae 100644 --- a/tool/tctl/common/recordings/detail.go +++ b/tool/tctl/common/recordings/detail.go @@ -76,6 +76,13 @@ func renderDetail(s *sessionsearchv1pb.SessionSummary, p palette) string { } } field("Severity", formatSeverityColored(s.GetSeverity())) + if reasons := s.GetNeedsFurtherReviewReasons(); len(reasons) > 0 { + labels := make([]string, len(reasons)) + for i, r := range reasons { + labels[i] = formatReviewReason(r) + } + field("Needs Review", strings.Join(labels, "; ")) + } b.WriteString("\n") section("User") @@ -148,6 +155,24 @@ func severityColor(level summarizerv1pb.RiskLevel) lipgloss.TerminalColor { } } +// formatReviewReason converts a NeedsReviewReason to a human-readable string. +func formatReviewReason(r summarizerv1pb.NeedsReviewReason) string { + switch r { + case summarizerv1pb.NeedsReviewReason_NEEDS_REVIEW_REASON_TOO_LARGE: + return "too large to fully analyze" + case summarizerv1pb.NeedsReviewReason_NEEDS_REVIEW_REASON_COMMAND_ANALYSIS_FAILED: + return "command analysis failed" + case summarizerv1pb.NeedsReviewReason_NEEDS_REVIEW_REASON_FAILED_TO_FETCH_ACCESS_REQUEST: + return "failed to fetch access request" + case summarizerv1pb.NeedsReviewReason_NEEDS_REVIEW_REASON_ACCESS_REQUEST_RESOURCE_MISMATCH: + return "access request resource mismatch" + case summarizerv1pb.NeedsReviewReason_NEEDS_REVIEW_REASON_OUTPUT_NOT_FULLY_CAPTURED: + return "output not fully captured" + default: + return strings.ToLower(strings.TrimPrefix(r.String(), "NEEDS_REVIEW_REASON_")) + } +} + // formatSeverity converts a RiskLevel to a short uppercase label. func formatSeverity(level summarizerv1pb.RiskLevel) string { return strings.TrimPrefix(level.String(), "RISK_LEVEL_") diff --git a/tool/tctl/common/recordings/item.go b/tool/tctl/common/recordings/item.go index a95990b0400..d98e4ba3cbf 100644 --- a/tool/tctl/common/recordings/item.go +++ b/tool/tctl/common/recordings/item.go @@ -46,13 +46,17 @@ func (i sessionItem) Title() string { return fmt.Sprintf("[%s] %s", strings.ToUpper(sanitize(i.s.GetKind())), name) } -// Description returns the secondary row text: "start • username • severity". +// Description returns the secondary row text: "start • username • severity [• ⚠ needs review]". func (i sessionItem) Description() string { start := "-" if ts := i.s.GetSessionStart(); ts != nil { start = ts.AsTime().UTC().Format("2006-01-02 15:04 UTC") } - return fmt.Sprintf("%s • %s • %s", start, sanitize(i.s.GetUsername()), formatSeverity(i.s.GetSeverity())) + desc := fmt.Sprintf("%s • %s • %s", start, sanitize(i.s.GetUsername()), formatSeverity(i.s.GetSeverity())) + if len(i.s.GetNeedsFurtherReviewReasons()) > 0 { + desc += " • ⚠ needs review" + } + return desc } // FilterValue is used by the list's built-in filter. diff --git a/tool/tctl/common/recordings_command.go b/tool/tctl/common/recordings_command.go index a348b20c018..5110d1bda9f 100644 --- a/tool/tctl/common/recordings_command.go +++ b/tool/tctl/common/recordings_command.go @@ -23,8 +23,11 @@ import ( "errors" "fmt" "io" + "maps" "os" "path/filepath" + "slices" + "sort" "strings" "github.com/alecthomas/kingpin/v2" @@ -127,6 +130,9 @@ type RecordingsCommand struct { searchMode string // searchResumeToken resumes a previous JSON/YAML search from a truncated result set. searchResumeToken string + // searchReviewReasons filters results to sessions that have at least one of + // the specified review reasons. An empty slice disables this filter. + searchReviewReasons []string // stdout allows to switch standard output source for resource command. Used in tests. stdout io.Writer @@ -169,6 +175,8 @@ func (c *RecordingsCommand) Initialize(app *kingpin.Application, t *tctlcfg.Glob c.recordingsSearch.Flag("limit", "Maximum number of results to return.").Default(defaults.TshTctlSessionListLimit).Uint32Var(&c.searchLimit) c.recordingsSearch.Flag("format", defaults.FormatFlagDescription(defaults.DefaultFormats...)+". Defaults to 'text'.").Default(teleport.Text).StringVar(&c.searchFormat) c.recordingsSearch.Flag("resume-token", "Resume a previous JSON/YAML search from a truncated result set (token printed to stderr when results are truncated).").StringVar(&c.searchResumeToken) + c.recordingsSearch.Flag("review-reason", "Filter to sessions that have at least one of the given review reasons. Use 'any' to match any flagged session. Can be specified multiple times."). + EnumsVar(&c.searchReviewReasons, append(reviewReasonFlagOptions(), "any")...) c.recordingsEncryption.Initialize(recordings, c.stdout) @@ -340,6 +348,31 @@ func (c *RecordingsCommand) SearchRecordings(ctx context.Context, tc *authclient } req.SetSearchMode(mode) } + if len(c.searchReviewReasons) > 0 { + var reasons []summarizerv1pb.NeedsReviewReason + hasAny := false + for _, s := range c.searchReviewReasons { + if s == "any" { + hasAny = true + break + } + } + if hasAny { + for _, k := range reviewReasonFlagOptions() { + reasons = append(reasons, reviewReasonNames[k]) + } + } else { + reasons = make([]summarizerv1pb.NeedsReviewReason, 0, len(c.searchReviewReasons)) + for _, s := range c.searchReviewReasons { + r, err := parseReviewReason(s) + if err != nil { + return trace.Wrap(err) + } + reasons = append(reasons, r) + } + } + req.SetFilterNeedsFurtherReviewReasons(reasons) + } // fetcher resends the full request with the batch token set. It is used for // both the first page (empty or resume token) and subsequent "load more" @@ -593,6 +626,37 @@ func parseSeverity(s string) (summarizerv1pb.RiskLevel, error) { } } +// reviewReasonNames maps every valid --review-reason CLI value to its proto +// enum counterpart. It is the single source of truth: the flag validates +// against its keys, and parseReviewReason converts using its values. +// A parity test asserts that exactly one entry exists per non-UNSPECIFIED +// NeedsReviewReason enum value. +var reviewReasonNames = map[string]summarizerv1pb.NeedsReviewReason{ + "access_request_resource_mismatch": summarizerv1pb.NeedsReviewReason_NEEDS_REVIEW_REASON_ACCESS_REQUEST_RESOURCE_MISMATCH, + "command_analysis_failed": summarizerv1pb.NeedsReviewReason_NEEDS_REVIEW_REASON_COMMAND_ANALYSIS_FAILED, + "failed_to_fetch_access_request": summarizerv1pb.NeedsReviewReason_NEEDS_REVIEW_REASON_FAILED_TO_FETCH_ACCESS_REQUEST, + "output_not_fully_captured": summarizerv1pb.NeedsReviewReason_NEEDS_REVIEW_REASON_OUTPUT_NOT_FULLY_CAPTURED, + "too_large": summarizerv1pb.NeedsReviewReason_NEEDS_REVIEW_REASON_TOO_LARGE, + "classifier_matched": summarizerv1pb.NeedsReviewReason_NEEDS_REVIEW_REASON_CLASSIFIER_MATCHED, +} + +// reviewReasonFlagOptions returns the valid --review-reason values in +// alphabetical order, used in flag help text and error messages. +func reviewReasonFlagOptions() []string { + keys := slices.Collect(maps.Keys(reviewReasonNames)) + sort.Strings(keys) + return keys +} + +// parseReviewReason converts a validated --review-reason string to its proto +// enum value using reviewReasonNames. +func parseReviewReason(s string) (summarizerv1pb.NeedsReviewReason, error) { + if r, ok := reviewReasonNames[s]; ok { + return r, nil + } + return 0, trace.BadParameter("invalid --review-reason %q: must be one of %s", s, strings.Join(reviewReasonFlagOptions(), ", ")) +} + // createFileWriter creates a file-based session event writer that outputs to a file. func createFileWriter(ctx context.Context, sessionID session.ID, outputDir string) (*events.SessionWriter, error) { fileStreamer, err := filesessions.NewStreamer( diff --git a/tool/tctl/common/recordings_command_test.go b/tool/tctl/common/recordings_command_test.go index c3402e2bd1b..71c4c35f442 100644 --- a/tool/tctl/common/recordings_command_test.go +++ b/tool/tctl/common/recordings_command_test.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/require" + summarizerv1pb "github.com/gravitational/teleport/api/gen/proto/go/teleport/summarizer/v1" "github.com/gravitational/teleport/lib/service/servicecfg" "github.com/gravitational/teleport/lib/utils" tctlcfg "github.com/gravitational/teleport/tool/tctl/common/config" @@ -52,6 +53,18 @@ func TestRecordingsSearchResourcePropertyFlags(t *testing.T) { require.Equal(t, "postgres", c.searchDatabaseName) } +// TestReviewReasonEnumParity fails when a new NeedsReviewReason enum value is +// added to the proto but not to reviewReasonNames, preventing silent omission +// from the --review-reason flag. +func TestReviewReasonEnumParity(t *testing.T) { + t.Parallel() + // NeedsReviewReason_value maps every proto name to its int32; subtract 1 + // for UNSPECIFIED, which is not a valid CLI filter value. + wantCount := len(summarizerv1pb.NeedsReviewReason_value) - 1 + require.Len(t, reviewReasonNames, wantCount, + "reviewReasonNames must have one entry per non-UNSPECIFIED NeedsReviewReason value; add or remove the missing entries") +} + func TestBuildSearchResourceProperties(t *testing.T) { t.Run("none", func(t *testing.T) { got, err := (&RecordingsCommand{}).buildSearchResourceProperties()