Install
$ agentstack add skill-tunahanaliozturk-secure-dotnet-skills-api-contract-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
API Contract Review
Directs the agent to review the HTTP contract of an ASP.NET Core API end-to-end: verb semantics, status-code correctness, error shape, idempotency guarantees, concurrency, versioning strategy, pagination bounds, and whether the OpenAPI document faithfully describes the real responses — producing a concrete, API-named finding for every gap before the contract becomes load-bearing for clients.
When to use
- A new REST endpoint, controller, or minimal-API handler is being added or modified and the HTTP contract must be reviewed before client teams depend on it.
- A PR changes a response shape, status code, or route and backward-compatibility risk must be assessed.
- An API is about to ship to external consumers and the OpenAPI document needs to be validated against real behavior.
- A code review reveals ad-hoc error JSON, missing
Locationheaders on creates, or unbounded list endpoints.
Process
- Enumerate the resources and verbs. List every route, its HTTP method, and what resource it operates on. Confirm verb semantics: GET is safe and idempotent (no side effects), POST creates or triggers, PUT is a full idempotent replace, PATCH is a partial update, DELETE is idempotent.
- Check status-code correctness for every outcome. Map each success and error path to the correct status code:
201 Created+Locationon create,204 No Contenton empty success,400for malformed input,422for semantically invalid input,409for conflict,404vs403for missing vs forbidden,412for failed precondition. - Check the error contract. Confirm all error responses use
ProblemDetailsorValidationProblemDetails(RFC 7807) viaResults.Problem,Results.ValidationProblem, orAddProblemDetails. Flag any ad-hoc{ "error": "..." }bodies or non-standard error shapes. - Check idempotency and verb safety. For unsafe, non-idempotent POSTs that have real-world side effects (payments, orders, emails) confirm an
Idempotency-Keyrequest header is accepted and the server deduplicates replayed requests. Confirm PUT and DELETE operations are genuinely idempotent (repeated calls return the same result). - Check versioning, pagination, and content negotiation. Verify an explicit versioning strategy (
Asp.VersioningURL segment or header). Verify all list endpoints have a bounded page size, and return a cursor or offset with a documentednexttoken orLinkheader. Confirm theAcceptheader is honored and media types are consistent. - Check the OpenAPI document matches reality. Confirm every status code emitted by the handler is declared via
[ProducesResponseType](controllers) or.Produces(statusCode)(minimal APIs), the document is generated withMicrosoft.AspNetCore.OpenApior Swashbuckle, and response schemas reference DTOs not EF entities. - Output findings with the concrete fix. For each gap, name the exact type, attribute, or method to apply. Re-check the same pattern in sibling handlers before closing.
.NET / Azure checks
- Verb semantics. GET must be safe and idempotent — no mutations, no visible side effects. POST creates a new resource or triggers a non-idempotent action. PUT performs a full, idempotent replace of a named resource (same outcome for repeated calls). PATCH applies a partial update via
JsonPatch(Microsoft.AspNetCore.JsonPatch) or JSON merge-patch (Content-Type: application/merge-patch+json). DELETE is idempotent — deleting an already-deleted resource must return204or404, not500. - Status codes.
201 Createdwith aLocation: /resource/{id}header on every successful POST that creates a resource.204 No Contenton mutations with no body to return.400 Bad Requestfor syntactically malformed input (unparseable JSON, wrong content-type, missing required header).422 Unprocessable Entityfor input that is well-formed but semantically invalid (a date range where end ` request header. The server stores the key and the response; repeated requests with the same key return the cached response without re-executing the side effect. Clients must be able to safely retry on network errors. Without this, a transient failure during a payment POST causes a double-charge. - Optimistic concurrency with ETag + If-Match. Resources that can be concurrently updated must emit an
ETagresponse header (a version hash or row-version value). Update operations (PUT/PATCH) must require the client to sendIf-Match: "". If the stored version does not match, return412 Precondition Failed(not409). This prevents a lost-update race between concurrent writers. In ASP.NET Core, readRequest.Headers.IfMatchand compare againstentry.RowVersionor a computed hash. - API versioning via Asp.Versioning. Every public API route must be versioned. Use the
Asp.Versioning.MvcNuGet package (andAsp.Versioning.Mvc.ApiExplorerfor OpenAPI explorer integration), namespaceAsp.Versioning. Prefer URL-segment versioning (/v{version:apiVersion}/) for public APIs; header versioning (api-version: 2.0) for internal or partner APIs. Declare versions on controllers with[ApiVersion("1.0")]and deprecate old versions with[ApiVersion("1.0", Deprecated = true)]. Making a breaking change on an unversioned route is never acceptable. - Bounded pagination. No list endpoint may return an unbounded collection. Require
pageSize(orlimit) with a maximum cap enforced server-side (e.g.Math.Min(pageSize, 100)). For offset-based pagination return{ "items": [...], "nextPage": "/orders?skip=20&limit=20" }; for cursor-based return an opaquenextCursortoken. Document thenexttoken orLink: ; rel="next"header in the OpenAPI spec. - OpenAPI document accuracy. Generate the document with
Microsoft.AspNetCore.OpenApi(builder.Services.AddOpenApi(),app.MapOpenApi()) or Swashbuckle (builder.Services.AddSwaggerGen()). Every handler must declare[ProducesResponseType(StatusCodes.Status201Created)],[ProducesResponseType(StatusCodes.Status422UnprocessableEntity)], etc. Response schemas must reference DTOs, not EF entity classes. Undocumented status codes confuse client code generators and SDK authors.
Red flags
| Signal | Why it matters | |--------|----------------| | 200 OK returned with an error body (e.g. { "success": false, "error": "..." }) | Clients cannot distinguish success from failure by status code; HTTP semantics are broken and SDK generators produce incorrect code. | | POST /orders returns 200 with no Location header on success | Violates RFC 7231 §6.3.2; the caller has no reliable way to retrieve the created resource without parsing the body or issuing a second query. | | An error response with Content-Type: application/json and a plain { "error": "..." } body | Not RFC 7807; different endpoints expose different error shapes, making client error-handling inconsistent and fragile. | | A list endpoint with no pageSize parameter or no server-side cap | A single request can return millions of rows; causes OOM on the server and a large, slow payload for the client. | | An unversioned public route accepting breaking changes in-place | Any client that has not opted in to the new behavior breaks silently; there is no way to communicate the change or deprecate safely. | | PUT /resource/{id} used for partial updates instead of PATCH | PUT semantics require a full replace; sending a partial body causes unset fields to be nulled out, silently corrupting data. | | EF entity class (e.g. Order, ApplicationUser) returned directly as the response DTO | Exposes server-managed columns (RowVersion, PasswordHash, IsDeleted, foreign-key navigations) and couples the wire contract to the database schema. | | 422 status code undeclared in the OpenAPI document | Code generators emit no error type for validation failures; client developers discover the shape at runtime from an unexpected response. |
Example
See [examples/api-contract-review/](../../examples/api-contract-review/) and the full before/after walkthrough in [examples/api-contract-review/README.md](../../examples/api-contract-review/README.md).
Related skills
- [design-dotnet-feature](../design-dotnet-feature/SKILL.md) — use first to validate the feature design and resource model before reviewing the HTTP contract in detail.
- [auth-flow-review](../auth-flow-review/SKILL.md) — review authorization on every endpoint produced by this contract: scopes, policies, and default-deny posture.
- [rate-limiting-review](../rate-limiting-review/SKILL.md) — once the contract is correct, review 429 semantics and Retry-After header contract for protected endpoints.
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.