{"record":{"id":"256f2837c2bcdfd0","repo":"elunez/eladmin","slug":"opt-home-256f28","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":98,"sourceCode":"        String appName = resources.getName();\n        if (appName.contains(\";\") || appName.contains(\"|\") || appName.contains(\"&\")) {\n            throw new IllegalArgumentException(\"非法的应用名称，请勿包含[; | &]等特殊字符\");\n        }\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) {","sourceCodeStart":80,"sourceCodeEnd":116,"githubUrl":"https://github.com/elunez/eladmin/blob/55fbf705956949697dbd68bf9003776609d3d029/eladmin-system/src/main/java/me/zhengjie/modules/maint/service/impl/AppServiceImpl.java#L80-L116","documentation":"Second rule of AppServiceImpl.verification: App.deployPath (where the app will be deployed/run on target servers) must start with '/opt' or '/home', else BadRequestException('文件只能部署在opt目录或者home目录 '). Same whitelist rationale as the upload path check.","triggerScenarios":"POST/PUT /api/app with deployPath such as '/usr/local/app', '/srv/deploy', '/root/app', or missing the leading slash ('home/app').","commonSituations":"Teams accustomed to /usr/local or /srv install roots; copying paths from older deployment docs; forgetting the leading '/'; multi-tenant servers where /opt is reserved.","solutions":["Set deployPath under /opt or /home (e.g. /opt/apps/my-service) and resubmit.","Prepare the directory with correct ownership on every deploy target server.","If a different root is organizationally required, modify verification() deliberately and re-audit the shell commands that consume deployPath in DeployServiceImpl."],"exampleFix":"// before\napp.setDeployPath(\"/usr/local/myapp\"); // -> 400\n\n// after\napp.setDeployPath(\"/opt/apps/myapp\");","handlingStrategy":"validation","validationCode":"String deploy = form.getDeployPath();\nif (!(deploy != null && (deploy.startsWith(\"/opt\") || deploy.startsWith(\"/home\")))) {\n    throw new IllegalArgumentException(\"deployPath must be under /opt or /home\");\n}\nappService.create(form); // or update","typeGuard":"boolean isWhitelistedPath(String p) {\n    return p != null && (p.startsWith(\"/opt\") || p.startsWith(\"/home\"));\n}","tryCatchPattern":"try {\n    appService.update(app);\n} catch (BadRequestException e) {\n    if (e.getMessage().contains(\"只能部署在\")) { showPathRuleError(\"deployPath\"); return; }\n    throw e;\n}","preventionTips":["Document the /opt//home rule in deployment runbooks so ops doesn't reach for /usr/local or /srv.","Validate all three App paths at once in the form so users see every violation, not just the first.","Remember startsWith semantics: '/optX' passes, 'opt/x' fails — always include the leading slash."],"tags":["security","path-validation","eladmin","app-management","deployment"],"backgroundTag":null,"analyzedSha":"55fbf705956949697dbd68bf9003776609d3d029","analyzedAt":"2026-08-14T11:56:12.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}