{"record":{"id":"efe0da854916af88","repo":"siyuan-note/siyuan","slug":"invalid-attribute-view-id","errorCode":null,"errorMessage":"invalid attribute view id","messagePattern":"invalid attribute view id","errorType":"http","errorClass":"ErrInvalidAttributeViewID","httpStatus":null,"severity":"error","filePath":"kernel/av/av.go","lineNumber":1294,"sourceCode":"\n\tav := filepath.Join(util.DataDir, \"storage\", \"av\")\n\tret = filepath.Join(av, avID+\".json\")\n\tif !gulu.File.IsDir(av) {\n\t\tif err := os.MkdirAll(av, 0755); err != nil {\n\t\t\tlogging.LogErrorf(\"create attribute view dir failed: %s\", err)\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc GetAttributeViewI18n(key string) string {\n\treturn util.AttrViewLangs[util.Lang][key].(string)\n}\n\nvar (\n\tErrAttributeViewNotFound  = errors.New(\"attribute view not found\")\n\tErrInvalidAttributeViewID = errors.New(\"invalid attribute view id\")\n\tErrInvalidBoxID           = errors.New(\"invalid box id\")\n\tErrViewNotFound           = errors.New(\"view not found\")\n\tErrKeyNotFound            = errors.New(\"key not found\")\n\tErrItemNotFound           = errors.New(\"item not found\")\n\tErrWrongLayoutType        = errors.New(\"wrong layout type\")\n\tErrInvalidColumnAlign     = errors.New(\"invalid column align\")\n\tErrSpecTooNew             = errors.New(\"attribute view spec is too new\")\n\tErrFilterTooDeep          = errors.New(\"filter nesting depth exceeds the maximum allowed\")\n)\n\nconst (\n\tNodeAttrNameAvs        = \"custom-avs\"                 // 用于标记块所属的属性视图，逗号分隔 av id\n\tNodeAttrView           = \"custom-sy-av-view\"          // 用于标记块所属的属性视图视图 view id Database block support specified view https://github.com/siyuan-note/siyuan/issues/10443\n\tNodeAttrVisibleViewIDs = \"custom-sy-av-visible-views\" // 用于标记数据库块显示的视图 ID，逗号分隔\n\tNodeAttrViewStaticText = \"custom-sy-av-s-text\"        // 用于标记块所属的属性视图静态文本 Database-bound block primary key supports setting static anchor text https://github.com/siyuan-note/siyuan/issues/10049\n\n\tNodeAttrViewNames = \"av-names\" // 用于临时标记块所属的属性视图名称，空格分隔\n)","sourceCodeStart":1276,"sourceCodeEnd":1312,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/afa823b6b4e4f183511e0bc0a3be93caa94c7c97/kernel/av/av.go#L1276-L1312","documentation":"Sentinel error av.ErrInvalidAttributeViewID (kernel/av/av.go:1294). ParseAttributeView (av.go:739), ParseAttributeViewInBox (av.go:758), SaveAttributeView (av.go:892) and the encrypted-box hook (encrypted_hook.go:206) reject any avID that fails lute's ast.IsNodeIDPattern — the fixed-format node-ID pattern used for all SiYuan block/av IDs. Because the ID becomes a filename under storage/av/, this doubles as a path-traversal guard: '../outside', 'nested/outside' and similar are rejected (covered by kernel/av/path_test.go).","triggerScenarios":"Passing a human-chosen name ('mydb'), a file path ('../outside'), an empty string, or any non-pattern string as avID to an /api/av/* endpoint or to the kernel Go functions; plugins feeding user input straight into ParseAttributeView.","commonSituations":"Plugin authors inventing their own ID scheme instead of using av.NewAttributeView(); API scripts copying an av 'name' where the ID belongs; IDs mangled by URL truncation or encoding.","solutions":["Use IDs produced by the system: av.NewAttributeView() / ast.NewNodeID(), or an avID copied from an existing database block's custom-avs attribute","Validate the ID shape before calling (fixed-length node-ID pattern, no slashes/dots)","Fetch the correct ID via the av APIs rather than guessing","If you maintain external tooling, keep the avID mapping table so IDs are never reconstructed from names"],"exampleFix":"// before\navView, err := av.ParseAttributeView(\"my-database\")\n\n// after\navID := ast.NewNodeID() // or an id obtained from an existing av\navView, err := av.ParseAttributeView(avID)","handlingStrategy":"type-guard","validationCode":"// Go callers: validate before calling any av API\nif !ast.IsNodeIDPattern(avID) {\n    return fmt.Errorf(\"rejecting malformed attribute view id %q\", avID)\n}\n_, err := av.ParseAttributeView(avID)","typeGuard":"func isValidAvID(id string) bool {\n    return ast.IsNodeIDPattern(id) // same pattern the kernel enforces; rejects paths and names\n}","tryCatchPattern":"if _, err := av.ParseAttributeView(avID); err != nil {\n    if errors.Is(err, av.ErrInvalidAttributeViewID) {\n        // the id is malformed or contains path characters: do not retry, reject the input\n    }\n}","preventionTips":["Only use IDs minted by av.NewAttributeView() or ast.NewNodeID() — never user-typed names","Treat avIDs as opaque tokens: store and forward them verbatim","Validate ids at the trust boundary in plugins before passing them to kernel APIs","Never build avIDs from path fragments — the check is also a path-traversal guard"],"tags":["attribute-view","id-validation","path-traversal","database"],"backgroundTag":"invalid-id-format","analyzedSha":"afa823b6b4e4f183511e0bc0a3be93caa94c7c97","analyzedAt":"2026-08-18T17:04:10.865Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}