Files
server/dsh-local-403-fix/apply-403-fix.sh
T

125 lines
5.3 KiB
Bash
Executable File
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.
#!/usr/bin/env bash
# apply-403-fix.sh — 幂等应用/回滚 dsh 403 修复补丁(方案 2)
#
# 背景:官方 dsh 把特权 /api 方法(settings.*、credentials.* 等)钉死在
# loopback,反向代理访问时即使配置了 --trusted-host 也返回 403。本脚本把
# 一行补丁打到运行时实际加载的全局 @deepseek-ai/dsh-client-connection 上,
# 让特权方法也使用配置的 trustedHosts 列表校验(trustedHosts 仍是
# DNS-rebinding 围栏,不是认证,语义与上游一致)。
#
# 用法:
# apply-403-fix.sh # 应用补丁(幂等,已应用则跳过)
# apply-403-fix.sh --check # 仅检查补丁状态,不修改
# apply-403-fix.sh --revert # 从备份回滚补丁
# apply-403-fix.sh --help # 帮助
#
# 说明:
# - 通过 `readlink -f $(which dsh)` 定位全局 dsh 包根,不依赖硬编码路径
# - 应用前自动备份为 lib/index.js.orig
# - 补丁文件在工程内维护:patches/@deepseek-ai+dsh-client-connection@0.1.0-rc.7.patch
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PATCH_FILE="$SCRIPT_DIR/patches/@deepseek-ai+dsh-client-connection@0.1.0-rc.7.patch"
# 期望补丁后特权检查行包含 trustedHosts(而非空列表)
PATCHED_MARKER='PRIVILEGED_METHODS.has(method) && !isTrustedApiRequest(request, trustedHosts)'
ORIGINAL_MARKER='PRIVILEGED_METHODS.has(method) && !isTrustedApiRequest(request, []))'
die() { echo "[apply-403-fix] 错误: $*" >&2; exit 1; }
info() { echo "[apply-403-fix] $*"; }
# ── 定位运行时实际加载的全局 dsh 包 ──────────────────────────────────────
DSH_BIN="$(command -v dsh || true)"
if [ -z "$DSH_BIN" ]; then
die "未找到 dsh 命令。请确认已全局安装 @deepseek-ai/dsh。"
fi
DSH_REAL="$(readlink -f "$DSH_BIN")" # .../lib/node_modules/@deepseek-ai/dsh/lib/bin.js
DSH_ROOT="$(dirname "$(dirname "$DSH_REAL")")" # .../lib/node_modules/@deepseek-ai/dsh
CC_DIR="$DSH_ROOT/node_modules/@deepseek-ai/dsh-client-connection"
TARGET="$CC_DIR/lib/index.js"
if [ ! -f "$TARGET" ]; then
die "未找到 $TARGET(dsh 依赖布局与预期不符,请检查 dsh 安装)。"
fi
if [ ! -f "$PATCH_FILE" ]; then
die "未找到补丁文件 $PATCH_FILE"
fi
# ── 检查状态 ──────────────────────────────────────────────────────────────
check_state() {
if grep -qF "$PATCHED_MARKER" "$TARGET"; then
echo "patched"
elif grep -qF "$ORIGINAL_MARKER" "$TARGET"; then
echo "unpatched"
else
echo "unknown"
fi
}
# ── --check ───────────────────────────────────────────────────────────────
if [ "${1:-}" = "--check" ]; then
case "$(check_state)" in
patched) info "已打补丁: $TARGET"; exit 0 ;;
unpatched) info "未打补丁: $TARGET"; exit 0 ;;
*) die "无法识别 $TARGET 的状态(可能 dsh 版本已升级,补丁已过期)。" ;;
esac
fi
# ── --revert ──────────────────────────────────────────────────────────────
if [ "${1:-}" = "--revert" ]; then
if [ "$(check_state)" = "unpatched" ]; then
info "当前未打补丁,无需回滚。"
exit 0
fi
if [ ! -f "$TARGET.orig" ]; then
die "没有备份文件 $TARGET.orig,无法回滚(可能不是本脚本打的补丁)。"
fi
cp "$TARGET.orig" "$TARGET"
info "已从备份回滚: $TARGET"
exit 0
fi
# ── --help / 未知参数 ────────────────────────────────────────────────────
if [ "${1:-}" = "--help" ] || [ "${1:-}" = "-h" ]; then
grep '^#' "$0" | sed 's/^# \{0,1\}//'
exit 0
fi
if [ $# -gt 0 ]; then
die "未知参数: $*(用 --help 查看用法)"
fi
# ── 应用补丁(幂等) ─────────────────────────────────────────────────────
case "$(check_state)" in
patched)
info "已打补丁,跳过: $TARGET"
exit 0
;;
unknown)
die "无法识别 $TARGET 的状态,拒绝修改。请人工检查(可能 dsh 已升级)。"
;;
esac
# 备份原始文件(仅在应用时创建,--revert 依赖它)
if [ ! -f "$TARGET.orig" ]; then
cp "$TARGET" "$TARGET.orig"
info "已备份原始文件: $TARGET.orig"
else
info "备份已存在,保留: $TARGET.orig"
fi
# 在包根目录执行 patch -p1(补丁内路径为 lib/index.js
if ! (cd "$CC_DIR" && patch -p1 --forward < "$PATCH_FILE"); then
info "patch 应用失败,正在回滚本次修改…"
if [ -f "$TARGET.orig" ]; then cp "$TARGET.orig" "$TARGET"; fi
die "补丁应用失败,已恢复原状。请人工检查。"
fi
# 应用后校验
if [ "$(check_state)" = "patched" ]; then
info "补丁已应用: $TARGET"
else
die "补丁应用后校验失败,请人工检查 $TARGET"
fi
info "提示: 需要重启 dshmy_deepseek.sh stop && my_deepseek.sh)后生效。"