siyuan-note/siyuan · error

asset path is sensitive: %s

Error message

asset path is sensitive: %s

What it means

The MCP asset upload tool refuses to process an asset whose absolute path resolves to a sensitive location in the workspace (e.g. the data directory internals, config, or other protected paths checked by util.IsSensitivePath). This is a safety guard in validateAssetUploadPaths so MCP callers cannot read or overwrite critical workspace files by naming them as assets. The error is returned before any upload happens and aborts the whole call.

Source

Thrown at kernel/mcp/tools/asset.go:242

	_, succFiles, failedFiles, err := model.InsertLocalAssets(id, fileList, true)
	if err != nil {
		return CallToolResult{Content: []ContentItem{{Type: "text", Text: "upload assets failed: " + err.Error()}}, IsError: true}, nil
	}
	return newAssetUploadToolResult(succFiles, failedFiles), nil
}

// validateAssetUploadPaths 将上传路径归一化为绝对路径,并拒绝敏感路径,
// 防止通过 AI 提示注入诱导上传本地凭据等敏感文件(如 SSH 私钥、云服务凭据)后外泄。
// 非敏感的工作区外绝对路径仍然允许上传,与 globalCopyFiles 等接受工作区外路径的接口保持一致。
func validateAssetUploadPaths(fileList []string) ([]string, error) {
	for i, f := range fileList {
		abs, err := filepath.Abs(strings.TrimSpace(f))
		if err != nil {
			return nil, err
		}
		if util.IsSensitivePath(abs) {
			return nil, fmt.Errorf("asset path is sensitive: %s", abs)
		}
		fileList[i] = abs
	}
	return fileList, nil
}

func assetUnused(args map[string]any) (CallToolResult, error) {
	items := model.UnusedAssets(true)
	if len(items) == 0 {
		return CallToolResult{Content: []ContentItem{{Type: "text", Text: "no unused assets found"}}}, nil
	}
	var sb strings.Builder
	sb.WriteString(fmt.Sprintf("Unused assets (%d):\n\n", len(items)))
	for _, item := range items {
		sb.WriteString(fmt.Sprintf("- %s (%s)\n", item.Item, item.Name))
	}
	return CallToolResult{Content: []ContentItem{{Type: "text", Text: sb.String()}}}, nil
}

View on GitHub (pinned to 8641553a1f)

Solutions

  1. Pick an asset source path that is not a sensitive/protected workspace path; place the file in a normal assets or user directory.
  2. Check which paths util.IsSensitivePath treats as protected (kernel/util) and verify your absolute path against them before calling the tool.
  3. If the intent was to read/modify protected workspace data, use the dedicated kernel API instead of the MCP asset upload tool.
  4. Log the resolved absolute path shown in the error and correct the input argument accordingly.

Example fix

// before
await mcp.call("assetUpload", { files: [workspaceDir + "/conf/conf.json"] });
// after
await mcp.call("assetUpload", { files: ["/home/user/pictures/logo.png"] });
Defensive patterns

Strategy: validation

Validate before calling

const path = require('path');
const protectedDirs = ['conf', 'temp', 'history', 'snapshots'];
function isSafeAssetPath(p, workspaceDir) {
  const abs = path.resolve(p);
  if (!abs.startsWith(path.resolve(workspaceDir) + path.sep)) return false;
  const rel = path.relative(workspaceDir, abs);
  return !protectedDirs.some(d => rel === d || rel.startsWith(d + path.sep));
}
if (!isSafeAssetPath(files[i], workspaceDir)) throw new Error('sensitive asset path: ' + files[i]);

Type guard

function isNonEmptyString(v) { return typeof v === 'string' && v.trim().length > 0; }

Try / catch

try {
  await mcp.call("assetUpload", { files });
} catch (e) {
  if (String(e.message).startsWith("asset path is sensitive")) {
    // relocate file outside protected dirs and retry
  }
}

Prevention

When it happens

Trigger: Calling the MCP assetUpload tool with a file path argument that, after filepath.Abs, matches util.IsSensitivePath — e.g. a path pointing at the workspace config dir, temp/backup internals, or any protected location. A path like '<workspace>/conf/conf.json' or another protected path triggers it.

Common situations: An MCP client (LLM agent) hallucinating or guessing a path that overlaps protected workspace directories; a caller using the workspace root itself or config paths as the source of an asset; automation scripts built with relative paths that resolve into protected dirs.

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/603df1b276538284. Report an issue: GitHub.