fix(engine): harden htpasswd validation

Signed-off-by: Asish Kumar <officialasishkumar@gmail.com>
pull/32033/head
Asish Kumar 6 months ago
parent ae347bfcbb
commit 900d0ab344

@ -92,6 +92,9 @@ func htpasswd(username, password string, hashAlgorithms ...string) (string, erro
if strings.Contains(username, ":") { if strings.Contains(username, ":") {
return fmt.Sprintf("invalid username: %s", username), nil 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 { if len(hashAlgorithms) > 1 {
return "", fmt.Errorf("wrong number of args for htpasswd: want 2 or 3 got %d", len(hashAlgorithms)+2) 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 { switch algorithm {
case "bcrypt": case "bcrypt":
return fmt.Sprintf("%s:%s", username, sprigBcrypt(password)), nil return bcryptHtpasswd(username, password)
case "sha", "sha1": case "sha", "sha1":
sum := sha1.Sum([]byte(password)) sum := sha1.Sum([]byte(password))
return fmt.Sprintf("%s:{SHA}%s", username, base64.StdEncoding.EncodeToString(sum[:])), nil 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 { func bcryptHtpasswd(username, password string) (string, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(input), bcrypt.DefaultCost) hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil { 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 // toYAML takes an interface, marshals it to yaml, and returns a string. It will

@ -17,6 +17,7 @@ limitations under the License.
package engine package engine
import ( import (
"fmt"
"strings" "strings"
"testing" "testing"
"text/template" "text/template"
@ -223,11 +224,26 @@ func TestHtpasswd(t *testing.T) {
tpl: `{{ htpasswd "bad:user" "testpassword" }}`, tpl: `{{ htpasswd "bad:user" "testpassword" }}`,
expect: `invalid username: bad:user`, 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", name: "rejects unsupported algorithms",
tpl: `{{ htpasswd "testuser" "testpassword" "md5" }}`, tpl: `{{ htpasswd "testuser" "testpassword" "md5" }}`,
expectError: `unsupported htpasswd hash algorithm "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 { for _, tt := range tests {

Loading…
Cancel
Save