diff --git a/common/config.go b/common/config.go index 03ecbbb..fe910e1 100644 --- a/common/config.go +++ b/common/config.go @@ -790,12 +790,12 @@ func ValidateConfig() error { if (Configuration.ListeningType == ListeningSecurely || Configuration.ListeningType == ListeningBoth || Configuration.ListeningType == ListeningSecureUnix) && Configuration.NodeType == CSS { if len(Configuration.ServerCertificate) > 0 && strings.HasPrefix(Configuration.ServerCertificate, "/") { - if _, err := ValidateFilePathWithExtension(Configuration.ServerCertificate, Configuration.PersistenceRootPath, certExtensions); err != nil { + if _, err := ValidateFilePathWithExtension(Configuration.ServerCertificate, "", certExtensions); err != nil { return &configError{fmt.Sprintf("Invalid ServerCertificate path: %s", err)} } } if len(Configuration.ServerKey) > 0 && strings.HasPrefix(Configuration.ServerKey, "/") { - if _, err := ValidateFilePathWithExtension(Configuration.ServerKey, Configuration.PersistenceRootPath, keyExtensions); err != nil { + if _, err := ValidateFilePathWithExtension(Configuration.ServerKey, "", keyExtensions); err != nil { return &configError{fmt.Sprintf("Invalid ServerKey path: %s", err)} } } @@ -804,17 +804,17 @@ func ValidateConfig() error { // Validate MQTT certificate paths (when using MQTT with SSL) if mqtt && Configuration.MQTTUseSSL { if len(Configuration.MQTTCACertificate) > 0 && strings.HasPrefix(Configuration.MQTTCACertificate, "/") { - if _, err := ValidateFilePathWithExtension(Configuration.MQTTCACertificate, Configuration.PersistenceRootPath, certExtensions); err != nil { + if _, err := ValidateFilePathWithExtension(Configuration.MQTTCACertificate, "", certExtensions); err != nil { return &configError{fmt.Sprintf("Invalid MQTTCACertificate path: %s", err)} } } if len(Configuration.MQTTSSLCert) > 0 && strings.HasPrefix(Configuration.MQTTSSLCert, "/") { - if _, err := ValidateFilePathWithExtension(Configuration.MQTTSSLCert, Configuration.PersistenceRootPath, certExtensions); err != nil { + if _, err := ValidateFilePathWithExtension(Configuration.MQTTSSLCert, "", certExtensions); err != nil { return &configError{fmt.Sprintf("Invalid MQTTSSLCert path: %s", err)} } } if len(Configuration.MQTTSSLKey) > 0 && strings.HasPrefix(Configuration.MQTTSSLKey, "/") { - if _, err := ValidateFilePathWithExtension(Configuration.MQTTSSLKey, Configuration.PersistenceRootPath, keyExtensions); err != nil { + if _, err := ValidateFilePathWithExtension(Configuration.MQTTSSLKey, "", keyExtensions); err != nil { return &configError{fmt.Sprintf("Invalid MQTTSSLKey path: %s", err)} } } @@ -823,7 +823,7 @@ func ValidateConfig() error { // Validate MongoDB certificate path (when using MongoDB with SSL) if Configuration.StorageProvider == Mongo && Configuration.MongoUseSSL { if len(Configuration.MongoCACertificate) > 0 && strings.HasPrefix(Configuration.MongoCACertificate, "/") { - if _, err := ValidateFilePathWithExtension(Configuration.MongoCACertificate, Configuration.PersistenceRootPath, certExtensions); err != nil { + if _, err := ValidateFilePathWithExtension(Configuration.MongoCACertificate, "", certExtensions); err != nil { return &configError{fmt.Sprintf("Invalid MongoCACertificate path: %s", err)} } } diff --git a/common/pathvalidation.go b/common/pathvalidation.go index d313f72..6039266 100644 --- a/common/pathvalidation.go +++ b/common/pathvalidation.go @@ -38,15 +38,17 @@ func ValidateFilePath(path, baseDir string) (string, error) { return "", fmt.Errorf("invalid file path: failed to get absolute path: %w", err) } - absBase, err := filepath.Abs(baseDir) - if err != nil { - return "", fmt.Errorf("invalid file path: failed to get absolute base directory: %w", err) - } + if baseDir != "" { + absBase, err := filepath.Abs(baseDir) + if err != nil { + return "", fmt.Errorf("invalid file path: failed to get absolute base directory: %w", err) + } - // Ensure the path is within the base directory (CWE-22: Path Traversal) - // The path must either be exactly the base directory or start with base directory + separator - if absPath != absBase && !strings.HasPrefix(absPath, absBase+string(os.PathSeparator)) { - return "", fmt.Errorf("invalid file path: path traversal detected: %s is outside allowed directory %s", absPath, absBase) + // Ensure the path is within the base directory (CWE-22: Path Traversal) + // The path must either be exactly the base directory or start with base directory + separator + if absPath != absBase && !strings.HasPrefix(absPath, absBase+string(os.PathSeparator)) { + return "", fmt.Errorf("invalid file path: path traversal detected: %s is outside allowed directory %s", absPath, absBase) + } } return absPath, nil diff --git a/core/base/certificates.go b/core/base/certificates.go index 214807b..eb21e1c 100644 --- a/core/base/certificates.go +++ b/core/base/certificates.go @@ -11,7 +11,6 @@ import ( "math/big" "net" "os" - "path/filepath" "time" "github.com/open-horizon/edge-utilities/logger" @@ -55,8 +54,8 @@ func setupCertificates() error { if len(keyExtensions) == 0 { keyExtensions = []string{".pem", ".key"} } - validatedCert, certPathErr := common.ValidateFilePathWithExtension(common.Configuration.ServerCertificate, filepath.Dir(common.Configuration.ServerCertificate), certExtensions) - validatedKey, keyPathErr := common.ValidateFilePathWithExtension(common.Configuration.ServerKey, filepath.Dir(common.Configuration.ServerKey), keyExtensions) + validatedCert, certPathErr := common.ValidateFilePathWithExtension(common.Configuration.ServerCertificate, "", certExtensions) + validatedKey, keyPathErr := common.ValidateFilePathWithExtension(common.Configuration.ServerKey, "", keyExtensions) if certPathErr == nil && keyPathErr == nil { _, err = tls.LoadX509KeyPair(validatedCert, validatedKey) if err == nil { diff --git a/core/communications/mqttCommunication.go b/core/communications/mqttCommunication.go index 87018e9..150ee56 100644 --- a/core/communications/mqttCommunication.go +++ b/core/communications/mqttCommunication.go @@ -384,7 +384,7 @@ func newTLSConfig() *tls.Config { if len(certExtensions) == 0 { certExtensions = []string{".pem", ".crt", ".cert"} } - validatedPath, pathErr := common.ValidateFilePathWithExtension(caCert, common.Configuration.PersistenceRootPath, certExtensions) + validatedPath, pathErr := common.ValidateFilePathWithExtension(caCert, "", certExtensions) if pathErr != nil { // If path validation fails, treat as certificate content rather than file path pemCerts = []byte(common.Configuration.MQTTCACertificate) @@ -429,8 +429,8 @@ func newTLSConfig() *tls.Config { if len(keyExtensions) == 0 { keyExtensions = []string{".pem", ".key"} } - validatedCert, certPathErr := common.ValidateFilePathWithExtension(cert, common.Configuration.PersistenceRootPath, certExtensions) - validatedKey, keyPathErr := common.ValidateFilePathWithExtension(key, common.Configuration.PersistenceRootPath, keyExtensions) + validatedCert, certPathErr := common.ValidateFilePathWithExtension(cert, "", certExtensions) + validatedKey, keyPathErr := common.ValidateFilePathWithExtension(key, "", keyExtensions) var clientCert tls.Certificate var err error diff --git a/core/storage/mongoStorage.go b/core/storage/mongoStorage.go index 0b911cd..3753456 100644 --- a/core/storage/mongoStorage.go +++ b/core/storage/mongoStorage.go @@ -137,7 +137,7 @@ func (store *MongoStorage) Init() common.SyncServiceError { // Use SecureString for password password := common.NewSecureString(common.Configuration.MongoPassword) defer password.Clear() - + credential := options.Credential{ AuthMechanism: common.Configuration.MongoAuthMechanism, AuthSource: common.Configuration.MongoAuthDbName, @@ -145,7 +145,7 @@ func (store *MongoStorage) Init() common.SyncServiceError { Password: password.String(), // Only convert when needed } clientOptions = clientOptions.SetAuth(credential) - + // Clear credential password after connection is established defer func() { credential.Password = "" @@ -164,14 +164,14 @@ func (store *MongoStorage) Init() common.SyncServiceError { } else { caFile = common.Configuration.PersistenceRootPath + common.Configuration.MongoCACertificate } - + // Validate certificate file path to prevent path traversal attacks (CWE-22) var serverCaCert []byte certExtensions := common.Configuration.AllowedCertificateExtensions if len(certExtensions) == 0 { certExtensions = []string{".pem", ".crt", ".cert"} } - validatedPath, pathErr := common.ValidateFilePathWithExtension(caFile, common.Configuration.PersistenceRootPath, certExtensions) + validatedPath, pathErr := common.ValidateFilePathWithExtension(caFile, "", certExtensions) if pathErr != nil { // If path validation fails, treat as certificate content rather than file path serverCaCert = []byte(common.Configuration.MongoCACertificate) @@ -198,22 +198,22 @@ func (store *MongoStorage) Init() common.SyncServiceError { // SECURITY WARNING: Certificate validation is DISABLED! // This should NEVER be used in production environments. // This configuration makes the connection vulnerable to MITM attacks. - + if log.IsLogging(logger.ERROR) { log.Error("SECURITY CRITICAL: MongoDB certificate validation is DISABLED! Connection is vulnerable to MITM attacks. Set MongoAllowInvalidCertificates=false for production.") } - + // Prevent use on CSS (Cloud Sync Service) nodes if common.Configuration.NodeType == common.CSS { message := "MongoAllowInvalidCertificates cannot be enabled on CSS nodes for security reasons" return &Error{message} } - + // Log every connection attempt with disabled validation if trace.IsLogging(logger.WARNING) { trace.Warning("Connecting to MongoDB with certificate validation DISABLED") } - + tlsConfig.InsecureSkipVerify = true } @@ -1259,7 +1259,7 @@ func (store *MongoStorage) RemoveObjectTempData(orgID string, objectType string, id := createTempObjectCollectionID(orgID, objectType, objectID, chunkNumber) if trace.IsLogging(logger.TRACE) { - trace.Trace("RemoveObjectTempData for org - %s, type - %s, id - %s, chunkNum - %d", orgID, objectType, objectID, chunkNumber) + trace.Trace("RemoveObjectTempData for org - %s, type - %s, id - %s, chunkNum - %d", orgID, objectType, objectID, chunkNumber) } fileHandle, _ := store.retrieveObjectTempData(id) @@ -1299,7 +1299,7 @@ func (store *MongoStorage) RetrieveObjectTempData(orgID string, objectType strin id := createTempObjectCollectionID(orgID, objectType, objectID, chunkNumber) if trace.IsLogging(logger.TRACE) { - trace.Trace("RetrieveObjectTempData for org - %s, type - %s, id - %s, chunkNum - %d", orgID, objectType, objectID, chunkNumber) + trace.Trace("RetrieveObjectTempData for org - %s, type - %s, id - %s, chunkNum - %d", orgID, objectType, objectID, chunkNumber) } fileHandle, err := store.retrieveObjectTempData(id) if err != nil { @@ -1316,7 +1316,7 @@ func (store *MongoStorage) RetrieveObjectTempData(orgID string, objectType strin } if trace.IsLogging(logger.TRACE) { - trace.Trace("returning %d file readers in the MultiReader from RetrieveObjectTempData", len(readers)) + trace.Trace("returning %d file readers in the MultiReader from RetrieveObjectTempData", len(readers)) } rr := io.MultiReader(readers...) return rr, nil