github.com/google/osv-scalibr@v0.4.1/veles/secrets/privatekey/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 privatekey_test
    16  
    17  import (
    18  	"crypto/ecdsa"
    19  	"crypto/elliptic"
    20  	"crypto/rand"
    21  	"crypto/rsa"
    22  	"crypto/x509"
    23  	"testing"
    24  
    25  	"github.com/google/osv-scalibr/veles/secrets/privatekey"
    26  )
    27  
    28  func TestDetector_PEMVariants(t *testing.T) {
    29  	d := privatekey.NewDetector()
    30  
    31  	cases := []struct {
    32  		name, data string
    33  	}{
    34  		{"Generic", `-----BEGIN PRIVATE KEY-----
    35  MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDDwxXFmUGrIkQZ
    36  AJ1yFkkXrOOCH3RL4QsGk24xTj7iO/ozauKXoVys3wKAzK9iwlfBY36kqqn0hvL9
    37  YhrnHAGfQJTGMnwkbZvWV6u6IKWxKqPGQp4ULIbveYd6FoXPMcwULdoRQeR+kez8
    38  aPzyu/L4cnlk31J6WqpNnjohmndAkaRnx/mGRYcn+xDvOcJbmLSHQHYgCigHzENg
    39  uilu4N9KLsD6+N0c1prr79STsiR9HRozkS+ySpiS1tG9jYMAlKSp3L+hgQDqvcK5
    40  j5zyJx7iDmIdmQjmbpnLaxj8cjW+c5FzEoqZ2pXCuLDMTOfGnzFxrRzf5Mc8tw+9
    41  hPRrkv+fAgMBAAECggEAGn6+5Z2We7kJinDA3n4Rqnil2iizrslomp09nsK+VBRW
    42  Crt+q5MVXfhY+GG7oxw2kGAM9fB7TDMvlAfBKGJr/cfZ2vFeR/flzZ7UCT797fqd
    43  a+n8RzK3mJXUNjvyJFbTDjAegZNvf4n0jz0ObzPs8J9dur9XBGRdBGBT8dRcK4rN
    44  +F9qh8JYwM+cXYbDjKvoLoxTSeCxREJ2KHVueCGxTBAwkmUXiF0jnxueqLoThoAT
    45  TLzyTYw+20F4vRJMVpLZO6X7GOht1NkIbi4vFTKh8iAnUGXRUZ8W+evW2NykGd0q
    46  0QsDsFO/Oc8Xn2DZTCutGUsHeDxq4XApnNk04t13sQKBgQDw+79+6E9xXjYUwZrG
    47  TyMb1j1va/oGEVaxPgSGm5RHcjW4xesdCftOT3eckFNVWzk4V7sG9as/s0FMQ0M3
    48  TchY8FkeK/iOBbZDExmmeDPvxzexC7nRCB+NOJZML9zCN8PTkje96uXWjTCCbJTu
    49  zqnliDLTBsXJQt1XJXU5ZnIQxQKBgQDP9fNEyRzNXttKL7lj9zutDe5AMHS10hoR
    50  gMBnRKlte3VKZfRauna/Lv3afhFHwZAEWnkUhQUK9lE8U9EnIoPNPW7jAaO69BHo
    51  1/gXR9rZibiTnYczCS9XlXaER3139Mjjn3W0v12Vi9Fylqgx2oyb1HaDtRr6HNun
    52  P33B8dbNEwKBgQDoOnLMJauJILUVQ42X1eOLi+YgXfnPpx3YKF/MKFm4kENdEL4G
    53  efwH92TZJ+xmsUZvGXxOtKiW9nPSvm8j+H092EDJZq5cjvyZnup1FhlW1LDCmP40
    54  hpOBUCrmuKkRMRQx6xJ0ns1m+SDqTyEnEVmArMPtwPURgrIyrRJOgn8h0QKBgAK4
    55  K6M1ogvJdsKklx8Ih54+tWPfflc2VSLvdRSkoDaPS7xaUvSwxYbAfY9S4LT4ggKc
    56  kELFbohzKiLI0c5aNDEF4aJUTijOskFCObtMND9t/pznjXIMZ7MUgEVAjhJ4f/wC
    57  BM8FRZsEBgwijjaAriAHijk0sBKfN/wa53EW0YFDAoGAE+k5Eq/L+/G4pDcOwm55
    58  kEzYclnfD38ZM9DfPB6k0K55TubLL9PeltRkR5yy4tjlBlDPx6wzMhbd0Xq3iCzq
    59  Twanj5YBWrq2yV2fqWgvyz3LIqlhmDNW89ThWmk7XYtD0em9dnEXlpH0JTxdQpCF
    60  tGOp/d/V3F66yalNSTXNbkA=
    61  -----END PRIVATE KEY-----
    62  `},
    63  		{"EC", `-----BEGIN EC PRIVATE KEY-----
    64  MHcCAQEEIJzFi+JZBWu0ivAZgkOuKD8OMs69xpRj7SqmVlqSbqb7oAoGCCqGSM49
    65  AwEHoUQDQgAE8cg/mxOyT54gVISe5vMWP//1lUcElOs5T2SNJ+PbCrzt6SrwRiM3
    66  Um1TNu0yGY4QbHWoW9iq8ZSVN7SSrcAong==
    67  -----END EC PRIVATE KEY-----
    68  `},
    69  		{"OpenSSH", `-----BEGIN OPENSSH PRIVATE KEY-----
    70  b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcn
    71  NhAAAAAwEAAQAAAIEA2QxOpGlLR/cvp54L6jwu+edKTVdPFs9K3w17oI1w5DhWTEce6nh7
    72  nCv+e3luyWbxuxnpyEiJiqTP7FjdX/lzKMHTvhe1PTP5nIIQhKQCunE4x9k5NrK1BZBKGo
    73  Ypg343xdPtGLNRdcaAIppuWhbmWCpgciYkAgcae7qP8ZujVCcAAAIIQp//80Kf//MAAAAH
    74  c3NoLXJzYQAAAIEA2QxOpGlLR/cvp54L6jwu+edKTVdPFs9K3w17oI1w5DhWTEce6nh7nC
    75  v+e3luyWbxuxnpyEiJiqTP7FjdX/lzKMHTvhe1PTP5nIIQhKQCunE4x9k5NrK1BZBKGoYp
    76  g343xdPtGLNRdcaAIppuWhbmWCpgciYkAgcae7qP8ZujVCcAAAADAQABAAAAgQDKSYYiBW
    77  B8OgzYE5zXOjAuCTpeyriTca8+I7rM8AX/LeKAROizbocGDpqnSY3Pd3pj/sq8N5648NI4
    78  XLo18K853cdGKdpbZHxJ//1CSp4s2YEbzJH2AYVITDv2Lc/f3Ze2Ra93y4RQDmz310R3+T
    79  9vQNi0F22XEb7kXa75ombdAQAAAEAmqs7ILluwoEzCIwJvl8qBAqAfGnDbTC1uFsUwlskX
    80  Ay6Ku8/HplhGvnl0fL51B7BC38qXopDRYU0avvACpnOcAAAAQQDtC80p4AE+fVW7Ltngca
    81  8K1TrL/GWkZSnuruXPiXiZFyQYoqiXyllLekjpBDxQSiKbGWa7t7AScAyso6EBbZvRAAAA
    82  QQDqZyhZXF7vc5Com3IzHkY2apHsWq5N8A5BQd9Wzh9KsOTvrH1W27m5TNcX7joVpHSYMl
    83  YE2rTfFMPD137muQZ3AAAAEnlzYXhlbmF4QGdtYWlsLmNvbQ==
    84  -----END OPENSSH PRIVATE KEY-----
    85  `},
    86  	}
    87  
    88  	for _, tc := range cases {
    89  		t.Run(tc.name, func(t *testing.T) {
    90  			secrets, offsets := d.Detect([]byte(tc.data))
    91  			if len(secrets) != 1 {
    92  				t.Fatalf("expected 1 match, got %d (offsets: %v)", len(secrets), offsets)
    93  			}
    94  		})
    95  	}
    96  }
    97  
    98  func TestDetector_DERVariants(t *testing.T) {
    99  	d := privatekey.NewDetector()
   100  	t.Run("PKCS1", func(t *testing.T) {
   101  		key, _ := rsa.GenerateKey(rand.Reader, 2048)
   102  		der := x509.MarshalPKCS1PrivateKey(key)
   103  
   104  		secrets, offsets := d.Detect(der)
   105  		if len(secrets) != 1 {
   106  			t.Fatalf("expected 1 matches, got %d (offsets: %v)", len(secrets), offsets)
   107  		}
   108  	})
   109  
   110  	t.Run("PKCS8", func(t *testing.T) {
   111  		key, _ := rsa.GenerateKey(rand.Reader, 2048)
   112  		der, _ := x509.MarshalPKCS8PrivateKey(key)
   113  
   114  		secrets, offsets := d.Detect(der)
   115  		if len(secrets) != 1 {
   116  			t.Fatalf("expected 1 matches, got %d (offsets: %v)", len(secrets), offsets)
   117  		}
   118  	})
   119  
   120  	t.Run("EC", func(t *testing.T) {
   121  		key, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
   122  		der, _ := x509.MarshalECPrivateKey(key)
   123  
   124  		secrets, offsets := d.Detect(der)
   125  		if len(secrets) != 1 {
   126  			t.Fatalf("expected 1 matches, got %d (offsets: %v)", len(secrets), offsets)
   127  		}
   128  	})
   129  }
   130  
   131  func TestDetector_MultipleBlocksIndependent(t *testing.T) {
   132  	d := privatekey.NewDetector()
   133  	data := `-----BEGIN EC PRIVATE KEY-----
   134  MHcCAQEEIJzFi+JZBWu0ivAZgkOuKD8OMs69xpRj7SqmVlqSbqb7oAoGCCqGSM49
   135  AwEHoUQDQgAE8cg/mxOyT54gVISe5vMWP//1lUcElOs5T2SNJ+PbCrzt6SrwRiM3
   136  Um1TNu0yGY4QbHWoW9iq8ZSVN7SSrcAong==
   137  -----END EC PRIVATE KEY-----
   138  ` +
   139  		"SOMEDATA\n" +
   140  		`-----BEGIN EC PRIVATE KEY-----
   141  MHcCAQEEIJzFi+JZBWu0ivAZgkOuKD8OMs69xpRj7SqmVlqSbqb7oAoGCCqGSM49
   142  AwEHoUQDQgAE8cg/mxOyT54gVISe5vMWP//1lUcElOs5T2SNJ+PbCrzt6SrwRiM3
   143  Um1TNu0yGY4QbHWoW9iq8ZSVN7SSrcAong==
   144  -----END EC PRIVATE KEY-----
   145  `
   146  	secrets, offsets := d.Detect([]byte(data))
   147  
   148  	if len(secrets) != 2 {
   149  		t.Fatalf("expected 2 matches, got %d (offsets: %v)", len(secrets), offsets)
   150  	}
   151  }
   152  
   153  func TestDetector_InvalidInputs(t *testing.T) {
   154  	d := privatekey.NewDetector()
   155  
   156  	cases := []struct {
   157  		name string
   158  		data []byte
   159  	}{
   160  		{
   161  			name: "CorruptedPEM",
   162  			data: []byte("-----BEGIN PRIVATE KEY-----\nINVALIDDATA\n-----END PRIVATE KEY-----"),
   163  		},
   164  		{
   165  			name: "MissingDashes",
   166  			data: []byte("BEGIN PRIVATE KEY (missing dashes) END PRIVATE KEY"),
   167  		},
   168  		{
   169  			name: "RandomString",
   170  			data: []byte("just some random text that looks nothing like a key"),
   171  		},
   172  		{
   173  			name: "InvalidDER",
   174  			data: []byte("notaderkey"),
   175  		},
   176  	}
   177  
   178  	for _, tc := range cases {
   179  		t.Run(tc.name, func(t *testing.T) {
   180  			secrets, offsets := d.Detect(tc.data)
   181  			if len(secrets) != 0 {
   182  				t.Fatalf("expected 0 matches, got %d (offsets: %v)", len(secrets), offsets)
   183  			}
   184  		})
   185  	}
   186  }