aboutsummaryrefslogtreecommitdiff
path: root/fuzz_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'fuzz_test.go')
-rw-r--r--fuzz_test.go122
1 files changed, 122 insertions, 0 deletions
diff --git a/fuzz_test.go b/fuzz_test.go
new file mode 100644
index 0000000..866863d
--- /dev/null
+++ b/fuzz_test.go
@@ -0,0 +1,122 @@
+package main
+
+import (
+ "path/filepath"
+ "slices"
+ "strings"
+ "testing"
+ "time"
+)
+
+// The parsers that are handed bytes from outside: an id token, a claim, a URL
+// path, and a secret typed into a form. Each target states the property the
+// caller relies on, so the seed corpus runs with the rest of the tests and a
+// longer search is one flag away.
+
+// splitJWT takes a token apart before anything is known about it, and
+// verifySignature is then handed whatever came out.
+func FuzzSplitJWT(f *testing.F) {
+ f.Add("aGVhZGVy.cGF5bG9hZA.c2ln")
+ f.Add("...")
+ f.Add("")
+ f.Fuzz(func(t *testing.T, token string) {
+ signed, sig, kid, alg, _, err := splitJWT(token)
+ if err != nil {
+ return
+ }
+ if verifySignature(jwk{Kty: "RSA", N: kid, E: "AQAB"}, alg, signed, sig) == nil {
+ t.Fatalf("a made up rsa key verified %q", token)
+ }
+ if verifySignature(jwk{Kty: "EC", Crv: "P-256", X: kid, Y: kid}, alg, signed, sig) == nil {
+ t.Fatalf("a made up ec key verified %q", token)
+ }
+ })
+}
+
+// The name becomes a key in repos.json and a component of a URL, out of a
+// claim from somewhere else.
+func FuzzSanitizeName(f *testing.F) {
+ f.Add("Carol")
+ f.Add("../../config")
+ f.Fuzz(func(t *testing.T, in string) {
+ if out := sanitizeName(in); out != "" && !localNameRE.MatchString(out) {
+ t.Fatalf("sanitizeName(%q) = %q, which is not a usable account name", in, out)
+ }
+ })
+}
+
+// Whatever this lets through is handed to both CGIs in an environment
+// variable.
+func FuzzServablePath(f *testing.F) {
+ f.Add("/notes/log/")
+ f.Add("/a/../b")
+ f.Fuzz(func(t *testing.T, p string) {
+ if !servablePath(p) {
+ return
+ }
+ if hasControlByte(p) {
+ t.Fatalf("servablePath(%q) accepted a control byte", p)
+ }
+ parts := strings.Split(strings.TrimPrefix(p, "/"), "/")
+ for i, part := range parts {
+ if part == "." || part == ".." || (part == "" && i < len(parts)-1) {
+ t.Fatalf("servablePath(%q) accepted a %q component", p, part)
+ }
+ }
+ })
+}
+
+// Whatever this lets through is joined onto the repos directory, for the
+// repository and for both of its siblings.
+func FuzzValidRepoPath(f *testing.F) {
+ f.Add("infra/dns")
+ f.Add("../etc")
+ f.Fuzz(func(t *testing.T, p string) {
+ if !validRepoPath(p) {
+ return
+ }
+ for _, suffix := range []string{".git", ".issues.git", ".wiki.git"} {
+ joined := filepath.Join("/repos", p+suffix)
+ if joined != filepath.Clean(joined) || !strings.HasPrefix(joined, "/repos/") {
+ t.Fatalf("validRepoPath(%q) accepted a path that lands on %q", p, joined)
+ }
+ if strings.Count(strings.TrimPrefix(joined, "/repos/"), "/") > 1 {
+ t.Fatalf("validRepoPath(%q) accepted more than one section deep", p)
+ }
+ }
+ })
+}
+
+// A page listed in the index has to open the file it was listed from. A push
+// can put any name in the tree.
+func FuzzWikiNameRoundTrip(f *testing.F) {
+ f.Add("docs/index.md")
+ f.Add("myindex.md")
+ f.Add(".md")
+ f.Fuzz(func(t *testing.T, file string) {
+ page := wikiPage(file)
+ files, ok := wikiFiles(page)
+ if !ok || !slices.Contains(files, file) {
+ return // not listed, so never linked to
+ }
+ if wikiPage(files[0]) != page {
+ t.Fatalf("%q is listed as %q, which reads back as %q", file, page, wikiPage(files[0]))
+ }
+ })
+}
+
+// The secret being enrolled arrives in a form, so this decodes whatever was
+// typed.
+func FuzzTOTPSecret(f *testing.F) {
+ f.Add("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ")
+ f.Add("!!!")
+ f.Fuzz(func(t *testing.T, secret string) {
+ code := totpCode(secret, time.Unix(1234567890, 0))
+ if code != "" && len(code) != totpDigits {
+ t.Fatalf("totpCode(%q) = %q", secret, code)
+ }
+ if code == "" && totpStep(secret, "", time.Unix(1234567890, 0)) >= 0 {
+ t.Fatalf("an unreadable secret accepted an empty code")
+ }
+ })
+}