unknwon/the-way-to-go_ZH_CN · error
err.Error()
Error message
err.Error()
What it means
saveHandler in the wiki tutorial (15.6) reports p.save() failures to the browser via http.Error(w, err.Error(), http.StatusInternalServerError) — an HTTP 500 whose body is the raw error string. p.save() writes <title>.txt through ioutil.WriteFile(..., 0600), so the underlying error is almost always filesystem-related and gets leaked verbatim to the client.
Source
Thrown at eBook/15.6.md:90
return
}
renderTemplate(w, "view", p)
}
func editHandler(w http.ResponseWriter, r *http.Request, title string) {
p, err := load(title)
if err != nil {
p = &Page{Title: title}
}
renderTemplate(w, "edit", p)
}
func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
body := r.FormValue("body")
p := &Page{Title: title, Body: []byte(body)}
err := p.save()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/view/"+title, http.StatusFound)
}
func renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {
err := templates[tmpl].Execute(w, p)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}
func (p *Page) save() error {
filename := p.Title + ".txt"
// file created with read-write permissions for the current user only
return ioutil.WriteFile(filename, p.Body, 0600)
}
View on GitHub (pinned to 7a54d34d36)
Solutions
- Validate the title with a whitelist regexp (e.g. ^[A-Za-z0-9]+$) at the front of every handler and return 404 otherwise — the tutorial's own later fix
- Reject titles containing os.PathSeparator or '..' before any filesystem use
- Run the binary in a writable directory or store pages under an explicit data directory
- Log err.Error() server-side and return a generic 500 message so paths don't leak to users
Example fix
// before
func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
body := r.FormValue("body")
p := &Page{Title: title, Body: []byte(body)}
err := p.save()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
...
}
// after
var titleValidator = regexp.MustCompile("^[a-zA-Z0-9]+$")
func saveHandler(w http.ResponseWriter, r *http.Request, title string) {
if !titleValidator.MatchString(title) {
http.NotFound(w, r)
return
}
...
if err := p.save(); err != nil {
log.Println("save failed:", err)
http.Error(w, "save failed", http.StatusInternalServerError)
return
}
} Defensive patterns
Strategy: validation
Validate before calling
// whitelist titles before any filesystem touch
var titleValidator = regexp.MustCompile("^[a-zA-Z0-9]+$")
func getTitle(w http.ResponseWriter, r *http.Request) (string, bool) {
m := validPath.FindStringSubmatch(r.URL.Path)
if m == nil {
http.NotFound(w, r)
return "", false
}
return m[2], true
} Prevention
- Whitelist route-derived filenames; reject anything outside [A-Za-z0-9]
- Run the app in a dedicated writable data directory, never next to sources
- Log the full error server-side; send generic text to the client to avoid leaking paths
When it happens
Trigger: A title containing path separators or characters illegal in filenames (e.g. '../x' or 'a/b'); the working directory not writable so the 0600 file cannot be created; disk full; the title colliding with an existing directory name.
Common situations: Crafted or accidental URLs like /edit/../secret traversing out of the data directory; running the wiki binary in a read-only or non-writable directory; containerized deployments with read-only filesystems.
Related errors
AI-assisted analysis of unknwon/the-way-to-go_ZH_CN@7a54d34d36 (2026-08-15).
Data as JSON: /api/errors/b43766f06abe819a.
Report an issue: GitHub.