Skip to content

[Bug] Smart Bypass 无法请求定位权限,导致无法识别当前 Wi‑Fi SSID #346

Description

@sy264115809

问题描述

ClashMac 的 Smart Bypass(在指定 Wi-Fi 下禁用代理)无法正常工作。

启用 Smart Bypass 后,ClashMac 没有弹出 macOS 定位权限请求,也没有出现在:

系统设置 → 隐私与安全性 → 定位服务

由于应用无法取得当前 Wi-Fi 的 SSID,即使手动将 SSID 添加到 trustedSSIDs,ClashMac 仍无法判断当前连接的 Wi-Fi 是否命中绕过列表,系统代理和 TUN 会继续保持开启。

环境信息

  • macOS:26.5.2
  • macOS Build:25F84
  • 架构:Apple Silicon / arm64
  • ClashMac:27.1.2
  • ClashMac Build:216
  • Bundle ID:app.clashmac
  • TUN:开启
  • 系统代理:开启
  • DNS Enhanced Mode:Fake IP

复现步骤

  1. 打开 ClashMac。
  2. 进入 Smart Bypass / 指定 Wi-Fi 禁用代理设置。
  3. 开启 Smart Bypass。
  4. 尝试添加当前连接的 Wi-Fi。
  5. 观察是否出现 macOS 定位权限请求。
  6. 检查“系统设置 → 隐私与安全性 → 定位服务”。
  7. 切换或重新连接已配置的 Wi-Fi。
  8. 检查 ClashMac 是否自动暂停系统代理和 TUN。

预期行为

  1. 第一次启用 Smart Bypass 或读取当前 Wi-Fi SSID 时,ClashMac 应调用 Core Location 授权 API。
  2. macOS 应弹出定位权限请求。
  3. ClashMac 应出现在系统定位服务应用列表中。
  4. 用户允许后,ClashMac 应能读取当前 SSID。
  5. 当前 SSID 命中 trustedSSIDs 时,应自动暂停系统代理和 TUN。
  6. 离开该 Wi-Fi 后,应自动恢复原有代理状态。

实际行为

  1. 启用 Smart Bypass 后没有出现定位权限弹窗。
  2. ClashMac 没有出现在 macOS 定位服务列表中。
  3. 系统没有为 app.clashmac 创建 Location TCC 授权记录。
  4. 当前 Wi-Fi 已连接且默认路由走 en0,但 SSID 对普通进程仍显示为 <redacted>
  5. 手动添加 SSID 后,trustedSSIDs 能保存,但无法触发 Smart Bypass。
  6. 系统代理和 TUN 仍保持开启。

ClashMac 当前偏好状态

执行:

defaults read app.clashmac enableSmartBypass
defaults read app.clashmac trustedSSIDs
defaults read app.clashmac tunEnabled

输出:

1
<SSID>
1

说明:

  • Smart Bypass 已开启。
  • SSID <SSID> 已保存。
  • TUN 仍然开启。
  • 当前连接的 Wi-Fi 已经是该 SSID,但绕过没有触发。

当前网络状态

默认路由确认使用 Wi-Fi 接口:

route -n get default

关键输出:

interface: en0

接口状态:

ifconfig en0

关键输出:

status: active

系统报告 Wi-Fi 已连接,但 SSID 被隐藏:

system_profiler SPAirPortDataType -json

关键输出:

{
  "spairport_status_information": "spairport_status_connected",
  "spairport_current_network_information": {
    "_name": "<redacted>"
  }
}

另一个查询同样无法取得 SSID:

networksetup -getairportnetwork en0

输出:

You are not associated with an AirPort network.

实际上设备已经连接 Wi-Fi,默认路由也使用 en0。该输出可能与新版 macOS 的 SSID 隐私限制有关。

TCC 权限检查

执行:

sqlite3 \
  "$HOME/Library/Application Support/com.apple.TCC/TCC.db" \
  "SELECT service,client,auth_value,auth_reason
   FROM access
   WHERE client='app.clashmac';"

输出中只有:

kTCCServiceAppleEvents|app.clashmac|2|3

没有发现:

kTCCServiceLocation

尝试执行:

tccutil reset Location app.clashmac

然后重新启动 ClashMac、重新启用 Smart Bypass,仍然没有出现定位授权弹窗。

tccutil reset 只能清理已有授权,不能代替应用主动请求授权。

Info.plist 检查

应用已经声明定位权限用途:

plutil -p /Applications/ClashMac.app/Contents/Info.plist |
  grep -i Location

输出:

NSLocationWhenInUseUsageDescription =
"ClashMac requires location permissions to detect the current Wi-Fi name (SSID) for the Smart Bypass feature."

因此定位权限说明字符串已经存在,但运行过程中没有观察到定位权限请求。

二进制依赖检查

执行:

otool -L /Applications/ClashMac.app/Contents/MacOS/ClashMac |
  grep -Ei 'CoreLocation|CoreWLAN|SystemConfiguration'

输出中存在:

CoreWLAN.framework
SystemConfiguration.framework

但没有发现:

CoreLocation.framework

进一步检查可执行文件中的相关符号和字符串:

strings /Applications/ClashMac.app/Contents/MacOS/ClashMac |
  grep -Ei \
  'requestWhenInUseAuthorization|requestAlwaysAuthorization|CLLocationManager|SSID changed|SmartBypass'

输出只发现:

[NetworkMonitor] SSID changed from %{public}s to %{public}s
[SmartBypass] State toggled: %{bool,public}d (SSID: %{public}s)

没有发现:

CLLocationManager
requestWhenInUseAuthorization
requestAlwaysAuthorization

此项只能作为辅助证据,不排除代码使用其他动态调用方式。但结合以下现象,怀疑当前版本只通过 CoreWLAN 读取 SSID,却没有先通过 Core Location 请求定位授权:

  • 没有定位权限弹窗。
  • 没有 TCC Location 记录。
  • SSID 始终为 <redacted>
  • Smart Bypass 无法命中已保存的 SSID。

签名信息

执行:

codesign -dv --verbose=4 /Applications/ClashMac.app

关键输出:

Identifier=app.clashmac
TeamIdentifier=not set

应用本身可以通过签名完整性检查:

codesign --verify --deep --strict /Applications/ClashMac.app

但是当前构建没有 TeamIdentifier。请确认这是否会影响 macOS 26 上 TCC 定位授权记录的创建或持久化。

可能根因

可能存在以下一种或多种情况:

  1. 启用 Smart Bypass 时没有创建并保留 CLLocationManager
  2. 没有调用 requestWhenInUseAuthorization()
  3. 只声明了 NSLocationWhenInUseUsageDescription,但没有实际请求权限。
  4. 在定位权限状态为 .notDetermined 时直接调用 CoreWLAN 读取 SSID,得到 nil<redacted>
  5. 当前发布构建没有 TeamIdentifier,可能影响新版 macOS 的 TCC 行为。
  6. SSID 读取失败后仍允许写入 trustedSSIDs,但运行时无法匹配当前网络。

建议修复方向

建议在 Smart Bypass 启用或首次读取 SSID 时请求定位权限,例如:

import CoreLocation

final class LocationPermissionManager: NSObject, CLLocationManagerDelegate {
    private let manager = CLLocationManager()

    override init() {
        super.init()
        manager.delegate = self
    }

    func requestPermissionIfNeeded() {
        switch manager.authorizationStatus {
        case .notDetermined:
            manager.requestWhenInUseAuthorization()

        case .authorized, .authorizedAlways:
            // 定位授权完成后再读取当前 SSID。
            break

        case .denied, .restricted:
            // 向用户展示前往系统设置的提示。
            break

        @unknown default:
            break
        }
    }
}

同时建议:

  1. 在定位授权成功后再读取 CoreWLAN SSID。
  2. SSID 为空时显示明确错误,不要静默失败。
  3. 定位权限被拒绝时提供“打开系统设置”按钮。
  4. 在界面中显示当前检测到的 SSID。
  5. 在界面中显示 Smart Bypass 当前是否已触发。
  6. 增加日志,输出定位授权状态、SSID 读取结果和匹配结果。
  7. 检查发布构建的 Developer ID 签名和 TeamIdentifier

其他说明

代理节点、TUN 和基础网络连接本身正常。问题集中在当前 SSID 获取及 Smart Bypass 匹配流程。

手动写入 SSID:

defaults write app.clashmac trustedSSIDs "<SSID>"

只能让目标 SSID 出现在配置中,不能解决当前 SSID 无法读取的问题。如果 ClashMac 运行时读取不到当前 SSID,仍然无法完成匹配。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions