Skip to content

feat: add Go backend env validation at startup (#901)#971

Open
Bhavy12-cell wants to merge 1 commit into
Muneerali199:mainfrom
Bhavy12-cell:feat/go-env-validation
Open

feat: add Go backend env validation at startup (#901)#971
Bhavy12-cell wants to merge 1 commit into
Muneerali199:mainfrom
Bhavy12-cell:feat/go-env-validation

Conversation

@Bhavy12-cell

@Bhavy12-cell Bhavy12-cell commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Closes #901

Added environment variable validation at Go backend startup.

Changes:

  • Created backend/go/pkg/config/env.go:
    • validateEnv() checks all required vars and logs ALL missing ones
    • PrintStartupSummary() shows all vars at startup with secrets masked
    • Optional vars get defaults applied automatically
    • No panic() — exits cleanly with log.Fatalf and code 1
  • Created backend/go/pkg/auth/middleware.go:
    • Removed panic for missing SUPABASE_JWT_SECRET
    • Returns HTTP 500 with clear message instead
  • Created backend/go/cmd/server/main.go:
    • Calls ValidateEnv() before starting HTTP server
    • Prints startup summary after validation passes
    • Health check endpoint at /health

Required vars: SUPABASE_JWT_SECRET, SUPABASE_URL
Optional vars: PORT (default: 8080), DATABASE_URL

Summary by CodeRabbit

  • New Features
    • Backend HTTP server now available with a health check endpoint.
    • JWT-based authentication middleware protects API endpoints with secure token validation.
    • Environment variable validation system provides startup diagnostics with masked secrets.

@netlify

netlify Bot commented Jun 7, 2026

Copy link
Copy Markdown

👷 Deploy request for docmagic1 pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit e07f04c

@netlify

netlify Bot commented Jun 7, 2026

Copy link
Copy Markdown

👷 Deploy request for docmagic-muneer pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit e07f04c

@vercel

vercel Bot commented Jun 7, 2026

Copy link
Copy Markdown

@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.

@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This 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.

Changes

Go Backend Server with JWT Auth and Env Validation

Layer / File(s) Summary
Environment configuration and validation
backend/go/pkg/config/env.go
Defines EnvVar struct with required/optional/secret flags and default values. ValidateEnv() validates all required vars, applies defaults for optional ones, and aggregates missing vars into a single error. PrintStartupSummary() logs env state at startup with secrets masked to prevent credential exposure.
Module and JWT dependency
backend/go/go.mod
Declares Go 1.22 module and adds github.com/golang-jwt/jwt/v5 v5.2.1 dependency required by authentication middleware.
JWT authentication middleware
backend/go/pkg/auth/middleware.go
Implements AuthMiddleware that reads SUPABASE_JWT_SECRET from config, requires Bearer token in Authorization header, validates HMAC-signed JWT, and returns HTTP 401 for invalid/expired tokens or HTTP 500 if secret is unset. Forwards valid requests to next handler.
Server entrypoint and HTTP startup
backend/go/cmd/server/main.go
Implements main() entry point that calls ValidateEnv() to check required configuration, prints masked startup summary via PrintStartupSummary(), registers /health endpoint returning JSON {"status":"ok"}, and starts HTTP server on :PORT with fatal exit on validation or startup failures.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

  • Muneerali199/Draftdeckai#509: Both PRs add HTTP JWT authentication middleware that reads SUPABASE_JWT_SECRET, validates the Authorization: Bearer <token> header with jwt/v5, and rejects invalid/expired tokens.

Suggested labels

level 2, level:beginner, gssoc:approved

Poem

🐰 A server hops to life with care,
Environment checks beyond compare,
Secrets masked so none may see,
JWT guards each API spree! 🔐

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: adding Go backend environment variable validation at startup. It is concise, specific, and clearly summarizes the primary objective.
Description check ✅ Passed The description covers key changes with specific file details and requirements. While testing details are minimal, the essential information about what was implemented is clearly present.
Linked Issues check ✅ Passed The implementation addresses all objectives from issue #901: validates required vars (SUPABASE_JWT_SECRET, SUPABASE_URL), logs all missing vars together, exits with code 1, supports optional vars with defaults, masks secrets in startup summary, removes panics, and adds the health endpoint.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the linked issue #901: env validation infrastructure, JWT middleware improvements, and server entrypoint setup. No unrelated modifications were introduced.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between c13823c and e07f04c.

📒 Files selected for processing (4)
  • backend/go/cmd/server/main.go
  • backend/go/go.mod
  • backend/go/pkg/auth/middleware.go
  • backend/go/pkg/config/env.go

Comment on lines +4 to +7
import (
"log"
"net/http"
"os"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 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" || true

Repository: 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.

Comment thread backend/go/go.mod

go 1.22

require github.com/golang-jwt/jwt/v5 v5.2.1 No newline at end of file

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 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:


🏁 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"
fi

Repository: 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.

Suggested change
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

Comment on lines +27 to +34
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 ")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 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 || true

Repository: 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 || true

Repository: 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.

Suggested change
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.

Comment on lines +72 to +75
if val == "" {
if v.Required {
missing = append(missing, v.Name)
} else if v.Default != "" {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Suggested change
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 Muneerali199 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: PR #971 — Go Env Validation at Startup

✅ Good

  • Creates backend/go/pkg/config/env.go with ValidateEnv() 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.go and 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 Muneerali199 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good env validation at startup. Consider collecting all missing vars before erroring (non-blocking nit).

@Muneerali199

Copy link
Copy Markdown
Owner

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.

@Muneerali199 Muneerali199 added gssoc:approved Required GSSoC approval label mentor:muneerali199 Reviewed by mentor muneerali199 labels Jun 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gssoc:approved Required GSSoC approval label mentor:muneerali199 Reviewed by mentor muneerali199

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Go Backend][Feature]: Add environment variable validation at startup

2 participants