From 5fe801ff0243856bbb70d2952de892404ea157ae Mon Sep 17 00:00:00 2001 From: Junwei Zhao Date: Mon, 4 Aug 2025 20:36:32 +1000 Subject: [PATCH] Fix gosec issues, also remove binary test --- .github/workflows/ci.yml | 4 ---- internal/config/config.go | 44 +++++++++++++++++++++++++++++++++++++++ main.go | 12 ++++++++++- 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fe2fcfb..93e0922 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,10 +57,6 @@ jobs: run: | CGO_ENABLED=1 go build -v -o passkey-auth . - - name: Test binary - run: | - ./passkey-auth --help || true # Some apps don't have --help - echo "Binary built successfully" docker: runs-on: ubuntu-latest diff --git a/internal/config/config.go b/internal/config/config.go index 5675523..ebc922f 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -1,7 +1,9 @@ package config import ( + "fmt" "os" + "path/filepath" "strings" "gopkg.in/yaml.v2" @@ -46,6 +48,11 @@ func Load() (*Config, error) { configPath = "config.yaml" } + // Validate config path to prevent path traversal attacks + if err := validateConfigPath(configPath); err != nil { + return nil, fmt.Errorf("invalid config path: %w", err) + } + // Set defaults config := &Config{ Server: ServerConfig{ @@ -72,6 +79,7 @@ func Load() (*Config, error) { // Load from file if it exists if _, err := os.Stat(configPath); err == nil { + // #nosec G304 - configPath is validated above to prevent path traversal data, err := os.ReadFile(configPath) if err != nil { return nil, err @@ -126,3 +134,39 @@ func (c *Config) IsEmailAllowed(email string) bool { return false } + +// validateConfigPath ensures the config path is safe and doesn't allow path traversal +func validateConfigPath(path string) error { + // Clean the path and check for path traversal attempts + cleanPath := filepath.Clean(path) + + // Don't allow paths that try to go up directories + if strings.Contains(cleanPath, "..") { + return fmt.Errorf("path traversal not allowed") + } + + // Only allow certain file extensions + ext := filepath.Ext(cleanPath) + if ext != ".yaml" && ext != ".yml" { + return fmt.Errorf("only .yaml and .yml files are allowed") + } + + // Convert to absolute path to check if it's within allowed directories + absPath, err := filepath.Abs(cleanPath) + if err != nil { + return fmt.Errorf("failed to get absolute path: %w", err) + } + + // Get current working directory + wd, err := os.Getwd() + if err != nil { + return fmt.Errorf("failed to get working directory: %w", err) + } + + // Only allow config files in current directory or its subdirectories + if !strings.HasPrefix(absPath, wd) { + return fmt.Errorf("config file must be in current directory or subdirectories") + } + + return nil +} diff --git a/main.go b/main.go index 5e621fe..8622c65 100644 --- a/main.go +++ b/main.go @@ -5,6 +5,7 @@ import ( "log" "net/http" "os" + "time" "github.com/gorilla/mux" "github.com/rs/cors" @@ -88,8 +89,17 @@ func main() { port = "8080" } + // Create server with proper timeouts to prevent resource exhaustion + server := &http.Server{ + Addr: ":" + port, + Handler: handler, + ReadTimeout: 15 * time.Second, + WriteTimeout: 15 * time.Second, + IdleTimeout: 60 * time.Second, + } + logrus.Infof("Starting server on port %s", port) - if err := http.ListenAndServe(":"+port, handler); err != nil { + if err := server.ListenAndServe(); err != nil { log.Fatalf("Server failed to start: %v", err) } }