{"record":{"id":"79a11266783fb174","repo":"siyuan-note/siyuan","slug":"do-not-include-symbols-quot-039-lt","errorCode":null,"errorMessage":"Do not include symbols \\ / : * ? &quot; &#039; &lt; &gt; |","messagePattern":"Do not include symbols \\\\ / : \\* \\? &quot; &#039; &lt; &gt; \\|","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/model/assets.go","lineNumber":1511,"sourceCode":"\t// 加密笔记本的资源磁盘文件名参与 AAD，重命名需要重新封装密文，当前不支持。\n\tif absPath, absErr := GetAssetAbsPathInBox(oldPath, \"\"); absErr == nil {\n\t\tif IsEncryptedAssetPath(absPath) {\n\t\t\terr = errors.New(\"renaming assets in encrypted notebooks is not supported\")\n\t\t\treturn\n\t\t}\n\t}\n\n\tnewName = strings.TrimSpace(newName)\n\tnewName = util.FilterUploadFileName(newName)\n\tif path.Base(oldCleanPath) == newName {\n\t\treturn\n\t}\n\tif \"\" == newName {\n\t\treturn\n\t}\n\n\tif !gulu.File.IsValidFilename(newName) {\n\t\terr = errors.New(Conf.Language(151))\n\t\treturn\n\t}\n\n\tnewName = util.AssetName(newName+filepath.Ext(oldCleanPath), ast.NewNodeID())\n\tparentDir := path.Dir(oldCleanPath)\n\tnewPath = path.Join(parentDir, newName)\n\toldAbsPath, getErr := GetAssetAbsPathInBox(oldPath, \"\")\n\tif getErr != nil {\n\t\tlogging.LogErrorf(\"get asset [%s] abs path failed: %s\", oldPath, getErr)\n\t\treturn\n\t}\n\tnewAbsPath := filepath.Join(filepath.Dir(oldAbsPath), newName)\n\tfilelock.Lock(oldAbsPath)\n\tif err = os.Rename(oldAbsPath, newAbsPath); err != nil {\n\t\tif err = gulu.File.Copy(oldAbsPath, newAbsPath); err != nil {\n\t\t\tfilelock.Unlock(oldAbsPath)\n\t\t\tlogging.LogErrorf(\"copy asset [%s] failed: %s\", oldAbsPath, err)\n\t\t\treturn","sourceCodeStart":1493,"sourceCodeEnd":1529,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/model/assets.go#L1493-L1529","documentation":"Returned by RenameAsset (assets.go:1511) when the new asset name fails gulu.File.IsValidFilename after trimming and util.FilterUploadFileName. The message is the kernel i18n key 151 listing forbidden characters: backslash, slash, colon, asterisk, question mark, double-quote, single-quote, less-than, greater-than, pipe. The HTML-entity form (&quot; &#039; &lt; &gt;) is used because the string is rendered in the UI.","triggerScenarios":"Calling /api/asset/renameAsset with a newName that contains any of \\ / : * ? < > | or a quote, is empty after filtering, or otherwise violates the portable-filename rules enforced by gulu.File.IsValidFilename. The check runs after FilterUploadFileName strips some characters, so the residual name still carried a forbidden symbol.","commonSituations":"User pastes a name with trailing punctuation or a date containing colons (e.g. \"img:2024.png\"); name copied from a URL with slashes/queries; plugin submits a raw filename without sanitization; cross-platform name using Windows-illegal characters on a Linux host (the guard is OS-independent).","solutions":["Strip or replace the forbidden characters (\\ / : * ? < > | and both quote kinds) before calling RenameAsset.","Run the candidate through gulu.File.IsValidFilename (or an equivalent regex) on the client side so the user gets immediate feedback.","If the name is user-typed, reject the characters in the input field rather than after submission."],"exampleFix":"// before: passing raw user input\nmodel.RenameAsset(oldPath, userInput)\n\n// after: validate client-side first\nconst forbidden = /[\\\\\\/:*?\"'<>|]/\nif (forbidden.test(userInput)) {\n    showMessage(window.siyuan.languages[151])\n    return\n}\nmodel.RenameAsset(oldPath, userInput)","handlingStrategy":"validation","validationCode":"// Reject forbidden filename characters before calling renameAsset.\nconst FORBIDDEN = /[\\\\\\/:*?\"'<>|]/\nif (!newName || FORBIDDEN.test(newName)) {\n  showMessage(window.siyuan.languages[151])\n  return\n}","typeGuard":null,"tryCatchPattern":"// Validate client-side; the kernel will also reject, so surface the i18n tip.\ntry { await renameAsset(oldPath, newName) }\ncatch (e) { if (/symbols/i.test(e.message)) highlightBadChars(newName); else throw e }","preventionTips":["Run a forbidden-character regex on the input field and block submission.","Sanitize pasted names (strip colons from dates, slashes from paths).","Remember quotes are also forbidden, not just the Windows reserved set."],"tags":["assets","rename","validation","filename","i18n"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}