From 46093195ccd25ad45a0e43d5d96db3e73335d3d9 Mon Sep 17 00:00:00 2001 From: su-amaas Date: Thu, 11 Sep 2025 09:06:50 +0000 Subject: [PATCH] update to latest version: v1.6.1 --- CHANGELOG.md | 4 ++ README.md | 15 +++++ VERSION | 2 +- grpc.go | 100 +++++++++++++++++++++--------- grpc_client_test.go | 146 ++++++++++++++++++++++++++++++++++++++++++++ sdk.go | 24 +++++++- 6 files changed, 259 insertions(+), 32 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02dc515..bb37e48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +## 1.6.1 - 2025-09-11 + +* Add customized cloud account id setting via `SetCloudAccountID` function + ## 1.6.0 - 2025-06-30 * Add active content detection support via `SetActiveContentEnable` function diff --git a/README.md b/README.md index a7fc602..4820dd9 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,21 @@ You can disable digest calculation by calling the `SetDigestDisable` function: client.SetDigestDisable() ``` +### Set Cloud Account ID + +You can set a cloud account ID that will be automatically appended to all scan tags in the format `cloudAccountId=value`: + +```go +err := client.SetCloudAccountID("633537927402") +if err != nil { + // Handle error - cloudAccountID too long +} +``` + +**Note**: +- The total tag length (including `cloudAccountId=` prefix) cannot exceed 63 characters +- Using cloud account ID occupies one tag slot, reducing max customer tags from 8 to 7 + ## Golang Client SDK API Reference ### ```func NewClient(key string, region string) (c *AmaasClient, e error)``` diff --git a/VERSION b/VERSION index dc1e644..9c6d629 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.6.0 +1.6.1 diff --git a/grpc.go b/grpc.go index 99c34c3..618381f 100644 --- a/grpc.go +++ b/grpc.go @@ -218,21 +218,22 @@ func (reader *AmaasClientBufferReader) Hash(algorithm string) (string, error) { /////////////////////////////////////// type AmaasClient struct { - conn *grpc.ClientConn - isC1Token bool - authKey string - addr string - useTLS bool - caCert string - verifyCert bool - timeoutSecs int - appName string - archHandler AmaasClientArchiveHandler - pml bool - feedback bool - verbose bool - activeContent bool - digest bool + conn *grpc.ClientConn + isC1Token bool + authKey string + addr string + useTLS bool + caCert string + verifyCert bool + timeoutSecs int + appName string + archHandler AmaasClientArchiveHandler + pml bool + feedback bool + verbose bool + activeContent bool + digest bool + cloudAccountID string } func getHashValue(dataReader AmaasClientReader) (string, string, error) { @@ -446,8 +447,7 @@ func runUploadLoop(stream pb.Scan_RunClient, dataReader AmaasClientReader, bulk return } -func (ac *AmaasClient) bufferScanRun(buffer []byte, identifier string, tags []string) (string, error) { - +func (ac *AmaasClient) bufferScanRun(ctx context.Context, buffer []byte, identifier string, tags []string) (string, error) { if ac.conn == nil { return "", makeInternalError(MSG("MSG_ID_ERR_CLIENT_NOT_READY")) } @@ -458,18 +458,20 @@ func (ac *AmaasClient) bufferScanRun(buffer []byte, identifier string, tags []st } defer bufferReader.Close() - ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(ac.timeoutSecs)) + ctx, cancel := context.WithTimeout(ctx, time.Second*time.Duration(ac.timeoutSecs)) ctx = ac.buildAuthContext(ctx) ctx = ac.buildAppNameContext(ctx) - return scanRun(ctx, cancel, pb.NewScanClient(ac.conn), bufferReader, tags, ac.pml, true, ac.feedback, + tags = ac.appendCloudAccountIDToTags(tags) + + return scanRun(ctx, cancel, pb.NewScanClient(ac.conn), bufferReader, + tags, ac.pml, true, ac.feedback, ac.verbose, ac.activeContent, ac.digest) } -func (ac *AmaasClient) fileScanRun(fileName string, tags []string) (string, error) { - +func (ac *AmaasClient) fileScanRun(ctx context.Context, fileName string, tags []string) (string, error) { if ac.conn == nil { return "", makeInternalError(MSG("MSG_ID_ERR_CLIENT_NOT_READY")) } @@ -478,10 +480,10 @@ func (ac *AmaasClient) fileScanRun(fileName string, tags []string) (string, erro return ac.archHandler.fileScanRun(fileName) } - return ac.fileScanRunNormalFile(fileName, tags) + return ac.fileScanRunNormalFile(ctx, fileName, tags) } -func (ac *AmaasClient) fileScanRunNormalFile(fileName string, tags []string) (string, error) { +func (ac *AmaasClient) fileScanRunNormalFile(ctx context.Context, fileName string, tags []string) (string, error) { fileReader, err := InitFileReader(fileName) if err != nil { @@ -489,29 +491,35 @@ func (ac *AmaasClient) fileScanRunNormalFile(fileName string, tags []string) (st } defer fileReader.Close() - ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(ac.timeoutSecs)) + ctx, cancel := context.WithTimeout(ctx, time.Second*time.Duration(ac.timeoutSecs)) ctx = ac.buildAuthContext(ctx) ctx = ac.buildAppNameContext(ctx) - return scanRun(ctx, cancel, pb.NewScanClient(ac.conn), fileReader, tags, ac.pml, true, ac.feedback, + tags = ac.appendCloudAccountIDToTags(tags) + + return scanRun(ctx, cancel, pb.NewScanClient(ac.conn), fileReader, + tags, ac.pml, true, ac.feedback, ac.verbose, ac.activeContent, ac.digest) } -func (ac *AmaasClient) readerScanRun(reader AmaasClientReader, tags []string) (string, error) { +func (ac *AmaasClient) readerScanRun(ctx context.Context, reader AmaasClientReader, tags []string) (string, error) { if ac.conn == nil { return "", makeInternalError(MSG("MSG_ID_ERR_CLIENT_NOT_READY")) } - ctx, cancel := context.WithTimeout(context.Background(), time.Second*time.Duration(ac.timeoutSecs)) + ctx, cancel := context.WithTimeout(ctx, time.Second*time.Duration(ac.timeoutSecs)) ctx = ac.buildAuthContext(ctx) ctx = ac.buildAppNameContext(ctx) - return scanRun(ctx, cancel, pb.NewScanClient(ac.conn), reader, tags, ac.pml, true, ac.feedback, + tags = ac.appendCloudAccountIDToTags(tags) + + return scanRun(ctx, cancel, pb.NewScanClient(ac.conn), reader, + tags, ac.pml, true, ac.feedback, ac.verbose, ac.activeContent, ac.digest) } @@ -1127,6 +1135,22 @@ func (ac *AmaasClient) SetDigestDisable() { ac.digest = false } +func (ac *AmaasClient) SetCloudAccountID(cloudAccountID string) error { + if cloudAccountID == "" { + ac.cloudAccountID = cloudAccountID + return nil + } + + // Calculate the total tag length with "cloudAccountId=" prefix + cloudAccountTag := fmt.Sprintf("cloudAccountId=%s", cloudAccountID) + if len(cloudAccountTag) > maxTagSize { + return fmt.Errorf("cloudAccountID tag 'cloudAccountId=%s' exceeds maximum tag size of %d characters", cloudAccountID, maxTagSize) + } + + ac.cloudAccountID = cloudAccountID + return nil +} + func validateTags(tags []string) error { if len(tags) == 0 { return errors.New("tags cannot be empty") @@ -1146,3 +1170,23 @@ func validateTags(tags []string) error { } return nil } + +func (ac *AmaasClient) appendCloudAccountIDToTags(tags []string) []string { + if ac.cloudAccountID == "" { + return tags + } + + cloudAccountTag := fmt.Sprintf("cloudAccountId=%s", ac.cloudAccountID) + + // Check if the cloudAccountTag exceeds maxTagSize (63 characters) + if len(cloudAccountTag) > maxTagSize { + logMsg(LogLevelWarning, "cloudAccountId tag exceeds maximum tag size (%d), skipping", maxTagSize) + return tags + } + + if tags == nil { + return []string{cloudAccountTag} + } + + return append(tags, cloudAccountTag) +} diff --git a/grpc_client_test.go b/grpc_client_test.go index e780755..258b23f 100644 --- a/grpc_client_test.go +++ b/grpc_client_test.go @@ -642,3 +642,149 @@ func generateJwtToken() (string, error) { return ss, nil } + +// +// CloudAccountID related tests +// + +func TestSetCloudAccountIDValid(t *testing.T) { + ac := &AmaasClient{} + + // Test with AWS account ID (12 digits) + err := ac.SetCloudAccountID("633537927402") + assert.Nil(t, err) + assert.Equal(t, "633537927402", ac.cloudAccountID) + + // Test with Azure UUID-v4 (36 characters) + err = ac.SetCloudAccountID("a47ac10b-58cc-4372-a567-0e02b2c3d479") + assert.Nil(t, err) + assert.Equal(t, "a47ac10b-58cc-4372-a567-0e02b2c3d479", ac.cloudAccountID) + + // Test with exactly 48 characters (63 - 15 for "cloudAccountId=") + longButValid := "123456789012345678901234567890123456789012345678" + err = ac.SetCloudAccountID(longButValid) + assert.Nil(t, err) + assert.Equal(t, longButValid, ac.cloudAccountID) +} + +func TestSetCloudAccountIDTooLong(t *testing.T) { + ac := &AmaasClient{} + + // Test with string longer than 48 characters (49 chars) + tooLong := "1234567890123456789012345678901234567890123456789" + err := ac.SetCloudAccountID(tooLong) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "exceeds maximum tag size of 63 characters") + assert.Equal(t, "", ac.cloudAccountID) +} + +func TestSetCloudAccountIDEmpty(t *testing.T) { + ac := &AmaasClient{} + + // Test with empty string (should be allowed) + err := ac.SetCloudAccountID("") + assert.Nil(t, err) + assert.Equal(t, "", ac.cloudAccountID) +} + +func TestAppendCloudAccountIDToTagsWithEmpty(t *testing.T) { + ac := &AmaasClient{} + ac.cloudAccountID = "" + + // Test with nil tags + result := ac.appendCloudAccountIDToTags(nil) + assert.Nil(t, result) + + // Test with empty tags slice + tags := []string{} + result = ac.appendCloudAccountIDToTags(tags) + assert.Equal(t, tags, result) + + // Test with existing tags + tags = []string{"tag1", "tag2"} + result = ac.appendCloudAccountIDToTags(tags) + assert.Equal(t, tags, result) +} + +func TestAppendCloudAccountIDToTagsWithValue(t *testing.T) { + ac := &AmaasClient{} + ac.cloudAccountID = "633537927402" + + // Test with nil tags + result := ac.appendCloudAccountIDToTags(nil) + expected := []string{"cloudAccountId=633537927402"} + assert.Equal(t, expected, result) + + // Test with empty tags slice + tags := []string{} + result = ac.appendCloudAccountIDToTags(tags) + expected = []string{"cloudAccountId=633537927402"} + assert.Equal(t, expected, result) + + // Test with existing tags + tags = []string{"tag1", "tag2"} + result = ac.appendCloudAccountIDToTags(tags) + expected = []string{"tag1", "tag2", "cloudAccountId=633537927402"} + assert.Equal(t, expected, result) +} + +func TestAppendCloudAccountIDToTagsImmutability(t *testing.T) { + ac := &AmaasClient{} + ac.cloudAccountID = "633537927402" + + // Test that original tags slice is not modified + originalTags := []string{"tag1", "tag2"} + originalTagsCopy := make([]string, len(originalTags)) + copy(originalTagsCopy, originalTags) + + result := ac.appendCloudAccountIDToTags(originalTags) + + // Original slice should remain unchanged + assert.Equal(t, originalTagsCopy, originalTags) + // Result should contain cloudAccountID + expected := []string{"tag1", "tag2", "cloudAccountId=633537927402"} + assert.Equal(t, expected, result) +} + +func TestCloudAccountIDIntegrationWithScanMethods(t *testing.T) { + // Create a mock AmaasClient with connection set to nil (to trigger early return) + ac := &AmaasClient{ + conn: nil, + } + + // Set cloudAccountID + err := ac.SetCloudAccountID("633537927402") + assert.Nil(t, err) + + // Test ScanFile with cloudAccountID + _, err = ac.ScanFile("nonexistent.txt", []string{"tag1"}) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "Client is not ready") + + // Test ScanBuffer with cloudAccountID + _, err = ac.ScanBuffer([]byte("test"), "buffer", []string{"tag1"}) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "Client is not ready") + + // Test ScanReader with cloudAccountID + bufferReader, _ := InitBufferReader([]byte("test"), "reader") + _, err = ac.ScanReader(bufferReader, []string{"tag1"}) + assert.NotNil(t, err) + assert.Contains(t, err.Error(), "Client is not ready") + + bufferReader.Close() +} + +func TestAppendCloudAccountIDToTagsTooLong(t *testing.T) { + ac := &AmaasClient{} + + // Set a cloudAccountID that would make the tag too long (49 characters) + ac.cloudAccountID = "1234567890123456789012345678901234567890123456789" // 49 chars + + // Test with existing tags - should skip the cloudAccountID + tags := []string{"tag1", "tag2"} + result := ac.appendCloudAccountIDToTags(tags) + + // Should return original tags unchanged (cloudAccountID skipped due to length) + assert.Equal(t, tags, result) +} diff --git a/sdk.go b/sdk.go index f99552f..b2ee7b7 100644 --- a/sdk.go +++ b/sdk.go @@ -98,18 +98,36 @@ func (ac *AmaasClient) Destroy() { // func (ac *AmaasClient) ScanFile(filePath string, tags []string) (resp string, e error) { + ctx := context.Background() currentLogLevel = getLogLevel() - return ac.fileScanRun(filePath, tags) + return ac.fileScanRun(ctx, filePath, tags) +} + +func (ac *AmaasClient) ScanFileWithContext(ctx context.Context, filePath string, tags []string) (resp string, e error) { + currentLogLevel = getLogLevel() + return ac.fileScanRun(ctx, filePath, tags) } func (ac *AmaasClient) ScanBuffer(buffer []byte, identifier string, tags []string) (resp string, e error) { + ctx := context.Background() currentLogLevel = getLogLevel() - return ac.bufferScanRun(buffer, identifier, tags) + return ac.bufferScanRun(ctx, buffer, identifier, tags) +} + +func (ac *AmaasClient) ScanBufferWithContext(ctx context.Context, buffer []byte, identifier string, tags []string) (resp string, e error) { + currentLogLevel = getLogLevel() + return ac.bufferScanRun(ctx, buffer, identifier, tags) } func (ac *AmaasClient) ScanReader(reader AmaasClientReader, tags []string) (resp string, e error) { + ctx := context.Background() + currentLogLevel = getLogLevel() + return ac.readerScanRun(ctx, reader, tags) +} + +func (ac *AmaasClient) ScanReaderWithContext(ctx context.Context, reader AmaasClientReader, tags []string) (resp string, e error) { currentLogLevel = getLogLevel() - return ac.readerScanRun(reader, tags) + return ac.readerScanRun(ctx, reader, tags) } func (ac *AmaasClient) DumpConfig() (output string) {