Skip to content

EDM-3750: Validate catalogItem versions correctly#626

Merged
celdrake merged 1 commit intoflightctl:mainfrom
celdrake:EDM-3750-catalog-items-version-validations
May 5, 2026
Merged

EDM-3750: Validate catalogItem versions correctly#626
celdrake merged 1 commit intoflightctl:mainfrom
celdrake:EDM-3750-catalog-items-version-validations

Conversation

@celdrake
Copy link
Copy Markdown
Collaborator

@celdrake celdrake commented Apr 21, 2026

The "version" field in the UI accepted a "v" prefix to the semver version, which is invalid.
The helper text even suggested v2.1.0-rc1 as a valid version, which it is not.

Includes adaptations to API changes done in flightctl/flightctl#2687 which is now already merged.

Summary by CodeRabbit

  • Bug Fixes

    • Improved semantic version validation to reject leading "v" prefix.
  • Documentation

    • Updated validation error messages to clarify that leading "v" is not allowed in semantic version inputs and semver ranges.
    • Corrected example formats in validation messages to reflect the new requirements.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 2026

Walkthrough

This PR enforces stricter semantic version validation by disallowing leading "v" prefixes in version strings and skip ranges, renames enum members in ApiVersion for clarity, makes K8sProviderSpec assignment fields nullable, and updates corresponding documentation and i18n messages throughout the codebase.

Changes

Semver Validation & Type Updates

Layer / File(s) Summary
Type & Enum Definitions
libs/types/alpha/models/ApiVersion.ts, libs/types/alpha/models/CatalogItemVersion.ts, libs/types/models/K8sProviderSpec.ts
ApiVersion enum members renamed from V1ALPHA1/FLIGHTCTL_IO_V1ALPHA1 to ApiVersionV1alpha1/ApiVersionFlightctlIoV1alpha1. CatalogItemVersion JSDoc example updated from v2.0.0-rc1 to 2.0.0-rc1. K8sProviderSpec organizationAssignment and roleAssignment fields made nullable.
Validation Logic
libs/ui-components/src/components/Catalog/AddCatalogItemWizard/utils.ts
Added helpers hasForbiddenLeadingVPrefix, isValidCatalogSingleSemver, isValidCatalogSemverRange, and toApiCatalogSkipRange to validate and normalize semver strings and ranges, rejecting leading "v" prefixes in all components.
Schema & Enum Integration
libs/ui-components/src/components/Catalog/AddCatalogItemWizard/utils.ts, libs/ui-components/src/constants.ts
Updated optionalSemver, optionalSemverList, and optionalSemverRange to use new validators. Updated versionSchema to use isValidCatalogSingleSemver. Updated formVersionsToApi to normalize skipRange via toApiCatalogSkipRange. Changed enum references from ApiVersion.V1ALPHA1 to ApiVersion.ApiVersionV1alpha1 in two locations and in getApiVersion helper.
Validation Messages & Documentation
libs/i18n/locales/en/translation.json
Updated three i18n translation entries for semver validation errors: removed "v" prefix from examples and added explicit text stating "Leading \"v\" is not allowed."

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly describes the main change: correcting validation of catalogItem versions to reject invalid formats with leading 'v' prefix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@celdrake celdrake force-pushed the EDM-3750-catalog-items-version-validations branch from 4634b6b to cf0a289 Compare May 5, 2026 09:05
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
libs/types/models/K8sProviderSpec.ts (1)

1-43: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

This generated file must not be hand-edited — regenerate via npm run gen-types.

The file header (/* generated using openapi-typescript-codegen -- do no edit */) and the project's coding guidelines both explicitly prohibit manual modifications to files under libs/types/. The nullable changes to organizationAssignment and roleAssignment appear to be made by hand here, while the upstream OpenAPI schema (from flightctl PR #2687) already reflects this change. Running npm run gen-types from the repo root should produce the same output without violating the "no hand-edit" rule, and will also keep any other generated artifacts in sync.

As per coding guidelines: "Models and API types in libs/types are generated from the flightctl OpenAPI schema and must not be modified by hand. Regenerate using npm run gen-types from the repo root."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/types/models/K8sProviderSpec.ts` around lines 1 - 43, Revert the manual
edits in the generated K8sProviderSpec type (specifically the nullable changes
to the properties organizationAssignment and roleAssignment) and do not
hand-edit files under libs/types; instead regenerate the types from the
authoritative OpenAPI schema by running the generator from the repo root (npm
run gen-types) so the K8sProviderSpec (and any other generated models) are
updated correctly and consistently.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@libs/ui-components/src/components/Catalog/AddCatalogItemWizard/utils.ts`:
- Around line 358-375: The validator is checking for a leading "v" after calling
semver.validRange(), so inputs like "v1.0.0" slip through; update
isValidCatalogSemverRange to first inspect the original trimmed input for
forbidden leading "v" on each token (use RANGE_OPERATOR_TRIM to strip
operators/spaces and hasForbiddenLeadingVPrefix to detect the prefix) and return
false if any token has a leading "v" before calling semver.validRange(), then
proceed with normalization and the existing checks; also add unit tests for
"v1.0.0", ">=v1.0.0 <2.0.0", ">= v1.0.0 < 2.0.0", and "v1.0.0 - v2.0.0" to cover
these cases.

---

Outside diff comments:
In `@libs/types/models/K8sProviderSpec.ts`:
- Around line 1-43: Revert the manual edits in the generated K8sProviderSpec
type (specifically the nullable changes to the properties organizationAssignment
and roleAssignment) and do not hand-edit files under libs/types; instead
regenerate the types from the authoritative OpenAPI schema by running the
generator from the repo root (npm run gen-types) so the K8sProviderSpec (and any
other generated models) are updated correctly and consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e18ff569-7a69-4171-8136-e1bf7b258222

📥 Commits

Reviewing files that changed from the base of the PR and between 2e10a91 and cf0a289.

📒 Files selected for processing (6)
  • libs/i18n/locales/en/translation.json
  • libs/types/alpha/models/ApiVersion.ts
  • libs/types/alpha/models/CatalogItemVersion.ts
  • libs/types/models/K8sProviderSpec.ts
  • libs/ui-components/src/components/Catalog/AddCatalogItemWizard/utils.ts
  • libs/ui-components/src/constants.ts

Comment on lines +358 to +375
const isValidCatalogSemverRange = (value: string): boolean => {
const trimmed = value.trim();
if (!trimmed) {
return false;
}
const normalizedRange = semver.validRange(trimmed);
if (normalizedRange === null) {
return false;
}
const versionParts = normalizedRange.split(/\s+/);
for (const part of versionParts) {
const version = part.replace(RANGE_OPERATOR_TRIM, '');
if (!version || hasForbiddenLeadingVPrefix(version)) {
return false;
}
return semver.valid(value) !== null;
});
}
return true;
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm semver.validRange strips leading "v" in 7.7.3 and that current code accepts these inputs.
node -e '
  const semver = require("semver");
  const cases = ["v1.0.0", ">=v1.0.0 <2.0.0", ">= v1.0.0 < 2.0.0", "v1.0.0 - v2.0.0"];
  for (const c of cases) console.log(JSON.stringify(c), "->", JSON.stringify(semver.validRange(c)));
'

Repository: flightctl/flightctl-ui

Length of output: 203


🏁 Script executed:

cat -n libs/ui-components/src/components/Catalog/AddCatalogItemWizard/utils.ts | sed -n '340,385p'

Repository: flightctl/flightctl-ui

Length of output: 1807


🏁 Script executed:

rg -n "hasForbiddenLeadingVPrefix" libs/ui-components/src/components/Catalog/AddCatalogItemWizard/utils.ts -A 3 -B 1

Repository: flightctl/flightctl-ui

Length of output: 537


🏁 Script executed:

fd -e "\.test\.(ts|tsx|js|jsx)$" libs/ui-components/src/components/Catalog/AddCatalogItemWizard | head -5

Repository: flightctl/flightctl-ui

Length of output: 620


🏁 Script executed:

fd . 'libs/ui-components/src/components/Catalog/AddCatalogItemWizard' --type f

Repository: flightctl/flightctl-ui

Length of output: 812


🏁 Script executed:

rg -l "isValidCatalogSemverRange|optionalSemverRange" libs/ui-components --type ts --type tsx

Repository: flightctl/flightctl-ui

Length of output: 94


🏁 Script executed:

rg -l "isValidCatalogSemverRange|optionalSemverRange" libs/ui-components -t ts

Repository: flightctl/flightctl-ui

Length of output: 138


🏁 Script executed:

fd . 'libs/ui-components/src/components/Catalog/AddCatalogItemWizard' --type f -name "*.test.*"

Repository: flightctl/flightctl-ui

Length of output: 297


🏁 Script executed:

rg "optionalSemverRange|isValidCatalogSemverRange" libs/ui-components/src/components/Catalog/AddCatalogItemWizard/utils.ts -A 5 -B 2

Repository: flightctl/flightctl-ui

Length of output: 1236


🏁 Script executed:

fd . 'libs/ui-components/src/components/Catalog/AddCatalogItemWizard' --type f | grep -i test

Repository: flightctl/flightctl-ui

Length of output: 48


🏁 Script executed:

rg "v1\.0\.0|hasForbiddenLeadingVPrefix" libs/ui-components --type ts -l

Repository: flightctl/flightctl-ui

Length of output: 138


🏁 Script executed:

cat -n libs/ui-components/src/components/Catalog/AddCatalogItemWizard/utils.ts | sed -n '386,430p'

Repository: flightctl/flightctl-ui

Length of output: 1623


🏁 Script executed:

rg "isValidCatalogSemverRange|isValidCatalogSingleSemver|optionalSemver" libs/ui-components/src/components/Catalog/AddCatalogItemWizard -A 10 | grep -i "test\|describe\|it("

Repository: flightctl/flightctl-ui

Length of output: 979


🏁 Script executed:

find libs/ui-components -name "*.test.ts" -o -name "*.test.tsx" -o -name "*.spec.ts" -o -name "*.spec.tsx" 2>/dev/null | head -10

Repository: flightctl/flightctl-ui

Length of output: 48


🏁 Script executed:

cat -n libs/ui-components/src/components/Catalog/AddCatalogItemWizard/utils.ts | sed -n '1,50p'

Repository: flightctl/flightctl-ui

Length of output: 1833


Range validator accepts leading "v" because it checks the normalized output instead of the user input.

semver.validRange() strips a leading v before returning the normalized range (per semver 7.7.3 docs). The current isValidCatalogSemverRange implementation normalizes first (line 363) and then checks for a leading v on the normalized parts (lines 367–373), meaning the check can never catch user input like "v1.0.0", ">=v1.0.0 <2.0.0", or "v1.0.0 - v2.0.0". All of these pass validation and are silently normalized to clean output by toApiCatalogSkipRange (line 383), defeating the error message on line 418 that says "Leading "v" is not allowed."

By contrast, isValidCatalogSingleSemver (lines 350–351) correctly checks the original string first before calling semver.valid().

Move the leading-v check to the original input before delegating to semver.validRange():

Proposed fix
 const isValidCatalogSemverRange = (value: string): boolean => {
   const trimmed = value.trim();
   if (!trimmed) {
     return false;
   }
+  // Check for leading "v" in the original input *before* semver strips it away.
+  for (const part of trimmed.split(/\s+/)) {
+    const operand = part.replace(RANGE_OPERATOR_TRIM, '');
+    if (hasForbiddenLeadingVPrefix(operand)) {
+      return false;
+    }
+  }
   const normalizedRange = semver.validRange(trimmed);
   if (normalizedRange === null) {
     return false;
   }
-  const versionParts = normalizedRange.split(/\s+/);
-  for (const part of versionParts) {
-    const version = part.replace(RANGE_OPERATOR_TRIM, '');
-    if (!version || hasForbiddenLeadingVPrefix(version)) {
-      return false;
-    }
-  }
   return true;
 };

Add unit tests covering "v1.0.0", ">=v1.0.0 <2.0.0", ">= v1.0.0 < 2.0.0", and "v1.0.0 - v2.0.0".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const isValidCatalogSemverRange = (value: string): boolean => {
const trimmed = value.trim();
if (!trimmed) {
return false;
}
const normalizedRange = semver.validRange(trimmed);
if (normalizedRange === null) {
return false;
}
const versionParts = normalizedRange.split(/\s+/);
for (const part of versionParts) {
const version = part.replace(RANGE_OPERATOR_TRIM, '');
if (!version || hasForbiddenLeadingVPrefix(version)) {
return false;
}
return semver.valid(value) !== null;
});
}
return true;
};
const isValidCatalogSemverRange = (value: string): boolean => {
const trimmed = value.trim();
if (!trimmed) {
return false;
}
// Check for leading "v" in the original input *before* semver strips it away.
for (const part of trimmed.split(/\s+/)) {
const operand = part.replace(RANGE_OPERATOR_TRIM, '');
if (hasForbiddenLeadingVPrefix(operand)) {
return false;
}
}
const normalizedRange = semver.validRange(trimmed);
if (normalizedRange === null) {
return false;
}
return true;
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/ui-components/src/components/Catalog/AddCatalogItemWizard/utils.ts`
around lines 358 - 375, The validator is checking for a leading "v" after
calling semver.validRange(), so inputs like "v1.0.0" slip through; update
isValidCatalogSemverRange to first inspect the original trimmed input for
forbidden leading "v" on each token (use RANGE_OPERATOR_TRIM to strip
operators/spaces and hasForbiddenLeadingVPrefix to detect the prefix) and return
false if any token has a leading "v" before calling semver.validRange(), then
proceed with normalization and the existing checks; also add unit tests for
"v1.0.0", ">=v1.0.0 <2.0.0", ">= v1.0.0 < 2.0.0", and "v1.0.0 - v2.0.0" to cover
these cases.

@celdrake celdrake merged commit f3e12c9 into flightctl:main May 5, 2026
10 checks passed
@celdrake celdrake deleted the EDM-3750-catalog-items-version-validations branch May 5, 2026 09:22
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.

2 participants