OpenNHP/opennhp · error
invalid file name
Error message
invalid file name
What it means
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.
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
Example fix
// before
if !strings.HasPrefix(absPath, safeDirAbs) {
return metadata, fmt.Errorf("invalid file name")
}
// after
rel, relErr := filepath.Rel(safeDirAbs, absPath)
if relErr != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) || filepath.IsAbs(rel) {
return metadata, fmt.Errorf("invalid file name")
} Defensive patterns
Strategy: validation
Validate before calling
stat, err := os.Stat(absPath)
if err != nil || stat.IsDir() {
return fmt.Errorf("not a regular file: %s", absPath)
}
rel, err := filepath.Rel(safeDirAbs, absPath)
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) || filepath.IsAbs(rel) {
return fmt.Errorf("path escapes metadata dir")
} Type guard
func isInsideDir(baseDir, target string) bool {
rel, err := filepath.Rel(baseDir, target)
return err == nil && rel != ".." && !strings.HasPrefix(rel, ".."+string(filepath.Separator)) && !filepath.IsAbs(rel)
} Try / catch
metadata, err := loadMetadata(absPath)
if err != nil {
if strings.Contains(err.Error(), "invalid file name") {
http.Error(w, "invalid metadata path", http.StatusBadRequest)
return
}
http.Error(w, "metadata unavailable", http.StatusInternalServerError)
return
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- invalid resource ID: potential path traversal attack
- unknown remote provider
- unknown remote provider
- unsupported key type, expect RSA
- JWT signing key is not initialized
AI-assisted analysis of OpenNHP/opennhp@6e04ca5ff0 (2026-09-07).
Data as JSON: /api/errors/4bdea34b1e958486.
Report an issue: GitHub.
Appendix: source
Thrown at endpoints/server/httpstorage.go:307
}
// loadMetadata use to load file metadata
func loadMetadata(uuid string) (FileMetadata, error) {
var metadata FileMetadata
metadataPath := filepath.Join(ExeDirPath, metadataDir, uuid+".json")
absPath, err := filepath.Abs(metadataPath)
if err != nil {
return metadata, err
}
safeDir := filepath.Join(ExeDirPath, metadataDir)
safeDirAbs, err := filepath.Abs(safeDir)
if err != nil {
return metadata, err
}
if !strings.HasPrefix(absPath, safeDirAbs) {
return metadata, fmt.Errorf("invalid file name")
}
file, err := os.Open(absPath)
if err != nil {
return metadata, err
}
defer file.Close()
decoder := json.NewDecoder(file)
err = decoder.Decode(&metadata)
return metadata, err
}
// checkFileExists use to check if file exists
func checkFileExists(md5 string) (FileMetadata, bool) {
// check all metadata files
files, err := os.ReadDir(filepath.Join(ExeDirPath, metadataDir))
if err != nil {View on GitHub (pinned to 6e04ca5ff0)