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

  1. 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
  2. Reject titles containing os.PathSeparator or '..' before any filesystem use
  3. Run the binary in a writable directory or store pages under an explicit data directory
  4. 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

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.