You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add OpenApiValidationPlugin to validate API traffic against local OpenAPI specs - #2037
Adds OpenApiValidationPlugin, a reporting plugin that checks intercepted requests and responses against local OpenAPI spec files and reports any discrepancies.
specFiles: list of spec files (JSON or YAML, OAS 3.x).
specsFolder: folder that's scanned recursively for .json, .yaml and .yml files.
At least one of specFiles or specsFolder is required.
reportUnmatchedRequests: if true, also report requests that don't match any spec's servers. Defaults to false.
Checks
Undocumented operations: paths, methods, status codes and content types that aren't in the spec. A request that matches a spec's server is reported if its path or method isn't documented.
Parameters:
Missing required path, query and header parameters.
Unknown query parameters.
Parameter values are checked for type and enum.
Bodies: request and response JSON bodies are validated with a built-in schema validator (no new dependencies). It supports type, nullable/null, required, properties, additionalProperties, items, enum, allOf, anyOf, oneOf and $ref.
Direction:readOnly properties aren't required in requests, and writeOnly properties aren't required in responses.
Mocked and simulated responses are validated too.
Output
Each request is logged live: a warning when it has issues, or a confirmation when it matches.
When recording stops, the plugin stores a report. It renders to Markdown and plain text, and also serializes to JSON.
Tests: 14 new integration tests in OpenApiValidationPluginTests pass. CommandExitCodeTests and ProcessSmokeTests fail locally on main without this change too.
Manually tested with a detached proxy, MockResponsePlugin and PlainTextReporter.
…nAPI specs
Adds a reporting plugin that validates intercepted requests and responses
against local OpenAPI specs (specFiles and/or specsFolder) and reports
undocumented paths/methods/status codes/content types, parameter issues
and request/response body schema violations. Closesdotnet#779
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
The depth-limit fallback treats unvalidated schemas as valid, allowing invalid bodies to pass, while unresolved matching and coercion defects can misclassify requests or abort report generation.
Adds OpenApiValidationPlugin to detect drift between intercepted API traffic and local OpenAPI specifications, addressing #779 through Dev Proxy’s existing reporting pipeline.
Changes:
Loads JSON/YAML specs from configured files or folders and checks operations, parameters, headers, and bodies.
Adds lightweight JSON schema validation and live discrepancy logging, with Markdown, plain-text, and JSON reports.
Adds configuration schema and integration tests for validation and reporting.
- Report schemas nested deeper than the supported maximum instead of treating them as valid
- Search all specs and matching paths for the requested method before reporting it as undocumented
- Recognize required deepObject query parameters
- Match operations against their effective (document-, path- or operation-level) servers
- Match server base paths and request paths case-sensitively and strictly on trailing slashes
- Decode array parameters according to their style and explode settings
- Reject non-finite numbers when coercing parameter values
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Schema validation lacks a total-work bound, allowing repeated references to trigger billions of synchronous evaluations and potentially stall response processing and report generation.
A required object query parameter using style: form and explode: true is serialized using its property names, not the parameter name. For example, filter with a status property is sent as ?status=active, but this lookup reports filter missing and the later unknown-parameter check reports status undocumented. Account for exploded object properties when checking presence and determining known query keys, even if object-value validation remains unsupported.
Limit bracket-name exemption to deepObject parameters
The bracket-name exemption applies to every documented query parameter, not just deepObject parameters. For example, with the optional integer top parameter in the test spec, ?top[unexpected]=10 produces neither an unknown-parameter warning nor value validation. Restrict this exemption to parameters whose declared style is deepObject so unrelated bracketed query keys are still reported.
Resolve external references relative to the spec file
Passing only the document text loses the source file URI needed to resolve relative external references. For example, a configured specs/openapi.yaml containing $ref: './schemas/User.yaml' cannot resolve that sibling schema relative to the spec file, so its constraints cannot be validated even though the spec is logged as loaded. Supply the file's base URI and resolve local references before accepting the document, or reject unresolved references explicitly.
Memoize composed (allOf/anyOf/oneOf) subschema results per schema and
value, dedupe repeated allOf errors, and cap total evaluations as a
backstop so repeated references can't cause exponential validation work.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Unresolved validation defects reject valid query parameters, skip checks for default-port server URLs, and allow undocumented query keys, malformed JSON, and invalid values to pass.
A required object query parameter using style: form and explode: true is serialized as its property names: for example, a filter object with a status property is sent as ?status=active. This lookup reports filter missing, and the unknown-query check also reports status as undocumented. Recognize exploded form-object property keys for both presence and unknown-parameter checking, even if object-value schema validation remains skipped.
Limit bracket-key exemption to deepObject parameters
The bracket-key exemption applies to every query parameter, regardless of its serialization style. With the existing optional integer top parameter, ?top[foo]=bar is neither validated as top nor reported as an unknown parameter, so an undocumented query key silently passes. Restrict this exemption to parameters explicitly declared with deepObject style.
Parse JSON bodies when the media type has no schema
A documented application/json media type can omit its schema, but its payload still needs to be valid JSON. With content: { application/json: {} }, this early return accepts a malformed body such as { and logs that the response matches the spec. Parse JSON regardless of whether a schema is supplied; OpenApiSchemaValidator.Validate already handles a null schema.
Normalize server authorities with explicit default ports
The server authority is compiled verbatim, while FindOperation matches against Uri.GetLeftPart, which omits an explicit default port. A valid server such as https://api.contoso.com:443/v1 therefore never matches requests to that endpoint, and validation is silently skipped. Normalize literal server authorities through Uri before compiling the regex so explicit default ports match the canonical request URL.
Coerce parameter values using composed schema types
A parameter schema can declare its type through composition without a top-level type, for example allOf: [{type: integer}]. This branch leaves ?count=42 as a JSON string, so the subsequent composition validation rejects a valid integer parameter. The same problem affects array item schemas declared through composition. Resolve applicable composed types, or evaluate schema-compatible coercion candidates, before validating parameter values.
- Use unambiguous property paths so memoized results don't collide
- Recognize exploded form-object query parameters
- Exempt bracketed query keys only for deepObject parameters
- Report malformed JSON even when the media type has no schema
- Match server URLs with explicit default ports
- Coerce parameter values using types declared via composition
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Unbounded traversal of repeated schema references in parameter type inference, array-item lookup, and property-name discovery can trigger billions of calls before the validation budget applies, stalling response processing.
Type inference, item lookup and property discovery now traverse
allOf/anyOf/oneOf with a visited set, so repeated references and cycles
can't cause exponential work.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Human assessment is needed to weigh unpublished correctness and performance findings involving rejected valid requests and responses and potentially stalled synchronous processing.
- When an allOf subschema with additionalProperties: false rejects a
property declared in a sibling subschema, explain why in the message
- Skip validating JSON bodies larger than 5 MB to avoid stalling the
proxy, and log that validation was skipped
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Follow-up to the latest Copilot review, which said it needed a closer look but posted no inline threads. I addressed the likely findings in fb8bfb5:
allOf with additionalProperties: false: each allOf subschema is still validated on its own, as the spec requires. When a branch rejects a property that a sibling branch declares, the message now explains why: "The property is declared in a sibling allOf schema; additionalProperties: false applies per subschema". Test: HintsWhenAllOfBranchRejectsPropertyDeclaredInSibling.
Large bodies validated inline: JSON bodies over a fixed 5 MB are no longer parsed or validated. The proxy logs that validation was skipped and why. Test: SkipsValidatingLargeBodies.
oneOf discriminator: out of scope for this PR. It will be tracked in a follow-up issue.
The reason will be displayed to describe this comment to others. Learn more.
🟡 Changes recommended
Oversized request bodies are counted as validated despite being skipped, so reports can incorrectly claim full validation without disclosing unchecked bodies.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Human assessment is needed because composed-schema error merging can stall synchronous processing, while parameter interpretation and depth-sensitive caching can incorrectly reject valid inputs.
- Dedupe allOf errors with a hash set instead of a linear scan
- Include depth in the composed-schema memo key so depth-limited results
aren't reused at shallower depths
- Accept parameter values that are valid as strings when the coerced
value isn't, e.g. for schemas allowing both integers and strings
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The latest Copilot review said "Needs a closer look" with no inline threads. I addressed the findings it hinted at in d2333a7:
Composed-schema error merging could stall processing: allOf error dedup used a linear List.Contains scan, which is O(n²) with many errors. It now uses a hash set.
Depth-sensitive caching could reject valid inputs: the composed-schema memo key didn't include depth, so a result that hit the depth limit could be reused for the same schema and value at a shallower depth. Depth is now part of the key. Test: DoesNotReuseDepthLimitedResultsAtShallowerDepths, which fails without the fix.
Parameter interpretation could reject valid inputs: values were only coerced to the first matching primitive type, so ?x=42 failed against anyOf: [{type: integer, enum: [1, 2]}, {type: string}]. When the coerced value fails, we now also try the raw string and accept the value if that's valid. Test: AcceptsParameterValuesValidAsStrings.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Human assessment is needed because unpublished findings span schema coercion, parameter parsing, reference traversal, and server-path matching, with multiple edge cases that can falsely reject valid requests.
- Most specific matching path takes precedence; a missing method is reported as undocumented
- OpenAPI 3.1+ specs require null to be listed in enums
- Honor readOnly/writeOnly declared in allOf subschemas and references
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Addressed the five findings from Copilot's review of #2042 (which builds on this branch) in 5bc98b1. Each one was first confirmed with a test.
Fixed: the most specific matching path takes precedence. A method it doesn't define is now reported as undocumented instead of falling through to a template.
Fixed: for OpenAPI 3.1+ specs, null must be listed in the enum. 3.0 specs stay lenient because of how nullable is mapped.
Fixed: the readOnly/writeOnly required exemption now honors declarations in allOf subschemas, including $refs.
Not changing: per-branch coercion for anyOf parameters that mix array and scalar branches, and for composed items with different item types. These schemas are rare and the extra complexity isn't justified.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Human assessment is needed because array validation can stall synchronous response processing through quadratic error handling, alongside subtle decoding and schema-composition correctness risks.
Copilot's review on 5bc98b1 had no inline threads, but its verdict flagged quadratic error handling in array validation. Confirmed: each allOf node copied the whole shared error list into its dedup set. With 20k invalid array items, validation took about 12.8 s. Fixed in c43ae1c by deduplicating only against errors reported for the current value. The same case now runs in milliseconds, and a regression test covers it. The verdict's other wording ('subtle decoding and schema-composition correctness risks') named nothing specific to act on.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Human assessment is needed because OpenAPI reader errors can be silently discarded, allowing incomplete specifications to skip constraints and falsely report traffic as matching.
Copilot's review on c43ae1c had no inline threads. Its verdict flagged OpenAPI reader errors being silently discarded. Confirmed: when a spec parsed with errors, the diagnostics were dropped. Fixed in e5c86f9: the plugin still loads the spec, but now logs a warning listing the reader errors, since parts of the spec may have been ignored. A test covers this.
The reason will be displayed to describe this comment to others. Learn more.
🔵 Needs a closer look
Human assessment is needed because interacting edge cases in parameter coercion, schema caching, and media-type matching can incorrectly accept invalid requests or reject valid ones.
Schema caching: the memo lives for a single validation and is keyed by resolved schema, value path and depth, so cached results always apply to the same value.
Media-type matching: handles parameters, case-insensitivity, +json, type/* and */*, with tests covering these.
I couldn't find a failing case, so I'm leaving these as they are unless a specific example comes up.
This branch has not been deployed
No deployments
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #779
Adds
OpenApiValidationPlugin, a reporting plugin that checks intercepted requests and responses against local OpenAPI spec files and reports any discrepancies.Configuration
specFiles: list of spec files (JSON or YAML, OAS 3.x).specsFolder: folder that's scanned recursively for.json,.yamland.ymlfiles.specFilesorspecsFolderis required.reportUnmatchedRequests: iftrue, also report requests that don't match any spec's servers. Defaults tofalse.Checks
type,nullable/null,required,properties,additionalProperties,items,enum,allOf,anyOf,oneOfand$ref.readOnlyproperties aren't required in requests, andwriteOnlyproperties aren't required in responses.Output
Notes
schemas/v4.1.0, in line with the version bump in Add per-mock rate to MockResponsePlugin #2027.OpenApiValidationPluginTestspass.CommandExitCodeTestsandProcessSmokeTestsfail locally onmainwithout this change too.