Files
sm-sitemap-plugin/models/settings_test.go
Jakub Zych 1bb89ce006 fix(17): T-17-15 refuse an unsafe frontend_url on save and import
- Settings.BeforeValidate answers a cabana validation error on
  frontend_url, so the settings save is a 422 and the row is unchanged
- sitemap:import-winter stops on a frontend_url that
  models.AllowedFrontendURL refuses, before the dry-run branch and before
  any database use; the value is printed quoted
- errors.frontend_url phrase in en and pl
- TestSettingsBeforeValidate, TestWinterSettingsFrontendURL and the save
  and import refusals in the Postgres smokes
2026-10-13 00:59:24 +02:00

98 lines
2.8 KiB
Go
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package models_test
import (
"errors"
"reflect"
"testing"
"git.golem15.com/golem15/sm-sitemap-plugin/models"
"git.golem15.com/golem15/summercms/modules/cabana"
"git.golem15.com/golem15/summercms/modules/lagoon"
)
// cabana calls the hook on a settings save through this interface.
var _ lagoon.HasBeforeValidate = (*models.Settings)(nil)
// TestAllowedFrontendURL pins the one definition of a frontend_url that may
// be saved, imported and applied (T-17-15): empty, or an absolute http(s)
// URL with a host and without whitespace, control characters, a double
// quote, <, > or &.
func TestAllowedFrontendURL(t *testing.T) {
for _, u := range []string{
"",
"https://www.ex.pl",
"https://www.ex.pl/",
"https://www.ex.pl///",
"http://localhost:3000",
"HTTPS://WWW.EX.PL",
"https://www.ex.pl/app",
"https://www.ex.pl:8443/base/",
"https://[::1]:3000",
// The frontend URLs of the golden inputs.
"https://api.example.test",
"https://www.example.test/",
} {
if !models.AllowedFrontendURL(u) {
t.Errorf("AllowedFrontendURL(%q) = false, want true", u)
}
}
for _, u := range []string{
"0",
"javascript:alert(document.domain)//",
"JavaScript://www.ex.pl/%0aalert(1)",
`https://x"/><y a="`,
"https://www.ex.pl/?a=1&b=2",
"https://www.ex.pl <x>",
" https://www.ex.pl",
"https://www.ex.pl\n",
"https://www.ex.pl\t",
"https://www.ex.pl ",
"https://www.ex.pl\x7f",
"https://www.ex.pl\xff",
"//www.ex.pl",
"www.ex.pl",
"/path",
"https:///x",
"https://:8080",
"https:www.ex.pl",
"http://",
"https://",
"https://%zz",
"data:text/html,x",
"ftp://www.ex.pl",
} {
if models.AllowedFrontendURL(u) {
t.Errorf("AllowedFrontendURL(%q) = true, want false", u)
}
}
}
// TestSettingsBeforeValidate pins the save check of T-17-15: the hook cabana
// calls on a settings save passes a null, empty or accepted frontend_url and
// answers a validation error keyed frontend_url for any other value.
func TestSettingsBeforeValidate(t *testing.T) {
text := func(s string) *string { return &s }
for _, url := range []*string{nil, text(""), text("https://www.ex.pl/")} {
if err := (&models.Settings{FrontendURL: url}).BeforeValidate(nil); err != nil {
t.Errorf("BeforeValidate(%q) = %v, want nil", models.Text(url), err)
}
}
want := map[string]any{"frontend_url": []string{"golem15.sitemap::lang.errors.frontend_url"}}
for _, url := range []string{
"javascript:alert(document.domain)//",
`https://x"/><y a="`,
"0",
" https://www.ex.pl",
} {
err := (&models.Settings{FrontendURL: &url}).BeforeValidate(nil)
var invalid *cabana.ValidationError
if !errors.As(err, &invalid) {
t.Errorf("BeforeValidate(%q) = %v, want a *cabana.ValidationError", url, err)
continue
}
if !reflect.DeepEqual(invalid.Details, want) {
t.Errorf("BeforeValidate(%q) details = %v, want %v", url, invalid.Details, want)
}
}
}