Bug Report: Windows 平台 exec provider 安全审计误判导致 lark-cli 绑定失败
问题描述
在 Windows 平台上,lark-channel-bridge run 启动时 lark-cli 配置绑定(config bind --source lark-channel)始终失败,报错:
exec provider security audit failed: exec provider command: path "C:\\Users\\<user>\\.lark-channel\\secrets-getter.cmd" is world-writable (mode 0666)
Bridge 消息收发不受影响(✓ 已连接),但 agent 无法通过 lark-cli 执行飞书 API 操作(创建文档、搜索联系人等)。
环境信息
- 操作系统: Windows 11 Home 10.0.26200
- Node.js: v24.14.0
- lark-channel-bridge: 0.3.0
- lark-cli: 1.0.53
- 安装方式: npm 全局安装(
npm i -g lark-channel-bridge)
复现步骤
- 在 Windows 上全局安装
lark-channel-bridge
- 完成初始化(创建 profile、绑定应用)
- 执行
lark-channel-bridge run
- 观察输出中 "lark-cli configuration failed" 及完整错误信息
根因分析
核心矛盾
lark-cli.exe(Go 编译)对 exec provider 的 command 路径(secrets-getter.cmd)做安全审计时,使用文件的 Unix mode 判断是否 "world-writable"。但在 Windows/NTFS 上:
- Go 的
os.Stat() 和 Node.js 的 fs.stat() 对非只读文件一律返回 mode 0666
- 对只读文件返回
mode 0444
- Windows 不使用 Unix 权限模型,这两个值是通过"只读属性标志"推断的,与 NTFS ACL 无关
死循环
| 文件状态 |
Node.js/Go 报告的 mode |
bridge 的 rename 操作 |
lark-cli 安全审计 |
| 非只读 |
0666 |
✅ 成功 |
❌ 拒绝("world-writable") |
| 只读 |
0444 |
❌ 失败(EPERM) |
✅ 通过 |
- 如果文件非只读:lark-cli 报 "world-writable" 拒绝执行
- 如果文件只读:bridge 的
writeFileAtomic() 在 rename 时报 EPERM 无法更新文件
验证
# Git Bash 中显示 644(因为 icacls /reset 修改了 ACL)
$ stat -c '%a' ~/.lark-channel/secrets-getter.cmd
644
# 但 Node.js 仍然返回 0666(只看只读标志,不看 ACL)
$ node -e "const s=require('fs').statSync(process.env.USERPROFILE+'/.lark-channel/secrets-getter.cmd'); console.log('0'+((s.mode)&0o777).toString(8))"
0666
相关代码位置
-
bridge 写入 secrets-getter.cmd:
src/platform/atomic-write.ts → writeFileAtomic(wrapperPath, content, { mode: 0o600 })
- Windows 上
chmod(tmp, 0o600) 实际效果等同于不设只读 → mode 显示为 0666
-
lark-cli 安全审计:
- lark-cli.exe 内部的
AssertSecurePath 逻辑
- 使用 Go 的
os.Stat().Mode() 检查路径是否 world-writable
-
bridge 绑定流程:
bindLarkCliWithCompatibility() → 执行 lark-cli config bind --source lark-channel
- lark-cli 读取
lark-cli-source/config.json 中的 exec provider 配置
- 调用
secrets-getter.cmd 前触发安全审计 → 失败
建议修复方案
方案 A:lark-cli 侧(推荐)
在 AssertSecurePath 中对 Windows 平台跳过 Unix mode 检查,改为检查 NTFS ACL(如使用 Go 的 windows.GetSecurityInfo API),或直接在 runtime.GOOS == "windows" 时跳过 mode 校验。
方案 B:bridge 侧
在 Windows 上写入 secrets-getter.cmd 后设置只读属性,并在需要更新时先移除只读再 rename:
// 写入后设为只读(通过 Windows API 或 attrib +R)
if (process.platform === 'win32') {
await exec(`attrib +R "${wrapperPath}"`);
}
// 更新前移除只读
if (process.platform === 'win32') {
await exec(`attrib -R "${targetPath}"`);
}
await rename(tmp, targetPath);
if (process.platform === 'win32') {
await exec(`attrib +R "${targetPath}"`);
}
方案 C:环境变量
lark-cli 提供环境变量(如 LARK_CLI_SKIP_EXEC_AUDIT=1)允许用户在受信任环境中跳过安全审计。
当前 Workaround
- 使用
--skip-check-lark-cli 启动 bridge(跳过自动绑定)
- 手动将
lark-cli-source/config.json 中的 exec provider 替换为明文 secret
- 手动执行
lark-cli config bind --source lark-channel --identity user-default
- 手动完成用户授权(
lark-cli auth login --recommend)
Issue 模板(可直接提交到 GitHub)
### Bug: Windows 上 exec provider 安全审计误判 "world-writable (mode 0666)" 导致 lark-cli 绑定失败
**环境**
- OS: Windows 11 (10.0.26200)
- Node.js: v24.14.0
- lark-channel-bridge: 0.3.0
- lark-cli: 1.0.53
**现象**
`lark-channel-bridge run` 启动后 lark-cli configuration 始终失败:
\```
failed to resolve appSecret for cli_xxx: exec provider security audit failed:
exec provider command: path "C:\\Users\\<user>\\.lark-channel\\secrets-getter.cmd"
is world-writable (mode 0666)
\```
**根因**
Go/Node.js 的 `stat()` 在 Windows 上对非只读文件返回 `mode 0666`(通过只读属性标志推断,与实际 NTFS ACL 无关)。lark-cli 的 `AssertSecurePath` 将此判定为 "world-writable" 并拒绝执行 exec provider。
而文件若设为只读,bridge 的 atomic-write rename 又会因 EPERM 失败,形成死循环。
**复现**
1. Windows 上 `npm i -g lark-channel-bridge`
2. 完成 profile 初始化
3. `lark-channel-bridge run` → 观察 lark-cli configuration failed
**期望**
Windows 上安全审计不应使用 Unix file mode 来判断文件是否 world-writable,或提供平台特定的替代检查方式。
**Workaround**
使用 `--skip-check-lark-cli` 启动 + 手动将 source config 改为明文 secret + 手动执行 `lark-cli config bind`。
Bug Report: Windows 平台 exec provider 安全审计误判导致 lark-cli 绑定失败
问题描述
在 Windows 平台上,
lark-channel-bridge run启动时 lark-cli 配置绑定(config bind --source lark-channel)始终失败,报错:Bridge 消息收发不受影响(
✓ 已连接),但 agent 无法通过 lark-cli 执行飞书 API 操作(创建文档、搜索联系人等)。环境信息
npm i -g lark-channel-bridge)复现步骤
lark-channel-bridgelark-channel-bridge run根因分析
核心矛盾
lark-cli.exe(Go 编译)对 exec provider 的 command 路径(
secrets-getter.cmd)做安全审计时,使用文件的 Unix mode 判断是否 "world-writable"。但在 Windows/NTFS 上:os.Stat()和 Node.js 的fs.stat()对非只读文件一律返回mode 0666mode 0444死循环
06660444writeFileAtomic()在 rename 时报 EPERM 无法更新文件验证
相关代码位置
bridge 写入
secrets-getter.cmd:src/platform/atomic-write.ts→writeFileAtomic(wrapperPath, content, { mode: 0o600 })chmod(tmp, 0o600)实际效果等同于不设只读 → mode 显示为0666lark-cli 安全审计:
AssertSecurePath逻辑os.Stat().Mode()检查路径是否 world-writablebridge 绑定流程:
bindLarkCliWithCompatibility()→ 执行lark-cli config bind --source lark-channellark-cli-source/config.json中的 exec provider 配置secrets-getter.cmd前触发安全审计 → 失败建议修复方案
方案 A:lark-cli 侧(推荐)
在
AssertSecurePath中对 Windows 平台跳过 Unix mode 检查,改为检查 NTFS ACL(如使用 Go 的windows.GetSecurityInfoAPI),或直接在runtime.GOOS == "windows"时跳过 mode 校验。方案 B:bridge 侧
在 Windows 上写入
secrets-getter.cmd后设置只读属性,并在需要更新时先移除只读再 rename:方案 C:环境变量
lark-cli 提供环境变量(如
LARK_CLI_SKIP_EXEC_AUDIT=1)允许用户在受信任环境中跳过安全审计。当前 Workaround
--skip-check-lark-cli启动 bridge(跳过自动绑定)lark-cli-source/config.json中的 exec provider 替换为明文 secretlark-cli config bind --source lark-channel --identity user-defaultlark-cli auth login --recommend)Issue 模板(可直接提交到 GitHub)