cleanups and code fixes incl templ
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -106,6 +106,7 @@ func (s *Settings) ReadYMLSettings() error {
|
||||
|
||||
// Init new YAML decode
|
||||
d := yaml.NewDecoder(file)
|
||||
d.KnownFields(true)
|
||||
|
||||
// Start YAML decoding from file
|
||||
if err := d.Decode(&settings); err != nil {
|
||||
@@ -149,9 +150,9 @@ func (s *Settings) WriteYMLSettings() error {
|
||||
return fmt.Errorf("unable to encode settings file: %w", err)
|
||||
}
|
||||
|
||||
mode := os.FileMode(0o644)
|
||||
mode := os.FileMode(0o600)
|
||||
if info, err := os.Stat(s.SettingsPath); err == nil {
|
||||
mode = info.Mode().Perm()
|
||||
mode = secureSettingsFileMode(info.Mode().Perm())
|
||||
}
|
||||
|
||||
dir := filepath.Dir(s.SettingsPath)
|
||||
@@ -181,3 +182,10 @@ func (s *Settings) WriteYMLSettings() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func secureSettingsFileMode(mode os.FileMode) os.FileMode {
|
||||
// Ensure owner read/write, strip world permissions and all execute bits.
|
||||
secured := mode & 0o660
|
||||
secured |= 0o600
|
||||
return secured
|
||||
}
|
||||
|
||||
55
internal/settings/settings_strict_test.go
Normal file
55
internal/settings/settings_strict_test.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestReadYMLSettingsRejectsUnknownField(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
settingsPath := filepath.Join(tmpDir, "vctp.yml")
|
||||
content := `settings:
|
||||
log_level: "info"
|
||||
unknown_field: true
|
||||
`
|
||||
if err := os.WriteFile(settingsPath, []byte(content), 0o600); err != nil {
|
||||
t.Fatalf("failed to write settings file: %v", err)
|
||||
}
|
||||
|
||||
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||
s := New(logger, settingsPath)
|
||||
err := s.ReadYMLSettings()
|
||||
if err == nil {
|
||||
t.Fatal("expected unknown field decode error")
|
||||
}
|
||||
if !strings.Contains(strings.ToLower(err.Error()), "unknown_field") {
|
||||
t.Fatalf("expected error to mention unknown field, got: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSecureSettingsFileMode(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
in os.FileMode
|
||||
want os.FileMode
|
||||
}{
|
||||
{name: "already strict", in: 0o600, want: 0o600},
|
||||
{name: "group read allowed", in: 0o640, want: 0o640},
|
||||
{name: "too open world", in: 0o666, want: 0o660},
|
||||
{name: "exec bits stripped", in: 0o755, want: 0o640},
|
||||
{name: "no perms gets owner rw", in: 0o000, want: 0o600},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := secureSettingsFileMode(tc.in)
|
||||
if got != tc.want {
|
||||
t.Fatalf("unexpected mode conversion: in=%#o got=%#o want=%#o", tc.in, got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user