{"record":{"id":"4bdea34b1e958486","repo":"OpenNHP/opennhp","slug":"invalid-file-name","errorCode":null,"errorMessage":"invalid file name","messagePattern":"invalid file name","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"endpoints/server/httpstorage.go","lineNumber":307,"sourceCode":"}\n\n// loadMetadata use to load file metadata\nfunc loadMetadata(uuid string) (FileMetadata, error) {\n\tvar metadata FileMetadata\n\tmetadataPath := filepath.Join(ExeDirPath, metadataDir, uuid+\".json\")\n\n\tabsPath, err := filepath.Abs(metadataPath)\n\tif err != nil {\n\t\treturn metadata, err\n\t}\n\n\tsafeDir := filepath.Join(ExeDirPath, metadataDir)\n\tsafeDirAbs, err := filepath.Abs(safeDir)\n\tif err != nil {\n\t\treturn metadata, err\n\t}\n\tif !strings.HasPrefix(absPath, safeDirAbs) {\n\t\treturn metadata, fmt.Errorf(\"invalid file name\")\n\t}\n\n\tfile, err := os.Open(absPath)\n\tif err != nil {\n\t\treturn metadata, err\n\t}\n\tdefer file.Close()\n\n\tdecoder := json.NewDecoder(file)\n\terr = decoder.Decode(&metadata)\n\treturn metadata, err\n}\n\n// checkFileExists use to check if file exists\nfunc checkFileExists(md5 string) (FileMetadata, bool) {\n\t// check all metadata files\n\tfiles, err := os.ReadDir(filepath.Join(ExeDirPath, metadataDir))\n\tif err != nil {","sourceCodeStart":289,"sourceCodeEnd":325,"githubUrl":"https://github.com/OpenNHP/opennhp/blob/6e04ca5ff03222a699c24205cd4bf8fee9af7ffe/endpoints/server/httpstorage.go#L289-L325","documentation":"loadMetadata in endpoints/server/httpstorage.go rejects paths that fall outside the metadata directory (ExeDirPath/metadataDir) before opening the file. The 'invalid file name' error is a security guard: the requested absPath does not have the resolved metadata base directory as a prefix, so the file name is considered invalid rather than trusted.","triggerScenarios":"Calling loadMetadata/checkFileExists with an absPath that is relative-resolved or cleaned differently than safeDirAbs, or a path like ../ escape, so strings.HasPrefix(absPath, safeDirAbs) fails.","commonSituations":"Path traversal attempts in the HTTP metadata API; passing a symlinked or differently-cased path; comparing an absPath computed before filepath.Abs against safeDirAbs; metadataDir moved or executable run from a different directory.","solutions":["Ensure absPath is computed with filepath.Abs/filepath.Clean against the same base used for safeDirAbs before calling loadMetadata","Fix the caller to pass file names relative to the metadata dir, letting loadMetadata join them itself","Harden the check with filepath.Rel and reject '..' or separator-prefixed results instead of a raw prefix match","Verify the executable is run from the expected directory so ExeDirPath/metadataDir points at the real metadata location"],"exampleFix":"// before\nif !strings.HasPrefix(absPath, safeDirAbs) {\n\treturn metadata, fmt.Errorf(\"invalid file name\")\n}\n// after\nrel, relErr := filepath.Rel(safeDirAbs, absPath)\nif relErr != nil || rel == \"..\" || strings.HasPrefix(rel, \"..\"+string(filepath.Separator)) || filepath.IsAbs(rel) {\n\treturn metadata, fmt.Errorf(\"invalid file name\")\n}","handlingStrategy":"validation","validationCode":"stat, err := os.Stat(absPath)\nif err != nil || stat.IsDir() {\n\treturn fmt.Errorf(\"not a regular file: %s\", absPath)\n}\nrel, err := filepath.Rel(safeDirAbs, absPath)\nif err != nil || rel == \"..\" || strings.HasPrefix(rel, \"..\"+string(filepath.Separator)) || filepath.IsAbs(rel) {\n\treturn fmt.Errorf(\"path escapes metadata dir\")\n}","typeGuard":"func isInsideDir(baseDir, target string) bool {\n\trel, err := filepath.Rel(baseDir, target)\n\treturn err == nil && rel != \"..\" && !strings.HasPrefix(rel, \"..\"+string(filepath.Separator)) && !filepath.IsAbs(rel)\n}","tryCatchPattern":"metadata, err := loadMetadata(absPath)\nif err != nil {\n\tif strings.Contains(err.Error(), \"invalid file name\") {\n\t\thttp.Error(w, \"invalid metadata path\", http.StatusBadRequest)\n\t\treturn\n\t}\n\thttp.Error(w, \"metadata unavailable\", http.StatusInternalServerError)\n\treturn\n}","preventionTips":["Always pass cleaned, absolute paths produced by filepath.Abs into loadMetadata","Never concatenate user input directly into file paths; join with filepath.Join","Add unit tests for traversal payloads like ../secrets and absolute overrides","Prefer filepath.Rel-based containment checks over raw prefix comparison"],"tags":["go","path-validation","security"],"backgroundTag":"path-traversal-blocked","analyzedSha":"6e04ca5ff03222a699c24205cd4bf8fee9af7ffe","analyzedAt":"2026-09-07T15:44:59.941Z","contentChangedAt":"2026-09-07T15:44:59.941Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}