{"record":{"id":"2e566c1863e24675","repo":"joewalnes/websocketd","slug":"no-cgi-script-named-in-path-q","errorCode":null,"errorMessage":"no CGI script named in path %q","messagePattern":"no CGI script named in path %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"libwebsocketd/http.go","lineNumber":197,"sourceCode":"// resolveCgiPath maps a request URL path to a file inside cgiDir, refusing\n// any path that would escape the directory.\n//\n// req.URL.Path arrives already percent-decoded, and this handler is not\n// mounted behind a ServeMux that would normalize it, so \"../\" segments and\n// (on Windows) \"..\\\" segments reach us verbatim. Naively joining such a\n// path lets a request name any file on the host, which cgi.Handler would\n// then execute — an unauthenticated RCE. We normalize the request path\n// ourselves and require the result to stay within cgiDir. checkPathBoundary\n// (applied by the caller) additionally defends against symlinks that point\n// out of the directory.\nfunc resolveCgiPath(cgiDir, urlPath string) (string, error) {\n\t// Normalize in slash space, then map to the OS separator. path.Clean\n\t// collapses \".\" and \"..\" lexically; a rooted clean path can never\n\t// retain a leading \"..\", so anything that tried to climb out is folded\n\t// back to the root and lands inside cgiDir.\n\tclean := path.Clean(\"/\" + filepath.ToSlash(urlPath))\n\tif clean == \"/\" {\n\t\treturn \"\", fmt.Errorf(\"no CGI script named in path %q\", urlPath)\n\t}\n\tfilePath := filepath.Join(cgiDir, filepath.FromSlash(clean))\n\n\t// Belt and suspenders: confirm the lexical result really is contained.\n\t// The rooted clean above should already guarantee this on every OS\n\t// (including Windows, where ToSlash folds \"..\\\" into \"../\" before the\n\t// clean sees it), so this check is not load-bearing today — it is here\n\t// to fail closed if the normalization above is ever weakened.\n\tif err := containsPath(cgiDir, filePath); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn filePath, nil\n}\n\n// containsPath reports an error unless child is dir itself or lies beneath it,\n// comparing lexically (no filesystem access, no symlink resolution).\nfunc containsPath(dir, child string) error {\n\trel, err := filepath.Rel(dir, child)","sourceCodeStart":179,"sourceCodeEnd":215,"githubUrl":"https://github.com/joewalnes/websocketd/blob/7a8683dc7f9778dc615945aaed2a8dc77290227b/libwebsocketd/http.go#L179-L215","documentation":"resolveCgiPath cleans the incoming URL path rooted at '/' and rejects it when the result is just '/', meaning no script filename was named. Without this check the handler would have no target file to execute, so it fails the request with this error before any filesystem join happens.","triggerScenarios":"GET http://host:8080/ (root) on a CGI-enabled server; a URL that collapses to root after cleaning, like /./ or /../; an empty path portion in the request.","commonSituations":"Browsers or uptime checks hitting the server root instead of a script endpoint; clients with a misconfigured base URL that omit the script name; link generators producing trailing-only paths.","solutions":["Request an actual script path, e.g. http://host:8080/script.cgi instead of the root","Fix the client/base URL so the script filename is included in the path","If a root health check is needed, point it at a real endpoint or a non-CGI route"],"exampleFix":"// before\nfetch('http://host:8080/')\n// after\nfetch('http://host:8080/handler.cgi')","handlingStrategy":"validation","validationCode":"const url = new URL(endpoint)\nif (url.pathname === '/' || url.pathname === '') {\n\tthrow new Error('CGI request must name a script path, e.g. /handler.cgi')\n}","typeGuard":"function namesScript(u) {\n  const p = new URL(u).pathname.replace(/\\/+$/, '')\n  return p.length > 0\n}","tryCatchPattern":"try {\n  const res = await fetch(endpoint)\n} catch (e) {\n  if (e.message.includes('no CGI script named in path')) {\n    endpoint = new URL('/handler.cgi', base).toString()\n  }\n}","preventionTips":["Always include the script filename in CGI endpoint URLs","Validate constructed URLs in client config (pathname must be non-root)","Configure health checks to target a real script endpoint, not '/'"],"tags":["http","cgi","routing"],"backgroundTag":"missing-cgi-script-path","analyzedSha":"7a8683dc7f9778dc615945aaed2a8dc77290227b","analyzedAt":"2026-09-03T13:52:22.309Z","contentChangedAt":"2026-09-03T13:52:22.309Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}