github.com/google/osv-scalibr@v0.4.1/detector/weakcredentials/etcshadow/cracker_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 etcshadow_test
    16  
    17  import (
    18  	"context"
    19  	"errors"
    20  	"testing"
    21  
    22  	"github.com/google/osv-scalibr/detector/weakcredentials/etcshadow"
    23  )
    24  
    25  // All the hashes below are for the string "Password123" and where obtained
    26  // running: 'mkpasswd -m METHOD PASSWORD'.
    27  //
    28  // Note the obsolete descrypt will limit password to 8 characters and
    29  // there will be cracked as the value 'Password'.
    30  var testHashes = map[string]string{
    31  	"yescrypt":      "$y$j9T$huXYrFRxr5.EtlA/GqJQg1$R36Nu5MbY5YM0SzRaWbBPyGpM7KMcWtbUmBq5gDZA9B",
    32  	"gost-yescrypt": "$gy$j9T$i.krMgTvuXE2doi6Hguka/$qwn482j7gJbWZNQ3cF0YdKAud.C3vUIorQGsF0ryox3",
    33  	"scrypt":        "$7$CU..../....oupVTCfqrgm0HQkQR3JaB1$2m9CeDTqL8i5pMsc8E73A2bCIsvQPhntxBmSVlbrql2",
    34  	"bcrypt":        "$2b$05$IYDlXvHmeORyyiUwu8KKuek2LE8VrxIYZ2skPvRDDNngpXJHRq7sG",
    35  	"bcrypt-a":      "$2a$05$pRmHHyGfKl9/9AZLORG/neKW39VHGF4ptLT2MLq1BqQOnbwL6DQM6",
    36  	"sha512crypt":   "$6$5dZ5RtTlA.rNzi8o$sE23IbqB0Q57/7nI2.AqazHUnWGP06HmkadfBJ90mHgAHkWVZteoaUWV25jITMIUXC/buIgZ9hU2JYQM5qGZn1",
    37  	"sha256crypt":   "$5$bMDt75aAcRJMgynJ$7dvcQe0UPWAlpr4VFNQI2iDDUQLgwcaTOV5oQVSIR56",
    38  	"sunmd5":        "$md5,rounds=46947$ieGPlcPv$$sJ4xQqZ5DHZu0Bma2EW/..",
    39  	"md5crypt":      "$1$emQTNiRX$kZ2UzRTLgfsTBGS0M1OOb1",
    40  	"NT-Hash":       "$3$$58a478135a93ac3bf058a5ea0e8fdb71",
    41  	// Quite old and insecure password hash algorithms. Do not use the $ format.
    42  	"bsdicrypt": "_J9..Sc51o5Op8yDIuHc",
    43  	"descrypt":  "chERDiI95PGCQ",
    44  }
    45  
    46  func TestPasswordHashCracker(t *testing.T) {
    47  	cracker := etcshadow.NewPasswordCracker()
    48  	crackableHash := map[string]bool{
    49  		"bcrypt":      true,
    50  		"bcrypt-a":    true,
    51  		"sha512crypt": true,
    52  	}
    53  	for k, v := range testHashes {
    54  		password, err := cracker.Crack(t.Context(), v)
    55  		_, isCrackable := crackableHash[k]
    56  		if isCrackable && err != nil {
    57  			t.Errorf("not cracked supported hash: [%v] [%v]", k, v)
    58  		} else if !isCrackable && err == nil {
    59  			t.Errorf("cracked unsupported hash: [%v] [%v] [%v]", k, password, v)
    60  		} else if password != "Password123" && err == nil {
    61  			t.Errorf("cracked password is not 'Password123': [%v] [%v] [%v]", k, password, v)
    62  		}
    63  	}
    64  }
    65  
    66  func TestPasswordHashCrackerBadHashes(t *testing.T) {
    67  	cracker := etcshadow.NewPasswordCracker()
    68  	badValues := []string{
    69  		"$2$",
    70  		"$2$123",
    71  		"$2$123$",
    72  		"$6$",
    73  		"$6$123",
    74  		"$6$123$",
    75  		"*",
    76  		"!!",
    77  		"!",
    78  		"$2$*",
    79  		"$2$!",
    80  		"$6$*",
    81  		"$6$!",
    82  	}
    83  
    84  	for _, v := range badValues {
    85  		if _, err := cracker.Crack(t.Context(), v); !errors.Is(err, etcshadow.ErrNotCracked) {
    86  			t.Errorf("expected ErrNotCracked on hash [%s] received [%v]", v, err)
    87  		}
    88  	}
    89  }
    90  
    91  func TestPasswordHashCrackerCancelled(t *testing.T) {
    92  	cracker := etcshadow.NewPasswordCracker()
    93  	ctx, cancelFunc := context.WithCancel(t.Context())
    94  	cancelFunc()
    95  	_, err := cracker.Crack(ctx, testHashes["bcrypt"])
    96  	if !errors.Is(err, ctx.Err()) {
    97  		t.Errorf("expected error %v on cancelled context, received error %v", ctx.Err(), err)
    98  	}
    99  }