{"record":{"id":"9d467bf6b5566bd6","repo":"JuliusBrussee/caveman","slug":"inspect-sqlite-parent-acl-q-w","errorCode":null,"errorMessage":"inspect sqlite parent ACL %q: %w","messagePattern":"inspect sqlite parent ACL %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/ccr/sqlite_parent_security_windows.go","lineNumber":20,"sourceCode":"\npackage ccr\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"unsafe\"\n\n\t\"golang.org/x/sys/windows\"\n)\n\nfunc validateSQLiteParentSecurity(path string, _ os.FileInfo) error {\n\tdescriptor, err := windows.GetNamedSecurityInfo(\n\t\tpath,\n\t\twindows.SE_FILE_OBJECT,\n\t\twindows.DACL_SECURITY_INFORMATION,\n\t)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"inspect sqlite parent ACL %q: %w\", path, err)\n\t}\n\tif descriptor == nil {\n\t\treturn fmt.Errorf(\"sqlite parent %q has no security descriptor\", path)\n\t}\n\tdacl, _, err := descriptor.DACL()\n\tif err != nil || dacl == nil {\n\t\treturn fmt.Errorf(\"sqlite parent %q has no restrictive DACL\", path)\n\t}\n\tbroad, err := windowsACLGrantsBroadWrite(dacl)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"inspect sqlite parent ACL %q: %w\", path, err)\n\t}\n\tif broad {\n\t\treturn fmt.Errorf(\"sqlite parent %q grants broad Windows write access\", path)\n\t}\n\treturn nil\n}\n","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/engine/ccr/sqlite_parent_security_windows.go#L2-L38","documentation":"On Windows, engine/ccr validates that the SQLite database's parent directory has a restrictive DACL so other local users cannot replace the database file. If GetNamedSecurityInfo fails while reading the directory's DACL, the failure is wrapped as 'inspect sqlite parent ACL %q: %w' so the caller sees both the path and the underlying Windows API error.","triggerScenarios":"Calling validateSQLiteParentSecurity on Windows when windows.GetNamedSecurityInfo(path, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION) fails — e.g. the path does not exist, is a network/UNC location without retrievable security info, or the caller lacks READ_CONTROL on the directory.","commonSituations":"Database placed on a network share or mapped drive where ACLs are unavailable; path deleted or on an unavailable volume at store-open time; running from a context without permission to read the directory's security descriptor; FAT/exFAT filesystems with no ACL support.","solutions":["Read the wrapped Win32 error: if it is file-not-found, ensure the parent directory exists before opening the store; if access-denied, grant the process READ_CONTROL on the directory","Move the database onto a local NTFS volume owned by the running user instead of a network share or FAT-formatted drive","Pre-create the parent directory with restrictive ACLs (icacls <dir> /inheritance:r /grant:r \"%USERNAME%\":F) before first store open"],"exampleFix":"// before\nstore, _ := engine.OpenCCRStore(`\\\\server\\share\\ccr\\db.sqlite`) // no ACLs on share\n// after\nos.MkdirAll(`C:\\ProgramData\\myapp\\ccr`, 0o700)\nstore, _ := engine.OpenCCRStore(`C:\\ProgramData\\myapp\\ccr\\db.sqlite`)","handlingStrategy":"try-catch","validationCode":"if fi, err := os.Stat(parentDir); err != nil || !fi.IsDir() {\n\treturn fmt.Errorf(\"parent %s must be a local directory\", parentDir)\n}","typeGuard":null,"tryCatchPattern":"store, err := engine.OpenCCRStore(path)\nif err != nil && strings.Contains(err.Error(), \"inspect sqlite parent ACL\") {\n\t// wrapped Win32 error: check for not-found (create dir) or access-denied (grant READ_CONTROL),\n\t// or relocate to local NTFS storage\n}","preventionTips":["Store databases on local NTFS volumes, not network shares or FAT drives","Ensure the parent directory exists and the process can read its security info","Pre-create data directories with explicit restrictive ACLs (icacls)"],"tags":["go","windows","sqlite","security","acl","filesystem"],"backgroundTag":"insufficient-permissions","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}