BREAKING CHANGE: /sitemap.xsl is gone (404), the definitions screen and the Phase 17 settings tile are replaced by the settings singleton, and priority, change frequency, priority tiers and the update manifest are no longer written or read. The config keys golem15.sitemap.cache_seconds and golem15.sitemap.priority_tiers are removed; site_url, lastmod_ignore and rebuild_delay_seconds are new. - sitemap:import and sitemap:import-winter write the additional addresses (and site_url, default_locale) instead of the definition - the golem15_sitemap_definitions table stays in the database, unread
92 lines
3.2 KiB
Go
92 lines
3.2 KiB
Go
package console
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/bonfire"
|
|
)
|
|
|
|
// TestWinterSettingsFrontendURL pins the import check of T-17-15: a settings
|
|
// export whose frontend_url models.AllowedFrontendURL refuses stops
|
|
// sitemap:import-winter with an error naming the setting, with and without
|
|
// --dry-run and before the database is used (the test passes none). An
|
|
// accepted, empty, null or missing frontend_url is read as before.
|
|
func TestWinterSettingsFrontendURL(t *testing.T) {
|
|
dir := t.TempDir()
|
|
n := 0
|
|
settingsFile := func(fields map[string]any) string {
|
|
t.Helper()
|
|
// json.Marshal encodes the quotes of the attack value.
|
|
raw, err := json.Marshal(fields)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
n++
|
|
path := filepath.Join(dir, "settings-"+string(rune('a'+n))+".json")
|
|
if err := os.WriteFile(path, raw, 0o644); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return path
|
|
}
|
|
|
|
for _, value := range []string{
|
|
"javascript:alert(document.domain)//",
|
|
`https://x"/><y a="`,
|
|
"0",
|
|
" https://www.example.test",
|
|
} {
|
|
file := settingsFile(map[string]any{"frontend_url": value, "enable_priority_tiers": "1"})
|
|
var report WinterReport
|
|
if _, err := readWinterSettings(file, &report); err == nil || !strings.Contains(err.Error(), "setting frontend_url") {
|
|
t.Errorf("readWinterSettings(%q) = %v, want an error naming setting frontend_url", value, err)
|
|
}
|
|
for _, dry := range []bool{false, true} {
|
|
var buf bytes.Buffer
|
|
out := bonfire.NewOutput(strings.NewReader(""), &buf, &buf)
|
|
report, err := ImportWinterFiles(context.Background(), nil, WinterOptions{SettingsFile: file, DryRun: dry}, out)
|
|
if err == nil || !strings.Contains(err.Error(), "setting frontend_url") {
|
|
t.Errorf("ImportWinterFiles(%q, dry run %v) = %v, want an error naming setting frontend_url", value, dry, err)
|
|
}
|
|
if report.Settings {
|
|
t.Errorf("ImportWinterFiles(%q, dry run %v) reports the settings as copied", value, dry)
|
|
}
|
|
if strings.Contains(buf.String(), "would be copied") || strings.Contains(buf.String(), "Settings copied") {
|
|
t.Errorf("ImportWinterFiles(%q, dry run %v) printed a copy line:\n%s", value, dry, buf.String())
|
|
}
|
|
}
|
|
}
|
|
|
|
// The error quotes the value, so a control character is not printed raw.
|
|
var report WinterReport
|
|
_, err := readWinterSettings(settingsFile(map[string]any{"frontend_url": "https://www.example.test\x1b[2J"}), &report)
|
|
if err == nil || strings.Contains(err.Error(), "\x1b") || !strings.Contains(err.Error(), `\x1b`) {
|
|
t.Errorf("a control character in frontend_url: %q", err)
|
|
}
|
|
|
|
for name, tc := range map[string]struct {
|
|
fields map[string]any
|
|
want string
|
|
}{
|
|
"valid": {map[string]any{"frontend_url": "https://www.example.test/"}, "https://www.example.test/"},
|
|
"empty": {map[string]any{"frontend_url": ""}, ""},
|
|
"null": {map[string]any{"frontend_url": nil}, ""},
|
|
"missing": {map[string]any{"enable_priority_tiers": "1"}, ""},
|
|
} {
|
|
var report WinterReport
|
|
s, err := readWinterSettings(settingsFile(tc.fields), &report)
|
|
if err != nil {
|
|
t.Errorf("%s: readWinterSettings = %v", name, err)
|
|
continue
|
|
}
|
|
if got := s.SiteURL; got != tc.want {
|
|
t.Errorf("%s: frontend_url = %q, want %q", name, got, tc.want)
|
|
}
|
|
}
|
|
}
|