Skip to content

Fix #802: report apiKey security scheme name changes with old and new values - #939

Draft
Inf1mum wants to merge 2 commits into
OpenAPITools:masterfrom
Inf1mum:fix/802-securityscheme-name-detail
Draft

Inf1mum wants to merge 2 commits into
OpenAPITools:masterfrom
Inf1mum:fix/802-securityscheme-name-detail

Conversation

@Inf1mum

@Inf1mum Inf1mum commented Nov 13, 2026 •

Copy link
Copy Markdown

Fixes #802

Reworked after the review on this PR (#939 (review) — DrSatyr, CHANGES_REQUESTED). The scheme matching was rebuilt around the reviewer's four-case spec and the renderers were made consistent.

Behavior per the review spec

An apiKey scheme has a component key (PAT) and a parameter name (the header/query/cookie clients send). Actual results on this branch, verified with the CLI on valid fixtures (operation-level security, PAT: []):

# Change Result Verdict
1 key renamed, name same no change reported no_changes
2 same key, name changed Changed security scheme name: Authorization -> pat (the #802 case) incompatible via incompatible.security.scheme.apikey.name.changed (default true, can be switched off)
3 both key and name changed removed + added scheme (New security scheme / Deleted security scheme), no rename guessed incompatible
4 new alternative requirement added New security requirement: petstore_auth: [read], nothing else compatible

What changed since the last push

  • Matching is now by component key first, then by parameter name (SecurityRequirementDiff). The previous name-first order mispaired schemes when a requirement contains several apiKeys — e.g. a parameter-name swap between two schemes was silently swallowed. Case 3 (both changed) falls through both matchers and is reported as removed + added; no rename is ever guessed.
  • Dropped the block that diffed schemes of newly added requirements (SecurityRequirementsDiff), as requested. It reported case 4 as breaking (and mis-attributed type changes). The ChangedSecurityRequirements#changedSchemes list was removed with it, which also eliminates the duplicate/misattributed-scheme-change risk in that loop.
  • SecuritySchemeDiff only compares the parameter name while the right-hand scheme is still an apiKey, so a same-key type change can no longer be reported as (and suppressed by) the apiKey-name rule.
  • Renderers are now consistent across console, markdown and html:
    • all three show schemes added to or removed from a changed requirement (console and html previously dropped them);
    • the html Changed security scheme : item is skipped when it has no displayable change, and description changes are now rendered there;
    • markdown security details use a two-space nested-list indent (* Changed security requirement: with * ... children) so they render as a list — the previous four-space indent rendered as an indented code block.

Review notes

  • Markdown still duplicates #933 for now: not rebased yet because #933 is unmerged (rebasing now would break this branch). Once #933 lands I will rebase and build on its code. The same applies to the related Fix #357: ignore changes inside readOnly properties in requests #934/Render changed operation description in HtmlRender #937/fix: detect property type changes on referenced component schemas in OpenAPI 3.1 #938.
  • cubic-dev-ai[bot] issues (9): fixed in this push — the malformed fixtures (issue-802-api-key-1.yaml, issue-802-api-key-2.yaml → valid operation-level security, empty PAT: []), the SecurityRequirementDiff fallback ordering (superseded by the key-first matching above), the SecuritySchemeDiff changedName guard (only apiKey→apiKey), the MarkdownRender four-space indent (now a proper nested list), the SecurityRequirementsDiff duplicate/misattributed-scheme loop (block removed entirely), the empty html Changed security scheme item (suppressed, description now rendered), and the console missing New security scheme / Deleted security scheme lines (now rendered). Refuted: the MarkdownRender extension-only OAuth flow issue (extension-only flow changes vanish from markdown) — the model only flags a flow as changed when a URL changes (ChangedOAuthFlow#isCoreChanged returns no_changes otherwise), and none of the renderers render extension diffs for security entities; rendering extension diffs is a separate feature, not something this PR introduced. Happy to take it in a follow-up.

Verification

./mvnw test green: core 285 tests, cli 13 (baseline was core 277 + cli 13; +8 from the per-case tests). New per-case tests: SecurityDiffTest (case 1–4 plus a two-apiKey name-swap regression proving key-first pairing), render tests per renderer for the name change and the removed+added shape, SecuritySchemeBCTest#apiKeyNameChanged for the on/off switch.

CLI output on the fixtures (console renderer):

# case 2 (#802)
- GET    /info
  Security Requirements:
    - Changed security requirement: PAT
      * Changed security scheme name: Authorization -> pat
                 API changes broke backward compatibility

# case 3
    - Changed security requirement: PAT2
      * New security scheme: PAT2: []
      * Deleted security scheme: PAT: []

# case 4
    - New security requirement: petstore_auth: [read]
                 API changes are backward compatible

Markdown (case 2) now renders as a proper nested list:

###### Security Requirements:

* Changed security requirement: `PAT`
  * Changed security scheme name: `Authorization` -> `pat`

Original description below.


What

A changed name under components.securitySchemes for an apiKey scheme is now captured in the diff model and rendered with old and new values in all three renderers:

  • console: * Changed security scheme name: Authorization -> pat
  • markdown: * Changed security scheme name: Authorization->pat``
  • html (--html-detailed): Changed security scheme : name: Authorization -> pat

It also feeds the backward-compatibility verdict through a new option, incompatible.security.scheme.apikey.name.changed (default true), so the check can be disabled the same way as the other security-scheme rules — the reporter specifically noted there was no switch for it. Changing the parameter name means existing clients keep sending the old header/query/cookie name, so incompatible-by-default seemed like the honest default.

Why

The model knew a security scheme changed but never recorded which field, so the renderers had nothing line-level to print. One note on the repro specs attached to the issue: in both old.json and new.json the securitySchemes block sits under paths (not components), and the security block sits at path-item level. Both are silently dropped by the swagger parser, so as attached, no diff engine can see the scheme definitions at all — which is why only a bare changed operation shows up. The fixtures in this PR use valid locations.

How

  • SecuritySchemeDiff sets changedName for APIKEY schemes; ChangedSecurityScheme#isCoreChanged gates the incompatibility on the new option (same handling as the existing changedScheme flag).
  • Console, markdown and html read the old and new values from the SecurityScheme objects the model already carries.

Summary by cubic

Captures apiKey security scheme name changes so console, markdown, and HTML renderers report the old and new values instead of a bare operation-level change, and aligns the security renderers.

  • A new incompatible.security.scheme.apikey.name.changed option (default true) controls the backward-compatibility verdict, since existing clients keep sending the old parameter name.
  • SecurityRequirementDiff pairs requirement schemes by component key first and only falls back to the parameter name when the key no longer matches, so a rename is diffed as a scheme change instead of a deleted plus re-added requirement, and several apiKey schemes in one requirement are no longer mispaired by name.
  • Schemes referenced by newly added security requirements are no longer diffed against the other spec; a new alternative security requirement is compatible and reports no scheme changes.
  • Console, markdown, and HTML renderers list schemes added to or removed from a changed requirement; HTML skips scheme items with no displayable change and shows description changes; markdown uses a two-space nested-list indent so security details render as a list rather than a code block.

Written for commit 0aa0347. Summary will update on new commits.

View guided diff

… old and new values

A changed name under components.securitySchemes for an apiKey scheme showed
up only as an operation-level change with no line-level detail, and none of
the incompatible.security.scheme.* options could disable the breaking
verdict, because the parameter name was never part of the scheme diff.

What:
- SecuritySchemeDiff captures the parameter name change on apiKey schemes
  (changedName), gated by the new
  incompatible.security.scheme.apikey.name.changed option (default true,
  since clients keep sending the old parameter name).
- SecurityRequirementDiff falls back to matching a requirement scheme by its
  component key when the parameter name no longer matches, so a rename is
  diffed as a scheme change instead of a deleted plus re-added requirement.
- When a security requirement is newly added at some level, the schemes it
  references are now also diffed against the other spec's components, so
  definition changes are not lost; they are collected in the new
  ChangedSecurityRequirements#getChangedSchemes list and feed into the
  backward-compatibility verdict like any other changed element.
- The console, markdown and html renderers print the collected scheme
  changes with old -> new values (markdown follows the same shape as OpenAPITools#933).

Why:
The reporter saw a bare changed-operation line with no indication of which
field changed and no way to turn the rule off.

How:
The flag flows through ChangedSecurityScheme#isCoreChanged (incompatible by
default via the new option), and the renderers read the old and new values
from the old/new SecurityScheme objects the model already carries.

Fixes OpenAPITools#802

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 17 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="core/src/main/java/org/openapitools/openapidiff/core/compare/SecurityRequirementsDiff.java">

<violation number="1" location="core/src/main/java/org/openapitools/openapidiff/core/compare/SecurityRequirementsDiff.java:113">
P2: This loop can emit a duplicate or misattributed scheme change when the same OAuth2 component appears in both a matched requirement and a newly added requirement. Track scheme refs already handled by matched requirements, or avoid reusing the cached requirement-scoped result before adding it to `changedSchemes`.</violation>
</file>

<file name="core/src/main/java/org/openapitools/openapidiff/core/output/MarkdownRender.java">

<violation number="1" location="core/src/main/java/org/openapitools/openapidiff/core/output/MarkdownRender.java:310">
P2: Extension-only OAuth flow changes are reported by the diff but disappear from Markdown because this method returns empty without a URL change. Render the flow extension diff or otherwise include that change in the output.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Re-trigger cubic

"%s* refreshUrl: %s -> %s\n",
indent(2), url(oldFlow.getRefreshUrl()), url(newFlow.getRefreshUrl())));
}
if (sb.length() == 0) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Extension-only OAuth flow changes are reported by the diff but disappear from Markdown because this method returns empty without a URL change. Render the flow extension diff or otherwise include that change in the output.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At core/src/main/java/org/openapitools/openapidiff/core/output/MarkdownRender.java, line 310:

<comment>Extension-only OAuth flow changes are reported by the diff but disappear from Markdown because this method returns empty without a URL change. Render the flow extension diff or otherwise include that change in the output.</comment>

<file context>
@@ -152,9 +156,167 @@ protected void listEndpoints(
+              "%s* refreshUrl: %s -> %s\n",
+              indent(2), url(oldFlow.getRefreshUrl()), url(newFlow.getRefreshUrl())));
+    }
+    if (sb.length() == 0) {
+      return "";
+    }
</file context>

// in the other spec's components. Diff those definitions as well so that their changes
// (e.g. an apiKey scheme whose parameter name changed) are not lost.
if (changedSecurityRequirements.getIncreased() != null) {
Set<String> diffedSchemeRefs = new HashSet<>();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: This loop can emit a duplicate or misattributed scheme change when the same OAuth2 component appears in both a matched requirement and a newly added requirement. Track scheme refs already handled by matched requirements, or avoid reusing the cached requirement-scoped result before adding it to changedSchemes.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At core/src/main/java/org/openapitools/openapidiff/core/compare/SecurityRequirementsDiff.java, line 113:

<comment>This loop can emit a duplicate or misattributed scheme change when the same OAuth2 component appears in both a matched requirement and a newly added requirement. Track scheme refs already handled by matched requirements, or avoid reusing the cached requirement-scoped result before adding it to `changedSchemes`.</comment>

<file context>
@@ -104,9 +106,46 @@ protected DeferredChanged<ChangedSecurityRequirements> diff(
+    // in the other spec's components. Diff those definitions as well so that their changes
+    // (e.g. an apiKey scheme whose parameter name changed) are not lost.
+    if (changedSecurityRequirements.getIncreased() != null) {
+      Set<String> diffedSchemeRefs = new HashSet<>();
+      for (SecurityRequirement increasedSecurity : changedSecurityRequirements.getIncreased()) {
+        for (Map.Entry<String, List<String>> entry : increasedSecurity.entrySet()) {
</file context>

@DrSatyr DrSatyr left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on #802. changedName and the new apikey.name.changed switch are the right direction, but the scheme matching needs fixing. An apiKey scheme has a component key (PAT) and a name (the header clients send). The expected results are:

  1. Key renamed, name same: no change (already works on master).
  2. Same key, name changed: apikey.name.changed. This is #802.
  3. Both changed: removed + added (requirements.decreased). Don't guess renames.
  4. New alternative requirement added: compatible.

Problems:

  • Case 4 is now reported as breaking. The new block in SecurityRequirementsDiff that diffs schemes of added requirements should be dropped. It also diffs schemes whose type changed (name: Authorization -> null).
  • Match by key first, then by name. The current order mispairs schemes when there are several apiKeys.
  • Please add a test per case and use valid fixtures (operation-level security, PAT: []). The #802 files are malformed, and the issue isn't 3.1-specific.
  • Renderers: keeping console/Markdown/HTML in this PR is fine, but the Markdown part duplicates #933. Please rebase onto #933 once it's merged and build on its code. Also make the three consistent: show schemes added or removed within a requirement in console/HTML too, avoid the empty "Changed security scheme :" item in HTML, and fix the 4-space Markdown indent, which renders as a code block.

@DrSatyr
DrSatyr marked this pull request as draft November 14, 2026 11:51
…ecurity renderers

What:
- SecurityRequirementDiff pairs a requirement scheme with the other spec by
  component key first and only falls back to the apiKey parameter name, so
  several apiKey schemes in one requirement are no longer mispaired by name
  (a parameter-name swap between two schemes was silently swallowed before).
- SecurityRequirementsDiff no longer diffs the schemes referenced by newly
  added requirements; the ChangedSecurityRequirements#changedSchemes list is
  dropped with it. A new alternative security requirement is compatible and
  no longer reports the referenced schemes as changed.
- SecuritySchemeDiff only compares the parameter name while the scheme is
  still an apiKey on the other side.
- Console, html and markdown renderers all show schemes added to or removed
  from a changed requirement; the html "Changed security scheme" item is
  skipped when it has no displayable change and now also shows description
  changes; markdown security details use a two-space nested-list indent so
  they no longer render as an indented code block.

Why:
Maintainer review of the PR: matching by name first mispairs schemes when
several apiKeys exist, a new alternative requirement was reported as
breaking, and the renderers were inconsistent.

How:
Requirement-level matching is unchanged, so the verdicts still follow the
existing options (incompatible.security.requirements.decreased,
incompatible.security.requirement.schemes.increased,
incompatible.security.scheme.apikey.name.changed). The four expected
outcomes are each covered by tests with valid operation-level fixtures
(apiKey requirement "PAT: []"): key renamed with same name (no change),
same key with changed name (OpenAPITools#802), both changed (removed + added, no
rename guessing), and a new alternative requirement (compatible).

Signed-off-by: Inf1mum <vadimlazuk5@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: no details when securitySchemes.*.name changes

2 participants