{"record":{"id":"87dca69e0160ea99","repo":"siyuan-note/siyuan","slug":"plugin-publish-access-denied","errorCode":null,"errorMessage":"plugin publish access denied","messagePattern":"plugin publish access denied","errorType":"exception","errorClass":"ErrPluginPublishDenied","httpStatus":null,"severity":"warning","filePath":"kernel/model/plugin_publish.go","lineNumber":21,"sourceCode":"import (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"slices\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/88250/gulu\"\n\t\"github.com/gin-gonic/gin\"\n\t\"github.com/siyuan-note/siyuan/kernel/bazaar\"\n\t\"github.com/siyuan-note/siyuan/kernel/util\"\n)\n\nvar (\n\tErrPluginPublishDenied  = errors.New(\"plugin publish access denied\")\n\tErrPluginPublishMissing = errors.New(\"plugin publish data has not been generated\")\n\tErrPluginPublishInvalid = errors.New(\"invalid plugin publish declaration or data\")\n\tpluginPublishLock       sync.Mutex\n)\n\n// PluginPublishDeclaration 的资源为精确文件名，数据为可公开的顶层标量字段，不支持目录或通配符。\ntype PluginPublishDeclaration struct {\n\tResources []string `json:\"resources\"`\n\tData      []string `json:\"data\"`\n}\n\ntype PluginPublishInfo struct {\n\tResources []string `json:\"resources\"`\n\tFields    []string `json:\"fields\"`\n\tGranted   bool     `json:\"granted\"`\n}\n\ntype pluginPublishState struct {","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/9f775e8a12daef8255556097396f9b2739078892/kernel/model/plugin_publish.go#L3-L39","documentation":"ErrPluginPublishDenied is returned when access to a plugin's publish-mode resources or data is not permitted. The kernel throws it whenever the package name is invalid, the plugin is not marked accessible in publish mode (CheckPluginAccessableInPublish), the requested resource is not declared in plugin.json's publish declaration, or the publish data fields are not granted. The API layer maps it to HTTP 403 Forbidden.","triggerScenarios":"OpenPluginPublishResource with an undeclared resource or an inaccessible plugin; pluginPublishDeclaration with a name failing bazaar.IsValidPackageName; LoadPluginPublishData when publishFieldsGranted is false; SavePluginPublishData/LoadPluginPublishData for plugins without publish access; accessing plugin.json or kernel.js which are explicitly excluded from declared resources.","commonSituations":"A frontend/theme requests a plugin file not listed under publish.resources in plugin.json; a plugin name contains invalid characters (path traversal attempts); an operator never enabled the plugin in publish service settings; requesting publish data fields the user did not grant.","solutions":["Enable the plugin's accessibility in the publish service settings so CheckPluginAccessableInPublish passes","Declare the file under publish.resources in the plugin's plugin.json (exact file names only, no directories or wildcards; index.js, index.css and i18n/*.json are implicit)","Grant the requested data fields for the plugin in the publish data authorization settings","Fix the package name to satisfy bazaar.IsValidPackageName (lowercase letters, digits, hyphens)","Handle HTTP 403 on the caller side and inspect kernel logs to see which guard failed"],"exampleFix":"// before: fetching an undeclared resource\nGET /publish/plugins/my-plugin/README.md  -> 403 plugin publish access denied\n// after: declare it in plugins/my-plugin/plugin.json\n\"publish\": { \"resources\": [\"README.md\"], \"data\": [] }","handlingStrategy":"validation","validationCode":"const validPkg = /^[a-z0-9-]+$/.test(pluginName);\nconst declared = publishDeclaration.resources.includes(resource) ||\n  resource === \"index.js\" || resource === \"index.css\" ||\n  /^i18n\\/[^/]+\\.json$/.test(resource);\nconst accessible = publishServiceSettings.plugins[pluginName]?.accessible;\nif (!(validPkg && declared && accessible)) throw new Error(\"request would be denied (403)\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep publish.resources limited to exact relative file paths that actually exist in the plugin","Always enable the plugin in publish service settings before exposing it","Never request plugin.json or kernel.js through the publish resource endpoint","Check HTTP 403 specifically in client error handling to distinguish denial from other failures"],"tags":["publish","permission-denied","plugin","security"],"backgroundTag":"permission-denied","analyzedSha":"9f775e8a12daef8255556097396f9b2739078892","analyzedAt":"2026-09-19T03:17:15.984Z","contentChangedAt":"2026-09-19T03:17:15.984Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}