amir20/dozzle · error · errInvalidUsername
invalid username: contains path separator or traversal
Error message
invalid username: contains path separator or traversal
What it means
errInvalidUsername is returned by safePath in the profile package when the supplied username is not a safe single path element. It guards against path traversal and separators that would escape the per-user data directory (e.g. "../host" or "a/b"). filepath.Base(username) is compared to the original, and "." and ".." are explicitly rejected.
Solutions
- Validate the username before calling profile functions: reject strings containing path separators, '.', '..', or empty strings
- Normalize/derive an internal identifier (e.g. hash or sanitized ID) from the upstream username instead of using it verbatim
- If the username legitimately contains separators, encode it (URL-safe base64) before use
- Check err with errors.Is(err, profile.errInvalidUsername) to return a 400 to the client instead of a 500
Example fix
// before
profile.UpdateFromReader(r.Header.Get("X-Forwarded-User"), body)
// after
user := r.Header.Get("X-Forwarded-User")
if user == "" || strings.ContainsAny(user, "/\\") || user == "." || user == ".." {
http.Error(w, "invalid username", http.StatusBadRequest)
return
}
profile.UpdateFromReader(user, body) Defensive patterns
Strategy: validation
Validate before calling
func validUsername(u string) bool {
return u != "" && u != "." && u != ".." && !strings.ContainsAny(u, "/\\") && u == filepath.Base(u)
}
if !validUsername(username) { return errInvalidUsername } Type guard
func isSafeUsername(u string) bool { return filepath.Base(u) == u && u != "." && u != ".." } Try / catch
if err := profile.UpdateFromReader(user, r.Body); err != nil {
if errors.Is(err, profile.ErrInvalidUsername) { http.Error(w, "invalid username", 400); return }
http.Error(w, "internal error", 500)
} Prevention
- Sanitize or hash usernames from auth headers before using them as file keys
- Add a unit test covering usernames with '/', '..', '.', and empty strings
- Never pass raw user input to filesystem-path-building functions
When it happens
Trigger: Calling profile.Load(username), profile.UpdateFromReader(username, ...), or save() with a username containing '/', '\\', or equal to "." or ".." (any value where filepath.Base(username) != username).
Common situations: Auth backends that derive usernames from headers or emails with slashes; misconfigured forward-proxy auth passing the full user DN; calling Load("") (Base("")=="."); tests feeding raw user-supplied names.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- unknown action
- Failed to save alert
- Toast id is required when once is true
- invalid credentials
- cloud: no API key configured
AI-assisted analysis of amir20/dozzle@d9463cbe21 (2026-09-07).
Data as JSON: /api/errors/1598505cde222e54.
Report an issue: GitHub.
Appendix: source
Thrown at internal/profile/disk.go:53
HourStyle string `json:"hourStyle,omitempty"`
DateLocale string `json:"dateLocale,omitempty"`
Locale string `json:"locale"`
GroupContainers string `json:"groupContainers,omitempty"`
}
type Profile struct {
Settings *Settings `json:"settings,omitempty"`
Pinned []string `json:"pinned"`
VisibleKeys []any `json:"visibleKeys,omitempty"`
ReleaseSeen string `json:"releaseSeen,omitempty"`
CollapsedGroups []string `json:"collapsedGroups"`
DismissedImageUpdates []string `json:"dismissedImageUpdates,omitempty"`
DismissedLinkHint bool `json:"dismissedLinkHint,omitempty"`
}
var dataPath string
var mux = &sync.Mutex{}
var errInvalidUsername = errors.New("invalid username: contains path separator or traversal")
func init() {
path, err := filepath.Abs("./data")
if err != nil {
log.Fatal().Err(err).Msg("Unable to get absolute path")
return
}
if _, err := os.Stat(path); os.IsNotExist(err) {
if err := os.Mkdir(path, 0755); err != nil {
log.Fatal().Err(err).Msg("Unable to create data directory")
return
}
}
dataPath = path
}
func safePath(username string) (string, error) {
clean := filepath.Base(username)View on GitHub (pinned to d9463cbe21)