aboutsummaryrefslogtreecommitdiff
path: root/internal/config
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-09-14 20:10:01 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-09-14 20:10:01 +0200
commitaa24cfb344b1b3eaef7217d996359023cd72ba28 (patch)
treeee0d981d3add58451a83de13ac56be4a8b165eea /internal/config
parent00aae60378982902b871a87850e0ed427b28a347 (diff)
downloadkrino-aa24cfb344b1b3eaef7217d996359023cd72ba28.tar.gz
krino-aa24cfb344b1b3eaef7217d996359023cd72ba28.zip
plan 8: fuzz decoders; fold ẞ and invalid UTF-8 correctly, refuse non-UTF-8 paths in krino new
Diffstat (limited to 'internal/config')
-rw-r--r--internal/config/fuzz_test.go51
-rw-r--r--internal/config/skel.go6
-rw-r--r--internal/config/skel_test.go4
3 files changed, 60 insertions, 1 deletions
diff --git a/internal/config/fuzz_test.go b/internal/config/fuzz_test.go
new file mode 100644
index 0000000..32926d6
--- /dev/null
+++ b/internal/config/fuzz_test.go
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+package config
+
+import (
+ "fmt"
+ "strconv"
+ "testing"
+ "time"
+)
+
+// FuzzParseSize: a size either fails to parse or is a non-negative number
+// of bytes that reads back the same written as plain digits - no overflow
+// ever wraps a huge size negative.
+func FuzzParseSize(f *testing.F) {
+ for _, s := range []string{"50M", "0", "1T", "9223372036854775807", "8388608T", "8388607T", "-1", "1.5G", "", "G", "007K"} {
+ f.Add(s)
+ }
+ f.Fuzz(func(t *testing.T, s string) {
+ v, err := ParseSize(s)
+ if err != nil {
+ return
+ }
+ if v < 0 {
+ t.Fatalf("ParseSize(%q) = %d, negative", s, v)
+ }
+ if back, err := ParseSize(strconv.FormatInt(v, 10)); err != nil || back != v {
+ t.Fatalf("ParseSize(%q) = %d, which reads back as %d, %v", s, v, back, err)
+ }
+ })
+}
+
+// FuzzParseDuration: a duration either fails to parse or is a non-negative
+// whole number of seconds that reads back the same written in seconds.
+func FuzzParseDuration(f *testing.F) {
+ for _, s := range []string{"30d", "0s", "2m", "1w", "15250284452w", "9223372036s", "d", "", "-1d", "1.5h"} {
+ f.Add(s)
+ }
+ f.Fuzz(func(t *testing.T, s string) {
+ d, err := ParseDuration(s)
+ if err != nil {
+ return
+ }
+ if d < 0 || d%time.Second != 0 {
+ t.Fatalf("ParseDuration(%q) = %v", s, d)
+ }
+ if back, err := ParseDuration(fmt.Sprintf("%ds", d/time.Second)); err != nil || back != d {
+ t.Fatalf("ParseDuration(%q) = %v, which reads back as %v, %v", s, d, back, err)
+ }
+ })
+}
diff --git a/internal/config/skel.go b/internal/config/skel.go
index 65d9f95..ca914e3 100644
--- a/internal/config/skel.go
+++ b/internal/config/skel.go
@@ -10,6 +10,7 @@ import (
"io/fs"
"os"
"path/filepath"
+ "unicode/utf8"
"krino/internal/sexp"
"krino/internal/xdg"
@@ -66,6 +67,11 @@ func NewDir(mainFile, name, path string) (string, error) {
if err != nil {
return "", err
}
+ // The path is written into the new file, and config files are UTF-8
+ // text (the reader refuses anything else).
+ if !utf8.ValidString(abs) {
+ return "", fmt.Errorf("%q is not valid UTF-8; krino's config is UTF-8 text, so rename the directory first", abs)
+ }
if fi, err := os.Stat(abs); err != nil || !fi.IsDir() {
return "", fmt.Errorf("%s is not a directory", abs)
}
diff --git a/internal/config/skel_test.go b/internal/config/skel_test.go
index 476af65..3815336 100644
--- a/internal/config/skel_test.go
+++ b/internal/config/skel_test.go
@@ -5,6 +5,7 @@ package config
import (
"bytes"
"errors"
+ "fmt"
"io/fs"
"os"
"path/filepath"
@@ -242,7 +243,7 @@ func TestNewDirErrors(t *testing.T) {
home := t.TempDir()
t.Setenv("HOME", home)
writeFiles(t, home, map[string]string{
- "krino.conf": `(include "a")`, "dirs/a.conf": `(path "~")`, "x/.keep": "", "f": "",
+ "krino.conf": `(include "a")`, "dirs/a.conf": `(path "~")`, "x/.keep": "", "f": "", "lat\xe9n/.keep": "",
})
main := filepath.Join(home, "krino.conf")
tests := []struct{ name, path, want string }{
@@ -251,6 +252,7 @@ func TestNewDirErrors(t *testing.T) {
{"b", "~/f", filepath.Join(home, "f") + " is not a directory"},
{"b", "~/missing", filepath.Join(home, "missing") + " is not a directory"},
{"a", "~/x", `"a" is already included`},
+ {"b", "~/lat\xe9n", fmt.Sprintf("%q is not valid UTF-8; krino's config is UTF-8 text, so rename the directory first", filepath.Join(home, "lat\xe9n"))},
}
for _, tt := range tests {
if _, err := NewDir(main, tt.name, tt.path); err == nil || err.Error() != tt.want {