cleanups and code fixes incl templ
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2026-03-20 13:21:15 +11:00
parent 4fbb2582e3
commit 9a561f3b07
24 changed files with 425 additions and 141 deletions

View File

@@ -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
}

View 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)
}
})
}
}