From 13e9f2c072631c73b064cc760911a840a6f26575 Mon Sep 17 00:00:00 2001 From: sneak Date: Sun, 9 Aug 2026 00:37:30 +0000 Subject: [PATCH] style: suppress gosec G703/G704 taint false positives with justification The v2.12.2 gosec ruleset's new path-traversal (G703) and SSRF (G704) taint checks flag os.Stat/os.Remove/os.Rename calls on paths that are never attacker-controlled: our own temp files created immediately before in the same function, content-hash- or cache-key-derived storage paths, the operator-supplied config search path, and the already SSRF-guarded upstream fetch (protected by ssrfSafeDialer at the transport layer). Each suppression carries the rule ID and a one-line justification, matching this repo's existing gosec nolint convention in internal/imgcache/storage.go. No behavior change. --- internal/config/config.go | 3 ++- internal/httpfetcher/httpfetcher.go | 1 + internal/imgcache/storage.go | 21 ++++++++++++--------- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index 49b5d25..839cc48 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -299,7 +299,7 @@ func (c *Config) ensureStateDirWritable() error { keyStateDir, probePath, err) } - err = os.Remove(probePath) + err = os.Remove(probePath) //nolint:gosec // G703: our own probe file, not user input if err != nil { return fmt.Errorf("config key %q: cannot remove probe file %q: %w", keyStateDir, probePath, err) @@ -411,6 +411,7 @@ func loadConfigFile(log *slog.Logger, appName string) (*smartconfig.Config, erro for _, path := range configPaths { cleanPath := filepath.Clean(path) + //nolint:gosec // G703: config path is operator-supplied by design _, statErr := os.Stat(cleanPath) if statErr == nil { // A config file that exists but does not parse is a fatal diff --git a/internal/httpfetcher/httpfetcher.go b/internal/httpfetcher/httpfetcher.go index 0abf1fa..edd78d7 100644 --- a/internal/httpfetcher/httpfetcher.go +++ b/internal/httpfetcher/httpfetcher.go @@ -233,6 +233,7 @@ func (f *HTTPFetcher) Fetch(ctx context.Context, url string) (*FetchResult, erro startTime := time.Now() + //nolint:gosec // G704: dialer enforces SSRF protection (ssrfSafeDialer) resp, err := f.client.Do(req) fetchDuration := time.Since(startTime) diff --git a/internal/imgcache/storage.go b/internal/imgcache/storage.go index 55abdcc..0577f42 100644 --- a/internal/imgcache/storage.go +++ b/internal/imgcache/storage.go @@ -94,22 +94,23 @@ func (s *ContentStorage) Store(r io.Reader) (ContentHash, int64, error) { _, err = tmpFile.Write(data) if err != nil { _ = tmpFile.Close() - _ = os.Remove(tmpPath) + _ = os.Remove(tmpPath) //nolint:gosec // G703: our own temp file, not user input return "", 0, fmt.Errorf("failed to write content: %w", err) } err = tmpFile.Close() if err != nil { - _ = os.Remove(tmpPath) + _ = os.Remove(tmpPath) //nolint:gosec // G703: our own temp file, not user input return "", 0, fmt.Errorf("failed to close temp file: %w", err) } // Atomic rename + //nolint:gosec // G703: tmp file is ours, path is derived from content hash err = os.Rename(filepath.Clean(tmpPath), filepath.Clean(path)) if err != nil { - _ = os.Remove(tmpPath) + _ = os.Remove(tmpPath) //nolint:gosec // G703: our own temp file, not user input return "", 0, fmt.Errorf("failed to rename temp file: %w", err) } @@ -253,22 +254,23 @@ func (s *MetadataStorage) Store( _, err = tmpFile.Write(data) if err != nil { _ = tmpFile.Close() - _ = os.Remove(tmpPath) + _ = os.Remove(tmpPath) //nolint:gosec // G703: our own temp file, not user input return fmt.Errorf("failed to write metadata: %w", err) } err = tmpFile.Close() if err != nil { - _ = os.Remove(tmpPath) + _ = os.Remove(tmpPath) //nolint:gosec // G703: our own temp file, not user input return fmt.Errorf("failed to close temp file: %w", err) } // Atomic rename + //nolint:gosec // G703: tmp file is ours, path is derived from cache key err = os.Rename(filepath.Clean(tmpPath), filepath.Clean(path)) if err != nil { - _ = os.Remove(tmpPath) + _ = os.Remove(tmpPath) //nolint:gosec // G703: our own temp file, not user input return fmt.Errorf("failed to rename temp file: %w", err) } @@ -410,22 +412,23 @@ func (s *VariantStorage) Store( _, err = tmpFile.Write(data) if err != nil { _ = tmpFile.Close() - _ = os.Remove(tmpPath) + _ = os.Remove(tmpPath) //nolint:gosec // G703: our own temp file, not user input return 0, fmt.Errorf("failed to write content: %w", err) } err = tmpFile.Close() if err != nil { - _ = os.Remove(tmpPath) + _ = os.Remove(tmpPath) //nolint:gosec // G703: our own temp file, not user input return 0, fmt.Errorf("failed to close temp file: %w", err) } // Atomic rename content + //nolint:gosec // G703: tmp file is ours, path is derived from cache key err = os.Rename(filepath.Clean(tmpPath), filepath.Clean(path)) if err != nil { - _ = os.Remove(tmpPath) + _ = os.Remove(tmpPath) //nolint:gosec // G703: our own temp file, not user input return 0, fmt.Errorf("failed to rename temp file: %w", err) }