ECC 的 Swift 安全规则:解析 .cursor/rules/swift-security.md 如何为 Swift 项目建立密钥、传输与输入三道防线
在 ECC 仓库中,.cursor/rules/swift-security.md 是一份面向 Cursor 环境的 Swift 安全规则文件,它在通用安全基线之上补充了 Swift 平台特有的密钥管理、传输安全与输入校验约束。本文以该文件为核心,逐条拆解其三大安全主题,并结合仓库中配套的通用安全规则(rules/common/security.md)、Swift 规则包与安装机制,说明这些规则如何在 Agent 辅助开发流程中落地,帮助你在 Swift/Swift Package 项目中建立可执行的安全基线。
规则文件本身:Cursor 规则的 Frontmatter 与生效条件
.cursor/rules/swift-security.md 文件头部的 Frontmatter 定义了它在 Cursor 中的行为方式:
description: "Swift security extending common rules"
globs: ["**/*.swift", "**/Package.swift"]
alwaysApply: false
三个字段的作用:
description:声明该规则"扩展通用规则"(extending common rules),提示其定位是增量补充而非独立基线;globs:**/*.swift与**/Package.swift表明该规则仅在编辑 Swift 源码文件或 SwiftPM 清单时被匹配注入,不会污染其他语言项目;alwaysApply: false:该规则不会被无条件加载到每个上下文,而是按文件类型惰性生效,从而控制 Agent 的上下文开销。
文件正文第一行即明确其与通用规则的关系:
This file extends the common security rule with Swift specific content.
这一"扩展"关系在仓库中有对应的通用层实现,即 通用安全规则。该通用规则(在 .cursor 下对应 .cursor/rules/common-security.md,且 alwaysApply: true 常驻生效)规定了三条对所有语言生效的基线:
- 强制安全检查清单(Mandatory Security Checks):任何 commit 前必须确认——无硬编码密钥、所有用户输入已校验、SQL 注入防护(参数化查询)、XSS 防护(HTML 消毒)、CSRF 防护开启、认证/授权已验证、所有端点有速率限制、错误信息不泄露敏感数据;
- 密钥管理:永远不在源码中硬编码密钥,始终使用环境变量或密钥管理器,并在启动时校验必需密钥存在,任何可能泄露的密钥必须轮换;
- 安全响应协议(Security Response Protocol):发现安全问题时立即停止,调用 security-reviewer agent 介入,先修复 CRITICAL 级问题再继续,轮换已暴露密钥,并排查整个代码库中的同类问题。
.cursor/rules/swift-security.md 的价值正在于:它把上述语言无关的"原则"翻译成 Swift 平台的具体技术手段。仓库中同样存在一份面向 Claude Code 规则体系的对应文件 rules/swift/security.md,内容与 .cursor 版本一致,仅 Frontmatter 使用 paths 字段而非 globs 字段——说明 ECC 对同一套 Swift 安全约束在多个 Agent 宿主(Cursor、Claude Code)下做了格式适配。按照 rules/README.md 的层级设计,语言特定规则与通用规则冲突时,语言特定规则优先(specific overrides general)。
密钥管理:Keychain、环境变量与启动校验
规则原文的 Secret Management 一节给出三条 Swift 特有约束:
- 敏感数据(tokens、passwords、keys)必须使用 Keychain Services——永远不要使用
UserDefaults; - 构建期密钥(build-time secrets)使用环境变量或
.xcconfig文件; - 永远不要在源码中硬编码密钥——反编译工具可以轻易将其提取出来。
为什么 UserDefaults 不适合存密钥
UserDefaults 的数据落在沙盒内的 Library/Preference plist 中,未加密、可被同设备的工具链与备份直接读取;而 Keychain Services 由操作系统级安全子系统托管,支持按 keychain 条目粒度设置访问控制(access control)与保护级别(如"设备解锁后且应用运行时可访问")。对于 token、密码、对称密钥这类凭据,Keychain 是 iOS/macOS 平台上的标准落点。
硬编码密钥为何在 Swift 二进制中"裸奔"
规则特别强调"decompilation tools extract them trivially"(反编译工具可轻易提取)。这一点在 Swift 项目中成立:编译后的二进制保留字符串常量与符号信息,攻击者无需完整反编译即可用字符串搜索定位硬编码的 API key。因此 ECC 的规则要求把密钥移出版本控制,落到环境变量或 .xcconfig。
规则给出的标准代码:从环境读取并启动即校验
let apiKey = ProcessInfo.processInfo.environment["API_KEY"]
guard let apiKey, !apiKey.isEmpty else {
fatalError("API_KEY not configured")
}
这段代码体现了通用规则"Validate that required secrets are present at startup"的 Swift 实现方式:
ProcessInfo.processInfo.environment是 Swift 读取进程环境变量的标准入口,适用于命令行工具与通过 Xcode 运行配置/CI 注入环境变量的场景;guard let apiKey, !apiKey.isEmpty采用 Swift 5.7+ 的省略类型guard let写法,同时校验"存在"与"非空"两个条件——避免拿到空字符串后在后续网络请求中才失败;- 缺失时直接
fatalError快速失败(fail fast),而不是带着未配置的密钥继续运行产生难以排查的运行时错误。
对 App 工程而言,对应的构建期注入渠道是 .xcconfig:将 API_KEY 等值写入配置片段,通过 Build Settings 的预处理器宏传递,既避免密钥进入源码文件,又保留"不同构建配置使用不同密钥"的灵活性。
与通用清单的对应关系
对照 通用安全规则 的 commit 前清单第一条 "No hardcoded secrets (API keys, passwords, tokens)",Swift 规则把它具体化为三个可检查的点:运行时密钥走 Keychain、构建期密钥走环境变量/.xcconfig、源码中零字面量密钥。Agent 在提交前扫描 Swift 文件时,这三条就是判定依据。
传输安全:ATS 默认强制、证书固定与证书校验
Transport Security 一节只有三条,但每条都指向 iOS/macOS 网络栈的关键防线:
- App Transport Security (ATS) 默认强制——不要禁用;
- 对关键端点使用证书固定(certificate pinning);
- 校验所有服务器证书。
ATS 是平台默认防线
ATS 自 iOS 10 起默认开启,强制 HTTPS、最低 TLS 版本与证书链校验,可防止中间人攻击和明文流量嗅探。"do not disable it" 对应的是 Info.plist 中的 NSAppTransportSecurity 字典:向其中添加 NSAllowsArbitraryLoads = true(或按域配置的 exception)都会削弱默认保护。ECC 规则的立场很明确——除非有无法升级的遗留内网服务,不应通过 ATS exception 关闭校验;即便临时使用,也应收窄到具体域并规划退出路径。
证书固定适用于关键端点
"Use certificate pinning for critical endpoints" 的限定词 "critical" 值得注意:证书固定(将服务端证书或公钥指纹写入客户端,在 TLS 握手后比对)能抵御"证书被信任的 CA 误发/私钥泄露"这类攻击,但代价是证书续期后必须随版本更新。因此规则建议只对支付、登录等关键链路启用固定,而非全局铺开。在 Swift 中,这一能力通常通过实现 URLSession 的委托方法(在 didReceive challenge 中比对固定指纹)来完成。
所有服务器证书都要校验
第三条要求与 ATS 形成互补:ATS 校验"证书由可信 CA 签发",而"validate all server certificates" 强调任何自行实现的 TLS 握手路径(例如直接基于 Security 框架的连接)都不应跳过验证链、有效期与主机名匹配——关闭任一项都会使连接退化为可被中间人劫持的"加密隧道"。
输入校验:显示前消毒、URL 安全构造与外部数据源
Input Validation 一节覆盖三条:
- 所有用户输入在显示前消毒,防止注入;
- 使用带校验的
URL(string:)而非强制解包; - 来自外部来源(APIs、deep links、pasteboard)的数据在处理前必须校验。
显示前消毒防止注入
在 SwiftUI/UIKit 中直接渲染用户输入时,若输入包含可被解释的标记(如 HTML 片段、格式化占位符),就可能造成界面注入甚至后续逻辑注入。规则的落点是:渲染前统一消毒(strip/escape 不可信标记),与通用清单中的 XSS prevention(sanitized HTML)一一对应,只是平台从 Web 换到了 Swift 界面层。
URL(string:) 而非强制解包
URL(string:) 的初始化器返回可选值——非法的 URL 字符串(包含空格、未编码的保留字符等)会得到 nil。ECC 规则要求对结果做校验处理,而不是 URL(string: raw)! 强解包:后者会把"构造非法输入"这一可预期分支变成崩溃点,在解析 deep link 或用户粘贴的链接时尤其危险。安全的写法是先 guard 解包,再校验 scheme/host 白名单后才进入业务处理。
外部数据源三件套:API、deep link、pasteboard
规则点名了三类不可信数据入口:
- API 响应:即使服务端可信,传输层之外的数据仍可能被篡改或字段缺失,反序列化前应校验结构(Codable 解码失败即拒绝)与取值范围;
- Deep links:外部 App 可以构造任意 URL 唤起你的 App,
onOpenURL/openURL回调里的参数应视为攻击者可控输入——校验 scheme、host、路径与参数白名单后再路由; - Pasteboard(剪贴板):系统剪贴板是跨 App 共享的,前一个 App 写入的内容随时可能被你读到,对其中携带的凭据类数据(如一次性验证码、token)在使用前同样要按"外部输入"处理,并且注意读取的隐私成本。
规则如何被 ECC 的规则体系激活与安装
理解了单条规则的内容之后,它在 ECC 中的生效方式也值得说明。仓库维护了两套同源的规则目录:
- Cursor 侧:.cursor/rules/ 下以
swift-security.md、common-security.md等扁平文件名存放,Frontmatter 使用globs/alwaysApply字段; - Claude Code 侧:rules/ 下按
common/与各语言子目录(swift/、python/等)分层组织,Frontmatter 使用paths字段。
rules/README.md 给出两种安装方式:
- 安装脚本(推荐):
./install.sh swift一次性安装 common + Swift 规则集,也可./install.sh typescript python批量安装多语言; - 手动安装:
mkdir -p ~/.claude/rules/ecc后分别cp -r rules/common与cp -r rules/swift到该命名空间。README 特别提醒要整目录复制、不要用/*扁平化,因为各语言目录内存在与 common 同名的文件(如security.md),扁平化会相互覆盖,并破坏语言文件里../common/的相对引用。
Swift 规则包本身由五个文件构成(见 rules/swift/):coding-style.md、testing.md、patterns.md、hooks.md 与本文聚焦的 security.md。其中 rules/swift/patterns.md 强调协议导向设计与 actor 隔离共享可变状态,rules/swift/testing.md 指定使用 Swift Testing(@Test/#expect)——这些与安全规则共同构成"改代码按 patterns 写、提交前按 security 查、验证靠 testing"的闭环。
此外,scripts/sync-ecc-to-codex.sh 中定义了 CURSOR_RULES_DIR="$REPO_ROOT/.cursor/rules",说明 .cursor/rules 目录还会被同步脚本读取并转换为 Codex CLI 的规则提示,即这份 Swift 安全规则实际上可以分发到 ECC 支持的多个 Agent 宿主,而不仅限于 Cursor。
小结:一条 Swift 安全规则的完整检查路径
综合 .cursor/rules/swift-security.md 与它扩展的通用规则,Swift 项目在 ECC 流程中的安全检查路径可以归纳为:
| 主题 | 通用层(common-security) | Swift 层(swift-security) |
|---|---|---|
| 密钥 | 不硬编码;启动校验;泄露即轮换 | Keychain 存运行时密钥;环境变量/.xcconfig 存构建期密钥;ProcessInfo.environment + guard + fatalError 启动校验 |
| 传输 | 错误信息不泄露敏感数据(通用面) | ATS 不关闭;关键端点证书固定;全量校验服务器证书 |
| 输入 | 所有用户输入已校验;XSS 防护 | 显示前消毒;URL(string:) 校验解包;API/deep link/pasteboard 数据先验证后处理 |
| 响应 | 停止 → security-reviewer agent → 先修 CRITICAL → 轮换密钥 → 全库排查 | 继承通用协议,无额外覆盖 |
这套"common 定原则、language 给手段、globs/paths 控生效范围"的分层设计,让安全约束既保持跨语言一致,又能在 Swift 文件被编辑时精准注入平台化的落地方案。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0624
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00