{"record":{"id":"0af5138bc3dd40c1","repo":"elunez/eladmin","slug":"opt-home-0af513","errorCode":null,"errorMessage":"文件只能备份在opt目录或者home目录 ","messagePattern":"文件只能备份在opt目录或者home目录 ","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"eladmin-system/src/main/java/me/zhengjie/modules/maint/service/impl/AppServiceImpl.java","lineNumber":101,"sourceCode":"        }\n        verification(resources);\n        App app = appRepository.findById(resources.getId()).orElseGet(App::new);\n        ValidationUtil.isNull(app.getId(),\"App\",\"id\",resources.getId());\n        app.copy(resources);\n        appRepository.save(app);\n    }\n\n    private void verification(App resources){\n        String opt = \"/opt\";\n        String home = \"/home\";\n        if (!(resources.getUploadPath().startsWith(opt) || resources.getUploadPath().startsWith(home))) {\n            throw new BadRequestException(\"文件只能上传在opt目录或者home目录 \");\n        }\n        if (!(resources.getDeployPath().startsWith(opt) || resources.getDeployPath().startsWith(home))) {\n            throw new BadRequestException(\"文件只能部署在opt目录或者home目录 \");\n        }\n        if (!(resources.getBackupPath().startsWith(opt) || resources.getBackupPath().startsWith(home))) {\n            throw new BadRequestException(\"文件只能备份在opt目录或者home目录 \");\n        }\n    }\n\n    @Override\n    @Transactional(rollbackFor = Exception.class)\n    public void delete(Set<Long> ids) {\n        for (Long id : ids) {\n            appRepository.deleteById(id);\n        }\n    }\n\n    @Override\n    public void download(List<AppDto> queryAll, HttpServletResponse response) throws IOException {\n        List<Map<String, Object>> list = new ArrayList<>();\n        for (AppDto appDto : queryAll) {\n            Map<String,Object> map = new LinkedHashMap<>();\n            map.put(\"应用名称\", appDto.getName());\n            map.put(\"端口\", appDto.getPort());","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/elunez/eladmin/blob/55fbf705956949697dbd68bf9003776609d3d029/eladmin-system/src/main/java/me/zhengjie/modules/maint/service/impl/AppServiceImpl.java#L83-L119","documentation":"Third rule of AppServiceImpl.verification: App.backupPath (where pre-deploy backups are stored) must start with '/opt' or '/home', else BadRequestException('文件只能备份在opt目录或者home目录 '). Backups are written by the deployment job, so their location is whitelisted the same way.","triggerScenarios":"POST/PUT /api/app with backupPath like '/tmp/backup', '/data/backup', or 'backup/' (relative). All three paths — upload, deploy, backup — are validated in one pass, so the first failing one names the message.","commonSituations":"Ops conventions using /data or /var/backups; cloned App rows with edited backup paths; forgetting that backupPath is mandatory and validated even if you never intend to roll back.","solutions":["Point backupPath to /opt/... or /home/... (e.g. /opt/eladmin/backup) and resubmit.","Ensure the directory exists and is writable by the deploying service account on target machines.","Keep all three App paths consistently under one whitelisted root to avoid partial validation failures."],"exampleFix":"// before\napp.setBackupPath(\"/data/backup/myapp\"); // -> 400\n\n// after\napp.setBackupPath(\"/opt/eladmin/backup/myapp\");","handlingStrategy":"validation","validationCode":"String backup = form.getBackupPath();\nif (!(backup != null && (backup.startsWith(\"/opt\") || backup.startsWith(\"/home\")))) {\n    throw new IllegalArgumentException(\"backupPath must be under /opt or /home\");\n}\nappService.create(form);","typeGuard":"boolean isWhitelistedPath(String p) {\n    return p != null && (p.startsWith(\"/opt\") || p.startsWith(\"/home\"));\n}","tryCatchPattern":"try {\n    appService.create(app);\n} catch (BadRequestException e) {\n    if (e.getMessage().contains(\"只能备份在\")) { showPathRuleError(\"backupPath\"); return; }\n    throw e;\n}","preventionTips":["Plan backup space under the whitelisted root and provision it with ample capacity.","Keep upload/deploy/backup paths consistent under one root to pass validation in one shot.","Do not point backups at /tmp or /data — they will be rejected and are also unsafe locations."],"tags":["security","path-validation","eladmin","app-management","backup"],"backgroundTag":null,"analyzedSha":"55fbf705956949697dbd68bf9003776609d3d029","analyzedAt":"2026-08-14T11:56:12.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}