{"record":{"id":"a1f66198d8176de5","repo":"JuliusBrussee/caveman","slug":"refusing-non-regular-file","errorCode":null,"errorMessage":"refusing non-regular file","messagePattern":"refusing non-regular file","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"engine/ccr/store_sqlite.go","lineNumber":352,"sourceCode":"\tinfo, err := os.Lstat(path)\n\tif errors.Is(err, os.ErrNotExist) && create {\n\t\tcreateErr := createSQLiteFile(path)\n\t\tif createErr == nil {\n\t\t\treturn nil\n\t\t}\n\t\tif !errors.Is(createErr, os.ErrExist) {\n\t\t\treturn createErr\n\t\t}\n\t\tinfo, err = os.Lstat(path)\n\t}\n\tif errors.Is(err, os.ErrNotExist) && !create {\n\t\treturn nil\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\tif info.Mode()&os.ModeSymlink != 0 || !info.Mode().IsRegular() {\n\t\treturn fmt.Errorf(\"refusing non-regular file\")\n\t}\n\terr = chmodSQLiteFile(path, info)\n\tif errors.Is(err, os.ErrNotExist) && !create {\n\t\t// The sidecar vanished while securing it — a concurrent process\n\t\t// checkpointed the WAL and removed it. Nothing left to secure.\n\t\treturn nil\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\tsecured, err := os.Lstat(path)\n\tif errors.Is(err, os.ErrNotExist) && !create {\n\t\treturn nil\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\tif !os.SameFile(info, secured) {","sourceCodeStart":334,"sourceCodeEnd":370,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/engine/ccr/store_sqlite.go#L334-L370","documentation":"secureSQLiteFile refuses to secure anything that is not a plain regular file: if Lstat (without following symlinks) shows the path is a symlink or otherwise non-regular, it returns 'refusing non-regular file'. This is a security guard preventing the CCR store from following a planted symlink when applying permissions or writing data.","triggerScenarios":"The sqlite path (or a -wal/-shm sidecar path) is a symlink pointing elsewhere; the 'file' is actually a directory, fifo, socket, or device node; an attacker or careless script replaced the db file with a symlink before Open.","commonSituations":"User symlinked the database from another location (e.g. ln -s ~/dropbox/recovery.db) to sync it; provisioning scripts created a fifo/socket at the db path; a broken deployment replaced the file with a directory.","solutions":["Replace the symlink with a real file: copy the target's contents to a regular file at that path (if intended) or remove the symlink and let Open create a fresh db","Verify with ls -la / stat that the path is a regular file","Never symlink CCR store files into sync folders (Dropbox/Google Drive); move the whole store directory instead","Remove any fifo/socket/directory occupying the db path and re-run"],"exampleFix":"// before\n$ ln -s /mnt/sync/recovery.db /var/lib/ccr/recovery.db\n// after\n$ rm /var/lib/ccr/recovery.db\n$ cp /mnt/sync/recovery.db /var/lib/ccr/recovery.db && chmod 600 /var/lib/ccr/recovery.db","handlingStrategy":"validation","validationCode":"func isRegularFile(p string) bool {\n    fi, err := os.Lstat(p)\n    return err == nil && fi.Mode().IsRegular() && fi.Mode()&os.ModeSymlink == 0\n}","typeGuard":null,"tryCatchPattern":"err := ccr.Open(dbPath)\nif err != nil && strings.Contains(err.Error(), \"refusing non-regular file\") {\n    return fmt.Errorf(\"%s (or a sidecar) is a symlink/special file; replace with a real file: %w\", dbPath, err)\n}","preventionTips":["Never symlink sqlite db or sidecar files","Check with os.Lstat (not Stat) when provisioning the db path","Exclude the store directory from scripts that create fifos/sockets"],"tags":["go","sqlite","security","symlink"],"backgroundTag":"path-traversal-blocked","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"}