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

  1. Ensure absPath is computed with filepath.Abs/filepath.Clean against the same base used for safeDirAbs before calling loadMetadata
  2. Fix the caller to pass file names relative to the metadata dir, letting loadMetadata join them itself
  3. Harden the check with filepath.Rel and reject '..' or separator-prefixed results instead of a raw prefix match
  4. 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

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


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)