3x-ui/docs/install-issue-assessment.md
root f5862abc2e feat: add CodeMirror YAML editor for Clash template and fix settings save button bug
- Replace plain textarea with CodeMirror editor (YAML syntax highlighting, line numbers, auto-indent) for Clash subscription template
- Fix confAlerts crash when subClashURI/subURI/subJsonURI is null/undefined (prevented save button from enabling)
- Add yaml.js CodeMirror mode asset
- Include docs and .gitignore cleanup
2026-04-24 16:15:22 +08:00

113 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# install.sh 问题评估与分级
## 说明
本文对 `install.sh` 及其直接依赖的服务文件进行静态审查,输出逻辑问题、潜在 Bug 与风险分级。
- 审查对象:`install.sh`、`x-ui.service.arch`、`x-ui.service.debian`、`x-ui.service.rhel`、`x-ui.rc`
- 审查方式:静态阅读 + `bash -n install.sh`
- 结论范围:以脚本逻辑为主,不包含联网下载内容真实性、外部服务可用性与目标主机现场状态
## 分级标准
| 级别 | 含义 |
|---|---|
| P0 / 致命 | 高概率导致安装结果错误、服务不可用、错误安全结论、数据错写或危险操作 |
| P1 / 高 | 在常见环境下容易触发,导致功能失败、行为不一致或明显错误 |
| P2 / 中 | 不是每次触发,但会造成兼容性、稳健性、可维护性或边界行为问题 |
| P3 / 低 | 风险较低,主要是误导、死代码、风格不一致或隐含维护成本 |
## 总览
| 级别 | 数量 | 重点 |
|---|---:|---|
| P0 / 致命 | 8 | 端口检测失效、SSL 失败仍报成功、MariaDB 新装数据错位、目录漂移 |
| P1 / 高 | 12 | 自定义目录不完整支持、安装失败继续、弱口令、服务路径错误 |
| P2 / 中 | 18 | 输入校验不足、平台兼容性差、下载/解析脆弱、引号缺失 |
| P3 / 低 | 8 | 死代码、注释偏差、提示文案不准确、维护性问题 |
---
## P0 / 致命
| ID | 位置 | 问题 | 影响 | 建议 |
|---|---|---|---|---|
| P0-01 | `install.sh:63`, `install.sh:67` | `is_port_in_use()``awk ... END {exit 1}` 覆盖前面的成功退出码,导致 `ss`/`netstat` 分支几乎总返回“端口未占用” | 端口冲突检测失效ACME 独立监听端口选择可能错误,安装过程可能与现有服务抢端口 | 改为纯 `grep`/`ss` 判断,或让 `awk` 使用状态变量并在 `END` 中按变量返回 |
| P0-02 | `install.sh:955`, `install.sh:966`, `install.sh:969` | SSL 配置函数失败后,安装摘要仍打印 HTTPS 地址和“SSL 已启用并配置” | 用户被误导为已启用 HTTPS可能错误暴露面板 | 必须检查 `prompt_and_setup_ssl` 返回值,失败时终止安装摘要或明确显示失败 |
| P0-03 | `install.sh:587`, `install.sh:589` | `ssl_cert_issue()` 返回值未检查,且用 `acme.sh --list | tail -1` 取最后一张证书推断本次签发结果 | 可能把旧证书/无关证书当成本次成功结果写入面板域名 | 直接在 `ssl_cert_issue()` 内返回明确域名或通过全局/输出参数传递,不要读列表尾行 |
| P0-04 | `install.sh:859`, `install.sh:863`, `install.sh:867`, `install.sh:869` | 提示称“留空或 rd 自动生成”,实际用户名/密码默认是 `admin/admin` | 新装面板默认弱口令,安全风险极高 | 留空时应真正随机生成密码,用户名至少提示确认 |
| P0-05 | `install.sh:890`, `install.sh:914-920`, `install.sh:991` | 新装流程先按默认 SQLite 写面板配置,再切换 `dbType=mariadb`,但没有执行 SQLite→MariaDB 数据迁移 | 最终服务连接 MariaDB 后,用户刚设置的账号/端口等可能不在实际使用库里 | 先确定数据库类型,再初始化与写配置;若从 SQLite 切 MariaDB必须执行显式迁移 |
| P0-06 | `install.sh:157`, `install.sh:1106`, `install.sh:1135-1176` | `install_acme()` 进入家目录后不恢复;`config_after_install()` 里一旦调用它,会污染后续当前工作目录 | 可能找不到解压包中的 service 文件,导致安装行为与预期不一致 | 在函数内保存并恢复目录,或完全使用绝对路径 |
| P0-07 | `x-ui.service.arch:10-11`, `install.sh:11`, `install.sh:1135-1159` | Arch 服务文件硬编码 `/usr/lib/x-ui`,而脚本默认安装到 `/usr/local/x-ui` | Arch 系统默认安装后服务大概率无法启动 | 安装时模板渲染 `ExecStart`/`WorkingDirectory`,不要硬编码 |
| P0-08 | `install.sh:1078-1084` | 依赖环境变量的目录删除缺乏安全防护,直接 `rm -rf ${xui_folder}/` | 若 `XUI_MAIN_FOLDER` 被误设,可能删除错误目录 | 删除前校验路径非空、非根目录、符合预期前缀,并统一加引号 |
## P1 / 高
| ID | 位置 | 问题 | 影响 | 建议 |
|---|---|---|---|---|
| P1-01 | `install.sh:1041` | 用 `${xui_folder%/x-ui}` 推导工作目录,隐式假设安装目录以 `/x-ui` 结尾 | 自定义目录时下载/解压位置可能错误 | 单独定义工作目录,不要从安装目录字符串裁剪推导 |
| P1-02 | `install.sh:1088-1091` | 解压与 `cd x-ui` 不检查失败 | 后续 chmod、复制 service、配置命令可能在错误目录执行 | 每一步关键文件操作都要显式检查退出码 |
| P1-03 | `install.sh:1106` | `config_after_install` 失败后仍继续安装 service 并启动 | 配置失败被掩盖,最终系统状态不可预测 | 将其纳入主流程错误链,失败立即退出 |
| P1-04 | `install.sh:76-104`, `install.sh:1232-1234` | `install_base()` 失败后脚本仍继续执行 | 缺依赖状态下继续安装,错误位置后移且更难排查 | 主流程必须检查基础依赖安装结果 |
| P1-05 | `install.sh:1071`, `install.sh:1176-1188` | 指定版本安装时,管理脚本和 fallback service 仍从 `main` 分支下载 | 脚本/服务文件与二进制版本不匹配,可能出现不兼容 | 统一按所选 tag 下载同版本配套文件 |
| P1-06 | `install.sh:1048`, `install.sh:1065`, `install.sh:1071`, `install.sh:1180-1186` | 多处强制 `curl -4` | IPv6-only 主机无法安装或更新 | 优先正常双栈请求,失败后再回退到 `-4` |
| P1-07 | `install.sh:981-988` | 已有安装路径中,只要 `cert` 字段非空就认定已配置 SSL并打印 HTTPS 地址 | 可能在没有有效证书/私钥时误导用户 | 同时校验 `cert`、`key`、文件存在性和可读性 |
| P1-08 | `install.sh:429-435` | 证书重复判断只比较 `acme.sh --list` 最后一条记录 | 已有当前域名证书时仍可能重复签发 | 遍历列表精确匹配域名,或直接查询目标证书目录 |
| P1-09 | `install.sh:384-386` | IP 证书流程中,`x-ui cert` 失败只告警不失败,仍打印“安装并配置成功” | 证书文件虽存在,但面板未实际启用证书 | 写入路径失败应判定整体失败 |
| P1-10 | `install.sh:611-618`, `install.sh:623-625` | IP 证书模式停止面板后,失败路径没有统一保证服务恢复 | 安装中断后面板可能保持停止状态 | 使用 `trap` 或统一清理/恢复逻辑 |
| P1-11 | `install.sh:1137-1203`, `x-ui.service.debian:10-11`, `x-ui.service.rhel:10-11`, `x-ui.rc:3`, `x-ui.rc:12` | 虽允许 `XUI_MAIN_FOLDER` 覆盖,但 service/init 模板大量硬编码 `/usr/local/x-ui` | 自定义安装目录时服务启动失败 | 安装时根据变量生成 service/init 文件 |
| P1-12 | `install.sh:994-1015` | 用 `tr`/`grep`/`sed` 解析 GitHub Releases JSON逻辑脆弱 | API 结构变化、字段顺序变化、错误响应时容易取错版本 | 使用 `jq`,或服务端返回最小化 API 请求并严格校验 JSON |
## P2 / 中
| ID | 位置 | 问题 | 影响 | 建议 |
|---|---|---|---|---|
| P2-01 | `install.sh:158` | `curl ... | sh` 缺少 `pipefail`,下载失败时可能误报 acme 安装成功 | 安装状态不可信 | 启用 `set -o pipefail`,或先下载再执行 |
| P2-02 | `install.sh:177`, `install.sh:249`, `install.sh:396`, `install.sh:732` | 用 `command -v ~/.acme.sh/acme.sh` 检查路径式命令,可用但不规范 | 兼容性与可读性较差 | 直接使用 `[ -x ~/.acme.sh/acme.sh ]` |
| P2-03 | `install.sh:193`, `install.sh:321`, `install.sh:463`, `install.sh:743` | 设置默认 CA 的命令结果多数未检查 | 前置动作失败时后续错误定位困难 | 对关键前置步骤逐一校验 |
| P2-04 | `install.sh:263-266`, `install.sh:46-50` | IPv4/IPv6 校验过于宽松IPv4 不校验每段范围IPv6 只看是否含 `:` | 非法地址可能进入证书流程 | 使用更严格的地址校验函数 |
| P2-05 | `install.sh:451-455` | 手动 SSL 端口输入为空时不会自然采用默认值,而是走“无效输入”分支 | 交互体验与提示不一致 | 先 `WebPort="${WebPort:-80}"` 再校验 |
| P2-06 | `install.sh:452` | 手动 SSL 端口校验未先判断是否纯数字 | 异常输入由 shell 算术比较隐式处理,不稳健 | 先正则校验再做范围比较 |
| P2-07 | `install.sh:881-888` | 面板端口输入没有有效校验,也不检测占用 | 可能写入非法端口或冲突端口 | 校验数字范围并复用端口占用检测 |
| P2-08 | `install.sh:886` | 随机端口不检查是否已被占用 | 启动服务时可能冲突 | 生成后循环检测空闲端口 |
| P2-09 | `install.sh:890-897` | 只验证端口写入成功,不验证用户名、密码、`webBasePath` 是否确实写入 | 配置失败可能部分隐藏 | 对关键配置项逐项校验 |
| P2-10 | `install.sh:899-920` | MariaDB 连接信息读取与写入缺少完整校验,账号/密码/库名可以为空 | 后续数据库初始化失败 | 按数据库类型做必填校验与连通性预检查 |
| P2-11 | `install.sh:929-945` | `worker` 仅校验 `dbType=mariadb`,不校验 MariaDB 是否可连接 | worker 模式可能被写入无效配置 | 在设置前做配置与连接校验 |
| P2-12 | `install.sh:846-855`, `install.sh:955` | 获取公网 IP 失败后不提示手动输入IP 证书默认路径会直接失败 | 默认交互路径容易走向失败 | IP 获取失败时要求用户手输或默认跳过 IP 证书 |
| P2-13 | `install.sh:707-729` | Cloudflare 域名未用 `is_domain` 校验API Key 非静默输入 | 容易误输,且敏感信息暴露在终端 | 域名校验 + `read -rsp` 输入密钥 |
| P2-14 | `install.sh:751-752` | Cloudflare 凭证 `export` 后未清理 | 后续子进程可见环境变量 | 完成后 `unset CF_Key CF_Email` |
| P2-15 | `install.sh:491` | 允许用户输入任意 `reloadcmd`,续期时将以高权限执行 | 误操作或恶意输入可能造成风险 | 至少明确安全提示,并对常见场景提供模板而非完全任意命令 |
| P2-16 | `install.sh:91-92` | Arch 依赖安装执行两次 `pacman -Syu`,且第一次无 `--noconfirm` | 交互阻塞、重复更新、耗时增加 | 合并为一次清晰的安装命令 |
| P2-17 | `install.sh:1088-1100` | ARM 分支先重命名 `bin/xray-linux-$(arch)`,后面又对原文件名 `chmod` | 可能输出错误信息并污染日志 | 重命名后按最终文件名处理权限 |
| P2-18 | `install.sh:1048`, `install.sh:1065`, `install.sh:1071` 等多处 | 路径、URL、命令参数大量未加引号 | 自定义目录、异常字符或空格路径下行为不稳定 | 统一为变量引用加引号 |
## P3 / 低
| ID | 位置 | 问题 | 影响 | 建议 |
|---|---|---|---|---|
| P3-01 | `install.sh:9` | `cur_dir` 未使用 | 增加噪音 | 删除死代码 |
| P3-02 | `install.sh:168-236` | `setup_ssl_certificate()` 未被调用,且参数 `server_ip`、`existing_port`、`existing_webBasePath` 未使用 | 阅读成本高,容易误导维护者 | 删除死函数或接入统一 SSL 流程 |
| P3-03 | `install.sh:52-54` | `is_ip()` 未被调用 | 维护噪音 | 删除或实际复用 |
| P3-04 | `install.sh:39` | 不支持架构时删除当前 `install.sh` | 行为危险且无必要 | 直接报错退出,不做文件删除 |
| P3-05 | `install.sh:578` | 注释“非 1、3、4 默认为 2”表述不完整 | 容易造成维护误解 | 注释改为“除 1/3/4 外均视为 2” |
| P3-06 | `install.sh:827-832` | “全新安装”的判定只看两个文件是否存在,注释与语义不够精确 | 可读性一般 | 把注释改成“基于配置/数据库文件存在性判断” |
| P3-07 | `install.sh:1044` | `[ $# == 0 ]` 属于 bash 风格写法,可运行但不如 `-eq` 清晰 | 风格一致性较差 | 改用 `[ $# -eq 0 ]` |
| P3-08 | `install.sh:1234` | `install_x-ui $1` 未加引号 | 边界输入下会参数拆分 | 改为 `install_x-ui "$1"` |
## 优先修复建议
建议按以下顺序修复:
1. 先修 `P0-01`、`P0-02`、`P0-05`、`P0-06`、`P0-07`
2. 再修 `P1-01`、`P1-03`、`P1-05`、`P1-11`
3. 然后补齐输入校验、错误处理和引号问题
4. 最后清理死代码、注释和交互文案
## 建议的验收点
- 端口占用检测在 `ss`、`netstat`、`lsof` 三种环境下都能正确判定
- SSL 任一路径失败时,最终摘要不再显示成功状态
- SQLite / MariaDB 两种新装流程都能得到一致且可登录的实际配置
- 自定义 `XUI_MAIN_FOLDER` 后,服务文件仍能正确启动
- Arch、Debian/RHEL、Alpine 三类服务安装路径与执行路径一致
- 在公网 IP 获取失败、80 端口不可用、证书签发失败等场景下,脚本能给出准确结果而非伪成功