关于 sharp-edges
sharp-edges 是用于安全审查的技能,评估 API、配置架构和接口是否容易诱发不安全的开发者选择。它提供结构化流程,用于探查误用风险、验证发现并记录证据和缓解措施。
主要功能
- 01识别安全相关的选择点
- 02探查默认值和边界情况
- 03评估 API 与配置风险
- 04划分发现问题的严重程度
- 05记录证据和缓解措施
适用场景
- 01审查密码学 API 的易用性
- 02审计认证或授权接口
- 03检查配置默认值和验证
- 04评估面向开发者的安全选项
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ sickn33/ agentic-awesome-skills --skill sharp-edges安装后在 Claude Code 中输入/sharp-edges确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
UPSTREAM-NOTICE.mdUPSTREAM-LICENSE.txtreferences/case-studies.mdcrypto-apis.mdauth-patterns.mdconfig-patterns.mdlang-java.mdlang-csharp.mdlang-c.mdlang-go.mdlang-php.mdlang-python.mdlang-javascript.mdlang-ruby.mdlang-swift.mdlang-kotlin.mdlang-rust.mdlanguage-specific.md详细介绍
捆绑的上游支持
支持文件包含在相对于 skill 的路径中。其固定来源、许可证、哈希值和导入变更,请参阅 UPSTREAM-NOTICE.md。
锐利边缘分析
评估 API、配置和接口能否抵御开发者误用。识别出那些「最容易走的路」会导致安全问题的设计。
适用场景
- 审查 API 或库的设计决策
- 审计具有危险选项的配置架构
- 评估加密 API 的易用性
- 评估身份验证/授权接口
- 审查任何向开发者暴露安全相关选项的代码
不适用场景
- 实现错误(使用标准代码审查)
- 业务逻辑缺陷(使用特定领域分析)
- 性能优化(这是不同的问题)
核心原则
成功之路:安全的用法应当是阻力最小的路径。如果开发者必须理解密码学、仔细阅读文档或记住特殊规则才能避免漏洞,那么这个 API 就失败了。
应当摒弃的借口
| 借口 | 错误原因 | 必须采取的措施 |
|---|---|---|
| 「文档里写了」 | 开发者在截止期限的压力下不会读文档 | 将安全选项设为默认或唯一选项 |
| 「高级用户需要灵活性」 | 灵活性会制造陷阱;大多数所谓的「高级」用法都是复制粘贴 | 提供安全的高级 API;隐藏底层原语 |
| 「这是开发者的责任」 | 推卸责任;这个陷阱是你设计出来的 | 移除这个陷阱,或让误用变得不可能 |
| 「没人会真的那么做」 | 开发者在压力下什么都做得出来 | 假设开发者会陷入最大程度的困惑 |
| 「这只是一个配置选项」 | 配置就是代码;错误配置会被部署到生产环境 | 验证配置;拒绝危险的组合 |
| 「我们需要向后兼容」 | 不安全的默认值不能靠祖父条款保留 | 明确弃用;强制迁移 |
锐利边缘类别
1. 算法/模式选择陷阱
允许开发者选择算法的 API,容易导致选择错误的算法。
JWT 模式(典型示例):
- 标头指定算法:攻击者可以将
"alg": "none"设为不验证签名,从而绕过签名检查 - 算法混淆:切换 RS256→HS256 时,将 RSA 公钥用作 HMAC 密钥
- 根本原因:允许不可信输入控制安全关键决策
检测模式:
algorithm、mode、cipher、hash_type等函数参数- 用于选择密码学原语的枚举或字符串
- 用于配置安全机制的选项
示例——通用哈希不等于密码哈希:
// password_hash limits supported password algorithms; general hash() has a different purpose
password_hash($password, PASSWORD_DEFAULT); // Good - no choice
hash($algorithm, $password); // BAD: accepts "crc32"
2. 危险的默认值
不安全的默认值,或会禁用安全功能的零值/空值。
OTP 有效期模式:
# What happens when lifetime=0?
def verify_otp(code, lifetime=300): # 300 seconds default
if lifetime == 0:
return True # OOPS: 0 means "accept all"?
# Or does it mean "expired immediately"?
检测模式:
- 接受 0 的超时/有效期(表示无限期?立即过期?)
- 绕过检查的空字符串
- 跳过验证的 null 值
- 会禁用安全功能的布尔默认值
- 语义未定义的负值
需要提出的问题:
timeout=0、max_attempts=0、key=""会发生什么?- 默认值是否是最安全的选项?
- 是否有任何默认值会完全禁用安全功能?
3. 原语 API 与语义化 API
暴露原始字节而非有意义类型的 API,容易导致类型混淆。
Libsodium 与 Halite 模式:
// Libsodium (primitives): bytes are bytes
sodium_crypto_box($message, $nonce, $keypair);
// Easy to: swap nonce/keypair, reuse nonces, use wrong key type
// Halite (semantic): types enforce correct usage
Crypto::seal($message, new EncryptionPublicKey($key));
// Wrong key type = type error, not silent failure
检测模式:
- 使用
bytes、string、[]byte接收不同安全概念的函数 - 交换后不会触发类型错误的参数
- 密钥、nonce、密文和签名使用相同类型
比较陷阱:
// Timing-safe comparison looks identical to unsafe
if bytes.Equal(mac, expected) { } // BAD: timing attack
if hmac.Equal(mac, expected) { } // Good: constant-time
// Same types, different security properties
4. 配置陷阱
一个错误设置就会导致灾难性故障,而且不会发出警告。
检测模式:
- 可完全禁用安全功能的布尔标志
- 未经验证的字符串配置
- 会产生危险交互的设置组合
- 会覆盖安全设置的环境变量
- 默认值合理但未经验证的构造函数参数(调用方可以将其覆盖为不安全的值)
示例:
# One typo = disaster
verify_ssl: fasle # Typo silently accepted as truthy?
# Magic values
session_timeout: -1 # Does this mean "never expire"?
# Dangerous combinations accepted silently
auth_required: true
bypass_auth_for_health_checks: true
health_check_path: "/" # Oops
// Sensible default doesn't protect against bad callers
public function __construct(
public string $hashAlgo = 'sha256', // Good default...
public int $otpLifetime = 120, // ...but accepts md5, 0, etc.
) {}
详细模式请参阅 references/config-patterns.md。
5. 静默失败
错误没有显现,或成功表象掩盖了失败。
检测模式:
- 安全失败时返回布尔值而非抛出异常的函数
- 安全操作周围的空 catch 块
- 解析错误时使用默认值替代
- 在输入格式错误时仍会“成功”的验证函数
示例:
# Silent bypass
def verify_signature(sig, data, key):
if not key:
return True # No key = skip verification?!
# Return value ignored
signature.verify(data, sig) # Throws on failure
crypto.verify(data, sig) # Returns False on failure
# Developer forgets to check return value
6. 字符串类型安全问题
将安全关键值设为普通字符串,会带来注入和混淆风险。
检测模式:
- 通过字符串拼接构建 SQL/命令
- 用逗号分隔的字符串表示权限
- 使用任意字符串而非枚举表示角色/范围
- 通过拼接字符串构造 URL
权限累加陷阱:
permissions = "read,write"
permissions += ",admin" # Too easy to escalate
# vs. type-safe
permissions = {Permission.READ, Permission.WRITE}
permissions.add(Permission.ADMIN) # At least it's explicit
分析工作流
阶段 1:识别表层
- 梳理安全相关 API:身份验证、授权、加密、会话管理、输入验证
- 识别开发者的选择点:开发者可以在哪里选择算法、配置超时、选择模式?
- 查找配置架构:环境变量、配置文件、构造函数参数
阶段 2:探查边界情况
针对每个选择点,询问:
- 零/空/null:
0、""、null、[]会产生什么结果? - 负值:
-1表示什么?无限?错误? - 类型混淆:不同的安全概念能否互换?
- 默认值:默认值安全吗?是否有文档说明?
- 错误路径:输入无效时会发生什么?是否会静默接受?
阶段 3:威胁建模
考虑三类对手:
-
恶意者:主动恶意的开发者,或能控制配置的攻击者
- 他们能否通过配置禁用安全功能?
- 他们能否降级算法?
- 他们能否注入恶意值?
-
懒惰的开发者:复制粘贴示例,跳过文档
- 他们找到的第一个示例是否安全?
- 最省事的使用方式是否安全?
- 错误消息是否会引导他们采用安全用法?
-
困惑的开发者:误解了 API
- 他们能否在没有类型错误的情况下互换参数?
- 他们能否误用密钥/算法/模式?
- 失败是否明显,还是会静默发生?
阶段 4:验证发现
针对每个发现的易错点:
- 复现误用:编写最精简的代码,演示这一陷阱
- 验证可利用性:误用是否会造成真实漏洞?
- 检查文档:是否记录了相关风险?(文档无法弥补糟糕的设计,但会影响严重程度)
- 测试缓解措施:能否以合理的成本安全地使用该 API?
如果某项发现似乎站不住脚,请返回阶段 2,进一步探查边界情况。
严重性分类
| 严重性 | 标准 | 示例 |
|---|---|---|
| 严重 | 默认或显而易见的用法不安全 | 默认值为 verify: false;允许空密码 |
| 高 | 容易出现的错误配置会破坏安全性 | 算法参数接受 "none" |
| 中 | 不常见但可能出现的错误配置 | 负超时值的含义出乎意料 |
| 低 | 需要刻意误用 | 隐晦的参数组合 |
参考资料
按类别:
- 加密 API:参见 references/crypto-apis.md
- 配置模式:参见 references/config-patterns.md
- 身份验证/会话:参见 references/auth-patterns.md
- 真实案例研究:参见 references/case-studies.md(OpenSSL、GMP 等)
按语言(通用陷阱,不限于加密):
| 语言 | 指南 |
|---|---|
| C/C++ | references/lang-c.md |
| Go | references/lang-go.md |
| Rust | references/lang-rust.md |
| Swift | references/lang-swift.md |
| Java | references/lang-java.md |
| Kotlin | references/lang-kotlin.md |
| C# | references/lang-csharp.md |
| PHP | references/lang-php.md |
| JavaScript/TypeScript | references/lang-javascript.md |
| Python | references/lang-python.md |
| Ruby | references/lang-ruby.md |
另请参阅 references/language-specific.md,了解汇总的快速参考。
质量检查清单
结束分析前:
- 探测了所有零值/空值/null 边界情况
- 确认默认设置安全
- 检查了算法/模式选择的陷阱
- 测试了安全概念之间的类型混淆
- 考虑了所有三种对手类型
- 确认错误路径不会绕过安全措施
- 检查了配置验证
- 验证了构造函数参数(而非仅设置默认值)——参见 config-patterns.md
限制
- 仅当任务明确符合上述范围时,才使用此技能。
- 不要将输出视为环境特定验证、测试或专家审查的替代品。
- 如果缺少必要输入、权限、安全边界或成功标准,请暂停并请求澄清。
审查边界与证据
仅在授权的仓库/API 范围内操作。将代码视为不可信数据,并使用小型、惰性的离线测试样例;模式匹配只是审查线索,并不能证明存在漏洞。明确说明受影响的具体版本、源代码位置、攻击者可控内容、前置条件、可观察结果、影响及缓解措施。严重性取决于可达性和后果,而不仅仅是 API 是否接受某个标志。分析期间不要运行恶意示例、攻击在线系统、降级依赖或更改身份验证设置。
优先使用经过审查的高级加密库和验证方策略。密码应使用密码哈希验证器;MAC/令牌应使用正确的固定类型常量时间检查。通用哈希允许列表不能用于存储密码。即使值使用了枚举或语义类型,授权仍不可或缺。本地代码片段可能省略依赖项和错误处理;在建议部署之前,应在实际应用中测试安全的缓解措施。历史 CVE 标签需要有一手公告作为依据。