From security-research
Detect business logic, concurrency, and API-specific vulnerabilities — race conditions, TOCTOU, double-spend, workflow bypass, price/quantity manipulation, cache poisoning/deception, distributed lock issues, rate limiting gaps, GraphQL DoS, excessive data exposure, and webhook security. Consolidated detection skill for all logic/timing/API patterns.
npx claudepluginhub pucagit/claude-plugin --plugin security-researchThis skill uses the workspace's default tool permissions.
Find flaws in application logic, timing, and API design that allow users to violate business rules, exploit race conditions, abuse caching, or exploit API-specific weaknesses.
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Find flaws in application logic, timing, and API design that allow users to violate business rules, exploit race conditions, abuse caching, or exploit API-specific weaknesses.
Before hunting, read references/cool_techniques.md for applicable logic detection techniques learned from previous audits. Apply any relevant techniques during your analysis.
| Category | Sub-Types |
|---|---|
| Race Conditions | TOCTOU, check-then-act without lock, concurrent balance/quota operations |
| Double-Spend | Concurrent debit/credit without row lock, missing idempotency |
| Quantity/Price | Negative quantity manipulation, client-side price accepted, coupon reuse |
| Workflow Bypass | State machine step-skipping, approval process bypass, improper state transitions |
| Distributed Locks | Advisory lock not enforced, TTL too short, single-node SETNX |
| Cache Attacks | Web cache poisoning (unkeyed header), web cache deception (public on private), stale auth from cached tokens |
| Rate Limiting | Missing limits on login/OTP/reset/payment, no account lockout |
| Replay | Missing nonce/timestamp, no idempotency key |
| GraphQL | Query depth/batching DoS, field suggestion enumeration, excessive data exposure |
| API Exposure | fields='__all__' returning secrets, missing pagination caps, exposed API docs |
| Webhooks | Missing HMAC signature, URL not validated against internal IPs |
# Check-then-act patterns (absence of transaction is suspicious)
grep -rn "if.*balance.*>=\|if.*credit.*>=\|if.*quota.*>\|if.*limit.*>\|if.*count.*<\|if.*available" \
--include="*.py" --include="*.js" --include="*.ts" --include="*.java" \
--include="*.rb" --include="*.go" --include="*.php" ${TARGET_SOURCE}
# Transaction / locking presence (safe patterns)
grep -rn "transaction\|with_transaction\|@transaction\|BEGIN\|COMMIT\|atomic\|select_for_update\|FOR UPDATE\|LOCK\|Mutex\|sync\.Mutex" \
--include="*.py" --include="*.js" --include="*.ts" --include="*.java" \
--include="*.rb" --include="*.go" ${TARGET_SOURCE}
grep -rn "\.balance\|\.credit\|\.quota\|\.inventory\|\.stock\|\.remaining\|\.usage\|\.count" \
--include="*.py" --include="*.js" --include="*.ts" --include="*.java" \
--include="*.rb" --include="*.go" \
${TARGET_SOURCE} | grep -i "update\|decrement\|\-=\|\+=\|save\|commit"
grep -rn "quantity\|amount\|price\|total\|balance\|credit\|debit\|refund\|discount\|coupon" \
--include="*.py" --include="*.js" --include="*.ts" --include="*.java" \
--include="*.rb" --include="*.go" --include="*.php" \
${TARGET_SOURCE} | grep -i "request\.\|req\.\|params\.\|body\.\|form\.\|input\."
grep -rn "status\|state\|step\|phase\|stage\|workflow\|approval\|pending\|approved\|rejected\|completed" \
--include="*.py" --include="*.js" --include="*.ts" --include="*.java" \
--include="*.rb" --include="*.go" \
${TARGET_SOURCE} | grep -i "update\|change\|transition\|set\|request\.\|req\."
grep -rn "redis\|redlock\|distributed_lock\|acquire_lock\|SETNX\|SET.*NX\|SET.*EX\|RedisLock\|Redlock" \
--include="*.py" --include="*.js" --include="*.ts" --include="*.java" \
--include="*.go" --include="*.rb" ${TARGET_SOURCE}
grep -rn "cache\.get(\|cache\.set(\|redis\.get(\|redis\.set(\|cache_key\|@cache\|@cached\|vary_on_headers" \
--include="*.py" --include="*.js" --include="*.ts" --include="*.java" \
--include="*.go" --include="*.rb" ${TARGET_SOURCE}
# User-controllable cache key input
grep -rn "request\.headers\|request\.META\|X-Forwarded\|X-Original\|X-Rewrite\|Host.*cache\|cache.*Host" \
--include="*.py" --include="*.js" --include="*.ts" --include="*.go" ${TARGET_SOURCE}
grep -rn "Cache-Control\|Vary\|X-Cache\|Surrogate-Key\|CDN-Cache\|max-age\|s-maxage\|public.*cache\|cache.*public" \
--include="*.py" --include="*.js" --include="*.ts" --include="*.go" \
--include="*.conf" --include="*.yml" ${TARGET_SOURCE}
grep -rn "cache.*token\|token.*cache\|cache.*session\|session.*cache\|TTL.*auth\|auth.*TTL\|expire.*session\|jwt.*cache" \
--include="*.py" --include="*.js" --include="*.ts" --include="*.java" \
--include="*.go" --include="*.rb" ${TARGET_SOURCE}
grep -rn "rate_limit\|ratelimit\|throttle\|RateLimit\|@limit\|slowDown\|express-rate-limit\|flask_limiter\|THROTTLE" \
--include="*.py" --include="*.js" --include="*.ts" --include="*.java" \
--include="*.rb" --include="*.go" ${TARGET_SOURCE}
grep -rn "nonce\|timestamp\|idempotency\|Idempotency-Key\|replay\|once\|one_time\|single_use\|dedup" \
--include="*.py" --include="*.js" --include="*.ts" --include="*.java" \
--include="*.go" --include="*.rb" ${TARGET_SOURCE}
grep -rn "depth_limit\|maxDepth\|query_depth\|complexity\|MAX_COMPLEXITY\|depthLimit\|query_cost\|NoSchemaIntrospection" \
--include="*.py" --include="*.js" --include="*.ts" ${TARGET_SOURCE}
grep -rn "fields\s*=\s*'__all__'\|fields\s*=\s*\"__all__\"\|res\.json(\|\.toJSON(\|\.lean(" \
--include="*.py" --include="*.js" --include="*.ts" ${TARGET_SOURCE}
grep -rn "password\|password_hash\|secret\|private_key\|api_key\|ssn\|credit_card\|token\|access_token\|refresh_token" \
--include="*.py" --include="*.js" --include="*.ts" --include="*.java" \
${TARGET_SOURCE} | grep -v "test\|spec\|comment\|#\|//"
grep -rn "page_size\|per_page\|limit\s*=\|paginate\|Paginator\|MAX_PAGE_SIZE\|pageSize" \
--include="*.py" --include="*.js" --include="*.ts" --include="*.java" \
--include="*.rb" --include="*.go" ${TARGET_SOURCE}
grep -rn "webhook\|X-Hub-Signature\|verify.*signature\|hmac.*verify\|webhook.*secret\|compute.*sig\|check.*sig" \
--include="*.py" --include="*.js" --include="*.ts" --include="*.java" \
--include="*.rb" --include="*.go" ${TARGET_SOURCE}
with transaction.atomic(): obj = Model.objects.select_for_update().get(id=id); obj.balance -= amount; obj.save()UPDATE accounts SET balance = balance - $1 WHERE id = $2 AND balance >= $1 (atomic SQL)obj = get(id=id); if obj.balance >= amount: obj.balance -= amount; obj.save()quantity > 0 check?quantity = -1?X-Forwarded-Host or similar used in response but not in cache keyHost header in link generationCache-Control: publicdepth_limit / complexity middleware| Pattern | Verdict |
|---|---|
| Balance deduct outside transaction | HIGH race condition |
select_for_update() in transaction.atomic() | FALSE POSITIVE |
UPDATE SET balance = balance - $1 WHERE balance >= $1 | FALSE POSITIVE (atomic) |
quantity from request without >0 check | HIGH negative quantity |
| Workflow step N doesn't check step N-1 state | HIGH bypass |
| Price from request body without DB re-fetch | HIGH price manipulation |
| Coupon not marked used after redemption | HIGH coupon abuse |
Cache key includes X-Forwarded-Host | HIGH cache poisoning |
Cache-Control: public on user-specific endpoint | HIGH cache deception |
| Auth token cached TTL >1hr with revocation possible | MEDIUM stale auth |
| Redis SETNX TTL shorter than operation | MEDIUM race window |
| Payment endpoint without idempotency key | MEDIUM duplicate charge |
| GraphQL without depth/complexity limiting | HIGH DoS |
fields='__all__' returning password_hash | CRITICAL data exposure |
| Pagination without max page_size cap | MEDIUM |
| Login endpoint without rate limit or lockout | HIGH |
| OTP endpoint without rate limit | CRITICAL |
| Webhook URL not validated against internal IPs | HIGH SSRF |
| Webhook signature missing entirely | HIGH |
hmac.compare_digest(sig1, sig2) | FALSE POSITIVE (constant-time) |
Use LSP diagnostics to confirm logic and concurrency issues:
mcp__ide__getDiagnostics on files with race condition candidates — check for type errors in atomic operations or lock usageThe grep patterns above catch known vulnerability shapes. After completing the pattern scan, perform semantic analysis on the code you've read:
For each handler/endpoint: Read the full function. Ask: "What security assumption does this code make? Can that assumption be violated?"
For custom abstractions: If the codebase has custom transaction wrappers, state machines, or workflow engines — read their implementations. Are they correct? Do they handle edge cases (null, empty, unicode, concurrent calls)?
Cross-module flows: If a variable passes through 3+ functions before reaching a sink, follow it through every hop. One missed encoding step in the middle = vulnerability.
Logic-specific deep analysis: