github.com/google/osv-scalibr@v0.4.1/veles/secrets/azurestorageaccountaccesskey/detector_test.go (about)

     1  // Copyright 2025 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package azurestorageaccountaccesskey_test
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  	"testing"
    21  
    22  	"github.com/google/go-cmp/cmp"
    23  	"github.com/google/go-cmp/cmp/cmpopts"
    24  	"github.com/google/osv-scalibr/veles"
    25  	"github.com/google/osv-scalibr/veles/secrets/azurestorageaccountaccesskey"
    26  )
    27  
    28  // Azure Storage account access key is composed by 86 base64 characters followed by '=='
    29  // Reference:
    30  // https://learn.microsoft.com/en-us/purview/sit-defn-azure-storage-account-key-generic
    31  const testKey = `YutGV0Vlauqsobd6tPWz2AKwHhBXMEWsAH+rSbz0UZUfaMVj1CFrcNQK47ygmrC4vHmc7eOp1LdM+AStk5mMYA==`
    32  
    33  // TestDetector_truePositives tests for cases where we know the Detector
    34  // will find a valid key/s.
    35  func TestDetector_truePositives(t *testing.T) {
    36  	engine, err := veles.NewDetectionEngine([]veles.Detector{azurestorageaccountaccesskey.NewDetector()})
    37  	if err != nil {
    38  		t.Fatal(err)
    39  	}
    40  	cases := []struct {
    41  		name  string
    42  		input string
    43  		want  []veles.Secret
    44  	}{{
    45  		name:  "simple matching string",
    46  		input: "AzureStoragekey : " + testKey,
    47  		want: []veles.Secret{
    48  			azurestorageaccountaccesskey.AzureStorageAccountAccessKey{Key: testKey},
    49  		},
    50  	}, {
    51  		// According to Microsoft documentation the key can start with
    52  		// zero to one of the greater than symbol (>), apostrophe ('),
    53  		// equal sign (=), quotation mark (?), or number sign (#)
    54  		// Therefore the equal sign after the api key should be present in the result
    55  		name:  "match at end of string",
    56  		input: `AZURE_ACCOUNT_STORAGE_ACCESS_KEY=` + testKey,
    57  		want: []veles.Secret{
    58  			azurestorageaccountaccesskey.AzureStorageAccountAccessKey{Key: `=` + testKey},
    59  		},
    60  	}, {
    61  		name:  "match in middle of string",
    62  		input: `AZURE_KEY="` + testKey + `"`,
    63  		want: []veles.Secret{
    64  			azurestorageaccountaccesskey.AzureStorageAccountAccessKey{Key: testKey},
    65  		},
    66  	}, {
    67  		name:  "multiple matches",
    68  		input: "AZURE_KEY:" + testKey + "AZURE_ACCOUNT_STORAGE_ACCESS_KEY:" + testKey + "AZURE_KEY:" + testKey,
    69  		want: []veles.Secret{
    70  			azurestorageaccountaccesskey.AzureStorageAccountAccessKey{Key: testKey},
    71  			azurestorageaccountaccesskey.AzureStorageAccountAccessKey{Key: testKey},
    72  			azurestorageaccountaccesskey.AzureStorageAccountAccessKey{Key: testKey},
    73  		},
    74  	}, {
    75  		name: "larger_input_containing_key",
    76  		input: fmt.Sprintf(`
    77  CONFIG_FILE=config.txt
    78  storage_access_KEY="%s"
    79  CLOUD_PROJECT=my-project
    80  		`, testKey),
    81  		want: []veles.Secret{
    82  			azurestorageaccountaccesskey.AzureStorageAccountAccessKey{Key: testKey},
    83  		},
    84  	}, {
    85  		name:  "potential match longer than max key length",
    86  		input: "azure_account_key:" + testKey + `test`,
    87  		want: []veles.Secret{
    88  			azurestorageaccountaccesskey.AzureStorageAccountAccessKey{Key: testKey},
    89  		},
    90  	}, {
    91  		name: "match_with_command_line",
    92  		input: fmt.Sprintf(`az storage container create
    93  			--account-name sample_username
    94  			--name sample-container
    95  			--account-key %s
    96  			--auth-mode key`, testKey),
    97  		want: []veles.Secret{
    98  			azurestorageaccountaccesskey.AzureStorageAccountAccessKey{Key: testKey},
    99  		},
   100  	}, {
   101  		// the equal sign in the result is present according to Ms Documentation
   102  		// See comment at row 51
   103  		name: "match_with_connection_string",
   104  		input: fmt.Sprintf(`
   105  			DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;
   106  AccountKey=%s;
   107  BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1;
   108  QueueEndpoint=http://127.0.0.1:10001/devstoreaccount1;
   109  TableEndpoint=http://127.0.0.1:10002/devstoreaccount1;`, testKey),
   110  		want: []veles.Secret{
   111  			azurestorageaccountaccesskey.AzureStorageAccountAccessKey{Key: "=" + testKey},
   112  		},
   113  	}, {
   114  		// the equal sign is present as per Microsoft documentation
   115  		// See comment at row 51
   116  		name:  "match with env vars",
   117  		input: "AZURE_STORAGE_KEY=" + testKey,
   118  		want: []veles.Secret{
   119  			azurestorageaccountaccesskey.AzureStorageAccountAccessKey{Key: "=" + testKey},
   120  		},
   121  	}}
   122  	for _, tc := range cases {
   123  		t.Run(tc.name, func(t *testing.T) {
   124  			got, err := engine.Detect(t.Context(), strings.NewReader(tc.input))
   125  			if err != nil {
   126  				t.Errorf("Detect() error: %v, want nil", err)
   127  			}
   128  			if diff := cmp.Diff(tc.want, got, cmpopts.EquateEmpty()); diff != "" {
   129  				t.Errorf("Detect() diff (-want +got):\n%s", diff)
   130  			}
   131  		})
   132  	}
   133  }
   134  
   135  // TestDetector_trueNegatives tests for cases where we know the Detector
   136  // will not find a valid key.
   137  func TestDetector_trueNegatives(t *testing.T) {
   138  	engine, err := veles.NewDetectionEngine([]veles.Detector{azurestorageaccountaccesskey.NewDetector()})
   139  	if err != nil {
   140  		t.Fatal(err)
   141  	}
   142  	cases := []struct {
   143  		name  string
   144  		input string
   145  		want  []veles.Secret
   146  	}{{
   147  		name:  "empty input",
   148  		input: "",
   149  	}, {
   150  		name:  "short key should not match",
   151  		input: "Azure_storage_key:" + testKey[:len(testKey)-1],
   152  	}, {
   153  		name:  "special character ($) in key should not match",
   154  		input: `Azure.storage.key : Yut$V0Vlauqsobd6tPWz2AKwHhBXMEWsAH+rSbz0UZUfaMVj1CFrcNQK47ygmrC4vHmc7eOp1LdM+AStk5mMYA==`,
   155  	},
   156  	}
   157  	for _, tc := range cases {
   158  		t.Run(tc.name, func(t *testing.T) {
   159  			got, err := engine.Detect(t.Context(), strings.NewReader(tc.input))
   160  			if err != nil {
   161  				t.Errorf("Detect() error: %v, want nil", err)
   162  			}
   163  			if diff := cmp.Diff(tc.want, got, cmpopts.EquateEmpty()); diff != "" {
   164  				t.Errorf("Detect() diff (-want +got):\n%s", diff)
   165  			}
   166  		})
   167  	}
   168  }