{"record":{"id":"b43766f06abe819a","repo":"unknwon/the-way-to-go_ZH_CN","slug":"err-error","errorCode":null,"errorMessage":"err.Error()","messagePattern":"err\\.Error\\(\\)","errorType":"http","errorClass":null,"httpStatus":500,"severity":"error","filePath":"eBook/15.6.md","lineNumber":90,"sourceCode":"\t\treturn\n\t}\n\trenderTemplate(w, \"view\", p)\n}\n\nfunc editHandler(w http.ResponseWriter, r *http.Request, title string) {\n\tp, err := load(title)\n\tif err != nil {\n\t\tp = &Page{Title: title}\n\t}\n\trenderTemplate(w, \"edit\", p)\n}\n\nfunc saveHandler(w http.ResponseWriter, r *http.Request, title string) {\n\tbody := r.FormValue(\"body\")\n\tp := &Page{Title: title, Body: []byte(body)}\n\terr := p.save()\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\thttp.Redirect(w, r, \"/view/\"+title, http.StatusFound)\n}\n\nfunc renderTemplate(w http.ResponseWriter, tmpl string, p *Page) {\n\terr := templates[tmpl].Execute(w, p)\n\tif err != nil {\n\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t}\n}\n\nfunc (p *Page) save() error {\n\tfilename := p.Title + \".txt\"\n\t// file created with read-write permissions for the current user only\n\treturn ioutil.WriteFile(filename, p.Body, 0600)\n}\n","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/unknwon/the-way-to-go_ZH_CN/blob/7a54d34d3657084b6a59e5618bd069b912d571aa/eBook/15.6.md#L72-L108","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nfunc saveHandler(w http.ResponseWriter, r *http.Request, title string) {\n    body := r.FormValue(\"body\")\n    p := &Page{Title: title, Body: []byte(body)}\n    err := p.save()\n    if err != nil {\n        http.Error(w, err.Error(), http.StatusInternalServerError)\n        return\n    }\n    ...\n}\n\n// after\nvar titleValidator = regexp.MustCompile(\"^[a-zA-Z0-9]+$\")\n\nfunc saveHandler(w http.ResponseWriter, r *http.Request, title string) {\n    if !titleValidator.MatchString(title) {\n        http.NotFound(w, r)\n        return\n    }\n    ...\n    if err := p.save(); err != nil {\n        log.Println(\"save failed:\", err)\n        http.Error(w, \"save failed\", http.StatusInternalServerError)\n        return\n    }\n}","handlingStrategy":"validation","validationCode":"// whitelist titles before any filesystem touch\nvar titleValidator = regexp.MustCompile(\"^[a-zA-Z0-9]+$\")\n\nfunc getTitle(w http.ResponseWriter, r *http.Request) (string, bool) {\n    m := validPath.FindStringSubmatch(r.URL.Path)\n    if m == nil {\n        http.NotFound(w, r)\n        return \"\", false\n    }\n    return m[2], true\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["go","http","wiki","filesystem","path-traversal","ioutil"],"backgroundTag":null,"analyzedSha":"7a54d34d3657084b6a59e5618bd069b912d571aa","analyzedAt":"2026-08-15T15:13:06.026Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}