fixed settings smtp password clear persistence
This commit is contained in:
@@ -2,9 +2,13 @@
|
|||||||
|
|
||||||
- Updated the Discord `AuthUser.Name` field to use `global_name` ([#7603](https://github.com/pocketbase/pocketbase/pull/7603); thanks @HansHans135).
|
- Updated the Discord `AuthUser.Name` field to use `global_name` ([#7603](https://github.com/pocketbase/pocketbase/pull/7603); thanks @HansHans135).
|
||||||
|
|
||||||
|
- Fixed settings SMTP password clear persistence.
|
||||||
|
|
||||||
|
- Added extra OAuth2 checks when downloading the avatar URL to prevent internal network probing requests in case of a malicious/vulnerable vendor.
|
||||||
|
|
||||||
- (@todo) Bumped min Go GitHub action version to 1.26.2 because it comes with several [minor security fixes](https://github.com/golang/go/issues?q=milestone%3AGo1.26.2).
|
- (@todo) Bumped min Go GitHub action version to 1.26.2 because it comes with several [minor security fixes](https://github.com/golang/go/issues?q=milestone%3AGo1.26.2).
|
||||||
|
|
||||||
- Other minor improvements _(updated `$apis.static` JSVM documentation, added extra OAuth2 checks when downloading the avatar URL to prevent internal network probing requests in case of a malicious/vulnerable vendor, etc.)_.
|
- Other minor improvements _(updated `$apis.static` JSVM documentation, fixed comment typos, etc.)_.
|
||||||
|
|
||||||
|
|
||||||
## v0.36.8
|
## v0.36.8
|
||||||
|
|||||||
@@ -327,6 +327,8 @@ func (s *Settings) MarshalJSON() ([]byte, error) {
|
|||||||
copy := s.settings
|
copy := s.settings
|
||||||
s.mu.RUnlock()
|
s.mu.RUnlock()
|
||||||
|
|
||||||
|
copy.SMTP.hidePassword = true
|
||||||
|
|
||||||
sensitiveFields := []*string{
|
sensitiveFields := []*string{
|
||||||
©.SMTP.Password,
|
©.SMTP.Password,
|
||||||
©.S3.Secret,
|
©.S3.Secret,
|
||||||
@@ -346,6 +348,12 @@ func (s *Settings) MarshalJSON() ([]byte, error) {
|
|||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
type SMTPConfig struct {
|
type SMTPConfig struct {
|
||||||
|
// @todo temp workaround to avoid introducing breaking changes;
|
||||||
|
// consider refactoring and/or normalizing with the other Settings sensitive fields
|
||||||
|
//
|
||||||
|
// hidePassword specifies whether to hide the password field from the struct JSON serialization.
|
||||||
|
hidePassword bool
|
||||||
|
|
||||||
Enabled bool `form:"enabled" json:"enabled"`
|
Enabled bool `form:"enabled" json:"enabled"`
|
||||||
Port int `form:"port" json:"port"`
|
Port int `form:"port" json:"port"`
|
||||||
Host string `form:"host" json:"host"`
|
Host string `form:"host" json:"host"`
|
||||||
@@ -392,6 +400,27 @@ func (c SMTPConfig) Validate() error {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalJSON implements the [json.Marshaler] interface.
|
||||||
|
func (c SMTPConfig) MarshalJSON() ([]byte, error) {
|
||||||
|
type alias SMTPConfig
|
||||||
|
|
||||||
|
if c.hidePassword {
|
||||||
|
v := struct {
|
||||||
|
alias
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
}{alias(c), ""}
|
||||||
|
|
||||||
|
return json.Marshal(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
v := struct {
|
||||||
|
alias
|
||||||
|
Password string `json:"password"`
|
||||||
|
}{alias(c), c.Password}
|
||||||
|
|
||||||
|
return json.Marshal(v)
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
type S3Config struct {
|
type S3Config struct {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package core_test
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -10,6 +11,7 @@ import (
|
|||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
"github.com/pocketbase/pocketbase/tests"
|
"github.com/pocketbase/pocketbase/tests"
|
||||||
"github.com/pocketbase/pocketbase/tools/mailer"
|
"github.com/pocketbase/pocketbase/tools/mailer"
|
||||||
|
"github.com/pocketbase/pocketbase/tools/security"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSettingsDelete(t *testing.T) {
|
func TestSettingsDelete(t *testing.T) {
|
||||||
@@ -24,6 +26,72 @@ func TestSettingsDelete(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSettings_DBExport(t *testing.T) {
|
||||||
|
scenarios := []struct {
|
||||||
|
name string
|
||||||
|
encryption bool
|
||||||
|
}{
|
||||||
|
{"no encryption", false},
|
||||||
|
{"with encryption", true},
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptionKey := strings.Repeat("a", 32)
|
||||||
|
|
||||||
|
for _, s := range scenarios {
|
||||||
|
t.Run(s.name, func(t *testing.T) {
|
||||||
|
app, _ := tests.NewTestApp()
|
||||||
|
defer app.Cleanup()
|
||||||
|
|
||||||
|
originalEnv := os.Getenv(app.EncryptionEnv())
|
||||||
|
defer func() {
|
||||||
|
os.Setenv(app.EncryptionEnv(), originalEnv)
|
||||||
|
}()
|
||||||
|
|
||||||
|
settings := &core.Settings{}
|
||||||
|
settings.Meta.AppName = "test_app_name"
|
||||||
|
settings.Logs.MaxDays = 123
|
||||||
|
settings.SMTP.Host = "smtp_host"
|
||||||
|
settings.SMTP.Username = "smtp_username"
|
||||||
|
settings.SMTP.Password = "" // ensures that empty password is exported
|
||||||
|
settings.S3.Endpoint = "s3_endpoint"
|
||||||
|
settings.S3.Secret = "s3_secret"
|
||||||
|
settings.Backups.Cron = "* * * * *"
|
||||||
|
settings.Backups.S3.Enabled = true
|
||||||
|
settings.Backups.S3.Secret = ""
|
||||||
|
settings.Batch.Timeout = 15
|
||||||
|
settings.RateLimits.Enabled = true
|
||||||
|
settings.TrustedProxy.UseLeftmostIP = true
|
||||||
|
|
||||||
|
if s.encryption {
|
||||||
|
os.Setenv(app.EncryptionEnv(), encryptionKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
export, err := settings.DBExport(app)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var valueStr string
|
||||||
|
|
||||||
|
if s.encryption {
|
||||||
|
decrypted, err := security.Decrypt(export["value"].(string), encryptionKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to decrypt test value: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
valueStr = string(decrypted)
|
||||||
|
} else {
|
||||||
|
valueStr = string(export["value"].([]byte))
|
||||||
|
}
|
||||||
|
|
||||||
|
expected := `{"smtp":{"enabled":false,"port":0,"host":"smtp_host","username":"smtp_username","authMethod":"","tls":false,"localName":"","password":""},"backups":{"cron":"* * * * *","cronMaxKeep":0,"s3":{"enabled":true,"bucket":"","region":"","endpoint":"","accessKey":"","forcePathStyle":false}},"s3":{"enabled":false,"bucket":"","region":"","endpoint":"s3_endpoint","accessKey":"","secret":"s3_secret","forcePathStyle":false},"meta":{"appName":"test_app_name","appURL":"","senderName":"","senderAddress":"","hideControls":false},"rateLimits":{"rules":[],"enabled":true},"trustedProxy":{"headers":[],"useLeftmostIP":true},"batch":{"enabled":false,"maxRequests":0,"timeout":15,"maxBodySize":0},"logs":{"maxDays":123,"minLevel":0,"logIP":false,"logAuthId":false}}`
|
||||||
|
if valueStr != expected {
|
||||||
|
t.Fatalf("Expected exported settings\n%s\ngot\n%s", expected, valueStr)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSettingsMerge(t *testing.T) {
|
func TestSettingsMerge(t *testing.T) {
|
||||||
s1 := &core.Settings{}
|
s1 := &core.Settings{}
|
||||||
s1.Meta.AppURL = "app_url" // should be unset
|
s1.Meta.AppURL = "app_url" // should be unset
|
||||||
|
|||||||
Reference in New Issue
Block a user