Skip to content

Commit 8388615

Browse files
committed
Refactor LoadConfigFromFile function in client_configuration.go to improve file path security and validation
1 parent f5229cf commit 8388615

File tree

1 file changed

+23
-5
lines changed

1 file changed

+23
-5
lines changed

httpclient/client_configuration.go

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"log"
99
"os"
10+
"path/filepath"
1011
"strconv"
1112
"strings"
1213
"time"
@@ -24,16 +25,35 @@ const (
2425
DefaultTimeout = 10 * time.Second
2526
FollowRedirects = true
2627
MaxRedirects = 10
28+
ConfigFileExtension = ".json"
2729
)
2830

2931
// LoadConfigFromFile loads configuration values from a JSON file into the ClientConfig struct.
3032
// This function opens the specified configuration file, reads its content, and unmarshals the JSON data
3133
// into the ClientConfig struct. It's designed to initialize the client configuration with values
3234
// from a file, complementing or overriding defaults and environment variable settings.
33-
// LoadConfigFromFile loads configuration values from a JSON file into the ClientConfig struct.
3435
func LoadConfigFromFile(filePath string) (*ClientConfig, error) {
36+
// Clean up the file path to prevent directory traversal
37+
cleanPath := filepath.Clean(filePath)
38+
39+
// Resolve the cleanPath to an absolute path to ensure it resolves any symbolic links
40+
absPath, err := filepath.EvalSymlinks(cleanPath)
41+
if err != nil {
42+
return nil, fmt.Errorf("unable to resolve the absolute path of the configuration file: %s, error: %w", filePath, err)
43+
}
44+
45+
// Check for suspicious patterns in the resolved path
46+
if strings.Contains(absPath, "..") {
47+
return nil, fmt.Errorf("invalid path, path traversal patterns detected: %s", filePath)
48+
}
49+
50+
// Ensure the file has the correct extension
51+
if filepath.Ext(absPath) != ConfigFileExtension {
52+
return nil, fmt.Errorf("invalid file extension for configuration file: %s, expected .json", filePath)
53+
}
54+
3555
// Read the entire file
36-
fileBytes, err := os.ReadFile(filePath)
56+
fileBytes, err := os.ReadFile(absPath)
3757
if err != nil {
3858
return nil, fmt.Errorf("failed to read the configuration file: %s, error: %w", filePath, err)
3959
}
@@ -48,11 +68,9 @@ func LoadConfigFromFile(filePath string) (*ClientConfig, error) {
4868

4969
log.Printf("Configuration successfully loaded from file: %s", filePath)
5070

51-
// Set default values if necessary
71+
// Set default values if necessary and validate the configuration
5272
setLoggerDefaultValues(&config)
5373
setClientDefaultValues(&config)
54-
55-
// Validate mandatory configuration fields
5674
if err := validateMandatoryConfiguration(&config); err != nil {
5775
return nil, fmt.Errorf("configuration validation failed: %w", err)
5876
}

0 commit comments

Comments
 (0)