siyuan-note/siyuan · error

asset path is outside data directory

Error message

asset path is outside data directory

What it means

dataRelativeAssetPath computes the path relative to util.DataDir and rejects any path that escapes the data directory (result ".." or starting with "../", or a failed Rel computation). SiYuan throws this as a path-traversal guard so asset download cannot target files outside the workspace data directory.

Source

Thrown at kernel/model/asset_download.go:121

	case conf.ProviderSiYuan:
		if !IsSubscriber() {
			return errors.New(Conf.Language(376))
		}
	case conf.ProviderS3, conf.ProviderWebDAV, conf.ProviderLocal:
		if !IsPaidUser() {
			return errors.New(Conf.Language(376))
		}
	}
	return nil
}

func dataRelativeAssetPath(absPath string) (string, error) {
	if !filepath.IsAbs(absPath) {
		return "", fmt.Errorf("asset path must be absolute")
	}
	rel, err := filepath.Rel(util.DataDir, filepath.Clean(absPath))
	if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
		return "", fmt.Errorf("asset path is outside data directory")
	}
	if rel == "." {
		return "/", nil
	}
	return "/" + filepath.ToSlash(rel), nil
}

// EnsureAssetLocal 在调用方完成访问校验后补齐资源,下载内容仍由原有读取流程认证。
func EnsureAssetLocal(absPath string) error {
	if _, err := os.Stat(absPath); err == nil {
		return nil
	} else if !errors.Is(err, os.ErrNotExist) {
		return err
	}
	assetDownloadSourceMu.RLock()
	defer assetDownloadSourceMu.RUnlock()
	rel, err := dataRelativeAssetPath(absPath)
	if err != nil {

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Verify the path is inside util.DataDir before calling (filepath.Rel and prefix check)
  2. Clean the path and remove .. components; reject escape attempts
  3. If the asset legitimately lives elsewhere, copy it into the workspace assets folder first

Example fix

// before
model.EnsureAssetLocal("/etc/hosts") // outside data dir
// after
rel, _ := filepath.Rel(util.DataDir, target)
if rel == ".." || strings.HasPrefix(rel, "..") {
	return fmt.Errorf("asset outside data dir")
}
model.EnsureAssetLocal(target)
Defensive patterns

Strategy: validation

Validate before calling

rel, err := filepath.Rel(util.DataDir, filepath.Clean(p))
if err != nil || rel == ".." || strings.HasPrefix(rel, ".."+string(filepath.Separator)) {
  return fmt.Errorf("path escapes data dir")
}

Prevention

When it happens

Trigger: Calling EnsureAssetLocal or EnsureAssetPrefixLocal with an absolute path that resolves outside util.DataDir, e.g. /etc/passwd or a sibling directory path via .. segments.

Common situations: Passing user-supplied paths without sanitization; symlinks resolving outside the data dir; constructing paths from untrusted plugin input; migrating code that used paths from a different workspace.

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 siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/e7fd662be5b87499. Report an issue: GitHub.