From 2f0338211dd62b9b8d91e4597d2c0b42e8eb4f59 Mon Sep 17 00:00:00 2001 From: "Alessandro (Ale) Segala" <43508+ItalyPaleAle@users.noreply.github.com> Date: Tue, 21 Apr 2026 09:53:06 -0700 Subject: [PATCH] chore: update golangci-lint (#1440) --- .github/workflows/backend-linter.yml | 2 +- backend/go.mod | 2 +- backend/internal/bootstrap/router_bootstrap.go | 12 ++++++++---- backend/internal/common/env_config.go | 1 + backend/internal/common/env_config_test.go | 2 +- backend/internal/middleware/auth_middleware_test.go | 4 ++-- backend/internal/middleware/cache_control_test.go | 4 ++-- backend/internal/service/app_config_service.go | 7 ++++--- backend/internal/service/app_images_service_test.go | 2 +- backend/internal/service/audit_log_service.go | 2 +- backend/internal/service/one_time_access_service.go | 2 +- backend/internal/service/user_service.go | 3 ++- backend/internal/utils/callback_url_util_test.go | 2 +- backend/internal/utils/http_util_test.go | 1 + 14 files changed, 27 insertions(+), 19 deletions(-) diff --git a/.github/workflows/backend-linter.yml b/.github/workflows/backend-linter.yml index c29f2efb..a5a1839a 100644 --- a/.github/workflows/backend-linter.yml +++ b/.github/workflows/backend-linter.yml @@ -34,7 +34,7 @@ jobs: - name: Run Golangci-lint uses: golangci/golangci-lint-action@v9.0.0 with: - version: v2.9.0 + version: v2.11.4 args: --build-tags=exclude_frontend working-directory: backend only-new-issues: ${{ github.event_name == 'pull_request' }} diff --git a/backend/go.mod b/backend/go.mod index 640c0600..def69387 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -15,6 +15,7 @@ require ( github.com/dunglas/go-urlpattern v0.0.0-20241020164140-716dfa1c80b1 github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 github.com/emersion/go-smtp v0.24.0 + github.com/fsnotify/fsnotify v1.8.0 github.com/gin-contrib/slog v1.2.1 github.com/gin-gonic/gin v1.12.0 github.com/glebarez/go-sqlite v1.22.0 @@ -86,7 +87,6 @@ require ( github.com/disintegration/gift v1.2.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/fxamacker/cbor/v2 v2.9.1 // indirect github.com/gabriel-vasile/mimetype v1.4.13 // indirect github.com/gin-contrib/sse v1.1.1 // indirect diff --git a/backend/internal/bootstrap/router_bootstrap.go b/backend/internal/bootstrap/router_bootstrap.go index 5413304b..4cf7e3ea 100644 --- a/backend/internal/bootstrap/router_bootstrap.go +++ b/backend/internal/bootstrap/router_bootstrap.go @@ -264,7 +264,10 @@ func runServer(ctx context.Context, config *serverConfig) error { notifySystemdReady() <-ctx.Done() - return shutdownServer(ctx, config.server) + + // We do not pass the context because it's already been canceled + //nolint:contextcheck + return shutdownServer(config.server) } func startCertWatcher(ctx context.Context, certProvider *tlsCertProvider) (*fsnotify.Watcher, error) { @@ -321,9 +324,10 @@ func notifySystemdReady() { } } -func shutdownServer(ctx context.Context, srv *http.Server) error { - shutdownCtx, shutdownCancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second) - shutdownErr := srv.Shutdown(shutdownCtx) +func shutdownServer(srv *http.Server) error { + // Note we use the background context here as ctx has been canceled already + shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 5*time.Second) + shutdownErr := srv.Shutdown(shutdownCtx) //nolint:contextcheck shutdownCancel() if shutdownErr != nil { // Log the error only (could be context canceled) diff --git a/backend/internal/common/env_config.go b/backend/internal/common/env_config.go index 8e3b3413..f65c4103 100644 --- a/backend/internal/common/env_config.go +++ b/backend/internal/common/env_config.go @@ -347,6 +347,7 @@ func resolveFileBasedEnvVariable(field reflect.Value, fieldType reflect.StructFi return nil } + // #nosec G703 - Path is passed by the admin fileContent, err := os.ReadFile(envVarFileValue) if err != nil { return fmt.Errorf("failed to read file for env var %s: %w", envVarFileName, err) diff --git a/backend/internal/common/env_config_test.go b/backend/internal/common/env_config_test.go index b209b441..a5cbb309 100644 --- a/backend/internal/common/env_config_test.go +++ b/backend/internal/common/env_config_test.go @@ -272,7 +272,7 @@ func TestPrepareEnvConfig_FileBasedAndToLower(t *testing.T) { require.NoError(t, err) dbConnFile := tempDir + "/db_connection.txt" - dbConnContent := "postgres://user:pass@localhost/testdb" + dbConnContent := "postgres://user:pass@localhost/testdb" // #nosec G101 - test credential err = os.WriteFile(dbConnFile, []byte(dbConnContent), 0600) require.NoError(t, err) diff --git a/backend/internal/middleware/auth_middleware_test.go b/backend/internal/middleware/auth_middleware_test.go index cb39bcdf..fecf01bb 100644 --- a/backend/internal/middleware/auth_middleware_test.go +++ b/backend/internal/middleware/auth_middleware_test.go @@ -60,7 +60,7 @@ func TestWithApiKeyAuthDisabled(t *testing.T) { }) t.Run("rejects API key auth when API key auth is disabled", func(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "/api/protected", nil) + req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/api/protected", nil) req.Header.Set("X-API-Key", apiKeyToken) recorder := httptest.NewRecorder() @@ -75,7 +75,7 @@ func TestWithApiKeyAuthDisabled(t *testing.T) { }) t.Run("allows JWT auth when API key auth is disabled", func(t *testing.T) { - req := httptest.NewRequest(http.MethodGet, "/api/protected", nil) + req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/api/protected", nil) req.Header.Set("Authorization", "Bearer "+jwtToken) recorder := httptest.NewRecorder() diff --git a/backend/internal/middleware/cache_control_test.go b/backend/internal/middleware/cache_control_test.go index 3f76fb0b..6d2cd1cb 100644 --- a/backend/internal/middleware/cache_control_test.go +++ b/backend/internal/middleware/cache_control_test.go @@ -18,7 +18,7 @@ func TestCacheControlMiddlewareSetsDefault(t *testing.T) { c.Status(http.StatusOK) }) - req := httptest.NewRequest(http.MethodGet, "/test", http.NoBody) + req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/test", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) @@ -36,7 +36,7 @@ func TestCacheControlMiddlewarePreservesExistingHeader(t *testing.T) { c.Status(http.StatusOK) }) - req := httptest.NewRequest(http.MethodGet, "/custom", http.NoBody) + req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/custom", http.NoBody) w := httptest.NewRecorder() router.ServeHTTP(w, req) diff --git a/backend/internal/service/app_config_service.go b/backend/internal/service/app_config_service.go index 613fdcf8..be1865db 100644 --- a/backend/internal/service/app_config_service.go +++ b/backend/internal/service/app_config_service.go @@ -265,9 +265,9 @@ func (s *AppConfigService) UpdateAppConfigValues(ctx context.Context, keysAndVal // We update the in-memory data (in the cfg struct) and collect values to update in the database // (Note the += 2, as we are iterating through key-value pairs) dbUpdate := make([]model.AppConfigVariable, 0, len(keysAndValues)/2) - for i := 0; i < len(keysAndValues); i += 2 { - key := keysAndValues[i] - value := keysAndValues[i+1] + for i := 1; i < len(keysAndValues); i += 2 { + key := keysAndValues[i-1] + value := keysAndValues[i] // Ensure that the field is valid // We do this by grabbing the default value @@ -408,6 +408,7 @@ func (s *AppConfigService) loadDbConfigFromEnv(ctx context.Context, tx *gorm.DB) if attrs == "sensitive" { fileName := os.Getenv(envVarName + "_FILE") if fileName != "" { + // #nosec G703 - Value is provided by admin b, err := os.ReadFile(fileName) if err != nil { return nil, fmt.Errorf("failed to read secret '%s' from file '%s': %w", envVarName, fileName, err) diff --git a/backend/internal/service/app_images_service_test.go b/backend/internal/service/app_images_service_test.go index 6753bc6b..006f0c43 100644 --- a/backend/internal/service/app_images_service_test.go +++ b/backend/internal/service/app_images_service_test.go @@ -104,7 +104,7 @@ func newFileHeader(t *testing.T, filename string, content []byte) *multipart.Fil require.NoError(t, writer.Close()) - req := httptest.NewRequest(http.MethodPost, "/", body) + req := httptest.NewRequestWithContext(t.Context(), http.MethodPost, "/", body) req.Header.Set("Content-Type", writer.FormDataContentType()) _, fileHeader, err := req.FormFile("file") diff --git a/backend/internal/service/audit_log_service.go b/backend/internal/service/audit_log_service.go index abb2a23b..0a2387df 100644 --- a/backend/internal/service/audit_log_service.go +++ b/backend/internal/service/audit_log_service.go @@ -92,7 +92,7 @@ func (s *AuditLogService) CreateNewSignInWithEmail(ctx context.Context, ipAddres // If the user hasn't logged in from the same device before and email notifications are enabled, send an email if s.appConfigService.GetDbConfig().EmailLoginNotificationEnabled.IsTrue() && count <= 1 { - // We use a background context here as this is running in a goroutine + // #nosec G118 - We use a background context here as this is running in a goroutine //nolint:contextcheck go func() { span := trace.SpanFromContext(ctx) diff --git a/backend/internal/service/one_time_access_service.go b/backend/internal/service/one_time_access_service.go index 54a399ac..4093dd9b 100644 --- a/backend/internal/service/one_time_access_service.go +++ b/backend/internal/service/one_time_access_service.go @@ -97,7 +97,7 @@ func (s *OneTimeAccessService) requestOneTimeAccessEmailInternal(ctx context.Con return nil, err } - // We use a background context here as this is running in a goroutine + // #nosec G118 - We use a background context here as this is running in a goroutine //nolint:contextcheck go func() { span := trace.SpanFromContext(ctx) diff --git a/backend/internal/service/user_service.go b/backend/internal/service/user_service.go index f3ec4fbc..8899d68e 100644 --- a/backend/internal/service/user_service.go +++ b/backend/internal/service/user_service.go @@ -136,7 +136,8 @@ func (s *UserService) GetProfilePicture(ctx context.Context, userID string) (io. // Save the default picture for future use (in a goroutine to avoid blocking) defaultPictureBytes := defaultPicture.Bytes() - //nolint:contextcheck + //#nosec G118 - We use a background context as this is running in background + // nolint:contextcheck go func() { // Use bytes.NewReader because we need an io.ReadSeeker rErr := s.fileStorage.Save(context.Background(), defaultPicturePath, bytes.NewReader(defaultPictureBytes)) diff --git a/backend/internal/utils/callback_url_util_test.go b/backend/internal/utils/callback_url_util_test.go index 9e109620..a6eb1625 100644 --- a/backend/internal/utils/callback_url_util_test.go +++ b/backend/internal/utils/callback_url_util_test.go @@ -35,7 +35,7 @@ func TestValidateCallbackURLPattern(t *testing.T) { }, { name: "wildcard userinfo", - pattern: "https://user:*@example.com/callback", + pattern: "https://user:*@example.com/callback", // #nosec G101 - Test credential shouldError: false, }, { diff --git a/backend/internal/utils/http_util_test.go b/backend/internal/utils/http_util_test.go index db5af19a..e0c828b2 100644 --- a/backend/internal/utils/http_util_test.go +++ b/backend/internal/utils/http_util_test.go @@ -64,6 +64,7 @@ func TestBearerAuth(t *testing.T) { } } +// #nosec G101 - Test credentials func TestOAuthClientBasicAuth(t *testing.T) { tests := []struct { name string