{"record":{"id":"603df1b276538284","repo":"siyuan-note/siyuan","slug":"asset-path-is-sensitive-s","errorCode":null,"errorMessage":"asset path is sensitive: %s","messagePattern":"asset path is sensitive: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/mcp/tools/asset.go","lineNumber":242,"sourceCode":"\n\t_, succFiles, failedFiles, err := model.InsertLocalAssets(id, fileList, true)\n\tif err != nil {\n\t\treturn CallToolResult{Content: []ContentItem{{Type: \"text\", Text: \"upload assets failed: \" + err.Error()}}, IsError: true}, nil\n\t}\n\treturn newAssetUploadToolResult(succFiles, failedFiles), nil\n}\n\n// validateAssetUploadPaths 将上传路径归一化为绝对路径，并拒绝敏感路径，\n// 防止通过 AI 提示注入诱导上传本地凭据等敏感文件（如 SSH 私钥、云服务凭据）后外泄。\n// 非敏感的工作区外绝对路径仍然允许上传，与 globalCopyFiles 等接受工作区外路径的接口保持一致。\nfunc validateAssetUploadPaths(fileList []string) ([]string, error) {\n\tfor i, f := range fileList {\n\t\tabs, err := filepath.Abs(strings.TrimSpace(f))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif util.IsSensitivePath(abs) {\n\t\t\treturn nil, fmt.Errorf(\"asset path is sensitive: %s\", abs)\n\t\t}\n\t\tfileList[i] = abs\n\t}\n\treturn fileList, nil\n}\n\nfunc assetUnused(args map[string]any) (CallToolResult, error) {\n\titems := model.UnusedAssets(true)\n\tif len(items) == 0 {\n\t\treturn CallToolResult{Content: []ContentItem{{Type: \"text\", Text: \"no unused assets found\"}}}, nil\n\t}\n\tvar sb strings.Builder\n\tsb.WriteString(fmt.Sprintf(\"Unused assets (%d):\\n\\n\", len(items)))\n\tfor _, item := range items {\n\t\tsb.WriteString(fmt.Sprintf(\"- %s (%s)\\n\", item.Item, item.Name))\n\t}\n\treturn CallToolResult{Content: []ContentItem{{Type: \"text\", Text: sb.String()}}}, nil\n}","sourceCodeStart":224,"sourceCodeEnd":260,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/mcp/tools/asset.go#L224-L260","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pick an asset source path that is not a sensitive/protected workspace path; place the file in a normal assets or user directory.","Check which paths util.IsSensitivePath treats as protected (kernel/util) and verify your absolute path against them before calling the tool.","If the intent was to read/modify protected workspace data, use the dedicated kernel API instead of the MCP asset upload tool.","Log the resolved absolute path shown in the error and correct the input argument accordingly."],"exampleFix":"// before\nawait mcp.call(\"assetUpload\", { files: [workspaceDir + \"/conf/conf.json\"] });\n// after\nawait mcp.call(\"assetUpload\", { files: [\"/home/user/pictures/logo.png\"] });","handlingStrategy":"validation","validationCode":"const path = require('path');\nconst protectedDirs = ['conf', 'temp', 'history', 'snapshots'];\nfunction isSafeAssetPath(p, workspaceDir) {\n  const abs = path.resolve(p);\n  if (!abs.startsWith(path.resolve(workspaceDir) + path.sep)) return false;\n  const rel = path.relative(workspaceDir, abs);\n  return !protectedDirs.some(d => rel === d || rel.startsWith(d + path.sep));\n}\nif (!isSafeAssetPath(files[i], workspaceDir)) throw new Error('sensitive asset path: ' + files[i]);","typeGuard":"function isNonEmptyString(v) { return typeof v === 'string' && v.trim().length > 0; }","tryCatchPattern":"try {\n  await mcp.call(\"assetUpload\", { files });\n} catch (e) {\n  if (String(e.message).startsWith(\"asset path is sensitive\")) {\n    // relocate file outside protected dirs and retry\n  }\n}","preventionTips":["Source assets from user/content directories, never workspace config or system dirs","Resolve to absolute paths and compare against the sensitive-path list before calling","Keep an allowlist of asset source directories in automation scripts"],"tags":["security","path-validation","mcp"],"backgroundTag":"path-traversal-blocked","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}