115 lines
3.6 KiB
Go
115 lines
3.6 KiB
Go
package adminapi
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/iceBear67/simplepages/api"
|
|
)
|
|
|
|
func TestCheckProjectName(t *testing.T) {
|
|
valid := []string{
|
|
"a", "0", "demo", "demo-site", "demo_site", "demo.site", "a1.b-c_d",
|
|
strings.Repeat("z", 63),
|
|
}
|
|
for _, name := range valid {
|
|
if err := checkProjectName(name); err != nil {
|
|
t.Errorf("checkProjectName(%q) = %v, want nil", name, err)
|
|
}
|
|
}
|
|
|
|
// Everything that could become a path separator, a traversal or a hidden
|
|
// file once the name is turned into a "~name" entry in $WEBROOT.
|
|
invalid := []string{
|
|
"", ".", "..", "./x", "../x", "/demo", "demo/", "a/b", `a\b`,
|
|
".hidden", "-lead", "_lead", "Demo", "DEMO", "démo", "demo ",
|
|
" demo", "de mo", "demo\t", "demo\n", "demo\x00", "~demo", "demo%2f",
|
|
strings.Repeat("z", 64),
|
|
}
|
|
for _, name := range invalid {
|
|
err := checkProjectName(name)
|
|
if err == nil {
|
|
t.Errorf("checkProjectName(%q) = nil, want an error", name)
|
|
continue
|
|
}
|
|
if got := api.CodeOf(err); got != api.CodeInvalidProjectName {
|
|
t.Errorf("checkProjectName(%q) code = %q, want %q", name, got, api.CodeInvalidProjectName)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCheckSitePath(t *testing.T) {
|
|
valid := []string{
|
|
"index.html", "404.html", "a/b/c.html", "a.b/c", "_next/index.html",
|
|
"страница.html", strings.Repeat("a", maxSitePathLen),
|
|
}
|
|
for _, p := range valid {
|
|
if err := checkSitePath("index_file", p); err != nil {
|
|
t.Errorf("checkSitePath(%q) = %v, want nil", p, err)
|
|
}
|
|
}
|
|
|
|
invalid := []string{
|
|
"", ".", "..", "/index.html", "index.html/", "a//b", "a/./b", "a/../b",
|
|
"../../etc/passwd", `a\b`, "a\x00b", "a\nb", "a\x7fb",
|
|
strings.Repeat("a", maxSitePathLen+1),
|
|
}
|
|
for _, p := range invalid {
|
|
err := checkSitePath("index_file", p)
|
|
if err == nil {
|
|
t.Errorf("checkSitePath(%q) = nil, want an error", p)
|
|
continue
|
|
}
|
|
if got := api.CodeOf(err); got != api.CodeInvalidPath {
|
|
t.Errorf("checkSitePath(%q) code = %q, want %q", p, got, api.CodeInvalidPath)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCheckHeaderValue(t *testing.T) {
|
|
valid := []string{
|
|
"", "public, max-age=0, must-revalidate", "no-store",
|
|
"public,\tmax-age=31536000, immutable", strings.Repeat("a", maxCacheControlLen),
|
|
}
|
|
for _, v := range valid {
|
|
if err := checkHeaderValue("cache_control", v); err != nil {
|
|
t.Errorf("checkHeaderValue(%q) = %v, want nil", v, err)
|
|
}
|
|
}
|
|
|
|
// A CR or LF here would let a project append headers of its own to every
|
|
// response it serves, on an origin it shares with every other project.
|
|
invalid := []string{
|
|
"public\r\nX-Evil: 1", "public\nX-Evil: 1", "public\r", "public\x00",
|
|
"public\x7f", "public é", strings.Repeat("a", maxCacheControlLen+1),
|
|
}
|
|
for _, v := range invalid {
|
|
if err := checkHeaderValue("cache_control", v); err == nil {
|
|
t.Errorf("checkHeaderValue(%q) = nil, want an error", v)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCheckText(t *testing.T) {
|
|
// Free text is allowed to be anything printable, including non-ASCII: it is
|
|
// a display name, not a header value.
|
|
valid := []string{"", "Demo Site", "デモ", "a — b", strings.Repeat("x", maxDisplayNameLen)}
|
|
for _, v := range valid {
|
|
if err := checkText("display_name", v, maxDisplayNameLen); err != nil {
|
|
t.Errorf("checkText(%q) = %v, want nil", v, err)
|
|
}
|
|
}
|
|
|
|
// Control characters are not: these strings are printed to operator
|
|
// terminals and written into structured logs.
|
|
invalid := []string{
|
|
"a\x1b[31mred", "line\nbreak", "car\rriage", "nul\x00", "del\x7f", "tab\there",
|
|
strings.Repeat("x", maxDisplayNameLen+1),
|
|
}
|
|
for _, v := range invalid {
|
|
if err := checkText("display_name", v, maxDisplayNameLen); err == nil {
|
|
t.Errorf("checkText(%q) = nil, want an error", v)
|
|
}
|
|
}
|
|
}
|