diff --git a/apps/cnquery/cmd/login.go b/apps/cnquery/cmd/login.go index f645e110a8..b04e282dbc 100644 --- a/apps/cnquery/cmd/login.go +++ b/apps/cnquery/cmd/login.go @@ -23,6 +23,7 @@ import ( func init() { rootCmd.AddCommand(LoginCmd) LoginCmd.Flags().StringP("token", "t", "", "Set a client registration token.") + LoginCmd.Flags().StringToString("annotation", nil, "Set the client annotations.") LoginCmd.Flags().String("name", "", "Set asset name.") LoginCmd.Flags().String("api-endpoint", "", "Set the Mondoo API endpoint.") } @@ -47,11 +48,12 @@ You remain logged in until you explicitly log out using the 'logout' subcommand. }, Run: func(cmd *cobra.Command, args []string) { token, _ := cmd.Flags().GetString("token") - register(token) + annotations, _ := cmd.Flags().GetStringToString("annotation") + register(token, annotations) }, } -func register(token string) { +func register(token string, annotations map[string]string) { var err error var credential *upstream.ServiceAccountCredentials @@ -142,7 +144,7 @@ func register(token string) { viper.Set("mrn", confirmation.Credential.Mrn) viper.Set("private_key", confirmation.Credential.PrivateKey) viper.Set("certificate", confirmation.Credential.Certificate) - + viper.Set("annotations", annotations) credential = confirmation.Credential } else { // try to read local options diff --git a/apps/cnquery/cmd/scan.go b/apps/cnquery/cmd/scan.go index 81bd023a25..5a98416d07 100644 --- a/apps/cnquery/cmd/scan.go +++ b/apps/cnquery/cmd/scan.go @@ -146,6 +146,8 @@ type scanConfig struct { Props map[string]string Bundle *explorer.Bundle runtime *providers.Runtime + // annotations that will be applied to all discovered assets + annotations map[string]string IsIncognito bool } @@ -167,6 +169,20 @@ func getCobraScanConfig(cmd *cobra.Command, runtime *providers.Runtime, cliRes * if err != nil { log.Fatal().Err(err).Msg("failed to parse inventory") } + + annotations, err := cmd.Flags().GetStringToString("annotation") + if err != nil { + log.Fatal().Err(err).Msg("failed to parse annotations") + } + + // merge the config and the user-provided annotations with the latter having precedence + optAnnotations := opts.Annotations + if optAnnotations == nil { + optAnnotations = map[string]string{} + } + for k, v := range annotations { + optAnnotations[k] = v + } conf := scanConfig{ Features: opts.GetFeatures(), IsIncognito: viper.GetBool("incognito"), @@ -175,6 +191,7 @@ func getCobraScanConfig(cmd *cobra.Command, runtime *providers.Runtime, cliRes * QueryPackNames: viper.GetStringSlice("querypacks"), Props: props, runtime: runtime, + annotations: optAnnotations, } // if users want to get more information on available output options, @@ -292,6 +309,7 @@ func RunScan(config *scanConfig) (*explorer.ReportCollection, error) { Bundle: config.Bundle, QueryPackFilters: config.QueryPackNames, Props: config.Props, + Annotations: config.annotations, }) } return scanner.Run( @@ -301,6 +319,7 @@ func RunScan(config *scanConfig) (*explorer.ReportCollection, error) { Bundle: config.Bundle, QueryPackFilters: config.QueryPackNames, Props: config.Props, + Annotations: config.annotations, }) } diff --git a/cli/config/config.go b/cli/config/config.go index 8bedc9c5c9..ac51efff89 100644 --- a/cli/config/config.go +++ b/cli/config/config.go @@ -199,6 +199,9 @@ type CommonOpts struct { // labels that will be applied to all assets Labels map[string]string `json:"labels,omitempty" mapstructure:"labels"` + + // annotations that will be applied to all assets + Annotations map[string]string `json:"annotations,omitempty" mapstructure:"annotations"` } type CliConfigAuthentication struct { diff --git a/explorer/scan/cnquery_explorer_scan.pb.go b/explorer/scan/cnquery_explorer_scan.pb.go index fbfd4be779..0046d29f6e 100644 --- a/explorer/scan/cnquery_explorer_scan.pb.go +++ b/explorer/scan/cnquery_explorer_scan.pb.go @@ -35,6 +35,8 @@ type Job struct { DoRecord bool `protobuf:"varint,20,opt,name=do_record,json=doRecord,proto3" json:"do_record,omitempty"` QueryPackFilters []string `protobuf:"bytes,21,rep,name=query_pack_filters,json=queryPackFilters,proto3" json:"query_pack_filters,omitempty"` Props map[string]string `protobuf:"bytes,22,rep,name=props,proto3" json:"props,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // annotations that will be applied to all assets in the job + Annotations map[string]string `protobuf:"bytes,23,rep,name=annotations,proto3" json:"annotations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *Job) Reset() { @@ -104,6 +106,13 @@ func (x *Job) GetProps() map[string]string { return nil } +func (x *Job) GetAnnotations() map[string]string { + if x != nil { + return x.Annotations + } + return nil +} + var File_cnquery_explorer_scan_proto protoreflect.FileDescriptor var file_cnquery_explorer_scan_proto_rawDesc = []byte{ @@ -115,7 +124,7 @@ var file_cnquery_explorer_scan_proto_rawDesc = []byte{ 0x2f, 0x69, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x72, 0x2f, 0x63, 0x6e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x5f, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x22, 0xb8, 0x02, 0x0a, 0x03, 0x4a, 0x6f, 0x62, 0x12, 0x3d, 0x0a, 0x09, 0x69, 0x6e, 0x76, + 0x6f, 0x22, 0xc7, 0x03, 0x0a, 0x03, 0x4a, 0x6f, 0x62, 0x12, 0x3d, 0x0a, 0x09, 0x69, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x63, 0x6e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x2e, 0x76, 0x31, 0x2e, 0x49, 0x6e, 0x76, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x79, 0x52, 0x09, 0x69, @@ -131,13 +140,22 @@ var file_cnquery_explorer_scan_proto_rawDesc = []byte{ 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x63, 0x6e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x61, 0x6e, 0x2e, 0x4a, 0x6f, 0x62, 0x2e, 0x50, 0x72, 0x6f, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x05, 0x70, 0x72, 0x6f, - 0x70, 0x73, 0x1a, 0x38, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x25, 0x5a, 0x23, - 0x67, 0x6f, 0x2e, 0x6d, 0x6f, 0x6e, 0x64, 0x6f, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6e, - 0x71, 0x75, 0x65, 0x72, 0x79, 0x2f, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x72, 0x2f, 0x73, - 0x63, 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x73, 0x12, 0x4d, 0x0a, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x18, 0x17, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x63, 0x6e, 0x71, 0x75, 0x65, 0x72, + 0x79, 0x2e, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x72, 0x2e, 0x73, 0x63, 0x61, 0x6e, 0x2e, + 0x4a, 0x6f, 0x62, 0x2e, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x1a, 0x38, 0x0a, 0x0a, 0x50, 0x72, 0x6f, 0x70, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x3e, 0x0a, 0x10, 0x41, + 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x25, 0x5a, 0x23, 0x67, + 0x6f, 0x2e, 0x6d, 0x6f, 0x6e, 0x64, 0x6f, 0x6f, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6e, 0x71, + 0x75, 0x65, 0x72, 0x79, 0x2f, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x72, 0x2f, 0x73, 0x63, + 0x61, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -152,22 +170,24 @@ func file_cnquery_explorer_scan_proto_rawDescGZIP() []byte { return file_cnquery_explorer_scan_proto_rawDescData } -var file_cnquery_explorer_scan_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_cnquery_explorer_scan_proto_msgTypes = make([]protoimpl.MessageInfo, 3) var file_cnquery_explorer_scan_proto_goTypes = []interface{}{ (*Job)(nil), // 0: cnquery.explorer.scan.Job nil, // 1: cnquery.explorer.scan.Job.PropsEntry - (*inventory.Inventory)(nil), // 2: cnquery.providers.v1.Inventory - (*explorer.Bundle)(nil), // 3: cnquery.explorer.Bundle + nil, // 2: cnquery.explorer.scan.Job.AnnotationsEntry + (*inventory.Inventory)(nil), // 3: cnquery.providers.v1.Inventory + (*explorer.Bundle)(nil), // 4: cnquery.explorer.Bundle } var file_cnquery_explorer_scan_proto_depIdxs = []int32{ - 2, // 0: cnquery.explorer.scan.Job.inventory:type_name -> cnquery.providers.v1.Inventory - 3, // 1: cnquery.explorer.scan.Job.bundle:type_name -> cnquery.explorer.Bundle + 3, // 0: cnquery.explorer.scan.Job.inventory:type_name -> cnquery.providers.v1.Inventory + 4, // 1: cnquery.explorer.scan.Job.bundle:type_name -> cnquery.explorer.Bundle 1, // 2: cnquery.explorer.scan.Job.props:type_name -> cnquery.explorer.scan.Job.PropsEntry - 3, // [3:3] is the sub-list for method output_type - 3, // [3:3] is the sub-list for method input_type - 3, // [3:3] is the sub-list for extension type_name - 3, // [3:3] is the sub-list for extension extendee - 0, // [0:3] is the sub-list for field type_name + 2, // 3: cnquery.explorer.scan.Job.annotations:type_name -> cnquery.explorer.scan.Job.AnnotationsEntry + 4, // [4:4] is the sub-list for method output_type + 4, // [4:4] is the sub-list for method input_type + 4, // [4:4] is the sub-list for extension type_name + 4, // [4:4] is the sub-list for extension extendee + 0, // [0:4] is the sub-list for field type_name } func init() { file_cnquery_explorer_scan_proto_init() } @@ -195,7 +215,7 @@ func file_cnquery_explorer_scan_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_cnquery_explorer_scan_proto_rawDesc, NumEnums: 0, - NumMessages: 2, + NumMessages: 3, NumExtensions: 0, NumServices: 0, }, diff --git a/explorer/scan/cnquery_explorer_scan.proto b/explorer/scan/cnquery_explorer_scan.proto index 20f1dff1fd..14d7385f2f 100644 --- a/explorer/scan/cnquery_explorer_scan.proto +++ b/explorer/scan/cnquery_explorer_scan.proto @@ -16,5 +16,7 @@ message Job { bool do_record = 20; repeated string query_pack_filters = 21; map props = 22; + // annotations that will be applied to all assets in the job + map annotations = 23; } diff --git a/explorer/scan/local_scanner.go b/explorer/scan/local_scanner.go index b0b14d4c85..add1bfc561 100644 --- a/explorer/scan/local_scanner.go +++ b/explorer/scan/local_scanner.go @@ -263,6 +263,7 @@ func (s *LocalScanner) distributeJob(job *Job, ctx context.Context, upstream *up justAssets := []*inventory.Asset{} for _, asset := range assets { + asset.asset.AddAnnotations(job.GetAnnotations()) asset.asset.KindString = asset.asset.GetPlatform().Kind justAssets = append(justAssets, asset.asset) }