Install any skill in seconds. Free to start, no credit card required.
Get Started Free →sharp-edges
| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 316% | 0% |
| case-06 | ✗→✓ | ▲ Improved | 113% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 88% | 0% |
| case-20 | ✓→✗ | ▼ Worse | 74% | 0% |
| case-02 | ✓→✓ | = Same ✓ | 59% | 0% |
name: sharp-edges description: "Identifies error-prone APIs, dangerous configurations, and footgun designs that enable security mistakes. Use when reviewing API designs, configuration schemas, cryptographic library ergonomics, or evaluating whether code follows 'secure by...
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:
php// 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:
python# 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:
php// 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:
go// 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:
yaml# 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
php// 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:
python# 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:
pythonpermissions = "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:
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 | | PHP | references/lang-php.md | | JavaScript/TypeScript | references/lang-javascript.md | | Python | references/lang-python.md | | Ruby | references/lang-ruby.md |
See also references/language-specific.md for a combined quick reference.
Before concluding analysis:
Other measured skills in the registry, with their headline benchmark lift.