From c87a1ae567ea290999fa0429aed2439cd800edfa Mon Sep 17 00:00:00 2001 From: kent_h_chen Date: Thu, 16 Nov 2023 12:33:13 -0500 Subject: [PATCH] updated to latest version --- README.md | 78 ++++------ client/base/scan.pb.go | 81 +++++----- client/base/scan_grpc.pb.go | 2 +- client/grpc.go | 289 ++++++++++++++++++++++++------------ client/grpc_client_test.go | 110 ++++++++++---- client/grpc_run_test.go | 46 +++++- client/messages.go | 72 ++++----- client/regions.go | 38 +++++ client/sdk.go | 34 +++-- go.mod | 9 +- go.sum | 17 ++- tools/client.go | 8 +- tools/scanfiles.go | 18 +-- 13 files changed, 525 insertions(+), 277 deletions(-) create mode 100644 client/regions.go diff --git a/README.md b/README.md index 8011a5f..658853c 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ To integrate with our service using the Golang SDK, you need to import the SDK p ## Obtain an API Key -The File Security SDK requires a valid API Key provided as parameter to the SDK client object. It can accept Trend Vision One API keys. +The File Security SDK requires a valid API Key provided as parameter to the SDK client object. It can accept Trend Vision One API keys. When obtaining the API Key, ensure that the API Key is associated with the region that you plan to use. It is important to note that Trend Vision One API Keys are associated with different regions, please refer to the region flag below to obtain a better understanding of the valid regions associated with the respective API Key. @@ -77,8 +77,9 @@ Once you have initialized the SDK, you can start using it to interact with our s ```go filePath := "path/to/your/file.txt" +tags := []string{"tag1", "tag2"} -response, err := client.ScanFile(filePath) +response, err := client.ScanFile(filePath, tags) if err != nil { // Handle scanning error panic(err) @@ -92,8 +93,9 @@ if err != nil { ```go data := []byte("Your data to be scanned") identifier := "UniqueIdentifier" +tags := []string{"tag1", "tag2"} -response, err := client.ScanBuffer(data, identifier) +response, err := client.ScanBuffer(data, identifier, tags) if err != nil { // Handle scanning error panic(err) @@ -102,27 +104,9 @@ if err != nil { // Use the 'response' as needed ``` -## Advanced Configuration +**_Note_** +- Max number of tags is 8. And the length of each tag can't exceed 63. -The SDK provides additional configuration options and functions that you can use to customize its behavior. Here are some advanced configuration options: - -### Getting Timeout Setting - -You can retrieve the current timeout setting: - -```go -timeout := client.GetTimeoutSetting() -fmt.Printf("Current Timeout Setting: %d seconds\n", timeout) -``` - -### Getting Connection and Authentication Context - -You can access the underlying gRPC connection and authentication context: - -```go -conn := client.GetConnection() // Get the gRPC connection -ctx := client.ConfigAuth(context.Background()) // Get the authentication context -``` ## Additional Functions The SDK provides additional functions for advanced usage, such as dumping the configuration and cleaning up resources: @@ -172,20 +156,20 @@ This program is located in the `tools/` folder. It supports the gRPC-based serve The following flags are supported: - `-tls` - Specify to enable server authentication by client for gRPC +`-tls` +Specify to enable server authentication by client for gRPC - `-region ` - Specify the region to connect to for gRPC +`-region ` +Specify the region to connect to for gRPC - `-addr ` - the address to connect to for gRPC (default "localhost:50051") +`-addr ` +the address to connect to for gRPC (default "localhost:50051") - `-filename ` - Path of file to scan +`-filename ` +Path of file to scan - `-apikey ` - API key for service authentication if authentication is enabled +`-apikey ` +API key for service authentication if authentication is enabled ### scanfiles @@ -195,23 +179,23 @@ If `-good` flag is specified, it indicates the files to be scanned are non-malic The following flags are supported by the program: - `-path ` - Directory or file to scan. This flag must be specified in all scenarios. +`-path ` +Directory or file to scan. This flag must be specified in all scenarios. - `-good` - Specify if scanning good/non-malicious files. +`-good` +Specify if scanning good/non-malicious files. - `-parallel` - Specify if scanning of multiple files should be carried out simultaneously instead of sequentially. +`-parallel` +Specify if scanning of multiple files should be carried out simultaneously instead of sequentially. - `-tls` - Specify to enable server authentication by client for gRPC +`-tls` +Specify to enable server authentication by client for gRPC - `-region ` - Specify the region to connect to for gRPC +`-region ` +Specify the region to connect to for gRPC - `-addr ` - The address to connect to for gRPC (default "localhost:50051") +`-addr ` +The address to connect to for gRPC (default "localhost:50051") - `-apikey ` - API key for service authentication if authentication is enabled +`-apikey ` +API key for service authentication if authentication is enabled diff --git a/client/base/scan.pb.go b/client/base/scan.pb.go index 3b92c16..2ec4afd 100644 --- a/client/base/scan.pb.go +++ b/client/base/scan.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.28.1 -// protoc v3.12.4 +// protoc-gen-go v1.31.0 +// protoc v4.24.4 // source: scan.proto // https://cloud.google.com/apis/design/versioning @@ -122,14 +122,15 @@ type C2S struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Stage Stage `protobuf:"varint,1,opt,name=stage,proto3,enum=amaas.scan.v1.Stage" json:"stage,omitempty"` - FileName string `protobuf:"bytes,2,opt,name=file_name,json=fileName,proto3" json:"file_name,omitempty"` - RsSize int32 `protobuf:"varint,3,opt,name=rs_size,json=rsSize,proto3" json:"rs_size,omitempty"` - Offset int32 `protobuf:"varint,4,opt,name=offset,proto3" json:"offset,omitempty"` - Chunk []byte `protobuf:"bytes,5,opt,name=chunk,proto3" json:"chunk,omitempty"` - Trendx bool `protobuf:"varint,6,opt,name=trendx,proto3" json:"trendx,omitempty"` - FileSha1 string `protobuf:"bytes,7,opt,name=file_sha1,json=fileSha1,proto3" json:"file_sha1,omitempty"` - FileSha256 string `protobuf:"bytes,8,opt,name=file_sha256,json=fileSha256,proto3" json:"file_sha256,omitempty"` + Stage Stage `protobuf:"varint,1,opt,name=stage,proto3,enum=amaas.scan.v1.Stage" json:"stage,omitempty"` + FileName string `protobuf:"bytes,2,opt,name=file_name,json=fileName,proto3" json:"file_name,omitempty"` + RsSize int32 `protobuf:"varint,3,opt,name=rs_size,json=rsSize,proto3" json:"rs_size,omitempty"` + Offset int32 `protobuf:"varint,4,opt,name=offset,proto3" json:"offset,omitempty"` + Chunk []byte `protobuf:"bytes,5,opt,name=chunk,proto3" json:"chunk,omitempty"` + Trendx bool `protobuf:"varint,6,opt,name=trendx,proto3" json:"trendx,omitempty"` + FileSha1 string `protobuf:"bytes,7,opt,name=file_sha1,json=fileSha1,proto3" json:"file_sha1,omitempty"` + FileSha256 string `protobuf:"bytes,8,opt,name=file_sha256,json=fileSha256,proto3" json:"file_sha256,omitempty"` + Tags []string `protobuf:"bytes,9,rep,name=tags,proto3" json:"tags,omitempty"` } func (x *C2S) Reset() { @@ -220,6 +221,13 @@ func (x *C2S) GetFileSha256() string { return "" } +func (x *C2S) GetTags() []string { + if x != nil { + return x.Tags + } + return nil +} + type S2C struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -303,7 +311,7 @@ var File_scan_proto protoreflect.FileDescriptor var file_scan_proto_rawDesc = []byte{ 0x0a, 0x0a, 0x73, 0x63, 0x61, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d, 0x61, 0x6d, - 0x61, 0x61, 0x73, 0x2e, 0x73, 0x63, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x22, 0xeb, 0x01, 0x0a, 0x03, + 0x61, 0x61, 0x73, 0x2e, 0x73, 0x63, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x22, 0xff, 0x01, 0x0a, 0x03, 0x43, 0x32, 0x53, 0x12, 0x2a, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x61, 0x6d, 0x61, 0x61, 0x73, 0x2e, 0x73, 0x63, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x12, @@ -318,31 +326,32 @@ var file_scan_proto_rawDesc = []byte{ 0x69, 0x6c, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x31, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x53, 0x68, 0x61, 0x31, 0x12, 0x1f, 0x0a, 0x0b, 0x66, 0x69, 0x6c, 0x65, 0x5f, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x66, - 0x69, 0x6c, 0x65, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x22, 0xa3, 0x01, 0x0a, 0x03, 0x53, 0x32, - 0x43, 0x12, 0x2a, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, - 0x32, 0x14, 0x2e, 0x61, 0x6d, 0x61, 0x61, 0x73, 0x2e, 0x73, 0x63, 0x61, 0x6e, 0x2e, 0x76, 0x31, - 0x2e, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x12, 0x28, 0x0a, - 0x03, 0x63, 0x6d, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, 0x2e, 0x61, 0x6d, 0x61, - 0x61, 0x73, 0x2e, 0x73, 0x63, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, - 0x6e, 0x64, 0x52, 0x03, 0x63, 0x6d, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, - 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, - 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x73, 0x75, 0x6c, 0x74, 0x2a, - 0x36, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x54, 0x41, 0x47, - 0x45, 0x5f, 0x49, 0x4e, 0x49, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x53, 0x54, 0x41, 0x47, - 0x45, 0x5f, 0x52, 0x55, 0x4e, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x53, 0x54, 0x41, 0x47, 0x45, - 0x5f, 0x46, 0x49, 0x4e, 0x49, 0x10, 0x02, 0x2a, 0x25, 0x0a, 0x07, 0x43, 0x6f, 0x6d, 0x6d, 0x61, - 0x6e, 0x64, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x4d, 0x44, 0x5f, 0x52, 0x45, 0x54, 0x52, 0x10, 0x00, - 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x4d, 0x44, 0x5f, 0x51, 0x55, 0x49, 0x54, 0x10, 0x01, 0x32, 0x3b, - 0x0a, 0x04, 0x53, 0x63, 0x61, 0x6e, 0x12, 0x33, 0x0a, 0x03, 0x52, 0x75, 0x6e, 0x12, 0x12, 0x2e, - 0x61, 0x6d, 0x61, 0x61, 0x73, 0x2e, 0x73, 0x63, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x32, - 0x53, 0x1a, 0x12, 0x2e, 0x61, 0x6d, 0x61, 0x61, 0x73, 0x2e, 0x73, 0x63, 0x61, 0x6e, 0x2e, 0x76, - 0x31, 0x2e, 0x53, 0x32, 0x43, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x33, 0x0a, 0x1d, 0x63, - 0x6f, 0x6d, 0x2e, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x6f, 0x6e, - 0x65, 0x2e, 0x61, 0x6d, 0x61, 0x61, 0x73, 0x2e, 0x73, 0x63, 0x61, 0x6e, 0x5a, 0x12, 0x61, 0x6d, - 0x61, 0x61, 0x73, 0x2f, 0x73, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x2f, 0x62, 0x61, 0x73, 0x65, - 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x69, 0x6c, 0x65, 0x53, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, + 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x22, 0xa3, 0x01, + 0x0a, 0x03, 0x53, 0x32, 0x43, 0x12, 0x2a, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x67, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0e, 0x32, 0x14, 0x2e, 0x61, 0x6d, 0x61, 0x61, 0x73, 0x2e, 0x73, 0x63, 0x61, + 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x74, 0x61, 0x67, 0x65, 0x52, 0x05, 0x73, 0x74, 0x61, 0x67, + 0x65, 0x12, 0x28, 0x0a, 0x03, 0x63, 0x6d, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x16, + 0x2e, 0x61, 0x6d, 0x61, 0x61, 0x73, 0x2e, 0x73, 0x63, 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x43, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x52, 0x03, 0x63, 0x6d, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6f, + 0x66, 0x66, 0x73, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x06, 0x6f, 0x66, 0x66, + 0x73, 0x65, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x05, 0x52, 0x06, 0x6c, 0x65, 0x6e, 0x67, 0x74, 0x68, 0x12, 0x16, 0x0a, 0x06, 0x72, + 0x65, 0x73, 0x75, 0x6c, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x73, + 0x75, 0x6c, 0x74, 0x2a, 0x36, 0x0a, 0x05, 0x53, 0x74, 0x61, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x0a, + 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x49, 0x4e, 0x49, 0x54, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, + 0x53, 0x54, 0x41, 0x47, 0x45, 0x5f, 0x52, 0x55, 0x4e, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x53, + 0x54, 0x41, 0x47, 0x45, 0x5f, 0x46, 0x49, 0x4e, 0x49, 0x10, 0x02, 0x2a, 0x25, 0x0a, 0x07, 0x43, + 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x4d, 0x44, 0x5f, 0x52, 0x45, + 0x54, 0x52, 0x10, 0x00, 0x12, 0x0c, 0x0a, 0x08, 0x43, 0x4d, 0x44, 0x5f, 0x51, 0x55, 0x49, 0x54, + 0x10, 0x01, 0x32, 0x3b, 0x0a, 0x04, 0x53, 0x63, 0x61, 0x6e, 0x12, 0x33, 0x0a, 0x03, 0x52, 0x75, + 0x6e, 0x12, 0x12, 0x2e, 0x61, 0x6d, 0x61, 0x61, 0x73, 0x2e, 0x73, 0x63, 0x61, 0x6e, 0x2e, 0x76, + 0x31, 0x2e, 0x43, 0x32, 0x53, 0x1a, 0x12, 0x2e, 0x61, 0x6d, 0x61, 0x61, 0x73, 0x2e, 0x73, 0x63, + 0x61, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x32, 0x43, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, + 0x33, 0x0a, 0x1d, 0x63, 0x6f, 0x6d, 0x2e, 0x74, 0x72, 0x65, 0x6e, 0x64, 0x2e, 0x63, 0x6c, 0x6f, + 0x75, 0x64, 0x6f, 0x6e, 0x65, 0x2e, 0x61, 0x6d, 0x61, 0x61, 0x73, 0x2e, 0x73, 0x63, 0x61, 0x6e, + 0x5a, 0x12, 0x61, 0x6d, 0x61, 0x61, 0x73, 0x2f, 0x73, 0x63, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x2f, + 0x62, 0x61, 0x73, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/client/base/scan_grpc.pb.go b/client/base/scan_grpc.pb.go index e2c2727..67a692e 100644 --- a/client/base/scan_grpc.pb.go +++ b/client/base/scan_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.2.0 -// - protoc v3.12.4 +// - protoc v4.24.4 // source: scan.proto package base diff --git a/client/grpc.go b/client/grpc.go index c597d4b..bbdf298 100644 --- a/client/grpc.go +++ b/client/grpc.go @@ -2,13 +2,13 @@ package client import ( "context" + "crypto/sha1" "crypto/sha256" "crypto/tls" - "encoding/base64" "encoding/hex" - "encoding/json" "errors" "fmt" + "hash" "io" "log" "os" @@ -16,6 +16,8 @@ import ( "strings" "time" + "golang.org/x/exp/slices" + "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/credentials" @@ -23,7 +25,7 @@ import ( gmd "google.golang.org/grpc/metadata" "google.golang.org/grpc/status" - pb "github.com/trendmicro/cloudone-antimalware-golang-sdk/client/base" + pb "github.com/trendmicro/tm-v1-fs-golang-sdk/client/base" ) const ( @@ -31,6 +33,12 @@ const ( _envvarServerAddr = "TM_AM_SERVER_ADDR" // : _envvarDisableTLS = "TM_AM_DISABLE_TLS" // Set to 1 to not use TLS for client-server communication; set to 0 or leave empty otherwise. _envvarDisableCertVerify = "TM_AM_DISABLE_CERT_VERIFY" // Set to 1 to not disable server certificate check by client; set to 0 or leave empty otherwise. + + appNameHTTPHeader = "tm-app-name" + appNameV1FS = "V1FS" + + maxTagsListSize = 8 + maxTagSize = 63 ) type LogLevel int @@ -50,7 +58,7 @@ type AmaasClientReader interface { DataSize() (int64, error) ReadBytes(offset int64, length int32) ([]byte, error) Close() - Hash() (string, error) + Hash(algorithm string) (string, error) } // File reader implementation @@ -90,9 +98,25 @@ func (reader *AmaasClientFileReader) DataSize() (int64, error) { return fi.Size(), nil } -func (reader *AmaasClientFileReader) Hash() (string, error) { - //TODO - return "", nil +func (reader *AmaasClientFileReader) Hash(algorithm string) (string, error) { + var h hash.Hash + + switch strings.ToLower(algorithm) { + case "sha256": + h = sha256.New() + case "sha1": + h = sha1.New() + default: + return "", fmt.Errorf(MSG("MSG_ID_ERR_UNSUPPORTED_ALGORITHM"), algorithm) + } + + if _, err := io.Copy(h, reader.fd); err != nil { + return "", err + } + + hashValue := hex.EncodeToString(h.Sum(nil)) + + return fmt.Sprintf("%s:%s", algorithm, hashValue), nil } func (reader *AmaasClientFileReader) ReadBytes(offset int64, length int32) ([]byte, error) { @@ -161,16 +185,25 @@ func (reader *AmaasClientBufferReader) Close() { } // return hash value of buffer -func (reader *AmaasClientBufferReader) Hash() (string, error) { +func (reader *AmaasClientBufferReader) Hash(algorithm string) (string, error) { + var h hash.Hash - //computer file hash - h := sha256.New() - _, err := h.Write(reader.buffer) - if err != nil { + switch strings.ToLower(algorithm) { + case "sha256": + h = sha256.New() + case "sha1": + h = sha1.New() + default: + return "", fmt.Errorf(MSG("MSG_ID_ERR_UNSUPPORTED_ALGORITHM"), algorithm) + } + + if _, err := h.Write(reader.buffer); err != nil { return "", err } + hashValue := hex.EncodeToString(h.Sum(nil)) - return "sha256:" + hashValue, nil + + return fmt.Sprintf("%s:%s", algorithm, hashValue), nil } /////////////////////////////////////// @@ -180,23 +213,32 @@ func (reader *AmaasClientBufferReader) Hash() (string, error) { /////////////////////////////////////// type AmaasClient struct { - conn *grpc.ClientConn - isC1Token bool - authKey string - addr string - useTLS bool - verifyCert bool - timeoutSecs int - - archHandler AmaasClientArchiveHandler + conn *grpc.ClientConn + isC1Token bool + authKey string + addr string + useTLS bool + verifyCert bool + timeoutSecs int + disableCache bool + appName string + archHandler AmaasClientArchiveHandler } -func scanRun(ctx context.Context, cancel context.CancelFunc, c pb.ScanClient, dataReader AmaasClientReader) (string, error) { +func scanRun(ctx context.Context, cancel context.CancelFunc, c pb.ScanClient, dataReader AmaasClientReader, disableCache bool, tags []string) (string, error) { defer cancel() var stream pb.Scan_RunClient var err error + var hashSha256 string + + // Validate the tags parameter + if tags != nil { + if err := validateTags(tags); err != nil { + return "", err + } + } // Where certificate and connections related checks first happen, so many different // error conditions can be returned here. @@ -206,11 +248,22 @@ func scanRun(ctx context.Context, cancel context.CancelFunc, c pb.ScanClient, da return makeFailedScanJSONResp(), sanitizeGRPCError(err) } - defer stream.CloseSend() + defer func(stream pb.Scan_RunClient) { + err := stream.CloseSend() + if err != nil { + panic(err) + } + }(stream) size, _ := dataReader.DataSize() - hashVal, _ := dataReader.Hash() - if err = runInitRequest(stream, dataReader.Identifier(), int32(size), hashVal); err != nil { + + if !disableCache { + hashSha256, _ = dataReader.Hash("sha256") + } + + hashSha1, _ := dataReader.Hash("sha1") + + if err = runInitRequest(stream, dataReader.Identifier(), int32(size), hashSha256, hashSha1, tags); err != nil { return makeFailedScanJSONResp(), err } @@ -226,10 +279,10 @@ func scanRun(ctx context.Context, cancel context.CancelFunc, c pb.ScanClient, da return result, nil } -func runInitRequest(stream pb.Scan_RunClient, identifier string, dataSize int32, hash string) error { +func runInitRequest(stream pb.Scan_RunClient, identifier string, dataSize int32, hashSha256 string, hashSha1 string, tags []string) error { if err := stream.Send(&pb.C2S{Stage: pb.Stage_STAGE_INIT, - FileName: identifier, RsSize: dataSize, FileSha256: hash}); err != nil { + FileName: identifier, RsSize: dataSize, FileSha256: hashSha256, FileSha1: hashSha1, Tags: tags}); err != nil { err = sanitizeGRPCError(err) logMsg(LogLevelError, MSG("MSG_ID_ERR_INIT"), err) return err @@ -312,10 +365,10 @@ func runUploadLoop(stream pb.Scan_RunClient, dataReader AmaasClientReader) (resu return } -func (ac *AmaasClient) bufferScanRun(buffer []byte, identifier string) (string, error) { +func (ac *AmaasClient) bufferScanRun(buffer []byte, identifier string, tags []string) (string, error) { if ac.conn == nil { - return "", errors.New(MSG("MSG_ID_ERR_CLIENT_NOT_READY")) + return "", makeInternalError(MSG("MSG_ID_ERR_CLIENT_NOT_READY")) } bufferReader, err := InitBufferReader(buffer, identifier) @@ -328,23 +381,25 @@ func (ac *AmaasClient) bufferScanRun(buffer []byte, identifier string) (string, ctx = ac.buildAuthContext(ctx) - return scanRun(ctx, cancel, pb.NewScanClient(ac.conn), bufferReader) + ctx = ac.buildAppNameContext(ctx) + + return scanRun(ctx, cancel, pb.NewScanClient(ac.conn), bufferReader, ac.disableCache, tags) } -func (ac *AmaasClient) fileScanRun(fileName string) (string, error) { +func (ac *AmaasClient) fileScanRun(fileName string, tags []string) (string, error) { if ac.conn == nil { - return "", errors.New(MSG("MSG_ID_ERR_CLIENT_NOT_READY")) + return "", makeInternalError(MSG("MSG_ID_ERR_CLIENT_NOT_READY")) } if ac.archHandler.archHandlingEnabled() { return ac.archHandler.fileScanRun(fileName) } - return ac.fileScanRunNormalFile(fileName) + return ac.fileScanRunNormalFile(fileName, tags) } -func (ac *AmaasClient) fileScanRunNormalFile(fileName string) (string, error) { +func (ac *AmaasClient) fileScanRunNormalFile(fileName string, tags []string) (string, error) { fileReader, err := InitFileReader(fileName) if err != nil { @@ -356,7 +411,9 @@ func (ac *AmaasClient) fileScanRunNormalFile(fileName string) (string, error) { ctx = ac.buildAuthContext(ctx) - return scanRun(ctx, cancel, pb.NewScanClient(ac.conn), fileReader) + ctx = ac.buildAppNameContext(ctx) + + return scanRun(ctx, cancel, pb.NewScanClient(ac.conn), fileReader, ac.disableCache, tags) } func (ac *AmaasClient) setupComm() error { @@ -387,7 +444,7 @@ func (ac *AmaasClient) buildAuthContext(ctx context.Context) context.Context { if ac.isC1Token { newCtx = gmd.AppendToOutgoingContext(ctx, "Authorization", fmt.Sprintf("Bearer %s", ac.authKey)) } else { - if isExplictAPIKey(ac.authKey) { + if isExplicitAPIKey(ac.authKey) { newCtx = gmd.AppendToOutgoingContext(ctx, "Authorization", ac.authKey) } else { newCtx = gmd.AppendToOutgoingContext(ctx, "Authorization", fmt.Sprintf("ApiKey %s", ac.authKey)) @@ -397,6 +454,14 @@ func (ac *AmaasClient) buildAuthContext(ctx context.Context) context.Context { return newCtx } +func (ac *AmaasClient) buildAppNameContext(ctx context.Context) context.Context { + newCtx := ctx + if len(ac.appName) > 0 { + newCtx = gmd.AppendToOutgoingContext(ctx, appNameHTTPHeader, ac.appName) + } + return newCtx +} + //////////////////////////////////////////////// // // Cloud One and client related helper functions @@ -416,60 +481,52 @@ func checkAuthKey(authKey string) (string, error) { envAuthKey := os.Getenv(_envvarAuthKey) if authKey == "" && envAuthKey == "" { - return "", errors.New(MSG("MSG_ID_ERR_MISSING_AUTH")) + return "", makeInternalError(MSG("MSG_ID_ERR_MISSING_AUTH")) } else if envAuthKey != "" { auth = envAuthKey } - - isToken := isC1Token(auth) - isKey := isC1APIKey(auth) - - if !isToken && !isKey { - return "", errors.New(MSG("MSG_ID_ERR_INVALID_AUTH")) - } else if isToken && isKey { - return "", errors.New(MSG("MSG_ID_ERR_UNKNOWN_AUTH")) - } - return auth, nil } -func isExplictAPIKey(auth string) bool { - if strings.HasPrefix(strings.ToLower(auth), "apikey") { - return true - } - return false + +func isExplicitAPIKey(auth string) bool { + return strings.HasPrefix(strings.ToLower(auth), "apikey") } func isC1Token(auth string) bool { - if isExplictAPIKey(auth) { - return false - } + //for now, we may only support apikey, not customer token or service token + return false - keySplitted := strings.Split(auth, ".") - if len(keySplitted) != 3 { // The JWT should contain three parts - return false - } + // if isExplicitAPIKey(auth) { + // return false + // } - jsonFirstPart, err := base64.StdEncoding.DecodeString(keySplitted[0]) - if err != nil { - return false - } + // keySplitted := strings.Split(auth, ".") + // if len(keySplitted) != 3 { // The JWT should contain three parts + // return false + // } - var firstPart struct { - Alg string `json:"alg"` - } - err = json.Unmarshal(jsonFirstPart, &firstPart) - if err != nil || firstPart.Alg == "" { // The first part should have the attribute "alg" - return false - } + // jsonFirstPart, err := base64.StdEncoding.DecodeString(keySplitted[0]) + // if err != nil { + // return false + // } - return true -} + // var firstPart struct { + // Alg string `json:"alg"` + // } + // err = json.Unmarshal(jsonFirstPart, &firstPart) + // if err != nil || firstPart.Alg == "" { // The first part should have the attribute "alg" + // return false + // } -func isC1APIKey(auth string) bool { - return strings.HasPrefix(auth, "tmc") || isExplictAPIKey(auth) + // return true } +// deprecated +// func isC1APIKey(auth string) bool { +// return strings.HasPrefix(auth, "tmc") || isExplicitAPIKey(auth) +// } + func identifyServerAddr(region string) (string, error) { envOverrideAddr := os.Getenv(_envvarServerAddr) @@ -477,9 +534,9 @@ func identifyServerAddr(region string) (string, error) { return envOverrideAddr, nil } - fqdn := getServiceFQDN(region) - if fqdn == "" { - return "", errors.New(fmt.Sprintf(MSG("MSG_ID_ERR_INVALID_REGION"), region)) + fqdn, err := getServiceFQDN(region) + if err != nil { + return "", err } return fmt.Sprintf("%s:%d", fqdn, _defaultCommPort), nil @@ -497,7 +554,7 @@ func getDefaultScanTimeout() (int, error) { if envScanTimeoutSecs != "" { if val, err := strconv.Atoi(envScanTimeoutSecs); err != nil { - return 0, errors.New(fmt.Sprintf(MSG("MSG_ID_ERR_ENVVAR_PARSING"), _envvarScanTimeoutSecs)) + return 0, fmt.Errorf(MSG("MSG_ID_ERR_ENVVAR_PARSING"), _envvarScanTimeoutSecs) } else { return val, nil } @@ -506,24 +563,40 @@ func getDefaultScanTimeout() (int, error) { return _defaultTimeoutSecs, nil } -func getServiceFQDN(targetRegion string) string { +func getServiceFQDN(targetRegion string) (string, error) { - const mappingJSON = `{ - "us-1": "antimalware.us-1.cloudone.trendmicro.com", - "in-1": "antimalware.in-1.cloudone.trendmicro.com", - "de-1": "antimalware.de-1.cloudone.trendmicro.com", - "sg-1": "antimalware.sg-1.cloudone.trendmicro.com", - "au-1": "antimalware.au-1.cloudone.trendmicro.com", - "jp-1": "antimalware.jp-1.cloudone.trendmicro.com", - "gb-1": "antimalware.gb-1.cloudone.trendmicro.com", - "ca-1": "antimalware.ca-1.cloudone.trendmicro.com" - }` + // ensure the region exists in v1 or c1 + region := targetRegion + if !slices.Contains(AllRegions, region) { + return "", fmt.Errorf(MSG("MSG_ID_ERR_INVALID_REGION"), region, AllRegions) + } + // if it is a V1 region, map it to a C1 region + if slices.Contains(V1Regions, region) { + regionClone := "" + exists := false + // Make sure the v1 region is part of the cloudone.SupportedV1Regions and cloudone.V1ToC1RegionMapping lists + if regionClone, exists = V1ToC1RegionMapping[region]; !exists || !slices.Contains(SupportedV1Regions, region) { + return "", fmt.Errorf(MSG("MSG_ID_ERR_INVALID_REGION"), region, AllRegions) + } + region = regionClone + } - mapping := map[string]string{} - json.Unmarshal([]byte(mappingJSON), &mapping) + mapping := map[string]string{ + C1_US_REGION: "antimalware.us-1.cloudone.trendmicro.com", + C1_IN_REGION: "antimalware.in-1.cloudone.trendmicro.com", + C1_DE_REGION: "antimalware.de-1.cloudone.trendmicro.com", + C1_SG_REGION: "antimalware.sg-1.cloudone.trendmicro.com", + C1_AU_REGION: "antimalware.au-1.cloudone.trendmicro.com", + C1_JP_REGION: "antimalware.jp-1.cloudone.trendmicro.com", + C1_GB_REGION: "antimalware.gb-1.cloudone.trendmicro.com", + C1_CA_REGION: "antimalware.ca-1.cloudone.trendmicro.com", + } - fqdn := mapping[targetRegion] - return fqdn + fqdn, exists := mapping[region] + if !exists { + return "", fmt.Errorf(MSG("MSG_ID_ERR_INVALID_REGION"), region, AllRegions) + } + return fqdn, nil } ////////////////////////////////////// @@ -629,7 +702,7 @@ func sanitizeGRPCError(err error) error { // The gRPC framework will generate this error code when the deadline is // exceeded. case codes.DeadlineExceeded: - + return status.Error(st.Code(), MSG("MSG_ID_ERR_TIMEOUT")) // NotFound means some requested entity (e.g., file or directory) was // not found. // @@ -793,6 +866,8 @@ func NewClientInternal(key string, addr string, useTLS bool) (*AmaasClient, erro ac.useTLS = useTLS ac.verifyCert = false + ac.appName = appNameV1FS + var err error if ac.timeoutSecs, err = getDefaultScanTimeout(); err != nil { @@ -809,3 +884,27 @@ func NewClientInternal(key string, addr string, useTLS bool) (*AmaasClient, erro return ac, nil } + +func (ac *AmaasClient) SetCacheDisable() { + ac.disableCache = true +} + +func validateTags(tags []string) error { + if len(tags) == 0 { + return errors.New("tags cannot be empty") + } + + if len(tags) > maxTagsListSize { + return fmt.Errorf("too many tags, maximum is %d", maxTagsListSize) + } + + for _, tag := range tags { + if len(tag) > maxTagSize { + return fmt.Errorf("tag length cannot exceed %d", maxTagSize) + } + if tag == "" { + return errors.New("each tag cannot be empty") + } + } + return nil +} diff --git a/client/grpc_client_test.go b/client/grpc_client_test.go index e27739c..1b35925 100644 --- a/client/grpc_client_test.go +++ b/client/grpc_client_test.go @@ -46,19 +46,7 @@ func TestCheckAuthKeyEmptyWithOverride(t *testing.T) { assert.Equal(t, "", key) } -const someInvalidKey = "eyblahblahblah.eydsfdfdfdf.EYhhhhhhhhhhhhhh" -const someAPIKey = "tmcfakeapikey" - -func TestCheckAuthKeyNonEmptyInvalid(t *testing.T) { - - os.Setenv(_envvarAuthKey, "") - os.Setenv(_envvarAuthKeyNotRequired, "") - - key, err := checkAuthKey(someInvalidKey) - - assert.NotNil(t, err) - assert.Equal(t, "", key) -} +const someAPIKey = "tmcmockapikey" func TestCheckAuthKeyNonEmptyValidAPIKey(t *testing.T) { @@ -112,13 +100,13 @@ func TestCheckAuthKeyNonEmptyEnvVarOverride(t *testing.T) { func TestGetServiceFQDNEmpty(t *testing.T) { - fqdn := getServiceFQDN("") + fqdn, _ := getServiceFQDN("") assert.Equal(t, "", fqdn) } func TestGetServiceFQDNGarbage(t *testing.T) { - fqdn := getServiceFQDN("blah blah okay") + fqdn, _ := getServiceFQDN("blah blah okay") assert.Equal(t, "", fqdn) } @@ -137,7 +125,29 @@ func TestGetServiceFQDNMapping(t *testing.T) { for _, region := range inputs { expected := fmt.Sprintf("antimalware.%s.cloudone.trendmicro.com", region) - fqdn := getServiceFQDN(region) + fqdn, _ := getServiceFQDN(region) + + assert.Equal(t, expected, fqdn) + } +} + +func TestGetServiceFQDNMappingVisionOne(t *testing.T) { + + var inputs = map[string]string{ + "us-1": "us-east-1", + "in-1": "ap-south-1", + "de-1": "eu-central-1", + "sg-1": "ap-southeast-1", + "au-1": "ap-southeast-2", + "jp-1": "ap-northeast-1", + // "gb-1": "", + // "ca-1": "", + // "trend-us-1": "", + } + + for c1, v1 := range inputs { + expected := fmt.Sprintf("antimalware.%s.cloudone.trendmicro.com", c1) + fqdn, _ := getServiceFQDN(v1) assert.Equal(t, expected, fqdn) } @@ -182,9 +192,9 @@ func TestIdServerAddressValid(t *testing.T) { os.Setenv(_envvarServerAddr, "") - addr, err := identifyServerAddr("us-1") - - expected := fmt.Sprintf("%s:%d", getServiceFQDN("us-1"), _defaultCommPort) + addr, err := identifyServerAddr("us-east-1") + fqdn, _ := getServiceFQDN("us-east-1") + expected := fmt.Sprintf("%s:%d", fqdn, _defaultCommPort) assert.Nil(t, err) assert.Equal(t, expected, addr) @@ -195,7 +205,7 @@ func TestIdServerAddressValidWithOverride(t *testing.T) { const testAddr = "this.is.a.fake.server.address:123" os.Setenv(_envvarServerAddr, testAddr) - addr, err := identifyServerAddr("us-1") + addr, err := identifyServerAddr("us-east-1") assert.Nil(t, err) assert.Equal(t, testAddr, addr) @@ -250,13 +260,13 @@ func TestGetDefaultScanTimeoutWithOverride(t *testing.T) { os.Setenv(_envvarScanTimeoutSecs, "what-the-heck") - timeout, err := getDefaultScanTimeout() + _, err := getDefaultScanTimeout() assert.NotNil(t, err) os.Setenv(_envvarScanTimeoutSecs, "1000") - timeout, err = getDefaultScanTimeout() + timeout, err := getDefaultScanTimeout() assert.Nil(t, err) assert.Equal(t, 1000, timeout) @@ -432,17 +442,57 @@ func TestBufferClientSha256(t *testing.T) { data := []byte("dummy") client, _ := InitBufferReader(data, "test") assert.NotNil(t, client) - r, _ := client.Hash() + r, _ := client.Hash("sha256") + assert.NotEmpty(t, r) +} + +func TestBufferClientSha1(t *testing.T) { + + data := []byte("dummy") + client, _ := InitBufferReader(data, "test") + assert.NotNil(t, client) + r, _ := client.Hash("sha1") assert.NotEmpty(t, r) } +func TestFileClientSha1WithUnsupportedAlgo(t *testing.T) { + + data := []byte("dummy") + client, _ := InitBufferReader(data, "test") + assert.NotNil(t, client) + r, err := client.Hash("sha224") + assert.Empty(t, r) + assert.NotNil(t, err) + assert.Error(t, err, "Unsupported algorithm for calculating the hash: sha224") +} + func TestFileClientSha256(t *testing.T) { file_path, _ := os.Executable() client, _ := InitFileReader(file_path) assert.NotNil(t, client) - r, _ := client.Hash() + r, _ := client.Hash("sha256") + assert.NotEmpty(t, r) +} + +func TestFileClientSha1(t *testing.T) { + + file_path, _ := os.Executable() + client, _ := InitFileReader(file_path) + assert.NotNil(t, client) + r, _ := client.Hash("sha1") + assert.NotEmpty(t, r) +} + +func TestFileClientSha256WithUnsupportedAlgo(t *testing.T) { + + file_path, _ := os.Executable() + client, _ := InitFileReader(file_path) + assert.NotNil(t, client) + r, err := client.Hash("sha224") assert.Empty(t, r) + assert.NotNil(t, err) + assert.Error(t, err, "Unsupported algorithm for calculating the hash: sha224") } func TestCheckAuthKey(t *testing.T) { @@ -465,9 +515,15 @@ func TestCheckAuthKey(t *testing.T) { expectedError: false, }, { - testCase: "invalid key", - input: "Dummy", - expectedResult: "Dummy", + testCase: "any key", + input: "Dummy key", + expectedResult: "Dummy key", + expectedError: false, + }, + { + testCase: "empty key", + input: "", + expectedResult: "", expectedError: true, }, } diff --git a/client/grpc_run_test.go b/client/grpc_run_test.go index 3b96106..2965d86 100644 --- a/client/grpc_run_test.go +++ b/client/grpc_run_test.go @@ -1,17 +1,19 @@ package client import ( + "context" "errors" "math/rand" "os" "testing" + "time" "github.com/stretchr/testify/assert" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - pb "github.com/trendmicro/cloudone-antimalware-golang-sdk/client/base" + pb "github.com/trendmicro/tm-v1-fs-golang-sdk/client/base" ) /*************************************************************************** @@ -351,3 +353,45 @@ func TestRunUploadLoopBadS2CMsg(t *testing.T) { _, err = stream.RecvToServer() assert.NotNil(t, err) } + +func TestScanRunWithInvalidTags(t *testing.T) { + tests := []struct { + name string + tags []string + expectedErr string + }{ + { + name: "Empty tags", + tags: []string{}, + expectedErr: "tags cannot be empty", + }, + { + name: "Too many tags", + tags: []string{"tag1", "tag2", "tag3", "tag4", "tag5", "tag6", "tag7", "tag8", "tag9"}, + expectedErr: "too many tags, maximum is 8", + }, + { + name: "Empty tag", + tags: []string{"", "tag1"}, + expectedErr: "each tag cannot be empty", + }, + { + name: "Tag length exceeds 63", + tags: []string{"1234567890123456789012345678901234567890123456789012345678901234"}, + expectedErr: "tag length cannot exceed 63", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // arrange + ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(180)) + + // act + _, err := scanRun(ctx, cancel, nil, nil, false, tt.tags) + + // assert + assert.Equal(t, tt.expectedErr, err.Error()) + }) + } +} diff --git a/client/messages.go b/client/messages.go index a1f525f..690481e 100644 --- a/client/messages.go +++ b/client/messages.go @@ -1,41 +1,43 @@ package client var messageMap = map[string]string{ - "MSG_ID_ERR_OPEN_FILE": "Cannot open file %s: %v", - "MSG_ID_ERR_RETRIEVE_DATA": "Attempted to read %d bytes but only retrieved %d", - "MSG_ID_ERR_SETUP_STREAM": "Failed to setup stream: %v", - "MSG_ID_DEBUG_UPLOADED_BYTES": "Uploaded %d bytes", - "MSG_ID_ERR_INIT": "Failed to send init msg: %v", - "MSG_ID_DEBUG_CLOSED_CONN": "Server closed connection", - "MSG_ID_ERR_RECV": "Failed to recv: %v", - "MSG_ID_DEBUG_QUIT": "Received QUIT command, exiting...\n", - "MSG_ID_DEBUG_RETR": "Received RETR command, offset: %d, length: %d", - "MSG_ID_ERR_RETR_DATA": "Failed to retrieve data from file: %s", - "MSG_ID_ERR_SEND_DATA": "Failed to send data chunk: %s", - "MSG_ID_ERR_UNKNOWN_CMD": "Received unknown command from server: %d", - "MSG_ID_ERR_CLIENT_NOT_READY": "Client is not ready to carry out scans, possibly due to invocation of Destroy()", - "MSG_ID_ERR_CLIENT_ERROR": "Client is not ready to carry out scans, due to %s", - "MSG_ID_ERR_MISSING_AUTH": "Must provide an API key to use the client", - "MSG_ID_ERR_INVALID_AUTH": "Invalid authorization key provided. Please provide a valid API key or token", - "MSG_ID_ERR_UNKNOWN_AUTH": "Internal error: unable to determine authorization key type", - "MSG_ID_ERR_INVALID_REGION": "%s is not a supported region", - "MSG_ID_ERR_ENVVAR_PARSING": "Cannot parse value specified by environment variable %s", - "MSG_ID_WARNING_LOG_LEVEL": "[%s] logMsg() invoked with level = LogLevelOff and format = %s", - "MSG_ID_DEBUG_GRPC_ERROR": "Received gRPC status code: %d, msg: %s", - "MSG_ID_ERR_UNKNOWN_ERROR": "Ecountered an unknown error", - "MSG_ID_ERR_NO_PERMISSION": "API key does not have permission to access the service", - "MSG_ID_ERR_INCOMPATIBLE_SDK": "Client SDK not compatible with the Anti-malware Service - please upgrade", - "MSG_ID_ERR_CERT_VERIFY": "Server certificate verification failed", - "MSG_ID_ERR_TLS_REQUIRED": "Use of TLS for client-server communication required but not detected", - "MSG_ID_ERR_KEY_AUTH_FAILED": "Authorization key cannot be authenticated", - "MSG_ID_ERR_RATE_LIMIT_EXCEEDED": "Too many requests. HTTP Status: 429; Exceeds rate limit", - "MSG_ID_DEBUG_AH_EXAM_FILE": "fileScanRunWithArchHandling(%s): examining file #%d \"%s\", size %d -> %d", - "MSG_ID_DEBUG_AH_DIR_SKIP": "..... is a directory, skipping to next file in archive", - "MSG_ID_DEBUG_AH_ZIP_FILE": "Encounted another ZIP file %s... recursively handle it", - "MSG_ID_DEBUG_AH_NEW_ROUTINE": "Create a new goroutine to do scan file \"%s\".....", - "MSG_ID_ERR_AH_GOROUTINE_ERR": "File scanning inside goroutine encountered error", - "MSG_ID_DEBUG_QUEUE_STATUS": "A job removed from work queue, queue status len %d / cap %d", - "MSG_ID_DEBUG_QUEUE_ERR": "Unexpected value read from queue: %d", + "MSG_ID_ERR_OPEN_FILE": "Cannot open file %s: %v", + "MSG_ID_ERR_RETRIEVE_DATA": "Attempted to read %d bytes but only retrieved %d", + "MSG_ID_ERR_SETUP_STREAM": "Failed to setup stream: %v", + "MSG_ID_DEBUG_UPLOADED_BYTES": "Uploaded %d bytes", + "MSG_ID_ERR_INIT": "Failed to send init msg: %v", + "MSG_ID_DEBUG_CLOSED_CONN": "Server closed connection", + "MSG_ID_ERR_RECV": "Failed to recv: %v", + "MSG_ID_DEBUG_QUIT": "Received QUIT command, exiting...\n", + "MSG_ID_DEBUG_RETR": "Received RETR command, offset: %d, length: %d", + "MSG_ID_ERR_RETR_DATA": "Failed to retrieve data from file: %s", + "MSG_ID_ERR_SEND_DATA": "Failed to send data chunk: %s", + "MSG_ID_ERR_UNKNOWN_CMD": "Received unknown command from server: %d", + "MSG_ID_ERR_CLIENT_NOT_READY": "Client is not ready to carry out scans, possibly due to invocation of Destroy()", + "MSG_ID_ERR_CLIENT_ERROR": "Client is not ready to carry out scans, due to %s", + "MSG_ID_ERR_MISSING_AUTH": "Must provide an API key to use the client", + "MSG_ID_ERR_INVALID_AUTH": "Invalid authorization key provided. Please provide a valid API key or token", + "MSG_ID_ERR_UNKNOWN_AUTH": "Internal error: unable to determine authorization key type", + "MSG_ID_ERR_INVALID_REGION": "%s is not a supported region, region value should be one of %v", + "MSG_ID_ERR_ENVVAR_PARSING": "Cannot parse value specified by environment variable %s", + "MSG_ID_WARNING_LOG_LEVEL": "[%s] logMsg() invoked with level = LogLevelOff and format = %s", + "MSG_ID_DEBUG_GRPC_ERROR": "Received gRPC status code: %d, msg: %s", + "MSG_ID_ERR_UNKNOWN_ERROR": "Ecountered an unknown error", + "MSG_ID_ERR_NO_PERMISSION": "API key does not have permission to access the service", + "MSG_ID_ERR_INCOMPATIBLE_SDK": "Client SDK not compatible with the Anti-malware Service - please upgrade", + "MSG_ID_ERR_CERT_VERIFY": "Server certificate verification failed", + "MSG_ID_ERR_TLS_REQUIRED": "Use of TLS for client-server communication required but not detected", + "MSG_ID_ERR_KEY_AUTH_FAILED": "Authorization key cannot be authenticated", + "MSG_ID_ERR_RATE_LIMIT_EXCEEDED": "Too many requests. HTTP Status: 429; Exceeds rate limit", + "MSG_ID_DEBUG_AH_EXAM_FILE": "fileScanRunWithArchHandling(%s): examining file #%d \"%s\", size %d -> %d", + "MSG_ID_DEBUG_AH_DIR_SKIP": "..... is a directory, skipping to next file in archive", + "MSG_ID_DEBUG_AH_ZIP_FILE": "Encounted another ZIP file %s... recursively handle it", + "MSG_ID_DEBUG_AH_NEW_ROUTINE": "Create a new goroutine to do scan file \"%s\".....", + "MSG_ID_ERR_AH_GOROUTINE_ERR": "File scanning inside goroutine encountered error", + "MSG_ID_DEBUG_QUEUE_STATUS": "A job removed from work queue, queue status len %d / cap %d", + "MSG_ID_DEBUG_QUEUE_ERR": "Unexpected value read from queue: %d", + "MSG_ID_ERR_UNSUPPORTED_ALGORITHM": "Unsupported algorithm for calculating the hash: %s", + "MSG_ID_ERR_TIMEOUT": "Context deadline exceeded", } func MSG(id string) string { diff --git a/client/regions.go b/client/regions.go new file mode 100644 index 0000000..9244621 --- /dev/null +++ b/client/regions.go @@ -0,0 +1,38 @@ +package client + +const ( + AWS_JP_REGION = "ap-northeast-1" + AWS_SG_REGION = "ap-southeast-1" + AWS_AU_REGION = "ap-southeast-2" + AWS_IN_REGION = "ap-south-1" + AWS_US_REGION = "us-east-1" + AWS_DE_REGION = "eu-central-1" + AWS_CA_REGION = "ca-central-1" + AWS_TREND_REGION = "us-east-2" + AWS_GB_REGION = "eu-west-2" + C1_JP_REGION = "jp-1" + C1_SG_REGION = "sg-1" + C1_AU_REGION = "au-1" + C1_IN_REGION = "in-1" + C1_US_REGION = "us-1" + C1_DE_REGION = "de-1" + C1_CA_REGION = "ca-1" + C1_TREND_REGION = "trend-us-1" + C1_GB_REGION = "gb-1" +) + +var C1Regions []string = []string{C1_AU_REGION, C1_CA_REGION, C1_DE_REGION, C1_GB_REGION, C1_IN_REGION, C1_JP_REGION, C1_SG_REGION, C1_US_REGION, C1_TREND_REGION} +var V1Regions []string = []string{AWS_AU_REGION, AWS_CA_REGION, AWS_DE_REGION, AWS_GB_REGION, AWS_IN_REGION, AWS_JP_REGION, AWS_SG_REGION /*AWS_TREND_REGION,*/, AWS_US_REGION} +var SupportedV1Regions []string = []string{AWS_AU_REGION, AWS_DE_REGION, AWS_IN_REGION, AWS_JP_REGION, AWS_SG_REGION, AWS_US_REGION} +var SupportedC1Regions []string = []string{C1_AU_REGION, C1_CA_REGION, C1_DE_REGION, C1_GB_REGION, C1_IN_REGION, C1_JP_REGION, C1_SG_REGION, C1_US_REGION} +var AllRegions []string = append(C1Regions, V1Regions...) +var AllValidRegions []string = append(SupportedC1Regions, SupportedV1Regions...) + +var V1ToC1RegionMapping = map[string]string{ + AWS_AU_REGION: C1_AU_REGION, + AWS_DE_REGION: C1_DE_REGION, + AWS_IN_REGION: C1_IN_REGION, + AWS_JP_REGION: C1_JP_REGION, + AWS_SG_REGION: C1_SG_REGION, + AWS_US_REGION: C1_US_REGION, +} diff --git a/client/sdk.go b/client/sdk.go index 1e766ca..da5e3d5 100644 --- a/client/sdk.go +++ b/client/sdk.go @@ -19,18 +19,20 @@ const ( ) const ( - LogLevelOff LogLevel = 0 - LogLevelFatal = 1 - LogLevelError = 2 - LogLevelWarning = 3 - LogLevelInfo = 4 - LogLevelDebug = 5 + LogLevelOff LogLevel = iota + LogLevelFatal LogLevel = 1 + LogLevelError LogLevel = 2 + LogLevelWarning LogLevel = 3 + LogLevelInfo LogLevel = 4 + LogLevelDebug LogLevel = 5 ) func NewClient(key string, region string) (c *AmaasClient, e error) { ac := &AmaasClient{} + ac.appName = appNameV1FS + var err error if ac.authKey, err = checkAuthKey(key); err != nil { @@ -78,14 +80,14 @@ func (ac *AmaasClient) Destroy() { // but all file scans must complete before Destroy() can be invoked. // -func (ac *AmaasClient) ScanFile(filePath string) (resp string, e error) { +func (ac *AmaasClient) ScanFile(filePath string, tags []string) (resp string, e error) { currentLogLevel = getLogLevel() - return ac.fileScanRun(filePath) + return ac.fileScanRun(filePath, tags) } -func (ac *AmaasClient) ScanBuffer(buffer []byte, identifier string) (resp string, e error) { +func (ac *AmaasClient) ScanBuffer(buffer []byte, identifier string, tags []string) (resp string, e error) { currentLogLevel = getLogLevel() - return ac.bufferScanRun(buffer, identifier) + return ac.bufferScanRun(buffer, identifier, tags) } func (ac *AmaasClient) DumpConfig() (output string) { @@ -111,3 +113,15 @@ func (ac *AmaasClient) GetConnection() *grpc.ClientConn { func (ac *AmaasClient) ConfigAuth(ctx context.Context) context.Context { return ac.buildAuthContext(ctx) } + +func (ac *AmaasClient) SetAppName(appName string) { + ac.appName = appName +} + +func (ac *AmaasClient) GetAppName() string { + return ac.appName +} + +func (ac *AmaasClient) ConfigAppName(ctx context.Context) context.Context { + return ac.buildAppNameContext(ctx) +} diff --git a/go.mod b/go.mod index 1535f80..d7ad4bf 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,11 @@ -module github.com/trendmicro/cloudone-antimalware-golang-sdk +module github.com/trendmicro/tm-v1-fs-golang-sdk go 1.18 require ( github.com/golang-jwt/jwt v3.2.2+incompatible github.com/stretchr/testify v1.8.1 + golang.org/x/exp v0.0.0-20231006140011-7918f672742d google.golang.org/grpc v1.51.0 google.golang.org/protobuf v1.28.1 ) @@ -13,9 +14,9 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/golang/protobuf v1.5.2 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/text v0.4.0 // indirect google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index b9fd350..4509555 100644 --- a/go.sum +++ b/go.sum @@ -27,7 +27,7 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= @@ -40,6 +40,8 @@ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKs github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -47,25 +49,24 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= diff --git a/tools/client.go b/tools/client.go index 5282a8f..4b96a3f 100644 --- a/tools/client.go +++ b/tools/client.go @@ -5,7 +5,7 @@ import ( "log" "os" - amaasclient "github.com/trendmicro/cloudone-antimalware-golang-sdk/client" + amaasclient "github.com/trendmicro/tm-v1-fs-golang-sdk/client" ) var ( @@ -23,7 +23,7 @@ func main() { var err error if *region != "" && *addr != "" { - log.Fatalf("Both region and addr are specified. Please specify only one.") + log.Fatal("Both region and addr are specified. Please specify only one.") } else if *region != "" { client, err = amaasclient.NewClient(*apiKey, *region) if err != nil { @@ -35,10 +35,10 @@ func main() { log.Fatalf("Unable to create AMaaS scan client object. error: %v", err) } } else { - log.Fatalf("Neither region nor addr is specified. Please specify one.") + log.Fatal("Neither region nor addr is specified. Please specify one.") } - result, err := client.ScanFile(*fileName) + result, err := client.ScanFile(*fileName, nil) if err != nil { log.Fatalf(err.Error()) } diff --git a/tools/scanfiles.go b/tools/scanfiles.go index 15add87..4b3c8ca 100644 --- a/tools/scanfiles.go +++ b/tools/scanfiles.go @@ -17,7 +17,7 @@ import ( "strings" "time" - amaasclient "github.com/trendmicro/cloudone-antimalware-golang-sdk/client" + amaasclient "github.com/trendmicro/tm-v1-fs-golang-sdk/client" ) /* @@ -31,7 +31,7 @@ type ScanResult struct { EndTime time.Time `json:"scan_endtime"` Test string `json:"test"` TestPassed bool `json:"test_passed"` - Data string `json:"data` + Data string `json:"data"` } type OverallTestResult struct { @@ -68,7 +68,7 @@ func main() { var err error if region != "" && grpcAddr != "" { - log.Fatalf("Both region and addr are specified. Please specify only one.") + log.Fatal("Both region and addr are specified. Please specify only one.") } else if region != "" { ac, err = amaasclient.NewClient(apiKey, region) if err != nil { @@ -80,7 +80,7 @@ func main() { log.Fatalf("Unable to create AMaaS scan client object. error: %v", err) } } else { - log.Fatalf("Neither region nor addr is specified. Please specify one.") + log.Fatal("Neither region nor addr is specified. Please specify one.") } if path == "" { @@ -89,7 +89,7 @@ func main() { fileInfo, err := os.Stat(path) if err != nil { - log.Fatalf("The path %s does not appear to be a valid one.") + log.Fatalf("The path %s does not appear to be a valid one.", path) } if fileInfo.IsDir() { @@ -147,9 +147,9 @@ func scanFileListInSequence(fileList []string, scanGoodFiles bool, scanner *amaa sr.StartTime = time.Now() - jsonResult, err := scanner.ScanFile(fileList[i]) + jsonResult, err := scanner.ScanFile(fileList[i], nil) if err != nil { - log.Printf(err.Error()) + log.Print(err.Error()) } sr.EndTime = time.Now() @@ -186,9 +186,9 @@ func scanFileListInParallel(fileList []string, scanGoodFiles bool, scanner *amaa sr.StartTime = time.Now() - jsonResult, err := scanner.ScanFile(f) + jsonResult, err := scanner.ScanFile(f, nil) if err != nil { - log.Printf(err.Error()) + log.Print(err.Error()) } sr.EndTime = time.Now()