feat: add Go backend env validation at startup (#901)#971
Conversation
👷 Deploy request for docmagic1 pending review.Visit the deploys page to approve it
|
👷 Deploy request for docmagic-muneer pending review.Visit the deploys page to approve it
|
|
@Bhavy12-cell is attempting to deploy a commit to the muneerali199's projects Team on Vercel. A member of the Team first needs to authorize it. |
📝 WalkthroughWalkthroughThis PR introduces a complete Go backend server with centralized environment configuration, JWT authentication middleware, and HTTP startup validation. The server validates required environment variables at startup before accepting requests, masks secrets in startup logs, and enforces JWT Bearer token validation via AuthMiddleware. ChangesGo Backend Server with JWT Auth and Env Validation
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 golangci-lint (2.12.2)level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies" 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.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@backend/go/cmd/server/main.go`:
- Around line 4-7: Replace the bare call to http.ListenAndServe with an explicit
http.Server configured with sensible timeouts (ReadHeaderTimeout, ReadTimeout,
WriteTimeout, IdleTimeout) and the existing Handler (mux) and Addr (":"+port);
create the server struct (e.g., server := &http.Server{Addr: ":"+port, Handler:
mux, ReadHeaderTimeout: ..., ReadTimeout: ..., WriteTimeout: ..., IdleTimeout:
...}) and call server.ListenAndServe() instead of http.ListenAndServe, and
ensure the main function also handles graceful shutdown (server.Shutdown) on
interrupt to cleanly close connections.
In `@backend/go/go.mod`:
- Line 5: Update the pinned jwt module to a fixed version: change the module
requirement for github.com/golang-jwt/jwt/v5 from v5.2.1 to at least v5.2.2
(preferably v5.3.1) in the go.mod entry that currently reads "require
github.com/golang-jwt/jwt/v5 v5.2.1"; then run go get
github.com/golang-jwt/jwt/v5@v5.3.1 (or go get ...@v5.2.2) and regenerate
modules (go mod tidy) so go.sum is updated accordingly and the project builds
with the patched jwt release.
In `@backend/go/pkg/auth/middleware.go`:
- Around line 27-34: The Authorization header parsing in middleware.go is
case-sensitive and rejects valid headers like "bearer <token>"; update the logic
around authHeader and tokenStr to split the header on whitespace (e.g.,
strings.Fields or strings.SplitN) and compare the scheme to "bearer" using a
case-insensitive comparison (strings.EqualFold) before extracting the token;
ensure you return the same 401 on missing/invalid scheme and set tokenStr to the
second field when valid.
In `@backend/go/pkg/config/env.go`:
- Around line 72-75: The code treats whitespace-only env values as non-empty;
change the check to trim whitespace before validation: compute a trimmed value
(e.g., trimmed := strings.TrimSpace(val)) and use trimmed when deciding if a
required variable is missing (v.Required, v.Name) or when applying v.Default;
update the existing branch that appends to missing or uses v.Default to
reference the trimmed value instead of val and ensure strings.TrimSpace is
imported where the env-parsing logic (the block using val, v.Required,
v.Default, v.Name, and missing) lives.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 979a13dd-fece-4bc7-a941-3e5dd1739716
📒 Files selected for processing (4)
backend/go/cmd/server/main.gobackend/go/go.modbackend/go/pkg/auth/middleware.gobackend/go/pkg/config/env.go
| import ( | ||
| "log" | ||
| "net/http" | ||
| "os" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate and print the relevant parts of the file
FILE="backend/go/cmd/server/main.go"
if [ ! -f "$FILE" ]; then
echo "Missing file: $FILE"
exit 1
fi
echo "=== File: $FILE (first 140 lines) ==="
nl -ba "$FILE" | sed -n '1,140p'
echo "=== Search for ListenAndServe / http.Server usage ==="
rg -n "ListenAndServe|http\.Server|ReadHeaderTimeout|ReadTimeout|WriteTimeout|IdleTimeout" "$FILE" || trueRepository: Muneerali199/Draftdeckai
Length of output: 192
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="backend/go/cmd/server/main.go"
test -f "$FILE"
echo "=== File: $FILE (lines 1-140) ==="
cat -n "$FILE" | sed -n '1,140p'
echo "=== Search for ListenAndServe / http.Server usage ==="
rg -n "ListenAndServe|http\.Server|ReadHeaderTimeout|ReadTimeout|WriteTimeout|IdleTimeout" "$FILE" || true
echo "=== Search for mux and server start context (lines 1-120) ==="
cat -n "$FILE" | sed -n '25,120p'Repository: Muneerali199/Draftdeckai
Length of output: 2300
Use an explicit http.Server with timeouts instead of bare ListenAndServe.
backend/go/cmd/server/main.go starts the server with http.ListenAndServe(":"+port, mux) and does not configure ReadHeaderTimeout, ReadTimeout, WriteTimeout, or IdleTimeout, leaving it exposed to slow-client resource exhaustion.
Proposed fix
import (
"log"
"net/http"
"os"
+ "time"
"github.com/Muneerali199/Draftdeckai/backend/go/pkg/config"
)
@@
- log.Printf("[server] Listening on :%s", port)
- if err := http.ListenAndServe(":"+port, mux); err != nil {
+ server := &http.Server{
+ Addr: ":" + port,
+ Handler: mux,
+ ReadHeaderTimeout: 5 * time.Second,
+ ReadTimeout: 15 * time.Second,
+ WriteTimeout: 15 * time.Second,
+ IdleTimeout: 60 * time.Second,
+ }
+
+ log.Printf("[server] Listening on :%s", port)
+ if err := server.ListenAndServe(); err != nil {
log.Fatalf("[server] FATAL: %v", err)
}🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/go/cmd/server/main.go` around lines 4 - 7, Replace the bare call to
http.ListenAndServe with an explicit http.Server configured with sensible
timeouts (ReadHeaderTimeout, ReadTimeout, WriteTimeout, IdleTimeout) and the
existing Handler (mux) and Addr (":"+port); create the server struct (e.g.,
server := &http.Server{Addr: ":"+port, Handler: mux, ReadHeaderTimeout: ...,
ReadTimeout: ..., WriteTimeout: ..., IdleTimeout: ...}) and call
server.ListenAndServe() instead of http.ListenAndServe, and ensure the main
function also handles graceful shutdown (server.Shutdown) on interrupt to
cleanly close connections.
|
|
||
| go 1.22 | ||
|
|
||
| require github.com/golang-jwt/jwt/v5 v5.2.1 No newline at end of file |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
What is the first patched version for GHSA-mh63-6h87-95cp (GO-2025-3553) in github.com/golang-jwt/jwt/v5, and are there any newer recommended stable versions?
💡 Result:
The first patched version for GHSA-mh63-6h87-95cp (also known as GO-2025-3553) in github.com/golang-jwt/jwt/v5 is version 5.2.2 [1][2][3]. This vulnerability relates to excessive memory allocation during header parsing [1][4]. As of June 7, 2026, there are newer recommended stable versions. The latest stable release in the v5 series is v5.3.1, which was released on January 28, 2026 [5][6][7]. Users are encouraged to use the latest stable version to ensure they have the most recent fixes and improvements [6][7].
Citations:
- 1: GHSA-mh63-6h87-95cp
- 2: https://pkg.go.dev/vuln/GO-2025-3553
- 3: https://osv.dev/vulnerability/GHSA-mh63-6h87-95cp
- 4: https://nvd.nist.gov/vuln/detail/CVE-2025-30204
- 5: https://github.com/golang-jwt/jwt/releases/tag/v5.3.1
- 6: https://pkg.go.dev/github.com/golang-jwt/jwt/v5@v5.3.1
- 7: https://github.com/golang-jwt/jwt/tree/v5.3.1
🏁 Script executed:
#!/bin/bash
set -euo pipefail
ls -la
echo "---- backend/go/go.mod ----"
if [ -f "backend/go/go.mod" ]; then
cat -n backend/go/go.mod | head -n 80
else
echo "Missing: backend/go/go.mod"
fiRepository: Muneerali199/Draftdeckai
Length of output: 5016
Critical: Upgrade github.com/golang-jwt/jwt/v5 from v5.2.1
backend/go/go.mod pins github.com/golang-jwt/jwt/v5 v5.2.1, which is affected by GHSA-mh63-6h87-95cp (GO-2025-3553) — excessive memory allocation during header parsing. Minimum fixed version is v5.2.2; latest stable in the v5 line is v5.3.1.
Proposed fix
-require github.com/golang-jwt/jwt/v5 v5.2.1
+require github.com/golang-jwt/jwt/v5 v5.2.2📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| require github.com/golang-jwt/jwt/v5 v5.2.1 | |
| require github.com/golang-jwt/jwt/v5 v5.2.2 |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/go/go.mod` at line 5, Update the pinned jwt module to a fixed
version: change the module requirement for github.com/golang-jwt/jwt/v5 from
v5.2.1 to at least v5.2.2 (preferably v5.3.1) in the go.mod entry that currently
reads "require github.com/golang-jwt/jwt/v5 v5.2.1"; then run go get
github.com/golang-jwt/jwt/v5@v5.3.1 (or go get ...@v5.2.2) and regenerate
modules (go mod tidy) so go.sum is updated accordingly and the project builds
with the patched jwt release.
Source: Linters/SAST tools
| authHeader := r.Header.Get("Authorization") | ||
| if !strings.HasPrefix(authHeader, "Bearer ") { | ||
| http.Error(w, "missing or invalid Authorization header", http.StatusUnauthorized) | ||
| return | ||
| } | ||
|
|
||
| tokenStr := strings.TrimPrefix(authHeader, "Bearer ") | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate and print the relevant section
f="backend/go/pkg/auth/middleware.go"
if [ ! -f "$f" ]; then
echo "File not found: $f" >&2
exit 1
fi
echo "=== middleware.go (lines ~1-120) ==="
nl -ba "$f" | sed -n '1,140p'
echo
echo "=== Search for Authorization parsing in auth middleware ==="
rg -n "Authorization|Bearer |TrimPrefix|HasPrefix|EqualFold|strings\.EqualFold" backend/go/pkg/auth -S || true
echo
echo "=== Search whole backend for similar parsing patterns ==="
rg -n "HasPrefix\\(.*Authorization|TrimPrefix\\(.*Bearer|EqualFold\\(.*Bearer|strings\\.EqualFold" backend/go -S || true
echo
echo "=== Look for tests referencing Bearer header casing ==="
rg -n "bearer|Bearer" backend/go --glob '*_test.go' -S || trueRepository: Muneerali199/Draftdeckai
Length of output: 167
🏁 Script executed:
#!/bin/bash
set -euo pipefail
f="backend/go/pkg/auth/middleware.go"
echo "=== Check file existence ==="
if [ -f "$f" ]; then
echo "Found: $f"
else
echo "Missing: $f" >&2
exit 1
fi
echo
echo "=== middleware.go (first 160 lines with numbers) ==="
cat -n "$f" | sed -n '1,160p'
echo
echo "=== Search in backend/go/pkg/auth for Authorization parsing patterns ==="
rg -n "Authorization|Bearer |TrimPrefix|HasPrefix|EqualFold|strings\.EqualFold" backend/go/pkg/auth -S || true
echo
echo "=== Search whole backend for similar parsing patterns ==="
rg -n "HasPrefix\\(.*Authorization|TrimPrefix\\(.*Bearer|EqualFold\\(.*Bearer|strings\\.EqualFold\\(.*Bearer" backend/go -S || true
echo
echo "=== Look for tests referencing Bearer header casing ==="
rg -n "bearer|Bearer" backend/go --glob '*_test.go' -S || trueRepository: Muneerali199/Draftdeckai
Length of output: 2695
Accept case-insensitive Bearer scheme in Authorization.
strings.HasPrefix(authHeader, "Bearer ") / strings.TrimPrefix(...) are case-sensitive, so valid headers like Authorization: bearer <token> are rejected. Parse the scheme case-insensitively and split on whitespace.
Proposed fix
- authHeader := r.Header.Get("Authorization")
- if !strings.HasPrefix(authHeader, "Bearer ") {
+ authHeader := r.Header.Get("Authorization")
+ parts := strings.Fields(authHeader)
+ if len(parts) != 2 || !strings.EqualFold(parts[0], "Bearer") {
http.Error(w, "missing or invalid Authorization header", http.StatusUnauthorized)
return
}
-
- tokenStr := strings.TrimPrefix(authHeader, "Bearer ")
+ tokenStr := parts[1]📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| authHeader := r.Header.Get("Authorization") | |
| if !strings.HasPrefix(authHeader, "Bearer ") { | |
| http.Error(w, "missing or invalid Authorization header", http.StatusUnauthorized) | |
| return | |
| } | |
| tokenStr := strings.TrimPrefix(authHeader, "Bearer ") | |
| authHeader := r.Header.Get("Authorization") | |
| parts := strings.Fields(authHeader) | |
| if len(parts) != 2 || !strings.EqualFold(parts[0], "Bearer") { | |
| http.Error(w, "missing or invalid Authorization header", http.StatusUnauthorized) | |
| return | |
| } | |
| tokenStr := parts[1] |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/go/pkg/auth/middleware.go` around lines 27 - 34, The Authorization
header parsing in middleware.go is case-sensitive and rejects valid headers like
"bearer <token>"; update the logic around authHeader and tokenStr to split the
header on whitespace (e.g., strings.Fields or strings.SplitN) and compare the
scheme to "bearer" using a case-insensitive comparison (strings.EqualFold)
before extracting the token; ensure you return the same 401 on missing/invalid
scheme and set tokenStr to the second field when valid.
| if val == "" { | ||
| if v.Required { | ||
| missing = append(missing, v.Name) | ||
| } else if v.Default != "" { |
There was a problem hiding this comment.
Treat whitespace-only required env values as missing.
" " currently passes validation for required vars, which can let the service start with unusable config. Validate trimmed values instead.
Proposed fix
- val := os.Getenv(v.Name)
+ val := strings.TrimSpace(os.Getenv(v.Name))
if val == "" {
if v.Required {
missing = append(missing, v.Name)
} else if v.Default != "" {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if val == "" { | |
| if v.Required { | |
| missing = append(missing, v.Name) | |
| } else if v.Default != "" { | |
| val := strings.TrimSpace(os.Getenv(v.Name)) | |
| if val == "" { | |
| if v.Required { | |
| missing = append(missing, v.Name) | |
| } else if v.Default != "" { |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@backend/go/pkg/config/env.go` around lines 72 - 75, The code treats
whitespace-only env values as non-empty; change the check to trim whitespace
before validation: compute a trimmed value (e.g., trimmed :=
strings.TrimSpace(val)) and use trimmed when deciding if a required variable is
missing (v.Required, v.Name) or when applying v.Default; update the existing
branch that appends to missing or uses v.Default to reference the trimmed value
instead of val and ensure strings.TrimSpace is imported where the env-parsing
logic (the block using val, v.Required, v.Default, v.Name, and missing) lives.
Muneerali199
left a comment
There was a problem hiding this comment.
Review: PR #971 — Go Env Validation at Startup
✅ Good
- Creates
backend/go/pkg/config/env.gowithValidateEnv()function - Checks required env vars (
PORT,DATABASE_URL,SUPABASE_JWT_SECRET,SUPABASE_SERVICE_ROLE_KEY,CORS_ORIGINS) at startup - Provides clear error messages for each missing var
- Integrates into
main.goand auth middleware - Clean Go module setup
⚠️ Issue
1. CORS_ORIGINS parsing might be fragile
If CORS_ORIGINS is a comma-separated list but includes whitespace (e.g., http://a.com, http://b.com), the splits will include leading spaces. Consider trimming each value.
2. ValidateEnv() returns on first failure
If multiple env vars are missing, the user only sees the first error. Consider collecting all errors and reporting them at once for better DX.
Verdict: Approve.
Muneerali199
left a comment
There was a problem hiding this comment.
Good env validation at startup. Consider collecting all missing vars before erroring (non-blocking nit).
|
This PR was approved but now has conflicts with main (likely from #972 merge which changed the same Go files). Please rebase on the latest main and force-push to resolve. |
Closes #901
Added environment variable validation at Go backend startup.
Changes:
Required vars: SUPABASE_JWT_SECRET, SUPABASE_URL
Optional vars: PORT (default: 8080), DATABASE_URL
Summary by CodeRabbit