Install
$ agentstack add skill-tunahanaliozturk-secure-dotnet-skills-resilience-review ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Resilience Review
Directs the agent to audit every outbound dependency of a .NET service against concrete resilience patterns — timeouts, retry policies, circuit breakers, bulkheads, fallback, and cancellation propagation — and produce findings linked to real APIs and configuration options.
When to use
- A service has been reported as hanging or slow to fail when a downstream HTTP API, database, or queue is unavailable or degraded.
- A PR introduces or modifies calls to external HTTP endpoints, EF Core
DbContextoperations, or Azure Service Bus / Storage clients. - A service lacks
Microsoft.Extensions.Http.Resilienceor Polly v8 policies and needs them added before production hardening. - A post-incident review identified cascading failures caused by a missing circuit breaker or unbounded retry storm.
- A reliability or SLO review requires verifying that every outbound call has a bounded timeout and backoff.
Process
- Map the outbound dependencies. Enumerate every outbound call: HTTP endpoints (typed clients,
HttpClient), EF Core / SQL, Azure Service Bus / Storage / Key Vault, Redis /IDistributedCache, and any gRPC or message-queue producers. Record each dependency's latency profile and failure mode (transient vs permanent). - Confirm per-attempt and total timeouts. For each dependency, verify there is both a per-attempt timeout (bounding a single request) and an overall deadline / total timeout (bounding all retries combined). A call with only a per-attempt timeout can still block indefinitely if retries are unbounded.
- Review the retry policy. Check that retries target only idempotent operations and transient faults (
HttpRequestException, HTTP 5xx, 408 Request Timeout), honor429 Too Many Requestswith aRetry-Afterdelay, use exponential backoff with jitter to prevent thundering herd, and cap the attempt count. - Review circuit breaker and bulkhead isolation. Confirm a circuit breaker is configured on known-flaky dependencies to stop retry storms when the target is down. Confirm a bulkhead (concurrency limiter) isolates each slow dependency so it cannot exhaust the shared thread pool or connection pool.
- Review fallback and graceful degradation. For each dependency, confirm there is a defined fallback: a cached response, a default/empty result, a degraded mode, or a clear propagation of the
503upstream — not a swallowed exception that silently returns stale or corrupt data. - Check cancellation propagation. Verify
CancellationTokenis accepted and forwarded through every layer — controller action → service →HttpClientcall / EF Core query / queue operation. Deadlines set on an outerHttpContext.RequestAbortedorCancellationTokenSource.CreateLinkedTokenSourcemust flow to every I/O call. - Output findings. For each dependency with a gap, provide: the specific missing policy, the concrete API fix (
AddStandardResilienceHandler(),ResiliencePipelineBuilder,EnableRetryOnFailure), and the idempotency precondition that must be met before enabling retries.
.NET / Azure checks
Microsoft.Extensions.Http.Resilience—AddStandardResilienceHandler(). Register on typed clients withbuilder.Services.AddHttpClient().AddStandardResilienceHandler(). The standard handler composes — in order — a total request timeout, a retry with exponential backoff + jitter, a circuit breaker, an attempt timeout, and a hedging option. Override defaults viaHttpStandardResilienceOptions:TotalRequestTimeout.Timeout,Retry.MaxRetryAttempts,Retry.BackoffType = DelayBackoffType.Exponential,CircuitBreaker.SamplingDuration,AttemptTimeout.Timeout. For custom pipelines useAddResilienceHandler("name", builder => { ... })with aResiliencePipelineBuilder.- Polly v8
ResiliencePipelineBuilder. Polly v8 replacesPolicy.Handle()(v7) withnew ResiliencePipelineBuilder().AddRetry(...).AddCircuitBreaker(...).AddTimeout(...).Build(). Register pipelines withbuilder.Services.AddResiliencePipeline("key", builder => { ... })and resolve viaResiliencePipelineProvider. Strategies are composed in execution order: outermost strategy executes first. - Per-attempt timeout AND total timeout. A per-attempt timeout (
TimeoutStrategyOptions { Timeout = TimeSpan.FromSeconds(2) }) bounds a single attempt. A total timeout / deadline (TotalRequestTimeoutinHttpStandardResilienceOptions, or an outerAddTimeoutwrapping the retry strategy) bounds the entire retry sequence. Without a total timeout, three retries of a 2-second attempt timeout can still consume 6+ seconds; with jitter and backoff, indefinitely longer. - Retries only for idempotent operations and transient faults. Configure
ShouldHandleto matchHttpRequestException, HTTP 5xx, and 408. Honor429 Too Many Requests: inspectRetry-AfterviaRetryStrategyOptions.OnRetryand delay accordingly — useargs.Response?.Headers.RetryAfterto parse the value. Use exponential backoff with jitter:DelayBackoffType.ExponentialwithUseJitter = true. Cap attempts:MaxRetryAttempts = 3(or 4 total including the first attempt). Do not retry non-idempotent POSTs (payment creation, order submission) without an idempotency key that makes re-submission safe. - Circuit breaker. Configure
AddCircuitBreakerwithFailureRatio,SamplingDuration,MinimumThroughput, andBreakDuration. A half-open probe is automatic. Without a circuit breaker, a downed dependency receives full retry traffic from every in-flight request simultaneously, amplifying load on recovery and blocking threads/connections for the break duration. - Bulkhead / concurrency limiter. Add
AddConcurrencyLimiter(maxConcurrentCalls, queueDepth)to isolate a slow dependency. Without it, a dependency that starts taking 30 s per call will saturate the thread pool as requests queue up awaiting completion. Pair withIHttpClientFactory'sPooledConnectionLifetimeto prevent stale DNS entries. - Fallback strategy. Use
AddFallback(new FallbackStrategyOptions { FallbackAction = ... })to return a cached or default response when all retries and the circuit breaker have been exhausted. Cache the last-known-good response inIMemoryCache/IDistributedCacheand serve it on fallback. For write paths, enqueue to a durable outbox or return a503with aRetry-Afterheader rather than silently dropping the operation. CancellationTokenend-to-end. Every async method in the call chain must accept and forwardCancellationToken. For HTTP calls, passcttoGetAsync/SendAsync. For EF Core, passcttoToListAsync,FirstOrDefaultAsync,SaveChangesAsync. For Azure SDK clients (BlobClient,ServiceBusClient), passcancellationToken. UseCancellationTokenSource.CreateLinkedTokenSource(requestAbortedToken, timeoutToken)to combine an HTTP request abort with a hard deadline.IHttpClientFactorytyped clients — notnew HttpClient(). Register withbuilder.Services.AddHttpClient(client => { client.BaseAddress = ...; }). The factory poolsSocketsHttpHandlerinstances, rotating them onHandlerLifetime(default 2 min) to respect DNS TTLs.new HttpClient()per call creates a new handler with no connection reuse; sockets accumulate inTIME_WAIT.- EF Core
EnableRetryOnFailure. Configure inDbContextOptionsBuilder:options.UseSqlServer(conn, sql => sql.EnableRetryOnFailure(maxRetryCount: 5, maxRetryDelay: TimeSpan.FromSeconds(30), errorNumbersToAdd: null)). The built-in strategy retries on SQL transient errors (connection failures, timeouts, deadlocks). For Azure SQL / SQL MI, the default error list covers transient connectivity faults. Do not callEnableRetryOnFailureand then also wrap EF calls in a Polly retry — double-retry can cause excessive attempts. - Idempotency as a retry precondition. Before enabling retry on any operation, confirm the operation is idempotent or made idempotent with an idempotency key. GET, HEAD, PUT, and DELETE are semantically idempotent. POST is not — wrap payment-creation, order-submission, or any side-effectful POST in an
Idempotency-Keythat the server deduplicates before enabling retry on the client.
Red flags
| Signal | Why it matters | |--------|----------------| | HttpClient call with no Timeout and no CancellationToken | The call can block indefinitely if the server stops responding; threads accumulate and the thread pool starves. Always set client.Timeout or pass a CancellationToken with a deadline. | | Retry applied to a non-idempotent POST without an idempotency key | Retrying payment creation or order submission can cause duplicate charges or duplicate records. Confirm idempotency first. | | while (retries-- > 0) hand-rolled retry loop with Task.Delay | No jitter, no backoff calibration, no circuit-breaker integration, no CancellationToken support, and no standard observability hooks. Replace with Polly v8 ResiliencePipelineBuilder or AddStandardResilienceHandler(). | | Infinite or unbounded MaxRetryAttempts | A loop retrying indefinitely against a downed dependency holds connections and threads for the service's entire uptime. Cap at 3–5 attempts with a total timeout. | | Retries with no backoff (fixed or zero delay) | Synchronized retry waves from many concurrent callers hit the recovering dependency simultaneously — thundering herd. Use DelayBackoffType.Exponential with UseJitter = true. | | No circuit breaker on a known-flaky dependency | When the target is down, every in-flight request retries to exhaustion before failing; the circuit breaker stops this within one SamplingDuration window and allows the dependency to recover. | | catch (Exception) { return null; } swallowing all errors | Turns dependency failures into silent data corruption — callers receive a null or default response with no indication that the call failed. Propagate or convert to a structured fallback with logging. | | new HttpClient() per request or per method call | No handler pooling; TCP sockets linger in TIME_WAIT, exhausting ephemeral ports under moderate traffic. Register via IHttpClientFactory. | | Missing EnableRetryOnFailure on EF Core with Azure SQL | Transient SQL connectivity errors (error 40613, 40197, 49918) are common on Azure SQL; without retry-on-failure, a transient fault surfaces as an unhandled exception. | | Polly v7 Policy.Handle().WaitAndRetry(...) in a new .NET 8+ project | Polly v7 policies are not pipeline-composable with IHttpClientFactory's resilience extension; v8 ResiliencePipelineBuilder / Microsoft.Extensions.Http.Resilience is the current standard and integrates with IServiceCollection. |
Example
See [examples/resilience-review/](../../examples/resilience-review/).
Related skills
- [dotnet-performance-review](../dotnet-performance-review/SKILL.md) — overlapping concern:
IHttpClientFactory, connection pooling, and async/IO patterns affect both performance and resilience. - [async-concurrency-review](../async-concurrency-review/SKILL.md) —
CancellationTokenpropagation,Task.WhenAllfan-out, and thread-pool health are reviewed in depth there.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: tunahanaliozturk
- Source: tunahanaliozturk/secure-dotnet-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.