sharp-edges by trailofbits/skills
npx skills add https://github.com/trailofbits/skills --skill sharp-edges评估 API、配置和接口是否能够抵御开发者的误用。识别那些“简单路径”会导致不安全的设计。
成功之坑:安全使用应该是最省力的路径。如果开发者必须理解密码学、仔细阅读文档或记住特殊规则才能避免漏洞,那么这个 API 就失败了。
| 合理化说辞 | 为何错误 | 必要行动 |
|---|---|---|
| “有文档说明” | 开发者在截止日期压力下不会阅读文档 | 使安全选择成为默认或唯一选项 |
| “高级用户需要灵活性” | 灵活性会制造隐患;大多数“高级”用法是复制粘贴 | 提供安全的高级 API;隐藏原语 |
| “这是开发者的责任” | 推卸责任;是你设计了隐患 | 移除隐患或使其不可能被误用 |
| “没人会真的那么做” | 在压力下开发者会做出任何可以想象的事 | 假设开发者处于最大程度的困惑状态 |
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
| “这只是一个配置选项” | 配置即代码;错误的配置会部署到生产环境 | 验证配置;拒绝危险组合 |
| “我们需要向后兼容” | 不安全的默认值不能被“祖父条款”豁免 | 高调弃用;强制迁移 |
允许开发者选择算法的 API 会诱使他们选择错误的算法。
JWT 模式(典型示例):
"alg": "none" 来绕过签名检测模式:
algorithm、mode、cipher、hash_type示例 - PHP password_hash 允许弱算法:
// 危险:允许 crc32, md5, sha1
password_hash($password, PASSWORD_DEFAULT); // 好 - 没有选择
hash($algorithm, $password); // 坏:接受 "crc32"
不安全的默认值,或禁用安全的零/空值。
OTP 生命周期模式:
# 当 lifetime=0 时会发生什么?
def verify_otp(code, lifetime=300): # 300 秒默认值
if lifetime == 0:
return True # 糟糕:0 意味着“接受所有”?
# 还是意味着“立即过期”?
检测模式:
要问的问题:
timeout=0、max_attempts=0、key="" 会发生什么?暴露原始字节而非有意义类型的 API 容易引发类型混淆。
Libsodium 与 Halite 模式:
// Libsodium(原语):字节就是字节
sodium_crypto_box($message, $nonce, $keypair);
// 容易:交换 nonce/keypair、重用 nonce、使用错误的密钥类型
// Halite(语义):类型强制正确使用
Crypto::seal($message, new EncryptionPublicKey($key));
// 错误的密钥类型 = 类型错误,而非静默失败
检测模式:
bytes、string、[]byte 的函数比较隐患:
// 时序安全比较看起来与不安全比较相同
if hmac == expected { } // 坏:时序攻击
if hmac.Equal(mac, expected) { } // 好:恒定时间
// 相同类型,不同的安全属性
一个错误的设置就会导致灾难性故障,且没有警告。
检测模式:
示例:
# 一个拼写错误 = 灾难
verify_ssl: fasle # 拼写错误被静默接受为真值?
# 魔法值
session_timeout: -1 # 这表示“永不过期”吗?
# 危险组合被静默接受
auth_required: true
bypass_auth_for_health_checks: true
health_check_path: "/" # 糟糕
// 合理的默认值无法防止错误的调用者
public function __construct(
public string $hashAlgo = 'sha256', // 好的默认值...
public int $otpLifetime = 120, // ...但接受 md5, 0 等
) {}
详细模式请参见 config-patterns.md。
不浮出水面的错误,或掩盖失败的成功。
检测模式:
示例:
# 静默绕过
def verify_signature(sig, data, key):
if not key:
return True # 没有密钥 = 跳过验证?!
# 返回值被忽略
signature.verify(data, sig) # 失败时抛出异常
crypto.verify(data, sig) # 失败时返回 False
# 开发者忘记检查返回值
安全关键值作为纯字符串,容易导致注入和混淆。
检测模式:
权限累积隐患:
permissions = "read,write"
permissions += ",admin" # 太容易升级权限了
# 对比类型安全
permissions = {Permission.READ, Permission.WRITE}
permissions.add(Permission.ADMIN) # 至少它是明确的
针对每个选择点,询问:
0、""、null、[] 会发生什么?-1 是什么意思?无限?错误?考虑三种对手:
针对每个已识别的锐利边缘:
如果发现似乎有问题,请返回阶段 2 并探查更多边界情况。
| 严重性 | 标准 | 示例 |
|---|---|---|
| 严重 | 默认或明显用法不安全 | verify: false 默认值;允许空密码 |
| 高 | 容易错误配置破坏安全 | 算法参数接受 "none" |
| 中 | 不常见但可能的错误配置 | 负超时具有意外含义 |
| 低 | 需要故意误用 | 晦涩的参数组合 |
按类别:
按语言(通用隐患,非加密特定):
| 语言 | 指南 |
|---|---|
| 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 获取组合快速参考。
在结束分析之前:
每周安装量
1.3K
仓库
GitHub 星标
3.9K
首次出现
Jan 19, 2026
安全审计
安装于
claude-code1.2K
codex1.1K
opencode1.1K
gemini-cli995
cursor984
github-copilot949
Evaluates whether APIs, configurations, and interfaces are resistant to developer misuse. Identifies designs where the "easy path" leads to insecurity.
The pit of success : Secure usage should be the path of least resistance. If developers must understand cryptography, read documentation carefully, or remember special rules to avoid vulnerabilities, the API has failed.
| Rationalization | Why It's Wrong | Required Action |
|---|---|---|
| "It's documented" | Developers don't read docs under deadline pressure | Make the secure choice the default or only option |
| "Advanced users need flexibility" | Flexibility creates footguns; most "advanced" usage is copy-paste | Provide safe high-level APIs; hide primitives |
| "It's the developer's responsibility" | Blame-shifting; you designed the footgun | Remove the footgun or make it impossible to misuse |
| "Nobody would actually do that" | Developers do everything imaginable under pressure | Assume maximum developer confusion |
| "It's just a configuration option" | Config is code; wrong configs ship to production | Validate configs; reject dangerous combinations |
| "We need backwards compatibility" | Insecure defaults can't be grandfather-claused | Deprecate loudly; force migration |
APIs that let developers choose algorithms invite choosing wrong ones.
The JWT Pattern (canonical example):
"alg": "none" to bypass signaturesDetection patterns:
algorithm, mode, cipher, hash_typeExample - PHP password_hash allowing weak algorithms:
// DANGEROUS: allows crc32, md5, sha1
password_hash($password, PASSWORD_DEFAULT); // Good - no choice
hash($algorithm, $password); // BAD: accepts "crc32"
Defaults that are insecure, or zero/empty values that disable security.
The OTP Lifetime Pattern:
# 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"?
Detection patterns:
Questions to ask:
timeout=0? max_attempts=0? key=""?APIs that expose raw bytes instead of meaningful types invite type confusion.
The Libsodium vs. Halite Pattern:
// 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
Detection patterns:
bytes, string, []byte for distinct security conceptsThe comparison footgun:
// Timing-safe comparison looks identical to unsafe
if hmac == expected { } // BAD: timing attack
if hmac.Equal(mac, expected) { } // Good: constant-time
// Same types, different security properties
One wrong setting creates catastrophic failure, with no warning.
Detection patterns:
Examples:
# 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.
) {}
See config-patterns.md for detailed patterns.
Errors that don't surface, or success that masks failure.
Detection patterns:
Examples:
# 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
Security-critical values as plain strings enable injection and confusion.
Detection patterns:
The permission accumulation footgun:
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
For each choice point, ask:
0, "", null, []?-1 mean? Infinite? Error?Consider three adversaries:
The Scoundrel : Actively malicious developer or attacker controlling config
The Lazy Developer : Copy-pastes examples, skips documentation
The Confused Developer : Misunderstands the API
For each identified sharp edge:
If a finding seems questionable, return to Phase 2 and probe more edge cases.
| Severity | Criteria | Examples |
|---|---|---|
| Critical | Default or obvious usage is insecure | verify: false default; empty password allowed |
| High | Easy misconfiguration breaks security | Algorithm parameter accepts "none" |
| Medium | Unusual but possible misconfiguration | Negative timeout has unexpected meaning |
| Low | Requires deliberate misuse | Obscure parameter combination |
By category:
By language (general footguns, not crypto-specific):
| Language | Guide |
|---|---|
| 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 |
See also references/language-specific.md for a combined quick reference.
Before concluding analysis:
Weekly Installs
1.3K
Repository
GitHub Stars
3.9K
First Seen
Jan 19, 2026
Security Audits
Gen Agent Trust HubPassSocketWarnSnykPass
Installed on
claude-code1.2K
codex1.1K
opencode1.1K
gemini-cli995
cursor984
github-copilot949
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
102,200 周安装
| PHP | references/lang-php.md |
| JavaScript/TypeScript | references/lang-javascript.md |
| Python | references/lang-python.md |
| Ruby | references/lang-ruby.md |