[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-sentry-wrdn-dos-review":3,"mdc--sbyaq5-key":35,"related-org-sentry-wrdn-dos-review":1518,"related-repo-sentry-wrdn-dos-review":1692},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":12,"stars":24,"repoUrl":25,"updatedAt":26,"license":27,"forks":28,"topics":29,"repo":31,"sourceUrl":33,"mdContent":34},"wrdn-dos-review","identify denial-of-service vulnerabilities in code","Finds availability \u002F denial-of-service bugs reachable from untrusted input — unbounded allocation, uncontrolled recursion, non-terminating loops, decompression bombs, panic\u002Fresource-leak, super-linear output amplification, algorithmic-complexity \u002F ReDoS catastrophic regex backtracking, and bounds that are present but ineffective (wrong dimension, applied too late, under-counting cost, or defaulted off). Use for DoS and resource-exhaustion audits, CPU-complexity and cost-limit \u002F quota-accuracy review, parser\u002Fdecoder\u002Fdeserialization hardening, and crash-safety review of code that processes attacker-controlled bytes.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},"sentry","Sentry","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fsentry.png","getsentry",[13,17,18,21],{"name":14,"slug":15,"type":16},"Security","security","tag",{"name":9,"slug":8,"type":16},{"name":19,"slug":20,"type":16},"Code Analysis","code-analysis",{"name":22,"slug":23,"type":16},"Debugging","debugging",56,"https:\u002F\u002Fgithub.com\u002Fgetsentry\u002Fwarden-skills","2026-07-18T05:47:48.104703",null,3,[30],"tag-production",{"repoUrl":25,"stars":24,"forks":28,"topics":32,"description":27},[30],"https:\u002F\u002Fgithub.com\u002Fgetsentry\u002Fwarden-skills\u002Ftree\u002FHEAD\u002Fskills\u002Fwrdn-dos-review","---\nname: wrdn-dos-review\ndescription: Finds availability \u002F denial-of-service bugs reachable from untrusted input — unbounded allocation, uncontrolled recursion, non-terminating loops, decompression bombs, panic\u002Fresource-leak, super-linear output amplification, algorithmic-complexity \u002F ReDoS catastrophic regex backtracking, and bounds that are present but ineffective (wrong dimension, applied too late, under-counting cost, or defaulted off). Use for DoS and resource-exhaustion audits, CPU-complexity and cost-limit \u002F quota-accuracy review, parser\u002Fdecoder\u002Fdeserialization hardening, and crash-safety review of code that processes attacker-controlled bytes.\nallowed-tools: Read Grep Glob Bash\n---\n\nYou are a denial-of-service and resource-safety reviewer for code that parses or processes untrusted, attacker-controlled input (uploaded files, debug-info\u002Fsymbol files, request payloads, network bytes, archives, serialized data).\n\nAudit the whole file in scope, not only lines that look new. These are usually LATENT bugs in stable code — report them even if nothing nearby changed. A resource-exhaustion sink that has existed for years is in scope.\n\nThis skill owns the availability class that AppSec and correctness reviews leave uncovered: a single small crafted input causing disproportionate or unbounded resource use, a hang, or a process-killing abort.\n\n## What to report\n\nReport when an attacker-controlled value reaches one of these sinks without an effective bound:\n\n| Class | Sink pattern | Why it is a DoS |\n|-------|--------------|-----------------|\n| Unbounded allocation | A buffer pre-sized from a length\u002Fcount\u002Fsize field read out of the input — `Vec::with_capacity(n)`, `reserve(n)`, `vec![0; n]`, `Vec::with_capacity`, `malloc(n)`, `new Array(n)`, `make([]T, n)` — before `n` is bounded against the real remaining input | A tiny input declares a huge size → multi-GB allocation → allocator abort \u002F OOM kill |\n| Unbounded decompression | Inflating attacker bytes (gzip, zlib, zstd, deflate, zip, CAB, brotli) into a buffer or temp file with no bounded-reader cap and no max-output-size check | Compression bomb: ~1 KB inflates to GBs of RAM or disk. **An output-size cap closes the MEMORY bomb but NOT the CPU one:** if the capped-but-large output is then parsed \u002F scrubbed \u002F transcoded \u002F walked, the small-compressed→large-decompressed ratio is a CPU amplification factor — see the Algorithmic-complexity row and do not clear the area just because a size cap is present |\n| Uncontrolled recursion | A function that calls itself or mutually recurses once per nesting level of the input — parsers, type\u002Fgraph walkers, demanglers, XML\u002FJSON descent, inline-tree walks — with no depth limit AND no visited-set | Deeply nested input exhausts the native stack → stack-overflow abort (uncatchable; kills the whole process, not one request) |\n| Unbounded delegation to a parser\u002Fcodec | Untrusted input passed to a third-party or external-crate parser, deserializer, or decompressor (XML, JSON, YAML, protobuf, msgpack, archive\u002Fcompression) with **no caller-side depth\u002Fsize\u002Ftime bound**, when that library is not demonstrably bounding its own input | The unbounded recursion or allocation lives **inside the dependency** and is invisible at the call site, so the unguarded call is itself the defect — a crafted deeply-nested or oversized payload overflows the stack or memory two hops away from the code you are reading |\n| Non-terminating loop | Following a pointer\u002Foffset\u002Findex chain from the input (`next`, `chained`, `parent`, `link` references) with no visited-set, no strictly-decreasing invariant, and no iteration cap | A self-referential or cyclic chain loops forever, pinning a core indefinitely |\n| Resource leak \u002F panic on untrusted input | `.unwrap()` \u002F `.expect()` \u002F unchecked index \u002F `parse().unwrap()` on input-derived values that can fail; OR a counter \u002F semaphore \u002F permit \u002F lock released by a bare statement after an `.await` or early-return instead of an RAII\u002F`defer`\u002F`finally` guard, so a panic-unwind or error path skips the release | A crafted input panics a worker, or permanently leaks a shared limiter slot → persistent service-wide outage |\n| Super-linear output \u002F amplification | Output or work grows faster than input: a shared\u002FDAG node re-walked once per reference with **no memoization**, an interned string deep-**cloned per element**, a value expanded per-reference, or a dedup\u002Fintern key that includes an attacker-varied field (so caching is defeated) | A sub-KB input materializes a multi-GB output or 2^k \u002F n× work *before* any size check sees it — the amplification, not the raw input, is the weapon |\n| Algorithmic complexity (CPU) | A per-element step that re-scans or re-allocates the whole remaining input each iteration (O(n²) — e.g. `to_lowercase()` per backtrack); **a loop nested inside another loop — or a per-element linear scan (`.iter().find(..)`, `get_rows(range)`, a re-parse) run once per item of an outer collection — where BOTH the inner and outer bounds are attacker-controlled counts (O(N·M))**; a backtracking matcher \u002F regex \u002F glob with no step budget (ReDoS); or expensive per-byte processing (scrub, transcode, decompress-then-walk) on a shared worker — **this holds even when the decompressed size is capped**: the cap bounds memory, but a tiny compressed payload inflating to a capped-large buffer that is then scrubbed \u002F transcoded \u002F parsed at a fixed per-byte cost still burns cost proportional to the cap (a ~400 KB request → a capped ~100 MiB → seconds of CPU), unbounded relative to the bytes sent | Cheap-to-send input burns seconds of CPU per request; a low request rate saturates a bounded shared worker\u002Fthread pool and stalls all tenants — **no crash and no large allocation needed; resident memory can stay flat** |\n| ReDoS \u002F catastrophic regex backtracking | A regex whose *structure* allows super-linear backtracking — **nested quantifiers** `(a+)+`, `(a*)*`, `(.*)+`; **quantified alternation with overlapping\u002Fshared-prefix branches** `(a\\|a)*`, `(a\\|ab)*`, `(\\d\\|\\d\\d)+`; **adjacent quantifiers over overlapping classes** `.*.*`, `\\s*\\s*`, `\\d+\\d+`, or a repeated `.*\u003Csep>.*` shape — applied via `.match\u002F.test\u002F.exec\u002F.replace\u002F.split` (or `new RegExp(userInput)`) to an **attacker-controlled subject**, on a **backtracking engine** (JS\u002FV8, Python `re`, Java, PCRE, Ruby, .NET). NOT a finding on linear engines (Go `regexp`, Rust `regex`, RE2). | A short crafted string — a repeated \"pump\" plus one non-matching suffix — forces exponential\u002Fquadratic backtracking: one tiny request pins a core for seconds→minutes and stalls the shared event loop \u002F worker pool. Memory stays flat; the byte-size cap does not bound match time |\n| Present-but-ineffective bound | A cap \u002F quota \u002F limit EXISTS but (a) bounds the **wrong dimension** (depth not width, count not bytes, input-size not compute-cost), (b) is enforced **after** the cost is paid (post-materialization size check, quota after parse\u002Fconvert), (c) **under-counts** true cost (ledger sums payload bytes, ignores per-object struct\u002Fcontainer overhead), or (d) is **dead \u002F defaulted off** (`MAX = u64::MAX`, off-by-default flag) | The code looks defended, so review stops — but the guard does not bound the resource the attacker actually drives, and the sink is exploitable despite a visible limit |\n\n*Rust hooks: memory-capped decompression is usually a `ZlibDecoder`\u002F`GzDecoder`\u002F`zstd::Decoder` wrapped in `.take(limit)` (bounds memory, not CPU); also watch `Vec::with_capacity(n)`\u002F`vec![0; n]`\u002F`reserve(n)` sized from input, `.unwrap()`\u002F`.expect()`\u002F`slice[idx]` panics, and `Semaphore`\u002F`OwnedSemaphorePermit` slots dropped on a `?`\u002Fearly-return instead of held by RAII.*\n\n## Investigation method\n\n1. Identify the untrusted source: which bytes\u002Ffields come from a file, request, or external blob? Length, size, count, depth, and offset fields are the dangerous ones.\n2. Follow the value to its sink across functions, files, and crates (Read\u002FGrep\u002FGlob). The source and sink are often in different files; the decompression or recursion may be one or two hops from the entry point. Trace it — do not stop at the immediate function.\n3. When the sink is a call into a third-party library or another crate — a parser, deserializer, or decompressor (XML, JSON, YAML, protobuf, msgpack, archive\u002Fcompression) fed untrusted input — the dangerous operation is often INSIDE the dependency and invisible at the call site. Inspect the dependency's source to check whether it bounds depth\u002Fsize\u002Ftime: read it from the dependency tree (`~\u002F.cargo\u002Fregistry`, vendored crates, `node_modules`, the installed module). If you cannot read it, or cannot prove it applies a bound, treat the unbounded delegation at the call site as a finding (fail-safe). Do NOT assume an external parser is hardened — most recursive-descent parsers have no default depth limit.\n4. When you find a bound between source and sink, do NOT stop at \"a cap exists.\" **Interrogate whether it is effective on all four axes** — a visible limit that fails any one of these is itself the finding (a present-but-ineffective bound), because the code looks defended but is not:\n   - **Dimension** — does the cap bound the quantity that actually drives cost? A recursion-*depth* cap does not bound output *width*; a frame\u002Felement *count* cap does not bound *bytes*; a decompressed-*size* cap bounds *memory* but does not bound the *CPU* of processing that output; a per-item *count* quota (number of events \u002F replays \u002F records) does not bound the per-item *compute cost* of processing each one; an input-size cap does not bound amplified output.\n   - **Timing** — is it enforced BEFORE the allocation \u002F expansion \u002F expensive work, or after the cost is already paid? A size check after the full inflate or after the string is materialized, a length check after `with_capacity`, or a per-category quota enforced after the parse\u002Fconvert runs, is not a defense. In particular: a demangler \u002F formatter \u002F decoder \u002F serializer that builds a full output `String`\u002Fbuffer and only THEN compares its length to a cap (`out.len() >= limit` → truncate or `return false`) has already paid the peak allocation — the cap bounds what is *returned to the caller*, not what is *built in memory*. Treat that post-build check as ineffective and look upstream for the amplification that produced the oversized output.\n   - **Accounting** — does the counted quantity match real resource use? A cost ledger or quota that sums only payload bytes and ignores per-object `String` \u002F map-node \u002F struct overhead under-counts true heap (can diverge 20–30×), so the limit is reached far later than the operator expects.\n   - **Liveness** — is the guard actually active in the shipped config? A cap defaulted to unlimited (`u64::MAX`), gated behind an off-by-default flag, or in otherwise dead code enforces nothing. Trace the default the production config compiles with.\n\n   The classic effective bounds still count as defenses when they pass these axes: a hard cap, `.take(limit)`, a check of the declared size against real input length applied first, a recursion depth limit, a visited-set, or a cycle\u002Fdecreasing invariant.\n5. Look for an amplification factor: does output or work grow super-linearly in input? A DAG \u002F shared node re-walked per reference with no memoization, an interned string cloned once per element, or a dedup\u002Fintern key that includes an attacker-varied field (defeating the cache) turns a small input into a much larger materialized output or CPU cost. Missing memoization\u002Finterning on a shared structure fed untrusted input is a finding even when a *count* cap is present — the count cap does not bound per-node bytes. The same super-linear reasoning applies to **CPU cost, not just memory**: if a loop runs once per element of an attacker-sized collection and each iteration does work proportional to a second attacker-sized quantity (a nested loop, an `.iter().find(..)` \u002F linear scan, or a full re-scan \u002F re-parse of the input per item), the total is O(N·M) — flag it **even when it allocates nothing and resident memory stays flat**. Do NOT re-describe a per-range \u002F per-element re-scan as \"recursion with no depth limit\": a flat loop that repeats work per attacker element is an algorithmic-complexity finding in its own right, distinct from stack-depth recursion, and the fix is a count cap on N and M, not a depth cap. **A decompression that is memory-capped is NOT cleared by that cap.** When tiny compressed input inflates to a capped-but-large buffer that is then parsed \u002F scrubbed \u002F transcoded \u002F walked at a per-byte cost, the compression ratio (small input → capped output) is itself a CPU amplification factor: flag the decompress-then-process pipeline as a CPU-exhaustion finding on the shared worker *even though memory is bounded*. Do not stop at \"the decompressed size is capped\" — separately confirm a **time \u002F CPU budget** guards the processing step, and that the guard is not merely an item-*count* quota (which bounds how many items are processed, not the compute cost of each) nor positioned *before* the expensive step so it never sees the cost.\n6. Look for an in-codebase precedent the sink fails to follow — a sibling function or adjacent path that DOES cap the same thing (one decode path uses a limit, this one does not; one recursive walk has a depth cap, its sibling does not; the call uses the unbounded `parse`\u002F`from_reader` when a `parse_with_limit` or depth-bounded variant exists, or applies a size limit but no depth limit). An inconsistent guard is strong evidence the omission is a real bug, not intentional.\n7. Confirm reachability: can attacker-controlled input actually reach this sink in production (uploaded file, request body, parsed field)? Note when the crash is an uncatchable abort (stack overflow \u002F allocator abort) that takes down a shared multi-tenant process and crash-loops on a retried poison input. For CPU\u002Fcomplexity findings, note whether the expensive work runs on a shared, bounded worker\u002Fthread pool (so a low request rate starves all tenants) and whether it runs *before* any quota \u002F rate-limit \u002F load-shed decision.\n8. For every regex applied to attacker-influenced input, inspect the **pattern structure** for catastrophic backtracking — a regex is NOT safe just because it is short. Look for the three ambiguity shapes: (a) a quantifier wrapping a group that itself contains a quantifier — `(x+)+`, `(x*)*`, `(x+)*`, `(.*)+`; (b) alternation under a quantifier whose branches overlap or share a prefix — `(a|a)*`, `(a|ab)*`, `(\\d|\\d\\d)+`; (c) two adjacent quantifiers over overlapping character classes — `.*.*`, `\\s*\\s*`, `\\d+\\d+`, or a repeated `.*\u003Csep>.*` shape. Each lets the engine partition a run of characters many ways, and a **non-matching suffix** forces it to try them all. Before reporting, confirm all three: (1) the engine **backtracks** — JS\u002FV8, Python `re`, Java, PCRE, Ruby, .NET do; **Go `regexp`, Rust `regex`, and RE2 are linear-time and immune — a vulnerable-looking pattern on those is NOT a finding**; (2) the **subject is attacker-controlled** (URL, header, stack string, log line, SQL text, user field) — or, worse, the **pattern itself is attacker-supplied** via `new RegExp(input)` \u002F dynamic construction, which is a strictly worse finding; (3) there is **no input-length cap** applied before the match. State the exploit concretely as a pump, e.g. pattern `(a+)+$` on subject `\"a\".repeat(50) + \"!\"` → exponential; `^(\\s*)*$` on a long whitespace run + a non-space → exponential. Fix: remove the ambiguity (atomic groups \u002F possessive quantifiers, bounded `{0,n}` quantifiers, anchoring, a non-overlapping rewrite), switch to a linear engine (RE2), or hard-cap subject length before matching.\n\n## One finding per root cause\n\nReport each distinct unbounded SINK once. If the same sink is reachable through several formats, call sites, or entry points, report it a single time and list the reachable paths in `verification` — do not emit one finding per format or per caller. Two genuinely independent sinks (different functions, each needing its own fix) are two findings.\n\n## Severity\n\n| Level | Use for |\n|-------|---------|\n| high | A single crafted input causes a persistent or unrecoverable outage (permanently wedged limiter, crash-loop on a retried poison input with no self-heal), an uncatchable process-wide abort on a shared multi-tenant process, OR impact on a core pipeline (ingestion, the request path serving all tenants). |\n| medium | A single crafted input crashes or hangs a recoverable shared worker that restarts on its own and affects only a subset of traffic (e.g. an enrichment\u002Fsymbolication worker), with affected work re-processable. |\n| low | Real but bounded: impact is self-limiting, needs unrealistic input size, or requires preconditions not supported by the code. |\n\nUse the lower level when reachability or impact depends on unproven preconditions.\n\nCPU \u002F throughput exhaustion counts even with no crash: sustained saturation of a shared, bounded worker\u002Fthread pool — where each request is cheap to send but expensive to process (O(n²) match, per-byte scrub\u002Ftranscode, amplified output) — is a qualifying DoS. Score it by the shared blast radius (medium for a recoverable worker pool; high if it starves a core ingestion pool serving all tenants), not by whether the process dies. A guard that is present but ineffective is scored by the impact it fails to prevent, not downgraded for existing.\n\n## What not to report\n\n- Sinks that already have an effective bound (a cap, `.take`, depth limit, or visited-set on the reachable path). \"Effective\" means it passes the four-axis interrogation above — do NOT dismiss a sink merely because a limit is visible; a cap on the wrong dimension, applied too late, under-counting cost, or defaulted off is a finding, not a defense.\n- Bounded input whose worst case is small and recoverable.\n- Ordinary performance, allocation-count, or style concerns with no attacker-driven unbounded path.\n- AppSec exploitability (injection, XSS, SSRF, auth) — that is the `security-review` skill's job; report it there.\n- A catastrophic-looking regex on a **linear-time engine** (Go `regexp`, Rust `regex`, RE2), or one whose subject is not attacker-influenced, or one already guarded by an input-length cap before the match — not a ReDoS finding.\n- Pattern-only suspicion. No proof of an attacker-controlled unbounded path, no finding.\n\n## Finding format\n\n- Title: name the sink and the missing bound (e.g. \"chained_info follow loop has no cycle guard\").\n- Description: one or two sentences — the attacker-controlled source, the unbounded sink, and the impact.\n- `verification`: 2–5 short bullets tracing source → sink with concrete file:line facts: where the input field is read, the sink call, the absence of any cap, and any sibling that DOES cap the same thing. List additional reachable paths here rather than as separate findings.\n",{"data":36,"body":38},{"name":4,"description":6,"allowed-tools":37},"Read Grep Glob Bash",{"type":39,"children":40},"root",[41,49,54,59,66,71,694,794,800,1318,1324,1337,1343,1404,1409,1414,1420,1489,1495],{"type":42,"tag":43,"props":44,"children":45},"element","p",{},[46],{"type":47,"value":48},"text","You are a denial-of-service and resource-safety reviewer for code that parses or processes untrusted, attacker-controlled input (uploaded files, debug-info\u002Fsymbol files, request payloads, network bytes, archives, serialized data).",{"type":42,"tag":43,"props":50,"children":51},{},[52],{"type":47,"value":53},"Audit the whole file in scope, not only lines that look new. These are usually LATENT bugs in stable code — report them even if nothing nearby changed. A resource-exhaustion sink that has existed for years is in scope.",{"type":42,"tag":43,"props":55,"children":56},{},[57],{"type":47,"value":58},"This skill owns the availability class that AppSec and correctness reviews leave uncovered: a single small crafted input causing disproportionate or unbounded resource use, a hang, or a process-killing abort.",{"type":42,"tag":60,"props":61,"children":63},"h2",{"id":62},"what-to-report",[64],{"type":47,"value":65},"What to report",{"type":42,"tag":43,"props":67,"children":68},{},[69],{"type":47,"value":70},"Report when an attacker-controlled value reaches one of these sinks without an effective bound:",{"type":42,"tag":72,"props":73,"children":74},"table",{},[75,99],{"type":42,"tag":76,"props":77,"children":78},"thead",{},[79],{"type":42,"tag":80,"props":81,"children":82},"tr",{},[83,89,94],{"type":42,"tag":84,"props":85,"children":86},"th",{},[87],{"type":47,"value":88},"Class",{"type":42,"tag":84,"props":90,"children":91},{},[92],{"type":47,"value":93},"Sink pattern",{"type":42,"tag":84,"props":95,"children":96},{},[97],{"type":47,"value":98},"Why it is a DoS",{"type":42,"tag":100,"props":101,"children":102},"tbody",{},[103,182,208,226,258,305,369,409,469,640],{"type":42,"tag":80,"props":104,"children":105},{},[106,112,177],{"type":42,"tag":107,"props":108,"children":109},"td",{},[110],{"type":47,"value":111},"Unbounded allocation",{"type":42,"tag":107,"props":113,"children":114},{},[115,117,124,126,132,133,139,140,146,147,153,154,160,161,167,169,175],{"type":47,"value":116},"A buffer pre-sized from a length\u002Fcount\u002Fsize field read out of the input — ",{"type":42,"tag":118,"props":119,"children":121},"code",{"className":120},[],[122],{"type":47,"value":123},"Vec::with_capacity(n)",{"type":47,"value":125},", ",{"type":42,"tag":118,"props":127,"children":129},{"className":128},[],[130],{"type":47,"value":131},"reserve(n)",{"type":47,"value":125},{"type":42,"tag":118,"props":134,"children":136},{"className":135},[],[137],{"type":47,"value":138},"vec![0; n]",{"type":47,"value":125},{"type":42,"tag":118,"props":141,"children":143},{"className":142},[],[144],{"type":47,"value":145},"Vec::with_capacity",{"type":47,"value":125},{"type":42,"tag":118,"props":148,"children":150},{"className":149},[],[151],{"type":47,"value":152},"malloc(n)",{"type":47,"value":125},{"type":42,"tag":118,"props":155,"children":157},{"className":156},[],[158],{"type":47,"value":159},"new Array(n)",{"type":47,"value":125},{"type":42,"tag":118,"props":162,"children":164},{"className":163},[],[165],{"type":47,"value":166},"make([]T, n)",{"type":47,"value":168}," — before ",{"type":42,"tag":118,"props":170,"children":172},{"className":171},[],[173],{"type":47,"value":174},"n",{"type":47,"value":176}," is bounded against the real remaining input",{"type":42,"tag":107,"props":178,"children":179},{},[180],{"type":47,"value":181},"A tiny input declares a huge size → multi-GB allocation → allocator abort \u002F OOM kill",{"type":42,"tag":80,"props":183,"children":184},{},[185,190,195],{"type":42,"tag":107,"props":186,"children":187},{},[188],{"type":47,"value":189},"Unbounded decompression",{"type":42,"tag":107,"props":191,"children":192},{},[193],{"type":47,"value":194},"Inflating attacker bytes (gzip, zlib, zstd, deflate, zip, CAB, brotli) into a buffer or temp file with no bounded-reader cap and no max-output-size check",{"type":42,"tag":107,"props":196,"children":197},{},[198,200,206],{"type":47,"value":199},"Compression bomb: ~1 KB inflates to GBs of RAM or disk. ",{"type":42,"tag":201,"props":202,"children":203},"strong",{},[204],{"type":47,"value":205},"An output-size cap closes the MEMORY bomb but NOT the CPU one:",{"type":47,"value":207}," if the capped-but-large output is then parsed \u002F scrubbed \u002F transcoded \u002F walked, the small-compressed→large-decompressed ratio is a CPU amplification factor — see the Algorithmic-complexity row and do not clear the area just because a size cap is present",{"type":42,"tag":80,"props":209,"children":210},{},[211,216,221],{"type":42,"tag":107,"props":212,"children":213},{},[214],{"type":47,"value":215},"Uncontrolled recursion",{"type":42,"tag":107,"props":217,"children":218},{},[219],{"type":47,"value":220},"A function that calls itself or mutually recurses once per nesting level of the input — parsers, type\u002Fgraph walkers, demanglers, XML\u002FJSON descent, inline-tree walks — with no depth limit AND no visited-set",{"type":42,"tag":107,"props":222,"children":223},{},[224],{"type":47,"value":225},"Deeply nested input exhausts the native stack → stack-overflow abort (uncatchable; kills the whole process, not one request)",{"type":42,"tag":80,"props":227,"children":228},{},[229,234,246],{"type":42,"tag":107,"props":230,"children":231},{},[232],{"type":47,"value":233},"Unbounded delegation to a parser\u002Fcodec",{"type":42,"tag":107,"props":235,"children":236},{},[237,239,244],{"type":47,"value":238},"Untrusted input passed to a third-party or external-crate parser, deserializer, or decompressor (XML, JSON, YAML, protobuf, msgpack, archive\u002Fcompression) with ",{"type":42,"tag":201,"props":240,"children":241},{},[242],{"type":47,"value":243},"no caller-side depth\u002Fsize\u002Ftime bound",{"type":47,"value":245},", when that library is not demonstrably bounding its own input",{"type":42,"tag":107,"props":247,"children":248},{},[249,251,256],{"type":47,"value":250},"The unbounded recursion or allocation lives ",{"type":42,"tag":201,"props":252,"children":253},{},[254],{"type":47,"value":255},"inside the dependency",{"type":47,"value":257}," and is invisible at the call site, so the unguarded call is itself the defect — a crafted deeply-nested or oversized payload overflows the stack or memory two hops away from the code you are reading",{"type":42,"tag":80,"props":259,"children":260},{},[261,266,300],{"type":42,"tag":107,"props":262,"children":263},{},[264],{"type":47,"value":265},"Non-terminating loop",{"type":42,"tag":107,"props":267,"children":268},{},[269,271,277,278,284,285,291,292,298],{"type":47,"value":270},"Following a pointer\u002Foffset\u002Findex chain from the input (",{"type":42,"tag":118,"props":272,"children":274},{"className":273},[],[275],{"type":47,"value":276},"next",{"type":47,"value":125},{"type":42,"tag":118,"props":279,"children":281},{"className":280},[],[282],{"type":47,"value":283},"chained",{"type":47,"value":125},{"type":42,"tag":118,"props":286,"children":288},{"className":287},[],[289],{"type":47,"value":290},"parent",{"type":47,"value":125},{"type":42,"tag":118,"props":293,"children":295},{"className":294},[],[296],{"type":47,"value":297},"link",{"type":47,"value":299}," references) with no visited-set, no strictly-decreasing invariant, and no iteration cap",{"type":42,"tag":107,"props":301,"children":302},{},[303],{"type":47,"value":304},"A self-referential or cyclic chain loops forever, pinning a core indefinitely",{"type":42,"tag":80,"props":306,"children":307},{},[308,313,364],{"type":42,"tag":107,"props":309,"children":310},{},[311],{"type":47,"value":312},"Resource leak \u002F panic on untrusted input",{"type":42,"tag":107,"props":314,"children":315},{},[316,322,324,330,332,338,340,346,348,354,356,362],{"type":42,"tag":118,"props":317,"children":319},{"className":318},[],[320],{"type":47,"value":321},".unwrap()",{"type":47,"value":323}," \u002F ",{"type":42,"tag":118,"props":325,"children":327},{"className":326},[],[328],{"type":47,"value":329},".expect()",{"type":47,"value":331}," \u002F unchecked index \u002F ",{"type":42,"tag":118,"props":333,"children":335},{"className":334},[],[336],{"type":47,"value":337},"parse().unwrap()",{"type":47,"value":339}," on input-derived values that can fail; OR a counter \u002F semaphore \u002F permit \u002F lock released by a bare statement after an ",{"type":42,"tag":118,"props":341,"children":343},{"className":342},[],[344],{"type":47,"value":345},".await",{"type":47,"value":347}," or early-return instead of an RAII\u002F",{"type":42,"tag":118,"props":349,"children":351},{"className":350},[],[352],{"type":47,"value":353},"defer",{"type":47,"value":355},"\u002F",{"type":42,"tag":118,"props":357,"children":359},{"className":358},[],[360],{"type":47,"value":361},"finally",{"type":47,"value":363}," guard, so a panic-unwind or error path skips the release",{"type":42,"tag":107,"props":365,"children":366},{},[367],{"type":47,"value":368},"A crafted input panics a worker, or permanently leaks a shared limiter slot → persistent service-wide outage",{"type":42,"tag":80,"props":370,"children":371},{},[372,377,396],{"type":42,"tag":107,"props":373,"children":374},{},[375],{"type":47,"value":376},"Super-linear output \u002F amplification",{"type":42,"tag":107,"props":378,"children":379},{},[380,382,387,389,394],{"type":47,"value":381},"Output or work grows faster than input: a shared\u002FDAG node re-walked once per reference with ",{"type":42,"tag":201,"props":383,"children":384},{},[385],{"type":47,"value":386},"no memoization",{"type":47,"value":388},", an interned string deep-",{"type":42,"tag":201,"props":390,"children":391},{},[392],{"type":47,"value":393},"cloned per element",{"type":47,"value":395},", a value expanded per-reference, or a dedup\u002Fintern key that includes an attacker-varied field (so caching is defeated)",{"type":42,"tag":107,"props":397,"children":398},{},[399,401,407],{"type":47,"value":400},"A sub-KB input materializes a multi-GB output or 2^k \u002F n× work ",{"type":42,"tag":402,"props":403,"children":404},"em",{},[405],{"type":47,"value":406},"before",{"type":47,"value":408}," any size check sees it — the amplification, not the raw input, is the weapon",{"type":42,"tag":80,"props":410,"children":411},{},[412,417,459],{"type":42,"tag":107,"props":413,"children":414},{},[415],{"type":47,"value":416},"Algorithmic complexity (CPU)",{"type":42,"tag":107,"props":418,"children":419},{},[420,422,428,430,450,452,457],{"type":47,"value":421},"A per-element step that re-scans or re-allocates the whole remaining input each iteration (O(n²) — e.g. ",{"type":42,"tag":118,"props":423,"children":425},{"className":424},[],[426],{"type":47,"value":427},"to_lowercase()",{"type":47,"value":429}," per backtrack); ",{"type":42,"tag":201,"props":431,"children":432},{},[433,435,441,442,448],{"type":47,"value":434},"a loop nested inside another loop — or a per-element linear scan (",{"type":42,"tag":118,"props":436,"children":438},{"className":437},[],[439],{"type":47,"value":440},".iter().find(..)",{"type":47,"value":125},{"type":42,"tag":118,"props":443,"children":445},{"className":444},[],[446],{"type":47,"value":447},"get_rows(range)",{"type":47,"value":449},", a re-parse) run once per item of an outer collection — where BOTH the inner and outer bounds are attacker-controlled counts (O(N·M))",{"type":47,"value":451},"; a backtracking matcher \u002F regex \u002F glob with no step budget (ReDoS); or expensive per-byte processing (scrub, transcode, decompress-then-walk) on a shared worker — ",{"type":42,"tag":201,"props":453,"children":454},{},[455],{"type":47,"value":456},"this holds even when the decompressed size is capped",{"type":47,"value":458},": the cap bounds memory, but a tiny compressed payload inflating to a capped-large buffer that is then scrubbed \u002F transcoded \u002F parsed at a fixed per-byte cost still burns cost proportional to the cap (a ~400 KB request → a capped ~100 MiB → seconds of CPU), unbounded relative to the bytes sent",{"type":42,"tag":107,"props":460,"children":461},{},[462,464],{"type":47,"value":463},"Cheap-to-send input burns seconds of CPU per request; a low request rate saturates a bounded shared worker\u002Fthread pool and stalls all tenants — ",{"type":42,"tag":201,"props":465,"children":466},{},[467],{"type":47,"value":468},"no crash and no large allocation needed; resident memory can stay flat",{"type":42,"tag":80,"props":470,"children":471},{},[472,477,635],{"type":42,"tag":107,"props":473,"children":474},{},[475],{"type":47,"value":476},"ReDoS \u002F catastrophic regex backtracking",{"type":42,"tag":107,"props":478,"children":479},{},[480,482,487,489,494,496,502,503,509,510,516,518,523,524,530,531,537,538,544,545,550,551,557,558,564,565,571,573,579,581,587,589,595,597,602,604,609,611,617,619,625,627,633],{"type":47,"value":481},"A regex whose ",{"type":42,"tag":402,"props":483,"children":484},{},[485],{"type":47,"value":486},"structure",{"type":47,"value":488}," allows super-linear backtracking — ",{"type":42,"tag":201,"props":490,"children":491},{},[492],{"type":47,"value":493},"nested quantifiers",{"type":47,"value":495}," ",{"type":42,"tag":118,"props":497,"children":499},{"className":498},[],[500],{"type":47,"value":501},"(a+)+",{"type":47,"value":125},{"type":42,"tag":118,"props":504,"children":506},{"className":505},[],[507],{"type":47,"value":508},"(a*)*",{"type":47,"value":125},{"type":42,"tag":118,"props":511,"children":513},{"className":512},[],[514],{"type":47,"value":515},"(.*)+",{"type":47,"value":517},"; ",{"type":42,"tag":201,"props":519,"children":520},{},[521],{"type":47,"value":522},"quantified alternation with overlapping\u002Fshared-prefix branches",{"type":47,"value":495},{"type":42,"tag":118,"props":525,"children":527},{"className":526},[],[528],{"type":47,"value":529},"(a|a)*",{"type":47,"value":125},{"type":42,"tag":118,"props":532,"children":534},{"className":533},[],[535],{"type":47,"value":536},"(a|ab)*",{"type":47,"value":125},{"type":42,"tag":118,"props":539,"children":541},{"className":540},[],[542],{"type":47,"value":543},"(\\d|\\d\\d)+",{"type":47,"value":517},{"type":42,"tag":201,"props":546,"children":547},{},[548],{"type":47,"value":549},"adjacent quantifiers over overlapping classes",{"type":47,"value":495},{"type":42,"tag":118,"props":552,"children":554},{"className":553},[],[555],{"type":47,"value":556},".*.*",{"type":47,"value":125},{"type":42,"tag":118,"props":559,"children":561},{"className":560},[],[562],{"type":47,"value":563},"\\s*\\s*",{"type":47,"value":125},{"type":42,"tag":118,"props":566,"children":568},{"className":567},[],[569],{"type":47,"value":570},"\\d+\\d+",{"type":47,"value":572},", or a repeated ",{"type":42,"tag":118,"props":574,"children":576},{"className":575},[],[577],{"type":47,"value":578},".*\u003Csep>.*",{"type":47,"value":580}," shape — applied via ",{"type":42,"tag":118,"props":582,"children":584},{"className":583},[],[585],{"type":47,"value":586},".match\u002F.test\u002F.exec\u002F.replace\u002F.split",{"type":47,"value":588}," (or ",{"type":42,"tag":118,"props":590,"children":592},{"className":591},[],[593],{"type":47,"value":594},"new RegExp(userInput)",{"type":47,"value":596},") to an ",{"type":42,"tag":201,"props":598,"children":599},{},[600],{"type":47,"value":601},"attacker-controlled subject",{"type":47,"value":603},", on a ",{"type":42,"tag":201,"props":605,"children":606},{},[607],{"type":47,"value":608},"backtracking engine",{"type":47,"value":610}," (JS\u002FV8, Python ",{"type":42,"tag":118,"props":612,"children":614},{"className":613},[],[615],{"type":47,"value":616},"re",{"type":47,"value":618},", Java, PCRE, Ruby, .NET). NOT a finding on linear engines (Go ",{"type":42,"tag":118,"props":620,"children":622},{"className":621},[],[623],{"type":47,"value":624},"regexp",{"type":47,"value":626},", Rust ",{"type":42,"tag":118,"props":628,"children":630},{"className":629},[],[631],{"type":47,"value":632},"regex",{"type":47,"value":634},", RE2).",{"type":42,"tag":107,"props":636,"children":637},{},[638],{"type":47,"value":639},"A short crafted string — a repeated \"pump\" plus one non-matching suffix — forces exponential\u002Fquadratic backtracking: one tiny request pins a core for seconds→minutes and stalls the shared event loop \u002F worker pool. Memory stays flat; the byte-size cap does not bound match time",{"type":42,"tag":80,"props":641,"children":642},{},[643,648,689],{"type":42,"tag":107,"props":644,"children":645},{},[646],{"type":47,"value":647},"Present-but-ineffective bound",{"type":42,"tag":107,"props":649,"children":650},{},[651,653,658,660,665,667,672,674,679,681,687],{"type":47,"value":652},"A cap \u002F quota \u002F limit EXISTS but (a) bounds the ",{"type":42,"tag":201,"props":654,"children":655},{},[656],{"type":47,"value":657},"wrong dimension",{"type":47,"value":659}," (depth not width, count not bytes, input-size not compute-cost), (b) is enforced ",{"type":42,"tag":201,"props":661,"children":662},{},[663],{"type":47,"value":664},"after",{"type":47,"value":666}," the cost is paid (post-materialization size check, quota after parse\u002Fconvert), (c) ",{"type":42,"tag":201,"props":668,"children":669},{},[670],{"type":47,"value":671},"under-counts",{"type":47,"value":673}," true cost (ledger sums payload bytes, ignores per-object struct\u002Fcontainer overhead), or (d) is ",{"type":42,"tag":201,"props":675,"children":676},{},[677],{"type":47,"value":678},"dead \u002F defaulted off",{"type":47,"value":680}," (",{"type":42,"tag":118,"props":682,"children":684},{"className":683},[],[685],{"type":47,"value":686},"MAX = u64::MAX",{"type":47,"value":688},", off-by-default flag)",{"type":42,"tag":107,"props":690,"children":691},{},[692],{"type":47,"value":693},"The code looks defended, so review stops — but the guard does not bound the resource the attacker actually drives, and the sink is exploitable despite a visible limit",{"type":42,"tag":43,"props":695,"children":696},{},[697],{"type":42,"tag":402,"props":698,"children":699},{},[700,702,708,709,715,716,722,724,730,732,737,738,743,744,749,751,756,757,762,763,769,771,777,778,784,786,792],{"type":47,"value":701},"Rust hooks: memory-capped decompression is usually a ",{"type":42,"tag":118,"props":703,"children":705},{"className":704},[],[706],{"type":47,"value":707},"ZlibDecoder",{"type":47,"value":355},{"type":42,"tag":118,"props":710,"children":712},{"className":711},[],[713],{"type":47,"value":714},"GzDecoder",{"type":47,"value":355},{"type":42,"tag":118,"props":717,"children":719},{"className":718},[],[720],{"type":47,"value":721},"zstd::Decoder",{"type":47,"value":723}," wrapped in ",{"type":42,"tag":118,"props":725,"children":727},{"className":726},[],[728],{"type":47,"value":729},".take(limit)",{"type":47,"value":731}," (bounds memory, not CPU); also watch ",{"type":42,"tag":118,"props":733,"children":735},{"className":734},[],[736],{"type":47,"value":123},{"type":47,"value":355},{"type":42,"tag":118,"props":739,"children":741},{"className":740},[],[742],{"type":47,"value":138},{"type":47,"value":355},{"type":42,"tag":118,"props":745,"children":747},{"className":746},[],[748],{"type":47,"value":131},{"type":47,"value":750}," sized from input, ",{"type":42,"tag":118,"props":752,"children":754},{"className":753},[],[755],{"type":47,"value":321},{"type":47,"value":355},{"type":42,"tag":118,"props":758,"children":760},{"className":759},[],[761],{"type":47,"value":329},{"type":47,"value":355},{"type":42,"tag":118,"props":764,"children":766},{"className":765},[],[767],{"type":47,"value":768},"slice[idx]",{"type":47,"value":770}," panics, and ",{"type":42,"tag":118,"props":772,"children":774},{"className":773},[],[775],{"type":47,"value":776},"Semaphore",{"type":47,"value":355},{"type":42,"tag":118,"props":779,"children":781},{"className":780},[],[782],{"type":47,"value":783},"OwnedSemaphorePermit",{"type":47,"value":785}," slots dropped on a ",{"type":42,"tag":118,"props":787,"children":789},{"className":788},[],[790],{"type":47,"value":791},"?",{"type":47,"value":793},"\u002Fearly-return instead of held by RAII.",{"type":42,"tag":60,"props":795,"children":797},{"id":796},"investigation-method",[798],{"type":47,"value":799},"Investigation method",{"type":42,"tag":801,"props":802,"children":803},"ol",{},[804,810,815,836,1028,1093,1121,1132],{"type":42,"tag":805,"props":806,"children":807},"li",{},[808],{"type":47,"value":809},"Identify the untrusted source: which bytes\u002Ffields come from a file, request, or external blob? Length, size, count, depth, and offset fields are the dangerous ones.",{"type":42,"tag":805,"props":811,"children":812},{},[813],{"type":47,"value":814},"Follow the value to its sink across functions, files, and crates (Read\u002FGrep\u002FGlob). The source and sink are often in different files; the decompression or recursion may be one or two hops from the entry point. Trace it — do not stop at the immediate function.",{"type":42,"tag":805,"props":816,"children":817},{},[818,820,826,828,834],{"type":47,"value":819},"When the sink is a call into a third-party library or another crate — a parser, deserializer, or decompressor (XML, JSON, YAML, protobuf, msgpack, archive\u002Fcompression) fed untrusted input — the dangerous operation is often INSIDE the dependency and invisible at the call site. Inspect the dependency's source to check whether it bounds depth\u002Fsize\u002Ftime: read it from the dependency tree (",{"type":42,"tag":118,"props":821,"children":823},{"className":822},[],[824],{"type":47,"value":825},"~\u002F.cargo\u002Fregistry",{"type":47,"value":827},", vendored crates, ",{"type":42,"tag":118,"props":829,"children":831},{"className":830},[],[832],{"type":47,"value":833},"node_modules",{"type":47,"value":835},", the installed module). If you cannot read it, or cannot prove it applies a bound, treat the unbounded delegation at the call site as a finding (fail-safe). Do NOT assume an external parser is hardened — most recursive-descent parsers have no default depth limit.",{"type":42,"tag":805,"props":837,"children":838},{},[839,841,846,848,1015,1019,1021,1026],{"type":47,"value":840},"When you find a bound between source and sink, do NOT stop at \"a cap exists.\" ",{"type":42,"tag":201,"props":842,"children":843},{},[844],{"type":47,"value":845},"Interrogate whether it is effective on all four axes",{"type":47,"value":847}," — a visible limit that fails any one of these is itself the finding (a present-but-ineffective bound), because the code looks defended but is not:",{"type":42,"tag":849,"props":850,"children":851},"ul",{},[852,924,980,997],{"type":42,"tag":805,"props":853,"children":854},{},[855,860,862,867,869,874,876,881,883,888,890,895,897,902,904,909,911,915,917,922],{"type":42,"tag":201,"props":856,"children":857},{},[858],{"type":47,"value":859},"Dimension",{"type":47,"value":861}," — does the cap bound the quantity that actually drives cost? A recursion-",{"type":42,"tag":402,"props":863,"children":864},{},[865],{"type":47,"value":866},"depth",{"type":47,"value":868}," cap does not bound output ",{"type":42,"tag":402,"props":870,"children":871},{},[872],{"type":47,"value":873},"width",{"type":47,"value":875},"; a frame\u002Felement ",{"type":42,"tag":402,"props":877,"children":878},{},[879],{"type":47,"value":880},"count",{"type":47,"value":882}," cap does not bound ",{"type":42,"tag":402,"props":884,"children":885},{},[886],{"type":47,"value":887},"bytes",{"type":47,"value":889},"; a decompressed-",{"type":42,"tag":402,"props":891,"children":892},{},[893],{"type":47,"value":894},"size",{"type":47,"value":896}," cap bounds ",{"type":42,"tag":402,"props":898,"children":899},{},[900],{"type":47,"value":901},"memory",{"type":47,"value":903}," but does not bound the ",{"type":42,"tag":402,"props":905,"children":906},{},[907],{"type":47,"value":908},"CPU",{"type":47,"value":910}," of processing that output; a per-item ",{"type":42,"tag":402,"props":912,"children":913},{},[914],{"type":47,"value":880},{"type":47,"value":916}," quota (number of events \u002F replays \u002F records) does not bound the per-item ",{"type":42,"tag":402,"props":918,"children":919},{},[920],{"type":47,"value":921},"compute cost",{"type":47,"value":923}," of processing each one; an input-size cap does not bound amplified output.",{"type":42,"tag":805,"props":925,"children":926},{},[927,932,934,940,942,948,950,956,958,964,966,971,973,978],{"type":42,"tag":201,"props":928,"children":929},{},[930],{"type":47,"value":931},"Timing",{"type":47,"value":933}," — is it enforced BEFORE the allocation \u002F expansion \u002F expensive work, or after the cost is already paid? A size check after the full inflate or after the string is materialized, a length check after ",{"type":42,"tag":118,"props":935,"children":937},{"className":936},[],[938],{"type":47,"value":939},"with_capacity",{"type":47,"value":941},", or a per-category quota enforced after the parse\u002Fconvert runs, is not a defense. In particular: a demangler \u002F formatter \u002F decoder \u002F serializer that builds a full output ",{"type":42,"tag":118,"props":943,"children":945},{"className":944},[],[946],{"type":47,"value":947},"String",{"type":47,"value":949},"\u002Fbuffer and only THEN compares its length to a cap (",{"type":42,"tag":118,"props":951,"children":953},{"className":952},[],[954],{"type":47,"value":955},"out.len() >= limit",{"type":47,"value":957}," → truncate or ",{"type":42,"tag":118,"props":959,"children":961},{"className":960},[],[962],{"type":47,"value":963},"return false",{"type":47,"value":965},") has already paid the peak allocation — the cap bounds what is ",{"type":42,"tag":402,"props":967,"children":968},{},[969],{"type":47,"value":970},"returned to the caller",{"type":47,"value":972},", not what is ",{"type":42,"tag":402,"props":974,"children":975},{},[976],{"type":47,"value":977},"built in memory",{"type":47,"value":979},". Treat that post-build check as ineffective and look upstream for the amplification that produced the oversized output.",{"type":42,"tag":805,"props":981,"children":982},{},[983,988,990,995],{"type":42,"tag":201,"props":984,"children":985},{},[986],{"type":47,"value":987},"Accounting",{"type":47,"value":989}," — does the counted quantity match real resource use? A cost ledger or quota that sums only payload bytes and ignores per-object ",{"type":42,"tag":118,"props":991,"children":993},{"className":992},[],[994],{"type":47,"value":947},{"type":47,"value":996}," \u002F map-node \u002F struct overhead under-counts true heap (can diverge 20–30×), so the limit is reached far later than the operator expects.",{"type":42,"tag":805,"props":998,"children":999},{},[1000,1005,1007,1013],{"type":42,"tag":201,"props":1001,"children":1002},{},[1003],{"type":47,"value":1004},"Liveness",{"type":47,"value":1006}," — is the guard actually active in the shipped config? A cap defaulted to unlimited (",{"type":42,"tag":118,"props":1008,"children":1010},{"className":1009},[],[1011],{"type":47,"value":1012},"u64::MAX",{"type":47,"value":1014},"), gated behind an off-by-default flag, or in otherwise dead code enforces nothing. Trace the default the production config compiles with.",{"type":42,"tag":1016,"props":1017,"children":1018},"br",{},[],{"type":47,"value":1020},"The classic effective bounds still count as defenses when they pass these axes: a hard cap, ",{"type":42,"tag":118,"props":1022,"children":1024},{"className":1023},[],[1025],{"type":47,"value":729},{"type":47,"value":1027},", a check of the declared size against real input length applied first, a recursion depth limit, a visited-set, or a cycle\u002Fdecreasing invariant.",{"type":42,"tag":805,"props":1029,"children":1030},{},[1031,1033,1037,1039,1044,1046,1051,1053,1058,1060,1065,1067,1072,1074,1079,1081,1085,1087,1091],{"type":47,"value":1032},"Look for an amplification factor: does output or work grow super-linearly in input? A DAG \u002F shared node re-walked per reference with no memoization, an interned string cloned once per element, or a dedup\u002Fintern key that includes an attacker-varied field (defeating the cache) turns a small input into a much larger materialized output or CPU cost. Missing memoization\u002Finterning on a shared structure fed untrusted input is a finding even when a ",{"type":42,"tag":402,"props":1034,"children":1035},{},[1036],{"type":47,"value":880},{"type":47,"value":1038}," cap is present — the count cap does not bound per-node bytes. The same super-linear reasoning applies to ",{"type":42,"tag":201,"props":1040,"children":1041},{},[1042],{"type":47,"value":1043},"CPU cost, not just memory",{"type":47,"value":1045},": if a loop runs once per element of an attacker-sized collection and each iteration does work proportional to a second attacker-sized quantity (a nested loop, an ",{"type":42,"tag":118,"props":1047,"children":1049},{"className":1048},[],[1050],{"type":47,"value":440},{"type":47,"value":1052}," \u002F linear scan, or a full re-scan \u002F re-parse of the input per item), the total is O(N·M) — flag it ",{"type":42,"tag":201,"props":1054,"children":1055},{},[1056],{"type":47,"value":1057},"even when it allocates nothing and resident memory stays flat",{"type":47,"value":1059},". Do NOT re-describe a per-range \u002F per-element re-scan as \"recursion with no depth limit\": a flat loop that repeats work per attacker element is an algorithmic-complexity finding in its own right, distinct from stack-depth recursion, and the fix is a count cap on N and M, not a depth cap. ",{"type":42,"tag":201,"props":1061,"children":1062},{},[1063],{"type":47,"value":1064},"A decompression that is memory-capped is NOT cleared by that cap.",{"type":47,"value":1066}," When tiny compressed input inflates to a capped-but-large buffer that is then parsed \u002F scrubbed \u002F transcoded \u002F walked at a per-byte cost, the compression ratio (small input → capped output) is itself a CPU amplification factor: flag the decompress-then-process pipeline as a CPU-exhaustion finding on the shared worker ",{"type":42,"tag":402,"props":1068,"children":1069},{},[1070],{"type":47,"value":1071},"even though memory is bounded",{"type":47,"value":1073},". Do not stop at \"the decompressed size is capped\" — separately confirm a ",{"type":42,"tag":201,"props":1075,"children":1076},{},[1077],{"type":47,"value":1078},"time \u002F CPU budget",{"type":47,"value":1080}," guards the processing step, and that the guard is not merely an item-",{"type":42,"tag":402,"props":1082,"children":1083},{},[1084],{"type":47,"value":880},{"type":47,"value":1086}," quota (which bounds how many items are processed, not the compute cost of each) nor positioned ",{"type":42,"tag":402,"props":1088,"children":1089},{},[1090],{"type":47,"value":406},{"type":47,"value":1092}," the expensive step so it never sees the cost.",{"type":42,"tag":805,"props":1094,"children":1095},{},[1096,1098,1104,1105,1111,1113,1119],{"type":47,"value":1097},"Look for an in-codebase precedent the sink fails to follow — a sibling function or adjacent path that DOES cap the same thing (one decode path uses a limit, this one does not; one recursive walk has a depth cap, its sibling does not; the call uses the unbounded ",{"type":42,"tag":118,"props":1099,"children":1101},{"className":1100},[],[1102],{"type":47,"value":1103},"parse",{"type":47,"value":355},{"type":42,"tag":118,"props":1106,"children":1108},{"className":1107},[],[1109],{"type":47,"value":1110},"from_reader",{"type":47,"value":1112}," when a ",{"type":42,"tag":118,"props":1114,"children":1116},{"className":1115},[],[1117],{"type":47,"value":1118},"parse_with_limit",{"type":47,"value":1120}," or depth-bounded variant exists, or applies a size limit but no depth limit). An inconsistent guard is strong evidence the omission is a real bug, not intentional.",{"type":42,"tag":805,"props":1122,"children":1123},{},[1124,1126,1130],{"type":47,"value":1125},"Confirm reachability: can attacker-controlled input actually reach this sink in production (uploaded file, request body, parsed field)? Note when the crash is an uncatchable abort (stack overflow \u002F allocator abort) that takes down a shared multi-tenant process and crash-loops on a retried poison input. For CPU\u002Fcomplexity findings, note whether the expensive work runs on a shared, bounded worker\u002Fthread pool (so a low request rate starves all tenants) and whether it runs ",{"type":42,"tag":402,"props":1127,"children":1128},{},[1129],{"type":47,"value":406},{"type":47,"value":1131}," any quota \u002F rate-limit \u002F load-shed decision.",{"type":42,"tag":805,"props":1133,"children":1134},{},[1135,1137,1142,1144,1150,1151,1157,1158,1164,1165,1170,1172,1177,1178,1183,1184,1189,1191,1196,1197,1202,1203,1208,1209,1214,1216,1221,1223,1228,1230,1235,1237,1255,1257,1262,1264,1269,1271,1277,1279,1284,1286,1292,1294,1300,1302,1308,1310,1316],{"type":47,"value":1136},"For every regex applied to attacker-influenced input, inspect the ",{"type":42,"tag":201,"props":1138,"children":1139},{},[1140],{"type":47,"value":1141},"pattern structure",{"type":47,"value":1143}," for catastrophic backtracking — a regex is NOT safe just because it is short. Look for the three ambiguity shapes: (a) a quantifier wrapping a group that itself contains a quantifier — ",{"type":42,"tag":118,"props":1145,"children":1147},{"className":1146},[],[1148],{"type":47,"value":1149},"(x+)+",{"type":47,"value":125},{"type":42,"tag":118,"props":1152,"children":1154},{"className":1153},[],[1155],{"type":47,"value":1156},"(x*)*",{"type":47,"value":125},{"type":42,"tag":118,"props":1159,"children":1161},{"className":1160},[],[1162],{"type":47,"value":1163},"(x+)*",{"type":47,"value":125},{"type":42,"tag":118,"props":1166,"children":1168},{"className":1167},[],[1169],{"type":47,"value":515},{"type":47,"value":1171},"; (b) alternation under a quantifier whose branches overlap or share a prefix — ",{"type":42,"tag":118,"props":1173,"children":1175},{"className":1174},[],[1176],{"type":47,"value":529},{"type":47,"value":125},{"type":42,"tag":118,"props":1179,"children":1181},{"className":1180},[],[1182],{"type":47,"value":536},{"type":47,"value":125},{"type":42,"tag":118,"props":1185,"children":1187},{"className":1186},[],[1188],{"type":47,"value":543},{"type":47,"value":1190},"; (c) two adjacent quantifiers over overlapping character classes — ",{"type":42,"tag":118,"props":1192,"children":1194},{"className":1193},[],[1195],{"type":47,"value":556},{"type":47,"value":125},{"type":42,"tag":118,"props":1198,"children":1200},{"className":1199},[],[1201],{"type":47,"value":563},{"type":47,"value":125},{"type":42,"tag":118,"props":1204,"children":1206},{"className":1205},[],[1207],{"type":47,"value":570},{"type":47,"value":572},{"type":42,"tag":118,"props":1210,"children":1212},{"className":1211},[],[1213],{"type":47,"value":578},{"type":47,"value":1215}," shape. Each lets the engine partition a run of characters many ways, and a ",{"type":42,"tag":201,"props":1217,"children":1218},{},[1219],{"type":47,"value":1220},"non-matching suffix",{"type":47,"value":1222}," forces it to try them all. Before reporting, confirm all three: (1) the engine ",{"type":42,"tag":201,"props":1224,"children":1225},{},[1226],{"type":47,"value":1227},"backtracks",{"type":47,"value":1229}," — JS\u002FV8, Python ",{"type":42,"tag":118,"props":1231,"children":1233},{"className":1232},[],[1234],{"type":47,"value":616},{"type":47,"value":1236},", Java, PCRE, Ruby, .NET do; ",{"type":42,"tag":201,"props":1238,"children":1239},{},[1240,1242,1247,1248,1253],{"type":47,"value":1241},"Go ",{"type":42,"tag":118,"props":1243,"children":1245},{"className":1244},[],[1246],{"type":47,"value":624},{"type":47,"value":626},{"type":42,"tag":118,"props":1249,"children":1251},{"className":1250},[],[1252],{"type":47,"value":632},{"type":47,"value":1254},", and RE2 are linear-time and immune — a vulnerable-looking pattern on those is NOT a finding",{"type":47,"value":1256},"; (2) the ",{"type":42,"tag":201,"props":1258,"children":1259},{},[1260],{"type":47,"value":1261},"subject is attacker-controlled",{"type":47,"value":1263}," (URL, header, stack string, log line, SQL text, user field) — or, worse, the ",{"type":42,"tag":201,"props":1265,"children":1266},{},[1267],{"type":47,"value":1268},"pattern itself is attacker-supplied",{"type":47,"value":1270}," via ",{"type":42,"tag":118,"props":1272,"children":1274},{"className":1273},[],[1275],{"type":47,"value":1276},"new RegExp(input)",{"type":47,"value":1278}," \u002F dynamic construction, which is a strictly worse finding; (3) there is ",{"type":42,"tag":201,"props":1280,"children":1281},{},[1282],{"type":47,"value":1283},"no input-length cap",{"type":47,"value":1285}," applied before the match. State the exploit concretely as a pump, e.g. pattern ",{"type":42,"tag":118,"props":1287,"children":1289},{"className":1288},[],[1290],{"type":47,"value":1291},"(a+)+$",{"type":47,"value":1293}," on subject ",{"type":42,"tag":118,"props":1295,"children":1297},{"className":1296},[],[1298],{"type":47,"value":1299},"\"a\".repeat(50) + \"!\"",{"type":47,"value":1301}," → exponential; ",{"type":42,"tag":118,"props":1303,"children":1305},{"className":1304},[],[1306],{"type":47,"value":1307},"^(\\s*)*$",{"type":47,"value":1309}," on a long whitespace run + a non-space → exponential. Fix: remove the ambiguity (atomic groups \u002F possessive quantifiers, bounded ",{"type":42,"tag":118,"props":1311,"children":1313},{"className":1312},[],[1314],{"type":47,"value":1315},"{0,n}",{"type":47,"value":1317}," quantifiers, anchoring, a non-overlapping rewrite), switch to a linear engine (RE2), or hard-cap subject length before matching.",{"type":42,"tag":60,"props":1319,"children":1321},{"id":1320},"one-finding-per-root-cause",[1322],{"type":47,"value":1323},"One finding per root cause",{"type":42,"tag":43,"props":1325,"children":1326},{},[1327,1329,1335],{"type":47,"value":1328},"Report each distinct unbounded SINK once. If the same sink is reachable through several formats, call sites, or entry points, report it a single time and list the reachable paths in ",{"type":42,"tag":118,"props":1330,"children":1332},{"className":1331},[],[1333],{"type":47,"value":1334},"verification",{"type":47,"value":1336}," — do not emit one finding per format or per caller. Two genuinely independent sinks (different functions, each needing its own fix) are two findings.",{"type":42,"tag":60,"props":1338,"children":1340},{"id":1339},"severity",[1341],{"type":47,"value":1342},"Severity",{"type":42,"tag":72,"props":1344,"children":1345},{},[1346,1362],{"type":42,"tag":76,"props":1347,"children":1348},{},[1349],{"type":42,"tag":80,"props":1350,"children":1351},{},[1352,1357],{"type":42,"tag":84,"props":1353,"children":1354},{},[1355],{"type":47,"value":1356},"Level",{"type":42,"tag":84,"props":1358,"children":1359},{},[1360],{"type":47,"value":1361},"Use for",{"type":42,"tag":100,"props":1363,"children":1364},{},[1365,1378,1391],{"type":42,"tag":80,"props":1366,"children":1367},{},[1368,1373],{"type":42,"tag":107,"props":1369,"children":1370},{},[1371],{"type":47,"value":1372},"high",{"type":42,"tag":107,"props":1374,"children":1375},{},[1376],{"type":47,"value":1377},"A single crafted input causes a persistent or unrecoverable outage (permanently wedged limiter, crash-loop on a retried poison input with no self-heal), an uncatchable process-wide abort on a shared multi-tenant process, OR impact on a core pipeline (ingestion, the request path serving all tenants).",{"type":42,"tag":80,"props":1379,"children":1380},{},[1381,1386],{"type":42,"tag":107,"props":1382,"children":1383},{},[1384],{"type":47,"value":1385},"medium",{"type":42,"tag":107,"props":1387,"children":1388},{},[1389],{"type":47,"value":1390},"A single crafted input crashes or hangs a recoverable shared worker that restarts on its own and affects only a subset of traffic (e.g. an enrichment\u002Fsymbolication worker), with affected work re-processable.",{"type":42,"tag":80,"props":1392,"children":1393},{},[1394,1399],{"type":42,"tag":107,"props":1395,"children":1396},{},[1397],{"type":47,"value":1398},"low",{"type":42,"tag":107,"props":1400,"children":1401},{},[1402],{"type":47,"value":1403},"Real but bounded: impact is self-limiting, needs unrealistic input size, or requires preconditions not supported by the code.",{"type":42,"tag":43,"props":1405,"children":1406},{},[1407],{"type":47,"value":1408},"Use the lower level when reachability or impact depends on unproven preconditions.",{"type":42,"tag":43,"props":1410,"children":1411},{},[1412],{"type":47,"value":1413},"CPU \u002F throughput exhaustion counts even with no crash: sustained saturation of a shared, bounded worker\u002Fthread pool — where each request is cheap to send but expensive to process (O(n²) match, per-byte scrub\u002Ftranscode, amplified output) — is a qualifying DoS. Score it by the shared blast radius (medium for a recoverable worker pool; high if it starves a core ingestion pool serving all tenants), not by whether the process dies. A guard that is present but ineffective is scored by the impact it fails to prevent, not downgraded for existing.",{"type":42,"tag":60,"props":1415,"children":1417},{"id":1416},"what-not-to-report",[1418],{"type":47,"value":1419},"What not to report",{"type":42,"tag":849,"props":1421,"children":1422},{},[1423,1436,1441,1446,1459,1484],{"type":42,"tag":805,"props":1424,"children":1425},{},[1426,1428,1434],{"type":47,"value":1427},"Sinks that already have an effective bound (a cap, ",{"type":42,"tag":118,"props":1429,"children":1431},{"className":1430},[],[1432],{"type":47,"value":1433},".take",{"type":47,"value":1435},", depth limit, or visited-set on the reachable path). \"Effective\" means it passes the four-axis interrogation above — do NOT dismiss a sink merely because a limit is visible; a cap on the wrong dimension, applied too late, under-counting cost, or defaulted off is a finding, not a defense.",{"type":42,"tag":805,"props":1437,"children":1438},{},[1439],{"type":47,"value":1440},"Bounded input whose worst case is small and recoverable.",{"type":42,"tag":805,"props":1442,"children":1443},{},[1444],{"type":47,"value":1445},"Ordinary performance, allocation-count, or style concerns with no attacker-driven unbounded path.",{"type":42,"tag":805,"props":1447,"children":1448},{},[1449,1451,1457],{"type":47,"value":1450},"AppSec exploitability (injection, XSS, SSRF, auth) — that is the ",{"type":42,"tag":118,"props":1452,"children":1454},{"className":1453},[],[1455],{"type":47,"value":1456},"security-review",{"type":47,"value":1458}," skill's job; report it there.",{"type":42,"tag":805,"props":1460,"children":1461},{},[1462,1464,1469,1471,1476,1477,1482],{"type":47,"value":1463},"A catastrophic-looking regex on a ",{"type":42,"tag":201,"props":1465,"children":1466},{},[1467],{"type":47,"value":1468},"linear-time engine",{"type":47,"value":1470}," (Go ",{"type":42,"tag":118,"props":1472,"children":1474},{"className":1473},[],[1475],{"type":47,"value":624},{"type":47,"value":626},{"type":42,"tag":118,"props":1478,"children":1480},{"className":1479},[],[1481],{"type":47,"value":632},{"type":47,"value":1483},", RE2), or one whose subject is not attacker-influenced, or one already guarded by an input-length cap before the match — not a ReDoS finding.",{"type":42,"tag":805,"props":1485,"children":1486},{},[1487],{"type":47,"value":1488},"Pattern-only suspicion. No proof of an attacker-controlled unbounded path, no finding.",{"type":42,"tag":60,"props":1490,"children":1492},{"id":1491},"finding-format",[1493],{"type":47,"value":1494},"Finding format",{"type":42,"tag":849,"props":1496,"children":1497},{},[1498,1503,1508],{"type":42,"tag":805,"props":1499,"children":1500},{},[1501],{"type":47,"value":1502},"Title: name the sink and the missing bound (e.g. \"chained_info follow loop has no cycle guard\").",{"type":42,"tag":805,"props":1504,"children":1505},{},[1506],{"type":47,"value":1507},"Description: one or two sentences — the attacker-controlled source, the unbounded sink, and the impact.",{"type":42,"tag":805,"props":1509,"children":1510},{},[1511,1516],{"type":42,"tag":118,"props":1512,"children":1514},{"className":1513},[],[1515],{"type":47,"value":1334},{"type":47,"value":1517},": 2–5 short bullets tracing source → sink with concrete file:line facts: where the input field is read, the sink call, the absence of any cap, and any sibling that DOES cap the same thing. List additional reachable paths here rather than as separate findings.",{"items":1519,"total":1691},[1520,1543,1557,1572,1586,1603,1617,1631,1639,1650,1660,1678],{"slug":1521,"name":1521,"fn":1522,"description":1523,"org":1524,"tags":1525,"stars":1540,"repoUrl":1541,"updatedAt":1542},"xcodebuildmcp","build and test Apple apps with XcodeBuildMCP","Official skill for XcodeBuildMCP. Use when doing iOS\u002FmacOS\u002FwatchOS\u002FtvOS\u002FvisionOS work (build, test, run, debug, log, UI automation).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1526,1527,1530,1533,1534,1537],{"name":22,"slug":23,"type":16},{"name":1528,"slug":1529,"type":16},"iOS","ios",{"name":1531,"slug":1532,"type":16},"macOS","macos",{"name":9,"slug":8,"type":16},{"name":1535,"slug":1536,"type":16},"Testing","testing",{"name":1538,"slug":1539,"type":16},"Xcode","xcode",6176,"https:\u002F\u002Fgithub.com\u002Fgetsentry\u002FXcodeBuildMCP","2026-04-06T18:13:34.8719",{"slug":1544,"name":1544,"fn":1545,"description":1546,"org":1547,"tags":1548,"stars":1540,"repoUrl":1541,"updatedAt":1556},"xcodebuildmcp-cli","build and test Apple apps via CLI","Official skill for the XcodeBuildMCP CLI. Use when doing iOS\u002FmacOS\u002FwatchOS\u002FtvOS\u002FvisionOS work (build, test, run, debug, log, UI automation).",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1549,1552,1553,1554,1555],{"name":1550,"slug":1551,"type":16},"CLI","cli",{"name":1528,"slug":1529,"type":16},{"name":1531,"slug":1532,"type":16},{"name":1535,"slug":1536,"type":16},{"name":1538,"slug":1539,"type":16},"2026-04-06T18:13:36.13414",{"slug":1558,"name":1558,"fn":1559,"description":1560,"org":1561,"tags":1562,"stars":1569,"repoUrl":1570,"updatedAt":1571},"agents-md","maintain project instruction files","Creates and maintains concise AGENTS.md and CLAUDE.md project instruction files. Use when asked to create AGENTS.md, update AGENTS.md, maintain agent docs, set up CLAUDE.md, document repository agent conventions, or keep coding-agent instructions minimal and reference-backed.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1563,1566],{"name":1564,"slug":1565,"type":16},"Documentation","documentation",{"name":1567,"slug":1568,"type":16},"Engineering","engineering",861,"https:\u002F\u002Fgithub.com\u002Fgetsentry\u002Fskills","2026-05-15T06:16:29.695991",{"slug":1573,"name":1573,"fn":1574,"description":1575,"org":1576,"tags":1577,"stars":1569,"repoUrl":1570,"updatedAt":1585},"blog-writing-guide","write and review engineering blog posts","Write, review, and improve blog posts for the Sentry engineering blog following Sentry's specific writing standards, voice, and quality bar. Use this skill whenever someone asks to write a blog post, draft a technical article, review blog content, improve a draft, write a product announcement, create an engineering deep-dive, or produce any written content destined for the Sentry blog or developer audience. Also trigger when the user mentions \"blog post,\" \"blog draft,\" \"write-up,\" \"announcement post,\" \"engineering post,\" \"deep dive,\" \"postmortem,\" or asks for help with technical writing for Sentry. Even if the user just says \"help me write about [feature\u002Ftopic]\" — if it sounds like it could become a Sentry blog post, use this skill.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1578,1581,1582],{"name":1579,"slug":1580,"type":16},"Communications","communications",{"name":9,"slug":8,"type":16},{"name":1583,"slug":1584,"type":16},"Technical Writing","technical-writing","2026-05-15T06:16:33.38217",{"slug":1587,"name":1587,"fn":1588,"description":1589,"org":1590,"tags":1591,"stars":1569,"repoUrl":1570,"updatedAt":1602},"brand-guidelines","write copy following Sentry brand guidelines","Write copy following Sentry brand guidelines. Use when writing UI text, error messages, empty states, onboarding flows, 404 pages, documentation, marketing copy, or any user-facing content. Covers both Plain Speech (default) and Sentry Voice tones.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1592,1595,1598,1599],{"name":1593,"slug":1594,"type":16},"Branding","branding",{"name":1596,"slug":1597,"type":16},"Content Creation","content-creation",{"name":9,"slug":8,"type":16},{"name":1600,"slug":1601,"type":16},"UX Copy","ux-copy","2026-05-15T06:16:22.395707",{"slug":1604,"name":1604,"fn":1605,"description":1606,"org":1607,"tags":1608,"stars":1569,"repoUrl":1570,"updatedAt":1616},"claude-settings-audit","generate Claude Code settings permissions","Analyze a repository to generate recommended Claude Code settings.json permissions. Use when setting up a new project, auditing existing settings, or determining which read-only bash commands to allow. Detects tech stack, build tools, and monorepo structure.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1609,1612,1615],{"name":1610,"slug":1611,"type":16},"Claude Code","claude-code",{"name":1613,"slug":1614,"type":16},"Configuration","configuration",{"name":14,"slug":15,"type":16},"2026-05-15T06:16:44.335977",{"slug":1618,"name":1618,"fn":1619,"description":1620,"org":1621,"tags":1622,"stars":1569,"repoUrl":1570,"updatedAt":1630},"code-review","perform code reviews for Sentry projects","Perform code reviews following Sentry engineering practices. Use when reviewing pull requests, examining code changes, or providing feedback on code quality. Covers security, performance, testing, and design review.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1623,1625,1626,1629],{"name":1624,"slug":1618,"type":16},"Code Review",{"name":1567,"slug":1568,"type":16},{"name":1627,"slug":1628,"type":16},"Performance","performance",{"name":14,"slug":15,"type":16},"2026-05-15T06:16:35.824864",{"slug":1632,"name":1632,"fn":1633,"description":1634,"org":1635,"tags":1636,"stars":1569,"repoUrl":1570,"updatedAt":1638},"code-simplifier","simplify and refine source code","Simplifies and refines code for clarity, consistency, and maintainability while preserving all functionality. Use when asked to \"simplify code\", \"clean up code\", \"refactor for clarity\", \"improve readability\", or review recently modified code for elegance. Focuses on project-specific best practices.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1637],{"name":19,"slug":20,"type":16},"2026-05-15T06:16:32.127981",{"slug":1640,"name":1640,"fn":1641,"description":1642,"org":1643,"tags":1644,"stars":1569,"repoUrl":1570,"updatedAt":1649},"commit","create commits with Sentry conventions","Use for every request to commit changes or draft a commit message. Creates Sentry-style conventional commits with issue references.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1645,1648],{"name":1646,"slug":1647,"type":16},"Git","git",{"name":9,"slug":8,"type":16},"2026-07-18T05:15:10.723937",{"slug":1651,"name":1651,"fn":1652,"description":1653,"org":1654,"tags":1655,"stars":1569,"repoUrl":1570,"updatedAt":1659},"create-branch","create git branches for Sentry workflows","Create a git branch following Sentry naming conventions. Use when asked to \"create a branch\", \"new branch\", \"start a branch\", \"make a branch\", \"switch to a new branch\", or when starting new work on the default branch.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1656,1657,1658],{"name":1567,"slug":1568,"type":16},{"name":1646,"slug":1647,"type":16},{"name":9,"slug":8,"type":16},"2026-05-15T06:16:39.458431",{"slug":1661,"name":1661,"fn":1662,"description":1663,"org":1664,"tags":1665,"stars":1569,"repoUrl":1570,"updatedAt":1677},"django-access-review","review Django access control and IDOR","Django access control and IDOR security review. Use when reviewing Django views, DRF viewsets, ORM queries, or any Python\u002FDjango code handling user authorization. Trigger keywords: \"IDOR\", \"access control\", \"authorization\", \"Django permissions\", \"object permissions\", \"tenant isolation\", \"broken access\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1666,1669,1670,1673,1676],{"name":1667,"slug":1668,"type":16},"Access Control","access-control",{"name":19,"slug":20,"type":16},{"name":1671,"slug":1672,"type":16},"Django","django",{"name":1674,"slug":1675,"type":16},"Python","python",{"name":14,"slug":15,"type":16},"2026-05-15T06:16:43.098698",{"slug":1679,"name":1679,"fn":1680,"description":1681,"org":1682,"tags":1683,"stars":1569,"repoUrl":1570,"updatedAt":1690},"django-perf-review","review and optimize Django performance","Django performance code review. Use when asked to \"review Django performance\", \"find N+1 queries\", \"optimize Django\", \"check queryset performance\", \"database performance\", \"Django ORM issues\", or audit Django code for performance problems.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1684,1685,1688,1689],{"name":1624,"slug":1618,"type":16},{"name":1686,"slug":1687,"type":16},"Database","database",{"name":1671,"slug":1672,"type":16},{"name":1627,"slug":1628,"type":16},"2026-05-15T06:16:24.832813",88,{"items":1693,"total":1787},[1694,1709,1723,1737,1748,1755,1771],{"slug":1695,"name":1695,"fn":1696,"description":1697,"org":1698,"tags":1699,"stars":24,"repoUrl":25,"updatedAt":1708},"vercel-deepsec","scan web applications for security vulnerabilities","Detects broad web application security vulnerabilities using the Vercel DeepSec benchmark prompt. Use when benchmarking security review coverage or running an open-ended appsec scan for auth bypass, missing auth, XSS, RCE, SQL injection, SSRF, path traversal, secrets, weak crypto, unsafe redirects, webhook verification, Next.js Server Actions, Lua\u002FOpenResty, Go, cache poisoning, or header trust bugs.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1700,1703,1704,1705],{"name":1701,"slug":1702,"type":16},"Audit","audit",{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},{"name":1706,"slug":1707,"type":16},"Vercel","vercel","2026-05-05T05:29:23.090902",{"slug":1710,"name":1710,"fn":1711,"description":1712,"org":1713,"tags":1714,"stars":24,"repoUrl":25,"updatedAt":1722},"wrdn-authz","detect authorization and IDOR flaws","Detects authorization flaws: IDOR, missing ownership or tenant scoping, role checks that fail open, privilege escalation, unauthenticated admin actions, mass assignment, and token\u002Fsession claims trusted for permission decisions. Use when asked to review route handlers, middleware, decorators, resolvers, RBAC\u002FACL logic, serializers, ORM queries, token-derived scopes, or admin surfaces.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1715,1716,1719,1720,1721],{"name":1667,"slug":1668,"type":16},{"name":1717,"slug":1718,"type":16},"Auth","auth",{"name":19,"slug":20,"type":16},{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},"2026-04-29T05:41:41.218677",{"slug":1724,"name":1724,"fn":1725,"description":1726,"org":1727,"tags":1728,"stars":24,"repoUrl":25,"updatedAt":1736},"wrdn-code-execution","detect code and command execution bugs","Detects bugs where untrusted input reaches a sink that produces code or command execution on the server. Covers command\u002Fshell injection, unsafe deserialization, server-side template injection, eval\u002FFunction\u002Fvm reached by user data, XXE-to-RCE gadgets, and prototype pollution that lands on a code-executing sink. Run on any diff touching subprocess\u002Fexec calls, template rendering, deserialization of bytes, XML parsing, or deep-merge of user-controlled objects.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1729,1732,1733,1734,1735],{"name":1730,"slug":1731,"type":16},"Backend","backend",{"name":19,"slug":20,"type":16},{"name":22,"slug":23,"type":16},{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},"2026-04-29T05:41:39.861655",{"slug":1738,"name":1738,"fn":1739,"description":1740,"org":1741,"tags":1742,"stars":24,"repoUrl":25,"updatedAt":1747},"wrdn-data-exfil","detect data exfiltration and SSRF bugs","Detects bugs where untrusted input reaches a sink that leaks data beyond its intended scope. Covers SSRF (including cloud metadata, internal services, image proxies), path traversal and archive zip-slip, SQL\u002FNoSQL injection enabling bulk reads, XXE file read, response serializers over-exposing internal fields, verbose error pages, logs capturing secrets, and CSV\u002Fformula injection in exports. Run on any diff touching HTTP clients with user URLs, file I\u002FO with user paths, raw queries, XML parsing, response serializers, error handlers, or export pipelines.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1743,1744,1745,1746],{"name":1730,"slug":1731,"type":16},{"name":19,"slug":20,"type":16},{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},"2026-04-29T05:41:42.567486",{"slug":4,"name":4,"fn":5,"description":6,"org":1749,"tags":1750,"stars":24,"repoUrl":25,"updatedAt":26},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1751,1752,1753,1754],{"name":19,"slug":20,"type":16},{"name":22,"slug":23,"type":16},{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},{"slug":1756,"name":1756,"fn":1757,"description":1758,"org":1759,"tags":1760,"stars":24,"repoUrl":25,"updatedAt":1770},"wrdn-gha-workflows","detect GitHub Actions workflow vulnerabilities","Detects exploitable GitHub Actions workflow vulnerabilities, including pull_request_target pwn requests, unsafe PR checkout, expression injection in run steps and actions\u002Fgithub-script blocks, workflow_dispatch and workflow_call input command injection, comment- and discussion-triggered commands, TOCTOU between approval and checkout, secret exposure, broad permissions, reusable workflows that consume undeclared secrets, ArtiPACKED-style token leaks through uploaded artifacts, cache poisoning and eviction-stuffing, supply-chain risk from unpinned third-party actions (tj-actions\u002Fchanged-files class), and self-hosted runner abuse. Run on diffs touching .github\u002Fworkflows, action.yml, action.yaml, repo-local actions, or CI-loaded scripts and config.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1761,1764,1765,1768,1769],{"name":1762,"slug":1763,"type":16},"CI\u002FCD","ci-cd",{"name":19,"slug":20,"type":16},{"name":1766,"slug":1767,"type":16},"GitHub Actions","github-actions",{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},"2026-04-29T05:41:43.93205",{"slug":1772,"name":1772,"fn":1773,"description":1774,"org":1775,"tags":1776,"stars":24,"repoUrl":25,"updatedAt":1786},"wrdn-pii","detect PII and confidential data in code","Detects real personally identifiable information, customer identifiers, and customer-confidential business data in code changes. Use when asked to find PII, customer IPs, real email addresses, revenue data, billing data, personal data, privacy leaks, customer info in logs, PII in URLs, or accidental production data in tests, fixtures, comments, docs, config, telemetry, or API responses.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[1777,1778,1781,1784,1785],{"name":19,"slug":20,"type":16},{"name":1779,"slug":1780,"type":16},"Compliance","compliance",{"name":1782,"slug":1783,"type":16},"Privacy","privacy",{"name":14,"slug":15,"type":16},{"name":9,"slug":8,"type":16},"2026-04-29T05:41:38.512082",7]