🧹 chore: Improve BasicAuth middleware RFC compliance#3743
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughReworks BasicAuth to strictly parse/validate Authorization headers (length limit, invalid chars, exact "Basic " scheme), distinguish 400/401/431 error paths, accept unpadded Base64, require UTF-8 (canonicalized), NFC-normalize credentials, add a configurable Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant C as Client
participant S as Server (Fiber)
participant BA as BasicAuth Middleware
participant H as Authorizer
C->>S: HTTP request with Authorization header
S->>BA: Invoke middleware
rect rgb(245,250,255)
BA->>BA: Check header presence & length
alt Missing header
BA-->>C: 401 Unauthorized + WWW-Authenticate
else Header too large
BA-->>C: 431 Request Header Fields Too Large
else Validate scheme & chars (Basic + single space)
alt Malformed (invalid chars/CTLs/format/Base64/UTF‑8/no colon)
BA-->>C: 400 Bad Request (cfg.BadRequest or default)
else Decode Base64 (Std → RawStd fallback) & NFC normalize
BA->>H: Call Authorizer(username, password)
alt Authorized
BA->>S: Next()
S-->>C: Handler response
else Unauthorized
BA-->>C: 401 Unauthorized + WWW-Authenticate
end
end
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Summary of Changes
Hello @gaby, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request significantly enhances the robustness and security of the BasicAuth middleware by implementing stricter validation for the 'Authorization' header and its contents. It introduces more granular error handling for malformed requests, ensuring that only properly formatted and encoded credentials are processed. These changes aim to prevent potential vulnerabilities arising from ambiguous or non-standard header formats and improve the overall reliability of authentication.
Highlights
- Stricter Authorization Header Parsing: The BasicAuth middleware now enforces space-only separation between the 'Basic' scheme and credentials, rejecting any whitespace within the credentials themselves. This improves adherence to RFC standards and security.
- Enhanced Credential Validation: Decoded BasicAuth credentials are now validated for canonical UTF-8 encoding and checked for control characters. Any invalid characters or non-UTF-8 content will result in a '400 Bad Request' response.
- Specific Error Responses: The middleware now differentiates between various types of malformed 'Authorization' headers, returning '400 Bad Request' for syntax errors or invalid content, and '431 Request Header Fields Too Large' for headers exceeding the configured limit, instead of a generic '401 Unauthorized'.
- Charset Enforcement: The
Charsetconfiguration for BasicAuth is now strictly enforced to 'UTF-8'. Configuring any other charset will cause the application to panic, ensuring consistent and secure credential interpretation. - Updated Go Dependencies: Several Go dependencies, including
golang.org/x/crypto,golang.org/x/net,golang.org/x/sys, andgolang.org/x/text, have been updated to their latest versions. - New BadRequest Handler: A new
BadRequesthandler option has been added to the BasicAuth configuration, allowing custom responses for requests with malformed 'Authorization' headers.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
There was a problem hiding this comment.
Pull Request Overview
This PR enhances the BasicAuth middleware to enforce stricter validation of Authorization headers and improve charset handling for security and RFC compliance.
- Adds comprehensive header validation to reject malformed Authorization headers with appropriate 400 Bad Request responses
- Restricts charset configuration to only allow UTF-8 and adds Unicode normalization support
- Implements extensive test coverage for edge cases including control characters, whitespace handling, and invalid encodings
Reviewed Changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| middleware/basicauth/config.go | Adds BadRequest handler config and enforces UTF-8-only charset with validation |
| middleware/basicauth/basicauth.go | Implements strict Authorization header parsing with control character detection and Unicode normalization |
| middleware/basicauth/basicauth_test.go | Adds comprehensive test coverage for malformed headers, charset validation, and edge cases |
| go.mod | Updates golang.org/x dependencies to newer versions |
| docs/middleware/basicauth.md | Updates documentation to reflect new validation behavior and UTF-8 charset restriction |
There was a problem hiding this comment.
Code Review
This pull request significantly improves the BasicAuth middleware by enhancing its RFC compliance and security. The changes introduce stricter validation for the Authorization header, such as enforcing space-only separation, rejecting whitespace within credentials, and validating for control characters. The middleware now also correctly handles different response statuses like 400 Bad Request and 431 Request Header Fields Too Large. Support for unpadded Base64 credentials and UTF-8 normalization are excellent additions. The test suite has been substantially expanded to cover these new features and edge cases, which is great to see. The documentation and configuration have also been updated accordingly. Overall, this is a high-quality contribution that makes the middleware more robust and secure.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (10)
docs/middleware/basicauth.md (4)
7-10: Tighten wording on whitespace semantics (SP-only between scheme and token).To prevent ambiguity: explicitly say that only SP (0x20) may separate "Basic" and the token; leading/trailing SP/HTAB are trimmed; any HTAB or SP inside the token is rejected.
Apply this doc tweak:
-Basic Authentication middleware for [Fiber](https://github.com/gofiber/fiber) that provides HTTP basic auth. It calls the next handler for valid credentials and returns [`401 Unauthorized`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401) for missing or invalid credentials, [`400 Bad Request`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400) for malformed `Authorization` headers, or [`431 Request Header Fields Too Large`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/431) when the header exceeds size limits. Credentials may omit Base64 padding as permitted by RFC 7235's `token68` syntax. +Basic Authentication middleware for [Fiber](https://github.com/gofiber/fiber) that provides HTTP basic auth. It calls the next handler for valid credentials and returns [`401 Unauthorized`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/401) for missing or invalid credentials, [`400 Bad Request`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/400) for malformed `Authorization` headers, or [`431 Request Header Fields Too Large`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/431) when the header exceeds size limits. Only SP (0x20) may separate the `Basic` scheme and token (one or more SPs allowed); leading/trailing SP/HTAB are ignored; any whitespace inside the token is rejected. Credentials may omit Base64 padding as permitted by RFC 7235 `token68`.
9-9: Clarify panic timing for invalid charset.Note it panics during middleware setup (New/configDefault), not at request time.
-Only the `UTF-8` charset is supported; any other value will panic. +Only the `UTF-8` charset is supported; any other value will panic during middleware initialization.
101-106: Disambiguate “case-insensitive” to excludeutf8(no hyphen).Readers may assume
utf8is allowed. The code only acceptsUTF-8(case-insensitive, hyphen required).-| Charset | `string` | Charset sent in the `WWW-Authenticate` header. Only `"UTF-8"` is supported (case-insensitive). | `"UTF-8"` | +| Charset | `string` | Charset sent in the `WWW-Authenticate` header. Only `"UTF-8"` is supported (case-insensitive; `"utf8"` without hyphen is not allowed). | `"UTF-8"` |
109-120: Mention default BadRequest behavior explicitly.You list
BadRequest: nilin the example, but the runtime default returns bare 400 withoutWWW-Authenticate. Spell that out for users.var ConfigDefault = Config{ @@ - BadRequest: nil, + BadRequest: nil, // when nil, the middleware responds with 400 without WWW-Authenticate }middleware/basicauth/config.go (2)
107-113: Charset enforcement is correct; consider explicitly rejectingutf8in docs, not code.Code canonically accepts only
UTF-8(case-insensitive) and panics otherwise—good. No change needed here; just ensure docs call out thatutf8(no hyphen) is invalid (see docs comment).
131-141: Micro: precompute the WWW-Authenticate header string once.Avoids rebuilding the header on every 401 while keeping behavior identical.
- if cfg.Unauthorized == nil { - cfg.Unauthorized = func(c fiber.Ctx) error { - header := "Basic realm=" + strconv.Quote(cfg.Realm) - if cfg.Charset != "" { - header += ", charset=" + strconv.Quote(cfg.Charset) - } - c.Set(fiber.HeaderWWWAuthenticate, header) + if cfg.Unauthorized == nil { + header := "Basic realm=" + strconv.Quote(cfg.Realm) + if cfg.Charset != "" { + header += ", charset=" + strconv.Quote(cfg.Charset) + } + cfg.Unauthorized = func(c fiber.Ctx) error { + c.Set(fiber.HeaderWWWAuthenticate, header) c.Set(fiber.HeaderCacheControl, "no-store") c.Set(fiber.HeaderVary, fiber.HeaderAuthorization) return c.SendStatus(fiber.StatusUnauthorized) } }middleware/basicauth/basicauth.go (1)
95-100: Tiny simplification: use strings.Cut for the colon split.Same behavior, slightly clearer.
- index := strings.Index(creds, ":") - if index == -1 { + if index := strings.Index(creds, ":"); index == -1 { return cfg.BadRequest(c) - } - - // Get the username and password - username := creds[:index] - password := creds[index+1:] + } else { + // Get the username and password + username := creds[:index] + password := creds[index+1:] + // existing CTL check + authorizer continue below... + _ = username + _ = password + }(Alternatively:
u, p, ok := strings.Cut(creds, ":").)middleware/basicauth/basicauth_test.go (3)
227-259: Whitespace matrix is comprehensive.Nice coverage of SP vs HTAB and internal-space rejection.
Add a case with leading/trailing HTAB only (no SP) to assert trimming still succeeds:
{"\tBasic " + creds + "\t", fiber.StatusTeapot},(You already have it; consider also "Basic "+creds+"\t\t".)
396-402: Charset panic tests are good; consider addingutf8(no hyphen) as a negative.require.Panics(t, func() { New(Config{Charset: "ISO-8859-1"}) }) +require.Panics(t, func() { New(Config{Charset: "utf8"}) }) // no hyphen should panic require.NotPanics(t, func() { New(Config{Charset: "utf-8"}) }) require.NotPanics(t, func() { New(Config{Charset: "UTF-8"}) }) require.NotPanics(t, func() { New(Config{}) })
227-259: Add a test for custom BadRequest handler.To lock in the no-challenge behavior and allow custom body/status, add a small test where BadRequest writes a body or different status.
app := fiber.New() app.Use(New(Config{ Users: map[string]string{"john": sha256Hash("doe")}, BadRequest: func(c fiber.Ctx) error { c.Set("X-Bad", "1") return c.Status(fiber.StatusBadRequest).SendString("bad auth") }, })) // send malformed header and assert status/body/header and no WWW-Authenticate.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
go.modis excluded by!**/*.modgo.sumis excluded by!**/*.sum,!**/*.sum
📒 Files selected for processing (4)
docs/middleware/basicauth.md(3 hunks)middleware/basicauth/basicauth.go(4 hunks)middleware/basicauth/basicauth_test.go(6 hunks)middleware/basicauth/config.go(5 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go
📄 CodeRabbit inference engine (AGENTS.md)
**/*.go: Format Go code using gofumpt (enforced viamake format)
Ensure code passes golangci-lint checks (enforced viamake lint)
Optimize struct field alignment using betteralign (enforced viamake betteralign)
Modernize Go code using gopls modernize (enforced viamake modernize)
Files:
middleware/basicauth/config.gomiddleware/basicauth/basicauth.gomiddleware/basicauth/basicauth_test.go
docs/**
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Review and update the contents of the
docsfolder if necessary when modifying code
Files:
docs/middleware/basicauth.md
🧠 Learnings (3)
📚 Learning: 2024-10-02T23:03:31.727Z
Learnt from: sixcolors
PR: gofiber/fiber#3016
File: middleware/session/store.go:164-167
Timestamp: 2024-10-02T23:03:31.727Z
Learning: Unit tests in this project use testify require.
Applied to files:
middleware/basicauth/basicauth_test.go
📚 Learning: 2024-10-08T19:06:06.583Z
Learnt from: sixcolors
PR: gofiber/fiber#2922
File: middleware/cors/utils.go:63-71
Timestamp: 2024-10-08T19:06:06.583Z
Learning: The project uses the testify/assert package for assertions in unit tests.
Applied to files:
middleware/basicauth/basicauth_test.go
📚 Learning: 2024-11-15T07:56:21.623Z
Learnt from: ReneWerner87
PR: gofiber/fiber#3161
File: app.go:923-932
Timestamp: 2024-11-15T07:56:21.623Z
Learning: In the Fiber framework, breaking changes are acceptable when moving from version 2 to version 3, including modifications to method signatures such as in the `Test` method in `app.go`.
Applied to files:
middleware/basicauth/basicauth_test.go
🧬 Code graph analysis (3)
middleware/basicauth/config.go (3)
app.go (1)
Handler(40-40)ctx_interface_gen.go (1)
Ctx(18-430)constants.go (1)
StatusBadRequest(73-73)
middleware/basicauth/basicauth.go (1)
constants.go (2)
HeaderAuthorization(163-163)StatusRequestHeaderFieldsTooLarge(100-100)
middleware/basicauth/basicauth_test.go (3)
middleware/basicauth/basicauth.go (1)
New(26-118)middleware/basicauth/config.go (1)
Config(19-73)constants.go (6)
StatusUnauthorized(74-74)HeaderWWWAuthenticate(166-166)StatusBadRequest(73-73)HeaderAuthorization(163-163)StatusTeapot(91-91)StatusRequestHeaderFieldsTooLarge(100-100)
🔇 Additional comments (24)
middleware/basicauth/config.go (2)
46-51: Good addition: dedicated BadRequest handler (400) without WWW-Authenticate.This cleanly separates malformed headers from auth failures and avoids misleading challenges.
151-191: Password hash parsing/verifiers look solid.Covers bcrypt,
{SHA512},{SHA256}, and raw SHA-256 (hex/base64) with constant-time compares. Nice.middleware/basicauth/basicauth.go (4)
38-69: Header validation and SP-only separation are correctly enforced.ASCII-only header check, trimming outer SP/HTAB, scheme match, and SP-only separation after
Basicall look correct and align with tests/docs.
71-79: Base64 fallback to RawStdEncoding is appropriate.Covers token68 unpadded credentials; invalid inputs route to 400 via BadRequest.
80-85: UTF-8 validation and NFC normalization are well-chosen.Ensures credentials are valid and normalized before processing.
106-109: CTL filter is correct (blocks C0/C1 controls).Prevents embedded control chars in decoded credentials and avoids invoking the authorizer on malformed data.
middleware/basicauth/basicauth_test.go (18)
150-161: UTF-8 header canonicalization test is on point.
174-176: Correct to expect 400 for non-base64 token.
178-193: 401 for missing Basic scheme plus challenge header—good coverage.
194-208: 400 on missing colon is correct and prevents authorizer invocation.
261-289: CTLs in decoded creds correctly yield 400 and skip authorizer.
291-307: Unpadded Base64 acceptance test—great.
308-323: Non-ASCII in header value returns 400 without challenge—good negative path.
324-344: Invalid UTF-8 rejection test ensures authorizer is not called.
345-369: NFC normalization test is excellent.
370-393: CR/LF edge cases: good defensive tests.
404-431: HeaderLimit tests properly exercise 431 vs allowed path.
545-572: Hash variant acceptance test suite is solid.Covers bcrypt, SHA-512, and SHA-256 encodings.
574-602: Negative hash-variant tests look good.
111-135: AuthorizerCtx test validates context propagation—nice.
210-225: Empty Authorization cases yield 401—good baseline.
495-512: Immutable mode coverage is appreciated.
433-462: Benchmarks retained—no concerns.
464-493: Upper-case scheme benchmark retained—LGTM.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
middleware/basicauth/basicauth.go (2)
59-59: Past nit addressed: replaced manual loop with TrimLeft.You adopted TrimLeft for skipping spaces after the scheme, which improves readability.
118-122: Past nit addressed and a tiny micro-optimization idea.You extracted a named helper (nice). If you want, scan bytes to avoid rune decoding, since non-ASCII is rejected anyway.
-func containsInvalidHeaderChars(s string) bool { - return strings.IndexFunc(s, func(r rune) bool { - return (r < 0x20 && r != '\t') || r == 0x7F || r >= 0x80 - }) != -1 -} +func containsInvalidHeaderChars(s string) bool { + for i := 0; i < len(s); i++ { + b := s[i] + if (b < 0x20 && b != '\t') || b == 0x7F || b >= 0x80 { + return true + } + } + return false +}
🧹 Nitpick comments (4)
middleware/basicauth/basicauth.go (4)
65-73: Minor: pick encoding once to avoid a second decode attempt.You can choose RawStdEncoding upfront when no '=' padding is present to skip the initial failing decode.
Apply:
- raw, err := base64.StdEncoding.DecodeString(rest) - if err != nil { - if _, ok := err.(base64.CorruptInputError); ok { - raw, err = base64.RawStdEncoding.DecodeString(rest) - } - if err != nil { - return cfg.BadRequest(c) - } - } + enc := base64.StdEncoding + if !strings.ContainsRune(rest, '=') { + enc = base64.RawStdEncoding + } + raw, err := enc.DecodeString(rest) + if err != nil { + return cfg.BadRequest(c) + }
83-87: Optional: avoid UnsafeString to reduce secret lifetime coupling.Using UnsafeString ties the password string to the mutable backing array. Prefer an explicit copy and wipe the byte slice afterward for a modest hardening, at the cost of one alloc.
- if c.App().Config().Immutable { - creds = string(raw) - } else { - creds = utils.UnsafeString(raw) - } + creds = string(raw) // copy + for i := range raw { // best-effort wipe + raw[i] = 0 + }If perf is critical, keep current behavior but add a short comment documenting the trade-off.
91-99: Use strings.Cut for first-colon split.Improves clarity and avoids manual index handling.
- index := strings.Index(creds, ":") - if index == -1 { - return cfg.BadRequest(c) - } - // Get the username and password - username := creds[:index] - password := creds[index+1:] + username, password, ok := strings.Cut(creds, ":") + if !ok { + return cfg.BadRequest(c) + }
100-103: Consider rejecting Unicode format controls (Cf) too.You currently block Cc controls. Adding Cf (e.g., bidi markers, zero-width) reduces spoofing risk in credentials.
-func containsCTL(s string) bool { - return strings.IndexFunc(s, unicode.IsControl) != -1 -} +func containsCTL(s string) bool { + return strings.IndexFunc(s, func(r rune) bool { + return unicode.IsControl(r) || unicode.Is(unicode.Cf, r) + }) != -1 +}And keep the existing call sites.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
.github/workflows/benchmark.ymlis excluded by!**/*.yml
📒 Files selected for processing (1)
middleware/basicauth/basicauth.go(4 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go
📄 CodeRabbit inference engine (AGENTS.md)
**/*.go: Format Go code using gofumpt (enforced viamake format)
Ensure code passes golangci-lint checks (enforced viamake lint)
Optimize struct field alignment using betteralign (enforced viamake betteralign)
Modernize Go code using gopls modernize (enforced viamake modernize)
Files:
middleware/basicauth/basicauth.go
🧬 Code graph analysis (1)
middleware/basicauth/basicauth.go (1)
constants.go (2)
HeaderAuthorization(163-163)StatusRequestHeaderFieldsTooLarge(100-100)
🔇 Additional comments (4)
middleware/basicauth/basicauth.go (4)
38-44: Good: Early presence/size gating with correct 431 response.Missing header → 401 and oversize header → 431 is a solid split and matches the intent of rejecting malformed/abusive inputs early.
45-62: Spec-aligned SP-only separation; tabs and internal WSP correctly rejected.The sequence (trim OWS, require at least one SP after “Basic”, TrimLeft spaces, then forbid any SP/HTAB in the token) matches RFC 9110/7617 expectations. Nice.
74-79: Good: UTF-8 validation and NFC normalization.Validating UTF-8 and normalizing to NFC before splitting credentials is the right call to prevent confusables and decoding surprises.
37-62: BadRequest defaulted in configDefault — no action needed.middleware/basicauth/config.go sets cfg.BadRequest to return StatusBadRequest when nil (middleware/basicauth/config.go:143–145), so the BadRequest calls are safe.
There was a problem hiding this comment.
Actionable comments posted: 0
♻️ Duplicate comments (2)
middleware/basicauth/basicauth.go (2)
114-116: Resolved prior suggestion: concise control-char check.Using
strings.IndexFuncwithunicode.IsControladdresses earlier feedback to streamline this check.
118-122: Resolved prior suggestion: header-char validation extracted.Encapsulating invalid header rune filtering into
containsInvalidHeaderCharsimproves readability and reuse.
🧹 Nitpick comments (5)
middleware/basicauth/basicauth.go (3)
55-62: Clarify and align whitespace policy after the scheme (exactly one space vs. multiple).Code enforces exactly one space; tests mirror that, but PR objectives say “allow multiple spaces.” Please confirm intent. If multiple spaces should be allowed, consider:
- rest := auth[len(basicScheme):] - if len(rest) < 2 || rest[0] != ' ' || rest[1] == ' ' { - return cfg.BadRequest(c) - } - rest = rest[1:] - if strings.IndexFunc(rest, unicode.IsSpace) != -1 { - return cfg.BadRequest(c) - } + rest := auth[len(basicScheme):] + orig := rest + rest = strings.TrimLeft(rest, " ") + if len(rest) == len(orig) || rest == "" { // require at least one space, then payload + return cfg.BadRequest(c) + } + // no whitespace inside token68 + if strings.ContainsAny(rest, " \t") { + return cfg.BadRequest(c) + }
60-62: Nit: prefer ASCII check for payload whitespace.Since non-ASCII was already rejected,
unicode.IsSpaceis heavier than needed. Usestrings.ContainsAny(rest, " \t").- if strings.IndexFunc(rest, unicode.IsSpace) != -1 { + if strings.ContainsAny(rest, " \t") { return cfg.BadRequest(c) }
100-103: Consider whether to also reject Unicode “format” characters (Cf) in creds.
unicode.IsControlcovers Cc; if you want to prevent invisible format characters (e.g., ZWJ) in usernames/passwords, extend the predicate.- if containsCTL(username) || containsCTL(password) { + if containsCTLOrFormat(username) || containsCTLOrFormat(password) { return cfg.BadRequest(c) }Additional helper (outside this hunk):
func containsCTLOrFormat(s string) bool { return strings.IndexFunc(s, func(r rune) bool { return unicode.IsControl(r) || unicode.In(r, unicode.Format) }) != -1 }middleware/basicauth/basicauth_test.go (2)
237-260: Augment whitespace table for coverage (leading multiples and case-insensitive scheme).Add a few benign-accept cases to cement intent and guard regressions.
cases := []struct { header string status int }{ {"Basic " + creds, fiber.StatusTeapot}, {" Basic " + creds, fiber.StatusTeapot}, + {" Basic " + creds, fiber.StatusTeapot}, // multiple leading spaces + {"\t Basic " + creds, fiber.StatusTeapot}, // mixed leading ws + {"basic " + creds, fiber.StatusTeapot}, // scheme case-insensitive {"Basic " + creds, fiber.StatusBadRequest}, {"Basic " + creds, fiber.StatusBadRequest}, {"Basic\t" + creds, fiber.StatusBadRequest}, {"Basic \t" + creds, fiber.StatusBadRequest}, {"Basic\u00A0" + creds, fiber.StatusBadRequest}, {"Basic\u3000" + creds, fiber.StatusBadRequest}, {"\tBasic " + creds + "\t", fiber.StatusTeapot}, {"Basic " + creds[:4] + " " + creds[4:], fiber.StatusBadRequest}, }
405-433: Add exact-boundary case for HeaderLimit.Minor: include a test where header length equals the limit to document inclusivity.
t.Run("allowed", func(t *testing.T) { t.Parallel() app := fiber.New() - app.Use(New(Config{Users: map[string]string{"john": hashedJohn}, HeaderLimit: 100})) + // exact boundary + limit := len("Basic ") + len(creds) + app.Use(New(Config{Users: map[string]string{"john": hashedJohn}, HeaderLimit: limit})) app.Get("/", func(c fiber.Ctx) error { return c.SendStatus(fiber.StatusTeapot) }) req := httptest.NewRequest(fiber.MethodGet, "/", nil) req.Header.Set(fiber.HeaderAuthorization, "Basic "+creds) resp, err := app.Test(req) require.NoError(t, err) require.Equal(t, fiber.StatusTeapot, resp.StatusCode) })
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
middleware/basicauth/basicauth.go(4 hunks)middleware/basicauth/basicauth_test.go(6 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go
📄 CodeRabbit inference engine (AGENTS.md)
**/*.go: Format Go code using gofumpt (enforced viamake format)
Ensure code passes golangci-lint checks (enforced viamake lint)
Optimize struct field alignment using betteralign (enforced viamake betteralign)
Modernize Go code using gopls modernize (enforced viamake modernize)
Files:
middleware/basicauth/basicauth_test.gomiddleware/basicauth/basicauth.go
🧠 Learnings (3)
📚 Learning: 2024-10-02T23:03:31.727Z
Learnt from: sixcolors
PR: gofiber/fiber#3016
File: middleware/session/store.go:164-167
Timestamp: 2024-10-02T23:03:31.727Z
Learning: Unit tests in this project use testify require.
Applied to files:
middleware/basicauth/basicauth_test.go
📚 Learning: 2024-10-08T19:06:06.583Z
Learnt from: sixcolors
PR: gofiber/fiber#2922
File: middleware/cors/utils.go:63-71
Timestamp: 2024-10-08T19:06:06.583Z
Learning: The project uses the testify/assert package for assertions in unit tests.
Applied to files:
middleware/basicauth/basicauth_test.go
📚 Learning: 2024-11-15T07:56:21.623Z
Learnt from: ReneWerner87
PR: gofiber/fiber#3161
File: app.go:923-932
Timestamp: 2024-11-15T07:56:21.623Z
Learning: In the Fiber framework, breaking changes are acceptable when moving from version 2 to version 3, including modifications to method signatures such as in the `Test` method in `app.go`.
Applied to files:
middleware/basicauth/basicauth_test.go
🧬 Code graph analysis (2)
middleware/basicauth/basicauth_test.go (3)
middleware/basicauth/basicauth.go (1)
New(26-112)middleware/basicauth/config.go (1)
Config(19-73)constants.go (6)
StatusUnauthorized(74-74)HeaderWWWAuthenticate(166-166)StatusBadRequest(73-73)HeaderAuthorization(163-163)StatusTeapot(91-91)StatusRequestHeaderFieldsTooLarge(100-100)
middleware/basicauth/basicauth.go (1)
constants.go (2)
HeaderAuthorization(163-163)StatusRequestHeaderFieldsTooLarge(100-100)
🔇 Additional comments (13)
middleware/basicauth/basicauth.go (2)
42-44: Good: header length guard mapped to 431.Early limit check with StatusRequestHeaderFieldsTooLarge is correct and RFC-aligned.
65-79: Good: robust Base64 handling + UTF-8 validation + NFC normalization.Fallback to RawStdEncoding, explicit BadRequest on decode failure, UTF-8 check, and NFC normalization look solid.
middleware/basicauth/basicauth_test.go (11)
150-161: LGTM: canonicalize charset in WWW-Authenticate.Verifies canonical “UTF-8” even when config is “utf-8”.
174-176: LGTM: malformed token yields 400.Matches middleware’s BadRequest path for decode failures.
178-192: LGTM: wrong scheme → 401 with challenge.Confirms presence of
WWW-Authenticateon 401.
194-208: LGTM: missing colon → 400 and no authorizer call.Tracks malformed creds distinctly from auth failures.
262-290: LGTM: control chars → 400, no challenge, no authorizer.Clear separation of malformed vs. unauthorized flows.
292-307: LGTM: unpadded Base64 accepted.Covers token68 padding variations.
309-323: LGTM: non-ASCII in header value → 400.Matches header ASCII-only policy.
325-344: LGTM: invalid UTF-8 in creds → 400 and authorizer not invoked.Prevents downstream confusion with invalid text.
346-369: LGTM: NFC normalization path verified.Ensures authorizer receives normalized username/password.
371-395: LGTM: CR/LF edges produce 400.Good low-level coverage with fasthttp handler.
397-404: LGTM: charset guardrails.Panics on non-UTF-8; accepts canonical and lowercased UTF-8; default OK.
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #3743 +/- ##
==========================================
- Coverage 92.08% 92.06% -0.02%
==========================================
Files 115 114 -1
Lines 11771 11799 +28
==========================================
+ Hits 10839 10863 +24
- Misses 675 678 +3
- Partials 257 258 +1
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
|
@gaby |
|
Fixing lint issues |
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (3)
middleware/basicauth/basicauth_test.go (3)
227-261: Whitespace matrix is thorough; add 1 trailing-space case?Optional: add {"Basic " + creds + " ", fiber.StatusTeapot} to cover right-trim behavior for a plain space (you already cover tabs).
262-290: Fix unused parameter in Authorizer (revive).Rename p to _ to satisfy lint without changing behavior.
Apply this diff:
- Authorizer: func(_, p string, _ fiber.Ctx) bool { + Authorizer: func(_, _ string, _ fiber.Ctx) bool { called = true return true },
325-345: Fix unused parameter in Authorizer (revive).Same lint as above; rename p to _.
Apply this diff:
- Authorizer: func(_, p string, _ fiber.Ctx) bool { + Authorizer: func(_, _ string, _ fiber.Ctx) bool { called = true return true },
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
middleware/basicauth/basicauth.go(5 hunks)middleware/basicauth/basicauth_test.go(6 hunks)middleware/basicauth/config.go(6 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
- middleware/basicauth/config.go
- middleware/basicauth/basicauth.go
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go
📄 CodeRabbit inference engine (AGENTS.md)
**/*.go: Format Go code using gofumpt (enforced viamake format)
Ensure code passes golangci-lint checks (enforced viamake lint)
Optimize struct field alignment using betteralign (enforced viamake betteralign)
Modernize Go code using gopls modernize (enforced viamake modernize)
Files:
middleware/basicauth/basicauth_test.go
🧠 Learnings (3)
📚 Learning: 2024-10-08T19:06:06.583Z
Learnt from: sixcolors
PR: gofiber/fiber#3016
File: middleware/session/store.go:164-167
Timestamp: 2024-10-08T19:06:06.583Z
Learning: Unit tests in this project use testify require.
Applied to files:
middleware/basicauth/basicauth_test.go
📚 Learning: 2024-10-08T19:06:06.583Z
Learnt from: sixcolors
PR: gofiber/fiber#2922
File: middleware/cors/utils.go:63-71
Timestamp: 2024-10-08T19:06:06.583Z
Learning: The project uses the testify/assert package for assertions in unit tests.
Applied to files:
middleware/basicauth/basicauth_test.go
📚 Learning: 2024-11-15T07:56:21.623Z
Learnt from: ReneWerner87
PR: gofiber/fiber#3161
File: app.go:923-932
Timestamp: 2024-11-15T07:56:21.623Z
Learning: In the Fiber framework, breaking changes are acceptable when moving from version 2 to version 3, including modifications to method signatures such as in the `Test` method in `app.go`.
Applied to files:
middleware/basicauth/basicauth_test.go
🧬 Code graph analysis (1)
middleware/basicauth/basicauth_test.go (3)
middleware/basicauth/basicauth.go (1)
New(27-115)middleware/basicauth/config.go (1)
Config(19-73)constants.go (6)
StatusUnauthorized(74-74)HeaderWWWAuthenticate(166-166)StatusBadRequest(73-73)HeaderAuthorization(163-163)StatusTeapot(91-91)StatusRequestHeaderFieldsTooLarge(100-100)
🪛 GitHub Check: lint
middleware/basicauth/basicauth_test.go
[failure] 331-331:
unused-parameter: parameter 'p' seems to be unused, consider removing or renaming it as _ (revive)
[failure] 267-267:
unused-parameter: parameter 'p' seems to be unused, consider removing or renaming it as _ (revive)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: repeated
- GitHub Check: lint
- GitHub Check: unit (1.25.x, windows-latest)
- GitHub Check: Compare
🔇 Additional comments (11)
middleware/basicauth/basicauth_test.go (11)
11-11: Import addition looks good."strings" is used for unpadded Base64 trimming; import grouping/order is fine.
150-161: Good validation of charset canonicalization in WWW-Authenticate.Ensures "utf-8" config yields uppercase "UTF-8" in header.
174-176: Correctly classifies invalid base64 as 400 Bad Request.Matches stricter header parsing behavior.
178-193: Missing scheme path tested well.401 with proper WWW-Authenticate header is correct.
194-208: No-colon credentials → 400 is correct.Covers malformed "username:password" shape.
292-308: Accepting unpadded Base64 covered nicely.Good use of TrimRight("=") to simulate missing padding.
309-323: Non-ASCII in header value correctly yields 400.Direct fasthttp path ensures header-char validation is exercised.
346-369: Great UTF-8 NFC normalization test.Confirms normalized username is passed to Authorizer.
371-395: CR/LF around header properly rejected.Good low-level coverage using RequestCtx.
397-404: Charset config guardrails are well tested.Panic on non-UTF-8 and acceptance of UTF-8/default look correct.
418-418: 431 expectation for over-limit header is correct.Matches HeaderLimit enforcement.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (7)
middleware/basicauth/basicauth_test.go (7)
174-176: Also assert no WWW-Authenticate header on 400.Strengthen the BadRequest contract.
Apply:
require.NoError(t, err) require.Equal(t, fiber.StatusBadRequest, resp.StatusCode) + require.Empty(t, resp.Header.Get(fiber.HeaderWWWAuthenticate))
194-208: Add header absence check for malformed credentials.Ensure 400 responses don’t include WWW-Authenticate.
Apply:
require.NoError(t, err) require.Equal(t, fiber.StatusBadRequest, resp.StatusCode) + require.Empty(t, resp.Header.Get(fiber.HeaderWWWAuthenticate))
227-260: Name subcases for easier debugging; add trailing-whitespace cases.Current table is good; subtests will pinpoint failures and a couple more cases close gaps.
Apply:
- for _, tt := range cases { + for i, tt := range cases { + t.Run(fmt.Sprintf("case_%d", i), func(t *testing.T) { req := httptest.NewRequest(fiber.MethodGet, "/", nil) req.Header.Set(fiber.HeaderAuthorization, tt.header) resp, err := app.Test(req) require.NoError(t, err) require.Equal(t, tt.status, resp.StatusCode) - } + }) + }And extend the cases:
cases := []struct { header string status int }{ {"Basic " + creds, fiber.StatusTeapot}, {" Basic " + creds, fiber.StatusTeapot}, + {"Basic " + creds + " ", fiber.StatusTeapot}, + {"Basic " + creds + "\t", fiber.StatusTeapot}, {"Basic " + creds, fiber.StatusBadRequest},
309-324: Non‑ASCII bytes in header should also assert no challenge.Mirror the 400-no-header contract.
Apply:
handler(fctx) require.Equal(t, fiber.StatusBadRequest, fctx.Response.StatusCode()) +require.Empty(t, string(fctx.Response.Header.Peek(fiber.HeaderWWWAuthenticate)))
325-345: Invalid UTF‑8 test: add absence of challenge header.Small assertion to lock in behavior.
Apply:
require.NoError(t, err) require.Equal(t, fiber.StatusBadRequest, resp.StatusCode) require.False(t, called) +require.Empty(t, resp.Header.Get(fiber.HeaderWWWAuthenticate))
346-369: Normalization path looks right.Optional: also assert UsernameFromContext(c) is the normalized form to exercise locals-setting.
If desired, change the route:
- app.Get("/", func(c fiber.Ctx) error { return c.SendStatus(fiber.StatusTeapot) }) + app.Get("/", func(c fiber.Ctx) error { return c.SendString(UsernameFromContext(c)) })And assert:
require.NoError(t, err) - require.Equal(t, fiber.StatusTeapot, resp.StatusCode) + require.Equal(t, fiber.StatusOK, resp.StatusCode) + body, _ := io.ReadAll(resp.Body) + require.Equal(t, "é", string(body)) require.True(t, called)
371-395: Header CR/LF edges: also assert no challenge.Keeps 400 semantics consistent.
Apply inside the loop:
handler(fctx) require.Equal(t, fiber.StatusBadRequest, fctx.Response.StatusCode()) +require.Empty(t, string(fctx.Response.Header.Peek(fiber.HeaderWWWAuthenticate)))
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
middleware/basicauth/basicauth_test.go(6 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go
📄 CodeRabbit inference engine (AGENTS.md)
**/*.go: Format Go code using gofumpt (enforced viamake format)
Ensure code passes golangci-lint checks (enforced viamake lint)
Optimize struct field alignment using betteralign (enforced viamake betteralign)
Modernize Go code using gopls modernize (enforced viamake modernize)
Files:
middleware/basicauth/basicauth_test.go
🧠 Learnings (3)
📚 Learning: 2024-10-08T19:06:06.583Z
Learnt from: sixcolors
PR: gofiber/fiber#3016
File: middleware/session/store.go:164-167
Timestamp: 2024-10-08T19:06:06.583Z
Learning: Unit tests in this project use testify require.
Applied to files:
middleware/basicauth/basicauth_test.go
📚 Learning: 2024-10-08T19:06:06.583Z
Learnt from: sixcolors
PR: gofiber/fiber#2922
File: middleware/cors/utils.go:63-71
Timestamp: 2024-10-08T19:06:06.583Z
Learning: The project uses the testify/assert package for assertions in unit tests.
Applied to files:
middleware/basicauth/basicauth_test.go
📚 Learning: 2024-11-15T07:56:21.623Z
Learnt from: ReneWerner87
PR: gofiber/fiber#3161
File: app.go:923-932
Timestamp: 2024-11-15T07:56:21.623Z
Learning: In the Fiber framework, breaking changes are acceptable when moving from version 2 to version 3, including modifications to method signatures such as in the `Test` method in `app.go`.
Applied to files:
middleware/basicauth/basicauth_test.go
🧬 Code graph analysis (1)
middleware/basicauth/basicauth_test.go (3)
middleware/basicauth/basicauth.go (1)
New(27-115)middleware/basicauth/config.go (1)
Config(19-73)constants.go (6)
StatusUnauthorized(74-74)HeaderWWWAuthenticate(166-166)StatusBadRequest(73-73)HeaderAuthorization(163-163)StatusTeapot(91-91)StatusRequestHeaderFieldsTooLarge(100-100)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
- GitHub Check: Compare
- GitHub Check: unit (1.25.x, macos-latest)
- GitHub Check: unit (1.25.x, windows-latest)
- GitHub Check: repeated
🔇 Additional comments (7)
middleware/basicauth/basicauth_test.go (7)
11-11: Import of strings is correct and necessary.Used in the unpadded Base64 test; no lint issues.
150-161: Canonicalize charset to UTF-8 — good coverage.Confirms header normalization when config sets "utf-8".
178-193: Missing scheme path looks correct.401 with proper WWW-Authenticate header is asserted.
262-290: Control character rejection and authorizer short‑circuit — solid.Covers ASCII CTLs and C1 bytes; verifies no WWW-Authenticate and no authorizer call.
292-308: Unpadded Base64 acceptance — good regression coverage.Exercise fallback to RawStdEncoding.
397-404: Charset tests vs docs — confirm intended casing behavior.Tests allow "utf-8" (case-insensitive). Ensure config.go/docs state that casing is ignored and header is emitted as "UTF-8".
418-418: 431 on header size limit — correct expectation.Matches new limit check path.
Co-authored-by: RW <rene@gofiber.io>

Summary
Authorizationheader and reject any whitespace within credentialscharsetto canonical UTF-8 value and validate decoded credentials for control charactersBasicscheme, allowing multiple spaces while rejecting tabs or spaces inside credentials