ASP.NET Core API Security Testing: Start From the Source, Not the Swagger File

ASP.NET Core API security testing means exercising the routes your application actually registers, fully authenticated, rather than the subset that reached a generated Swagger file. Reading routes and their authorization attributes directly from C# source produces a testing target derived from the code itself, not from whoever last remembered to annotate a controller.

Most teams treat swagger.json as the inventory. It is a reasonable instinct, because it is generated, it is checked in, and it validates. But a generated spec is a record of what the generator could see and what the team configured it to expose. That is a document of intent. The route table is a property of the code.

Key takeaways

  • A generated OpenAPI file reflects your annotation and configuration discipline. It is an output of the build, not a measurement of the running app.
  • A route table derived from C# source comes from the code that registers the endpoints, so it does not depend on anyone maintaining documentation.
  • ASP.NET Core is one of two language families where NightVision's discovery also reads authorization attributes and writes them into the spec as security requirements.
  • A spec that records which endpoints declare [Authorize] tells you not just what to test, but which session to test it with.
  • Discovery and testing are different jobs. The spec records the declared requirement. Running the scan fully authenticated is what exercises the endpoint.

What reaches swagger.json, and what it depends on

Swashbuckle builds its document from what it can reflect over: the controllers and actions registered in the application, filtered through your configuration, your XML comments, and any conventions or filters the team added along the way. In a well-tended project, that is close to complete. In a project that has been through three years, two reorganizations, and a migration, it drifts, because nothing breaks when it drifts. The build stays green. The spec still validates. It is simply describing a slightly older application than the one you deployed.

None of this is a scanner problem or a tooling failure. It is the ordinary entropy of a document that has to be maintained by people alongside code that gets changed by people under deadline. The question worth asking is not whether your spec is wrong. It is what your security testing is pointed at when it is.

Deriving the route table from C# source

The alternative is to skip the document and read the code. NightVision's API discovery is a compiler-style static analysis engine, a Go binary that runs locally against your repository and emits an OpenAPI spec. On an ASP.NET Core project it walks the controllers, resolves attribute routing, and produces the paths and operations the application registers.

Three properties matter more than the mechanism.

It is deterministic. The same source produces the same spec. There is no sampling, no crawl budget, and no model temperature. Two runs a week apart differ only if the code differed.

It is local, and no LLM reads your code. The analysis runs on your machine or your runner. Only metadata leaves: the endpoint and its source file and line. This scoping is worth being precise about, because it applies to spec generation. It is a real architectural difference from approaches that ship source to a cloud model service.

It is fast enough to run in CI. Spec generation completes in under 20 seconds, which means it can run on every merge rather than every quarter, and the inventory refreshes with the code instead of aging against it.

The output is an ordinary OpenAPI document. It feeds the scanner directly, and it is readable by anything else in your pipeline that consumes a spec.

The part that is specific to C#

Here is where ASP.NET Core gets a capability most languages in the matrix do not have.

When the engine reads a C# project, it does not stop at paths and methods. It reads the authorization attributes. An application using JWT bearer authentication produces a spec containing a securitySchemes entry for Bearer with bearerFormat: JWT, and every action carrying [Authorize] gets a per-operation security requirement attached to it.

That is a small detail with a large consequence. A plain route list tells you what exists. A route list annotated with declared auth requirements tells you how the application intends each endpoint to be reached, which is the input you need to configure a meaningful authenticated scan. You know which operations are supposed to be public, which are supposed to require a bearer token, and therefore which sessions your scan needs to hold to exercise them properly.

Auth-scheme emission is available for C# and Java. It is a deliberate scope, not an accident, and it is why an ASP.NET Core codebase is one of the strongest starting points for this workflow.

To be exact about what this is: the spec records what the code declares. It is a testing input, not a verdict on enforcement. Confirming how an endpoint behaves is the scan's job, not the spec's.

What that buys you in the scan

A source-derived spec is a means, not an end. The reason to build a better target list is to point a better scan at it.

Once the spec exists, NightVision uses it as the scan target and runs fully authenticated, with Playwright-scripted logins including TOTP and MFA flows, and credentials held in a vault rather than in the scan config. API scans complete in 10 to 15 minutes.

Findings come back with the full request and response exchange retained, so the result is reproducible rather than a severity label attached to a URL. And because this is an API scan on a target where source-based discovery also ran, findings carry Code Traceback: the finding is annotated back to the source file and line that declared the route, and the whole set exports to SARIF. That combination is what makes a finding something a developer, or a coding agent drafting a fix under human review, can act on directly.

For scale, on OWASP Juice Shop, source-based discovery surfaces 108 endpoints against 1 documented. Across customer applications, source-derived specs put roughly 200% more API endpoints into scan scope than the documented inventory alone.

Learn more

For the full picture of how dynamic testing fits alongside static analysis and software composition analysis, read What You Should Know About Application Security Testing.

Four ways to build an API testing target

ApproachWhere the endpoint list comes fromDrifts from the code?Records declared auth requirementsWhat it asks of the team
Hand-maintained OpenAPI fileWritten by peopleYes, silentlyOnly if hand-writtenOngoing documentation discipline
Generated from reflection and annotationsThe build, filtered by configPartially, config-dependentSometimesAnnotation discipline, generator config
Crawling the running applicationLinks and flows the crawler reachesNo, but bounded by reachabilityNoA reachable, exercisable deployment
Derived from sourceThe code that registers routesNoYes, for C# and JavaA repository and a language the engine reads

None of these is strictly dominant. A crawl sees what an application really serves at runtime, including things no static read predicts. A source-derived spec sees what the code registers regardless of whether anything links to it. Running both against the same application is the point, and it is why discovery feeds the crawler rather than replacing it.

One practical note: treat a generated spec as a strong starting inventory to review, not an oracle. Project layouts vary, and it is worth diffing the output against what you expect the first time you run it on an unfamiliar repository.

Frequently asked questions

Does Swashbuckle document every endpoint in an ASP.NET Core app?

It documents what it can reflect over, filtered through your generator configuration, conventions, and filters. In a carefully maintained project that is close to complete. What it produces is a function of the code plus the configuration plus the annotations, so it can describe a slightly different application than the one you deployed.

Does NightVision send my C# source to an LLM?

No. Spec generation is pure static analysis performed locally by a Go binary. Only metadata leaves, specifically the endpoint and its source file and line. This applies to spec generation.

Does a generated spec replace running a scan?

No. A spec is a target list. The scan is what exercises the endpoints against the running application and returns evidence.

Does source-based API discovery work for languages other than C#?

Yes. Discovery covers Python, JavaScript and TypeScript, Java, C#, Go, Ruby, and PHP, with per-framework support documented for each. Authorization-attribute emission specifically is available for C# and Java.

Point a scan at the routes your code registers

If your ASP.NET Core application is being tested against a spec someone maintains by hand, the fastest way to see the difference is to generate one from the source and diff it against what you have. NightVision is SOC 2 Type II attested; report available under NDA.

See the routes your code actually registers.

Start free, or book a demo to see NightVision derive an OpenAPI spec from your C# source and scan the authenticated application it describes.