diff --git a/pkg/engine/funcs.go b/pkg/engine/funcs.go index d95541021..bb8b96723 100644 --- a/pkg/engine/funcs.go +++ b/pkg/engine/funcs.go @@ -92,6 +92,9 @@ func htpasswd(username, password string, hashAlgorithms ...string) (string, erro if strings.Contains(username, ":") { return fmt.Sprintf("invalid username: %s", username), nil } + if strings.ContainsAny(username, "\n\r") { + return "", fmt.Errorf("invalid username %q: must not contain newline characters", username) + } if len(hashAlgorithms) > 1 { return "", fmt.Errorf("wrong number of args for htpasswd: want 2 or 3 got %d", len(hashAlgorithms)+2) @@ -104,7 +107,7 @@ func htpasswd(username, password string, hashAlgorithms ...string) (string, erro switch algorithm { case "bcrypt": - return fmt.Sprintf("%s:%s", username, sprigBcrypt(password)), nil + return bcryptHtpasswd(username, password) case "sha", "sha1": sum := sha1.Sum([]byte(password)) return fmt.Sprintf("%s:{SHA}%s", username, base64.StdEncoding.EncodeToString(sum[:])), nil @@ -113,13 +116,13 @@ func htpasswd(username, password string, hashAlgorithms ...string) (string, erro } } -func sprigBcrypt(input string) string { - hash, err := bcrypt.GenerateFromPassword([]byte(input), bcrypt.DefaultCost) +func bcryptHtpasswd(username, password string) (string, error) { + hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) if err != nil { - return fmt.Sprintf("failed to encrypt string with bcrypt: %s", err) + return "", fmt.Errorf("failed to encrypt password with bcrypt: %w", err) } - return string(hash) + return fmt.Sprintf("%s:%s", username, string(hash)), nil } // toYAML takes an interface, marshals it to yaml, and returns a string. It will diff --git a/pkg/engine/funcs_test.go b/pkg/engine/funcs_test.go index 36c4d9cb4..38b6277a7 100644 --- a/pkg/engine/funcs_test.go +++ b/pkg/engine/funcs_test.go @@ -17,6 +17,7 @@ limitations under the License. package engine import ( + "fmt" "strings" "testing" "text/template" @@ -223,11 +224,26 @@ func TestHtpasswd(t *testing.T) { tpl: `{{ htpasswd "bad:user" "testpassword" }}`, expect: `invalid username: bad:user`, }, + { + name: "rejects username with newline", + tpl: "{{ htpasswd \"bad\\nuser\" \"testpassword\" }}", + expectError: `must not contain newline characters`, + }, + { + name: "returns bcrypt errors", + tpl: fmt.Sprintf(`{{ htpasswd "testuser" %q }}`, strings.Repeat("x", 73)), + expectError: `password length exceeds 72 bytes`, + }, { name: "rejects unsupported algorithms", tpl: `{{ htpasswd "testuser" "testpassword" "md5" }}`, expectError: `unsupported htpasswd hash algorithm "md5"`, }, + { + name: "rejects too many hash algorithm args", + tpl: `{{ htpasswd "testuser" "testpassword" "sha" "extra" }}`, + expectError: `wrong number of args for htpasswd: want 2 or 3 got 4`, + }, } for _, tt := range tests {