Tencent/WeKnora · error

invalid file path: %w

Error message

invalid file path: %w

What it means

GetFile wraps the error from secutils.SafePathUnderBase when the requested filePath does not resolve to a location inside the storage base directory. This is the path-traversal guard: paths like '../secrets.txt' or absolute paths outside baseDir are rejected before any file is opened. The wrapped error explains which path check failed.

Source

Thrown at internal/application/service/file/local.go:118

	logger.Infof(ctx, "File saved successfully: %s", filePath)
	// Return provider:// path format: local://{relative_path}
	relPath, _ := filepath.Rel(s.baseDir, filePath)
	return localScheme + filepath.ToSlash(relPath), nil
}

// GetFile retrieves a file from the local file system by its path
// Returns a ReadCloser for reading the file content
// Supports both provider scheme: local://{relative_path} and legacy absolute paths.
// 路径必须在 baseDir 下,防止路径遍历(如 ../../)
func (s *localFileService) GetFile(ctx context.Context, filePath string) (io.ReadCloser, error) {
	logger.Infof(ctx, "Getting file: %s", filePath)

	candidate := s.normalizePathForBase(filePath)
	resolved, err := secutils.SafePathUnderBase(s.baseDir, candidate)
	if err != nil {
		logger.Errorf(ctx, "Path traversal denied for GetFile: %v", err)
		return nil, fmt.Errorf("invalid file path: %w", err)
	}

	file, err := os.Open(resolved)
	if err != nil {
		// baseDir/resolved are logged so a storage base-dir mismatch (e.g.
		// writer and reader started with different LOCAL_STORAGE_BASE_DIR)
		// is immediately visible instead of just "no such file or directory".
		logger.Errorf(ctx, "Failed to open file: baseDir=%s resolvedPath=%s err=%v", s.baseDir, resolved, err)
		return nil, fmt.Errorf("failed to open file: %w", err)
	}

	logger.Info(ctx, "File opened successfully")
	return file, nil
}

// DeleteFile removes a file from the local file system
// Returns an error if deletion fails
// 路径必须在 baseDir 下,防止路径遍历(如 ../../)

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Remove '..' segments and ensure the stored path is relative to baseDir before calling GetFile
  2. Verify the record was written by the same local service with the same LOCAL_STORAGE_BASE_DIR (provider scheme must be local://)
  3. Decode/sanitize URL-encoded paths before lookup and reject suspicious input at the API boundary
  4. Use the returned local:// path from SaveFile rather than constructing paths manually

Example fix

// before: unsafe, raw user input
f, err := svc.GetFile(ctx, r.URL.Query().Get("path"))
// after: validate the path shape first
p := r.URL.Query().Get("path")
if strings.Contains(p, "..") || filepath.IsAbs(p) {
	http.Error(w, "invalid path", http.StatusBadRequest)
	return
}
f, err := svc.GetFile(ctx, p)
Defensive patterns

Strategy: validation

Validate before calling

func safeLocalPath(p string) bool {
	if strings.Contains(p, "..") || filepath.IsAbs(p) {
		return false
	}
	if i := strings.Index(p, "://"); i >= 0 && p[:i+3] != "local://" {
		return false
	}
	return true
}

Type guard

func isLocalPath(p string) bool {
	return !filepath.IsAbs(p) && !strings.Contains(p, "..")
}

Try / catch

file, err := svc.GetFile(ctx, path)
if err != nil {
	if errors.Is(err, secutils.ErrPathTraversal) {
		http.Error(w, "invalid path", http.StatusBadRequest)
		return
	}
	http.Error(w, "internal error", http.StatusInternalServerError)
}

Prevention

When it happens

Trigger: Calling GetFile with a path containing '..' segments that escape baseDir, an absolute path pointing outside baseDir, a different-provider scheme (e.g. s3://...) reaching the local service, or a symlink trick that resolves outside the base.

Common situations: Storing raw client-supplied filenames and passing them straight to GetFile; storage rows containing paths written by a different backend or with a different LOCAL_STORAGE_BASE_DIR; URL-encoded traversal (%2e%2e%2f) not decoded before validation.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/1b7cd8471d1fd64d. Report an issue: GitHub.