diff --git a/backend/internal/api/middleware/recovery_test.go b/backend/internal/api/middleware/recovery_test.go index 9e6ec95a..991a7cc0 100644 --- a/backend/internal/api/middleware/recovery_test.go +++ b/backend/internal/api/middleware/recovery_test.go @@ -197,7 +197,9 @@ func TestRecoveryNoPanicNormalFlow(t *testing.T) { } } -// TestRecoveryPanicWithNilValue tests recovery from panic(nil). +// TestRecoveryPanicWithNilValue tests recovery from panic with a nil-like value. +// Note: panic(nil) behavior changed in Go 1.21+ and triggers linter warnings, +// so we use an explicit error value instead. func TestRecoveryPanicWithNilValue(t *testing.T) { old := log.Writer() buf := &bytes.Buffer{} @@ -210,22 +212,20 @@ func TestRecoveryPanicWithNilValue(t *testing.T) { router.Use(RequestID()) router.Use(Recovery(false)) router.GET("/panic-nil", func(c *gin.Context) { - panic(nil) + panic("intentional test panic with nil-like value") }) req := httptest.NewRequest(http.MethodGet, "/panic-nil", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) - // panic(nil) does not trigger recovery in Go 1.21+ (returns nil from recover()) - // Prior versions would catch it. This test documents the expected behavior. - // With Go 1.21+, the request should complete normally since recover() returns nil - if w.Code == http.StatusInternalServerError { - out := buf.String() - // If it was caught, should log the nil panic - if !strings.Contains(out, "PANIC") { - t.Log("panic(nil) was caught but no PANIC in log") - } + // Verify the panic was recovered and returned 500 + if w.Code != http.StatusInternalServerError { + t.Errorf("expected status 500, got %d", w.Code) + } + + out := buf.String() + if !strings.Contains(out, "PANIC") { + t.Error("expected PANIC in log output") } - // Either outcome is acceptable depending on Go version } diff --git a/backend/internal/api/middleware/security_test.go b/backend/internal/api/middleware/security_test.go index 42a3805f..0253229f 100644 --- a/backend/internal/api/middleware/security_test.go +++ b/backend/internal/api/middleware/security_test.go @@ -171,7 +171,7 @@ func TestSecurityHeaders_COOP_DevelopmentMode(t *testing.T) { c.Status(http.StatusOK) }) - req := httptest.NewRequest("GET", "/test", nil) + req := httptest.NewRequest("GET", "/test", http.NoBody) resp := httptest.NewRecorder() router.ServeHTTP(resp, req) @@ -188,7 +188,7 @@ func TestSecurityHeaders_COOP_ProductionMode(t *testing.T) { c.Status(http.StatusOK) }) - req := httptest.NewRequest("GET", "/test", nil) + req := httptest.NewRequest("GET", "/test", http.NoBody) resp := httptest.NewRecorder() router.ServeHTTP(resp, req) diff --git a/backend/internal/caddy/config.go b/backend/internal/caddy/config.go index 67ed109d..9901ed1a 100644 --- a/backend/internal/caddy/config.go +++ b/backend/internal/caddy/config.go @@ -1286,11 +1286,12 @@ func buildPermissionsPolicyString(permissionsJSON string) (string, error) { // Convert allowlist items to policy format items := make([]string, len(perm.Allowlist)) for i, item := range perm.Allowlist { - if item == "self" { + switch item { + case "self": items[i] = "self" - } else if item == "*" { + case "*": items[i] = "*" - } else { + default: items[i] = fmt.Sprintf("\"%s\"", item) } } diff --git a/backend/internal/network/safeclient_test.go b/backend/internal/network/safeclient_test.go index 86fc7c90..b3082821 100644 --- a/backend/internal/network/safeclient_test.go +++ b/backend/internal/network/safeclient_test.go @@ -372,7 +372,7 @@ func TestValidateRedirectTarget_EmptyHostname(t *testing.T) { } // Create request with empty hostname - req, _ := http.NewRequest("GET", "http:///path", nil) + req, _ := http.NewRequest("GET", "http:///path", http.NoBody) err := validateRedirectTarget(req, opts) if err == nil { t.Error("expected error for empty hostname") @@ -386,7 +386,7 @@ func TestValidateRedirectTarget_Localhost(t *testing.T) { } // Test localhost blocked - req, _ := http.NewRequest("GET", "http://localhost/path", nil) + req, _ := http.NewRequest("GET", "http://localhost/path", http.NoBody) err := validateRedirectTarget(req, opts) if err == nil { t.Error("expected error for localhost when AllowLocalhost=false") @@ -406,7 +406,7 @@ func TestValidateRedirectTarget_127(t *testing.T) { DialTimeout: time.Second, } - req, _ := http.NewRequest("GET", "http://127.0.0.1/path", nil) + req, _ := http.NewRequest("GET", "http://127.0.0.1/path", http.NoBody) err := validateRedirectTarget(req, opts) if err == nil { t.Error("expected error for 127.0.0.1 when AllowLocalhost=false") @@ -425,7 +425,7 @@ func TestValidateRedirectTarget_IPv6Loopback(t *testing.T) { DialTimeout: time.Second, } - req, _ := http.NewRequest("GET", "http://[::1]/path", nil) + req, _ := http.NewRequest("GET", "http://[::1]/path", http.NoBody) err := validateRedirectTarget(req, opts) if err == nil { t.Error("expected error for ::1 when AllowLocalhost=false") @@ -550,7 +550,7 @@ func TestValidateRedirectTarget_DNSFailure(t *testing.T) { } // Use a domain that will fail DNS resolution - req, _ := http.NewRequest("GET", "http://this-domain-does-not-exist-12345.invalid/path", nil) + req, _ := http.NewRequest("GET", "http://this-domain-does-not-exist-12345.invalid/path", http.NoBody) err := validateRedirectTarget(req, opts) if err == nil { t.Error("expected error for DNS resolution failure") @@ -578,7 +578,7 @@ func TestValidateRedirectTarget_PrivateIPInRedirect(t *testing.T) { for _, url := range privateHosts { t.Run(url, func(t *testing.T) { - req, _ := http.NewRequest("GET", url, nil) + req, _ := http.NewRequest("GET", url, http.NoBody) err := validateRedirectTarget(req, opts) if err == nil { t.Errorf("expected error for redirect to private IP: %s", url) @@ -725,7 +725,7 @@ func TestValidateRedirectTarget_AllowedLocalhost(t *testing.T) { for _, url := range localhostURLs { t.Run(url, func(t *testing.T) { - req, _ := http.NewRequest("GET", url, nil) + req, _ := http.NewRequest("GET", url, http.NoBody) err := validateRedirectTarget(req, opts) if err != nil { t.Errorf("expected no error for %s when AllowLocalhost=true, got: %v", url, err) diff --git a/backend/internal/utils/url_test.go b/backend/internal/utils/url_test.go index 94513ed6..c65f87f6 100644 --- a/backend/internal/utils/url_test.go +++ b/backend/internal/utils/url_test.go @@ -43,7 +43,7 @@ func TestGetPublicURL_WithConfiguredURL(t *testing.T) { gin.SetMode(gin.TestMode) w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - req := httptest.NewRequest(http.MethodGet, "http://localhost:8080/test", nil) + req := httptest.NewRequest(http.MethodGet, "http://localhost:8080/test", http.NoBody) c.Request = req // Test GetPublicURL @@ -68,7 +68,7 @@ func TestGetPublicURL_WithTrailingSlash(t *testing.T) { gin.SetMode(gin.TestMode) w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - req := httptest.NewRequest(http.MethodGet, "http://localhost:8080/test", nil) + req := httptest.NewRequest(http.MethodGet, "http://localhost:8080/test", http.NoBody) c.Request = req publicURL := GetPublicURL(db, c) @@ -87,7 +87,7 @@ func TestGetPublicURL_Fallback_HTTPSWithTLS(t *testing.T) { c, _ := gin.CreateTestContext(w) // Create request with TLS - req := httptest.NewRequest(http.MethodGet, "https://myapp.com:8443/path", nil) + req := httptest.NewRequest(http.MethodGet, "https://myapp.com:8443/path", http.NoBody) req.TLS = &tls.ConnectionState{} // Simulate TLS connection c.Request = req @@ -104,7 +104,7 @@ func TestGetPublicURL_Fallback_HTTP(t *testing.T) { gin.SetMode(gin.TestMode) w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - req := httptest.NewRequest(http.MethodGet, "http://localhost:8080/test", nil) + req := httptest.NewRequest(http.MethodGet, "http://localhost:8080/test", http.NoBody) c.Request = req publicURL := GetPublicURL(db, c) @@ -120,7 +120,7 @@ func TestGetPublicURL_Fallback_XForwardedProto(t *testing.T) { gin.SetMode(gin.TestMode) w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - req := httptest.NewRequest(http.MethodGet, "http://internal-server:8080/test", nil) + req := httptest.NewRequest(http.MethodGet, "http://internal-server:8080/test", http.NoBody) req.Header.Set("X-Forwarded-Proto", "https") c.Request = req @@ -145,7 +145,7 @@ func TestGetPublicURL_EmptyValue(t *testing.T) { gin.SetMode(gin.TestMode) w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - req := httptest.NewRequest(http.MethodGet, "http://localhost:9000/test", nil) + req := httptest.NewRequest(http.MethodGet, "http://localhost:9000/test", http.NoBody) c.Request = req publicURL := GetPublicURL(db, c) @@ -162,7 +162,7 @@ func TestGetPublicURL_NoSettingInDB(t *testing.T) { gin.SetMode(gin.TestMode) w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - req := httptest.NewRequest(http.MethodGet, "http://fallback-host.com/test", nil) + req := httptest.NewRequest(http.MethodGet, "http://fallback-host.com/test", http.NoBody) c.Request = req publicURL := GetPublicURL(db, c) @@ -423,7 +423,7 @@ func TestGetBaseURL(t *testing.T) { if tc.hasTLS { scheme = "https" } - req := httptest.NewRequest(http.MethodGet, scheme+"://"+tc.host+"/test", nil) + req := httptest.NewRequest(http.MethodGet, scheme+"://"+tc.host+"/test", http.NoBody) // Set TLS if needed if tc.hasTLS { @@ -451,7 +451,7 @@ func TestGetBaseURL_PrecedenceOrder(t *testing.T) { c, _ := gin.CreateTestContext(w) // Request with TLS but also X-Forwarded-Proto - req := httptest.NewRequest(http.MethodGet, "https://example.com/test", nil) + req := httptest.NewRequest(http.MethodGet, "https://example.com/test", http.NoBody) req.TLS = &tls.ConnectionState{} req.Header.Set("X-Forwarded-Proto", "http") // Should be ignored when TLS is present c.Request = req @@ -467,7 +467,7 @@ func TestGetBaseURL_EmptyHost(t *testing.T) { gin.SetMode(gin.TestMode) w := httptest.NewRecorder() c, _ := gin.CreateTestContext(w) - req := httptest.NewRequest(http.MethodGet, "http:///test", nil) + req := httptest.NewRequest(http.MethodGet, "http:///test", http.NoBody) req.Host = "" // Empty host c.Request = req diff --git a/backend/internal/utils/url_testing.go b/backend/internal/utils/url_testing.go index 0b28162d..6b8f0592 100644 --- a/backend/internal/utils/url_testing.go +++ b/backend/internal/utils/url_testing.go @@ -173,7 +173,7 @@ func TestURLConnectivity(rawURL string, transport ...http.RoundTripper) (reachab // Transform error message for backward compatibility with existing tests // The security package uses lowercase in error messages, but tests expect mixed case errMsg = strings.Replace(errMsg, "dns resolution failed", "DNS resolution failed", 1) - errMsg = strings.Replace(errMsg, "private ip", "private IP", -1) + errMsg = strings.ReplaceAll(errMsg, "private ip", "private IP") // Cloud metadata endpoints are considered private IPs for test compatibility if strings.Contains(errMsg, "cloud metadata endpoints") { errMsg = strings.Replace(errMsg, "access to cloud metadata endpoints is blocked for security", "connection to private IP addresses is blocked for security", 1) @@ -235,7 +235,7 @@ func TestURLConnectivity(rawURL string, transport ...http.RoundTripper) (reachab // Perform HTTP HEAD request with strict timeout ctx := context.Background() start := time.Now() - req, err := http.NewRequestWithContext(ctx, http.MethodHead, requestURL, nil) + req, err := http.NewRequestWithContext(ctx, http.MethodHead, requestURL, http.NoBody) if err != nil { return false, 0, fmt.Errorf("failed to create request: %w", err) } diff --git a/backend/internal/utils/url_testing_enhanced_test.go b/backend/internal/utils/url_testing_enhanced_test.go index 11805e07..8bd6b26c 100644 --- a/backend/internal/utils/url_testing_enhanced_test.go +++ b/backend/internal/utils/url_testing_enhanced_test.go @@ -331,7 +331,7 @@ func TestValidateRedirectTarget(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Create a request for the redirect target - req, err := http.NewRequest("GET", tt.url, nil) + req, err := http.NewRequest("GET", tt.url, http.NoBody) if err != nil { t.Fatalf("Failed to create request: %v", err) }