github.com/google/osv-scalibr@v0.4.1/veles/secrets/jwt/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 jwt_test
    16  
    17  import (
    18  	"strings"
    19  	"testing"
    20  
    21  	"github.com/google/go-cmp/cmp"
    22  	"github.com/google/go-cmp/cmp/cmpopts"
    23  	"github.com/google/osv-scalibr/veles"
    24  	"github.com/google/osv-scalibr/veles/secrets/jwt"
    25  )
    26  
    27  const (
    28  	testJWT    = `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c`
    29  	testJWTAlt = `eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJvc2NhbGliLXRlc3QiLCJhdWQiOiJleGFtcGxlLmNvbSIsImV4cCI6NDQ2ODg3MDQ5MX0.Q5rFj8b0cR2pD7eL1O4mK3vT5wA6xY7zB8C9dE0fG1hI2jJ3kL4mN5oP6qR7sT8uV9wX0yZ1a2b3c4d5e6f7g`
    30  )
    31  
    32  // TestJWTDetector_TruePositives verifies that the detector finds valid JWT tokens.
    33  func TestJWTDetector_TruePositives(t *testing.T) {
    34  	engine, err := veles.NewDetectionEngine([]veles.Detector{jwt.NewDetector()})
    35  	if err != nil {
    36  		t.Fatalf("Failed to initialize detection engine: %v", err)
    37  	}
    38  
    39  	cases := []struct {
    40  		name  string
    41  		input string
    42  		want  []veles.Secret
    43  	}{{
    44  		name:  "simple_matching_string",
    45  		input: testJWT,
    46  		want: []veles.Secret{
    47  			jwt.Token{Value: testJWT},
    48  		},
    49  	}, {
    50  		name:  "token_in_surrounding_text_(Bearer_token)",
    51  		input: "Authorization: Bearer " + testJWT + " end",
    52  		want: []veles.Secret{
    53  			jwt.Token{Value: testJWT},
    54  		},
    55  	}, {
    56  		name:  "multiple_distinct_matches",
    57  		input: testJWT + "\n" + testJWTAlt,
    58  		want: []veles.Secret{
    59  			jwt.Token{Value: testJWT},
    60  			jwt.Token{Value: testJWTAlt},
    61  		},
    62  	}}
    63  
    64  	for _, tc := range cases {
    65  		t.Run(tc.name, func(t *testing.T) {
    66  			got, err := engine.Detect(t.Context(), strings.NewReader(tc.input))
    67  			if err != nil {
    68  				t.Errorf("Detect() error: %v, want nil", err)
    69  			}
    70  			if diff := cmp.Diff(tc.want, got, cmpopts.EquateEmpty()); diff != "" {
    71  				t.Errorf("Detect() diff (-want +got):\n%s", diff)
    72  			}
    73  		})
    74  	}
    75  }
    76  
    77  // TestJWTDetector_TrueNegatives verifies that the detector ignores invalid, non-JWT strings.
    78  func TestJWTDetector_TrueNegative(t *testing.T) {
    79  	engine, err := veles.NewDetectionEngine([]veles.Detector{jwt.NewDetector()})
    80  	if err != nil {
    81  		t.Fatalf("Failed to initialize detection engine: %v", err)
    82  	}
    83  
    84  	cases := []struct {
    85  		name  string
    86  		input string
    87  	}{{
    88  		name:  "empty_input",
    89  		input: "",
    90  	}, {
    91  		name:  "not_enough_segments",
    92  		input: `header.payload`,
    93  	}, {
    94  		name:  "invalid_base64_characters",
    95  		input: `eyJh!!ciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.b.c`,
    96  	}, {
    97  		name:  "invalid_header",
    98  		input: `eyJhbGciOiJII1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c`,
    99  	}, {
   100  		name:  "invalid_payload",
   101  		input: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c`,
   102  	}}
   103  
   104  	for _, tc := range cases {
   105  		t.Run(tc.name, func(t *testing.T) {
   106  			got, err := engine.Detect(t.Context(), strings.NewReader(tc.input))
   107  			if err != nil {
   108  				t.Errorf("Detect() error: %v, want nil", err)
   109  			}
   110  			// We expect no secrets to be found.
   111  			var want []veles.Secret
   112  			if diff := cmp.Diff(want, got, cmpopts.EquateEmpty()); diff != "" {
   113  				t.Errorf("Detect() diff (-want +got):\n%s", diff)
   114  			}
   115  		})
   116  	}
   117  }