Skip to content

fix: resolve root security during join#2827

Open
Daryna-del wants to merge 6 commits into
mainfrom
fix/resolve-root-security-in-join
Open

fix: resolve root security during join#2827
Daryna-del wants to merge 6 commits into
mainfrom
fix/resolve-root-security-in-join

Conversation

@Daryna-del
Copy link
Copy Markdown
Contributor

@Daryna-del Daryna-del commented May 20, 2026

What/Why/How?

Fixes a bug where root-level security from joined API descriptions was silently dropped in the output.

Previously, the join command ignored the security field defined at the root of each input spec. According to OpenAPI semantics, root-level security serves as a default for all operations that don't declare their own security. Simply merging root securities into the joined output would incorrectly apply one API's security to another API's operations.

Reference

Closes #1409

Testing

Screenshots (optional)

Check yourself

  • This PR follows the contributing guide
  • All new/updated code is covered by tests
  • Core code changed? - Tested with other Redocly products (internal contributions only)
  • New package installed? - Tested in different environments (browser/node)
  • Documentation update has been considered

Security

  • The security impact of the change has been considered
  • Code follows company security practices and guidelines

Note

Medium Risk
Changes how join computes operation security and auto-prefixes components when duplicate security scheme names exist, which can alter merged OpenAPI output and downstream client behavior. Logic is localized and covered by new e2e fixtures, reducing regression risk.

Overview
Fixes join so operations inherit their own spec’s root-/path-level security when the operation doesn’t explicitly define security, matching OpenAPI defaulting semantics.

Adds resolveOperationSecurity to compute effective operation security (operation → pathItem → root) and introduces getEffectiveComponentsPrefix to auto-prefix components only when needed (e.g., duplicate securitySchemes names across input specs). Adds new e2e join cases covering root security propagation, operation override behavior, and duplicate/no-duplicate scheme prefixing.

Reviewed by Cursor Bugbot for commit 162da39. Bugbot is set up for automated code reviews on this repo. Configure here.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 20, 2026

🦋 Changeset detected

Latest commit: 162da39

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 3 packages
Name Type
@redocly/cli Patch
@redocly/openapi-core Patch
@redocly/respect-core Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 20, 2026

CLI Version Mean Time ± Std Dev (s) Relative Performance (Lower is Faster)
cli-latest 2.120s ± 0.029s ▓ 1.00x (Fastest)
cli-next 2.135s ± 0.030s ▓ 1.01x

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 20, 2026

Coverage Report

Status Category Percentage Covered / Total
🔵 Lines 80.74% (🎯 80%) 7287 / 9025
🔵 Statements 80.09% (🎯 80%) 7571 / 9452
🔵 Functions 83.8% (🎯 83%) 1459 / 1741
🔵 Branches 72.37% (🎯 72%) 4937 / 6821
File Coverage
File Stmts Branches Functions Lines Uncovered Lines
Changed Files
packages/cli/src/commands/join/index.ts 90.41% 83.67% 85.71% 90.27% 111, 119-128, 150, 201, 215, 234
packages/cli/src/commands/join/utils/collect-paths.ts 75% 54.54% 85.71% 74.68% 88-89, 96, 107-110, 122, 133-144, 163, 227-237
packages/cli/src/commands/join/utils/get-effective-components-prefix.ts 71.42% 65% 66.66% 73.52% 18-20, 29, 46, 61, 66, 90-91
packages/cli/src/commands/join/utils/resolve-operation-security.ts 57.14% 50% 100% 57.14% 19, 23-26, 30
Generated in workflow #9900 for commit 162da39 by the Vitest Coverage Report Action

@Daryna-del
Copy link
Copy Markdown
Contributor Author

@cursor review

Comment thread packages/cli/src/commands/join/utils/get-effective-components-prefix.ts Outdated
@Daryna-del
Copy link
Copy Markdown
Contributor Author

@cursor review

Comment thread tests/e2e/join/merged-root-security-from-other-apis/snapshot.txt Outdated
} from '../../utils/miscellaneous.js';
import type { CommandArgs } from '../../wrapper.js';
import { COMPONENTS } from '../split/constants.js';
// import { COMPONENTS } from '../split/constants.js';
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Commented-out import instead of removal

Low Severity

The COMPONENTS import is commented out (// import { COMPONENTS } from ...) rather than deleted. This constant is no longer used in index.ts because the new getEffectiveComponentsPrefix utility handles it internally. The commented-out line is dead code that appears to be left over from development.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit b647fd9. Configure here.

@Daryna-del
Copy link
Copy Markdown
Contributor Author

@cursor review

return '';
}

const raw = info?.title ?? apiFilename;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Nullish coalescing fails for empty title string

Low Severity

info?.title ?? apiFilename uses nullish coalescing (??), which only falls back to apiFilename when title is null or undefined. If info.title is an empty string '', the fallback is skipped and the function returns '' — a falsy prefix that silently disables all conflict resolution for duplicate security scheme names. Using || instead would correctly fall back to apiFilename for empty strings.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 5bc9605. Configure here.

@Daryna-del Daryna-del force-pushed the fix/resolve-root-security-in-join branch from 5bc9605 to ecc35f1 Compare May 20, 2026 11:29
@Daryna-del Daryna-del marked this pull request as ready for review May 20, 2026 11:40
@Daryna-del Daryna-del requested review from a team as code owners May 20, 2026 11:40
Comment thread .changeset/curvy-ghosts-listen.md Outdated
Comment thread packages/cli/src/commands/join/utils/resolve-operation-security.ts Outdated
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 2 potential issues.

There are 4 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 162da39. Configure here.


if (!duplicateSecuritySchemeNames.size) {
return '';
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Auto-prefixing applies to all APIs, not just conflicting ones

Medium Severity

getEffectiveComponentsPrefix checks duplicateSecuritySchemeNames.size which is a global set of all duplicate scheme names across all APIs. When any pair of APIs shares a security scheme name, every API in the join gets auto-prefixed — including APIs that have no conflicting scheme names at all. For example, joining three APIs where A and B both define oauth2 but C only defines a unique apiKey, API C's components and $refs would be unnecessarily rewritten with a prefix. The check needs to verify whether the current API's scheme names intersect with duplicateSecuritySchemeNames, not just whether the global set is non-empty.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 162da39. Configure here.

apiFilename,
prefixComponentsWithInfoProp,
duplicateSecuritySchemeNames,
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Info description prefix diverges from main component prefix

Low Severity

addInfoSectionAndSpecVersion computes its componentsPrefix via getInfoPrefix, which only returns a prefix when prefixComponentsWithInfoProp is explicitly provided. The main loop now uses getEffectiveComponentsPrefix, which can auto-generate a prefix when duplicate security scheme names exist. This means component references in the first API's info.description text won't be rewritten to match the auto-prefixed component names, leaving stale references.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 162da39. Configure here.

Copy link
Copy Markdown
Collaborator

@tatomyr tatomyr left a comment

Choose a reason for hiding this comment

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

Please also address the bugbot comments.

} from '../../utils/miscellaneous.js';
import type { CommandArgs } from '../../wrapper.js';
import { COMPONENTS } from '../split/constants.js';
// import { COMPONENTS } from '../split/constants.js';
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.

Let's remove comments.

},
});
}
if (!security && openapi.hasOwnProperty('security')) {
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.

Why the previous solution was not working? I see it refers to the root security already.

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.

join command does not join root security

3 participants