{"record":{"id":"328c8043736894e7","repo":"JuliusBrussee/caveman","slug":"password-must-be-at-least-d-bytes","errorCode":null,"errorMessage":"password must be at least %d bytes","messagePattern":"password must be at least (.+?) bytes","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"shared/platform/security/keys.go","lineNumber":22,"sourceCode":"\t\"crypto/hmac\"\n\t\"crypto/rand\"\n\t\"crypto/sha256\"\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"golang.org/x/crypto/argon2\"\n)\n\nconst (\n\tMinPasswordBytes = 12\n\tMaxPasswordBytes = 1024\n)\n\nfunc ValidatePassword(password string) error {\n\tif len(password) < MinPasswordBytes {\n\t\treturn fmt.Errorf(\"password must be at least %d bytes\", MinPasswordBytes)\n\t}\n\tif len(password) > MaxPasswordBytes {\n\t\treturn fmt.Errorf(\"password must be at most %d bytes\", MaxPasswordBytes)\n\t}\n\treturn nil\n}\n\nfunc GenerateProjectKey() (string, string, error) {\n\traw := make([]byte, 32)\n\tif _, err := rand.Read(raw); err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\tsecret := base64.RawURLEncoding.EncodeToString(raw)\n\tfull := \"cave_live_\" + secret[:12] + \"_\" + secret[12:]\n\treturn full, secret[:12], nil\n}\n\nfunc HashProjectKey(pepper, full string) string {","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/shared/platform/security/keys.go#L4-L40","documentation":"security.ValidatePassword enforces a minimum input length of MinPasswordBytes = 12 bytes (bytes, not runes) before the value is used for key derivation (argon2). Short passwords are rejected because argon2 cannot extract enough entropy from them regardless of cost parameters.","triggerScenarios":"Calling security.ValidatePassword with a string shorter than 12 bytes, e.g. a 8-character ASCII password or a multi-byte string whose UTF-8 encoding is under 12 bytes.","commonSituations":"User signup/login forms without a client-side minimum; test fixtures using 'pass' or '1234'; CJK input where fewer characters than expected still measure 12+ bytes or vice versa.","solutions":["Enforce a >=12-byte minimum in the UI/CLI before submitting (note bytes vs characters for multibyte input).","Update test fixtures and seed data to use passwords of at least 12 bytes.","If you genuinely need shorter values, generate a random passphrase rather than weakening the constant."],"exampleFix":"// before\npw := r.FormValue(\"password\")\nhash, err := security.DeriveKey(pw) // fails inside validation\n\n// after\npw := r.FormValue(\"password\")\nif err := security.ValidatePassword(pw); err != nil {\n    http.Error(w, err.Error(), http.StatusBadRequest)\n    return\n}","handlingStrategy":"validation","validationCode":"if len(password) < security.MinPasswordBytes {\n    return fmt.Errorf(\"password too short (min %d bytes)\", security.MinPasswordBytes)\n}","typeGuard":"func hasMinPasswordBytes(pw string) bool { return len(pw) >= security.MinPasswordBytes }","tryCatchPattern":"if err := security.ValidatePassword(pw); err != nil {\n    // 400 to the client with err.Error(); input problem, not a server fault\n}","preventionTips":["Enforce the 12-byte minimum in the UI and keep fixtures above it.","Remember len() counts bytes: multibyte characters count more than 1 each."],"tags":["validation","password","security","go"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}