appview/knots, appview/spindles: strip protocol and @ symbol from user inputs #748

merged
opened by evan.jarrett.net targeting master from evan.jarrett.net/core: input-sanitization

Summary#

Fixes input sanitization issues in spindle/knot registration and member management forms.

Changes#

  • Strip http://, https://, and trailing slashes from spindle/knot instance/domain inputs
  • Strip leading @ symbol from member inputs (add/remove) in both spindles and knots
  • Update form placeholders to show foo.bsky.social instead of @foo.bsky.social

Why#

Users naturally enter protocols (https://localhost:6555) or @ symbols (@evan.jarrett.net) based on UI placeholders, but the AT Protocol identity parser rejects these. This causes confusing failures during registration and member management.

Changed files
+21 -3
appview
knots
pages
templates
knots
repo
settings
spindles
spindles
+9
appview/knots/knots.go
···
"log/slog"
"net/http"
"slices"
+
"strings"
"time"
"github.com/go-chi/chi/v5"
···
}
domain := r.FormValue("domain")
+
// Strip protocol, trailing slashes, and whitespace
+
// Rkey cannot contain slashes
+
domain = strings.TrimSpace(domain)
+
domain = strings.TrimPrefix(domain, "https://")
+
domain = strings.TrimPrefix(domain, "http://")
+
domain = strings.TrimSuffix(domain, "/")
if domain == "" {
k.Pages.Notice(w, noticeId, "Incomplete form.")
return
···
}
member := r.FormValue("member")
+
member = strings.TrimPrefix(member, "@")
if member == "" {
l.Error("empty member")
k.Pages.Notice(w, noticeId, "Failed to add member, empty form.")
···
}
member := r.FormValue("member")
+
member = strings.TrimPrefix(member, "@")
if member == "" {
l.Error("empty member")
k.Pages.Notice(w, noticeId, "Failed to remove member, empty form.")
+1 -1
appview/pages/templates/knots/fragments/addMemberModal.html
···
id="member-did-{{ .Id }}"
name="member"
required
-
placeholder="@foo.bsky.social"
+
placeholder="foo.bsky.social"
/>
<div class="flex gap-2 pt-2">
<button
+1 -1
appview/pages/templates/repo/settings/access.html
···
id="add-collaborator"
name="collaborator"
required
-
placeholder="@foo.bsky.social"
+
placeholder="foo.bsky.social"
/>
<div class="flex gap-2 pt-2">
<button
+1 -1
appview/pages/templates/spindles/fragments/addMemberModal.html
···
id="member-did-{{ .Id }}"
name="member"
required
-
placeholder="@foo.bsky.social"
+
placeholder="foo.bsky.social"
/>
<div class="flex gap-2 pt-2">
<button
+9
appview/spindles/spindles.go
···
"log/slog"
"net/http"
"slices"
+
"strings"
"time"
"github.com/go-chi/chi/v5"
···
}
instance := r.FormValue("instance")
+
// Strip protocol, trailing slashes, and whitespace
+
// Rkey cannot contain slashes
+
instance = strings.TrimSpace(instance)
+
instance = strings.TrimPrefix(instance, "https://")
+
instance = strings.TrimPrefix(instance, "http://")
+
instance = strings.TrimSuffix(instance, "/")
if instance == "" {
s.Pages.Notice(w, noticeId, "Incomplete form.")
return
···
}
member := r.FormValue("member")
+
member = strings.TrimPrefix(member, "@")
if member == "" {
l.Error("empty member")
s.Pages.Notice(w, noticeId, "Failed to add member, empty form.")
···
}
member := r.FormValue("member")
+
member = strings.TrimPrefix(member, "@")
if member == "" {
l.Error("empty member")
s.Pages.Notice(w, noticeId, "Failed to remove member, empty form.")