{"record":{"id":"47f745677d99811c","repo":"shadow1ng/fscan","slug":"unable-to-create-persistence-directory","errorCode":null,"errorMessage":"Unable to create persistence directory","messagePattern":"Unable to create persistence directory","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"plugins/local/crontask.go","lineNumber":169,"sourceCode":"\t// 获取用户目录\n\tif usr, err := user.Current(); err == nil {\n\t\tuserDirs := []string{\n\t\t\tfilepath.Join(usr.HomeDir, \".local\", \"bin\"),\n\t\t\tfilepath.Join(usr.HomeDir, \".cache\"),\n\t\t}\n\t\tpersistDirs = append(userDirs, persistDirs...)\n\t}\n\n\tvar targetDir string\n\tfor _, dir := range persistDirs {\n\t\tif err := os.MkdirAll(dir, 0755); err == nil {\n\t\t\ttargetDir = dir\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif targetDir == \"\" {\n\t\treturn \"\", fmt.Errorf(\"%s\", i18n.GetText(\"persistence_dir_create_failed\"))\n\t}\n\n\t// 生成隐藏文件名\n\tbasename := filepath.Base(p.targetFile)\n\thiddenName := \".\" + strings.TrimSuffix(basename, filepath.Ext(basename))\n\tif p.isScriptFile() {\n\t\thiddenName += \".sh\"\n\t}\n\n\ttargetPath := filepath.Join(targetDir, hiddenName)\n\n\t// 复制文件\n\terr := p.copyFile(p.targetFile, targetPath)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\t// 设置执行权限","sourceCodeStart":151,"sourceCodeEnd":187,"githubUrl":"https://github.com/shadow1ng/fscan/blob/95cc12e753bf43de7004e5aef42a9ffba3934303/plugins/local/crontask.go#L151-L187","documentation":"copyToPersistPath tries a list of candidate persistence directories (via os.MkdirAll) and, if it could not create or use any of them (targetDir stays empty), returns this error. It typically means every candidate path was unwritable or failed to be created due to permissions or a full filesystem.","triggerScenarios":"Running the crontask plugin as a non-root user when all candidate persistence directories require root; read-only root filesystem (containers); MkdirAll failing on every candidate due to permission or disk errors.","commonSituations":"Persistence attempted on a hardened/minimal container without write access to standard cron locations; running the agent under a restricted service account; immutable or read-only /etc, /var, or $HOME.","solutions":["Re-run as root (or with CAP_DAC_OVERRIDE) so the persistence directories can be created.","Pre-create one of the candidate directories and grant the current user write permission.","Point the persistence location at a user-writable path (e.g. user crontab area) if root is unavailable.","Check disk space / mount flags (ro) if permissions look correct."],"exampleFix":"// before\n$ ./agent --plugin crontask   # running as low-priv user\n// after\n$ sudo ./agent --plugin crontask\n# or, pre-provision:\n$ sudo mkdir -p /var/spool/cron && sudo chown agent /var/spool/cron","handlingStrategy":"try-catch","validationCode":"for _, dir := range candidateDirs {\n    if info, err := os.Stat(dir); err == nil && info.IsDir() {\n        if f, err := os.CreateTemp(dir, \".w\"); err == nil { f.Close(); os.Remove(f.Name()); break }\n    }\n}","typeGuard":null,"tryCatchPattern":"res := plugin.Scan(ctx, cfg)\nif !res.Success && strings.Contains(res.Error.Error(), \"persistence directory\") {\n    return fmt.Errorf(\"needs root or writable persistence dir: %w\", res.Error)\n}","preventionTips":["Run persistence plugins with the privileges they require (root).","Pre-create the persistence directory with correct ownership in provisioning.","Avoid read-only root filesystems when persistence plugins are enabled."],"tags":["persistence","filesystem","permissions"],"backgroundTag":"mkdir-permission-denied","analyzedSha":"95cc12e753bf43de7004e5aef42a9ffba3934303","analyzedAt":"2026-09-06T17:07:30.094Z","contentChangedAt":"2026-09-06T17:07:30.094Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}