Skip to content

feat(aurora): add CORS configuration management for Ceph/S3 buckets - #1092

Draft
KirylSAP wants to merge 6 commits into
mainfrom
kiryl-ceph-cors
Draft

feat(aurora): add CORS configuration management for Ceph/S3 buckets#1092
KirylSAP wants to merge 6 commits into
mainfrom
kiryl-ceph-cors

Conversation

@KirylSAP

@KirylSAP KirylSAP commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds complete CORS (Cross-Origin Resource Sharing) configuration management for Ceph/S3 buckets. Includes a full-stack implementation with tRPC backend endpoints, comprehensive UI with create/edit/delete/view capabilities, and validation for S3 CORS rules.

Changes Made

Backend / tRPC Router

  • corsRouter.ts: New tRPC router with three endpoints:
    • get: Fetch current CORS configuration (returns null for no config, not an error)
    • set: Create or replace CORS configuration (full replacement, validates rules)
    • delete: Remove CORS configuration (idempotent)
  • corsRouter.test.ts: Comprehensive test suite (512 lines) covering all endpoints, error cases, edge cases, and S3 error mapping
  • ceph.ts (types): Added Zod schemas for CORS operations:
    • corsRuleSchema: Validates individual CORS rules (methods, origins, headers, MaxAgeSeconds, ID length)
    • corsConfigurationSchema: Validates full configuration (1-100 rules)
    • Input/output schemas for each endpoint
  • s3ErrorMapper.ts: Added NoSuchCORSConfiguration to error mapping

Frontend / UI Components

  • CorsModal.tsx: Primary modal component with tabbed interface:
    • View tab: Displays existing CORS rules via CorsRulesViewer
    • Add/Edit tab: Form for creating/editing rules via CorsRuleForm
    • Supports editing individual rules from the list
    • Mutation handling with optimistic UI updates
    • Modal tracking for analytics
  • CorsRulesViewer.tsx: Read-only viewer for CORS rules:
    • Displays all rules in a structured format (ID, allowed origins, methods, headers, exposed headers, max age)
    • Edit/Delete actions per rule
    • Empty state when no CORS configured
    • Collapsible sections for better UX with many rules
  • CorsRuleForm.tsx: Form for creating/editing individual CORS rules:
    • TagInput component for managing string arrays (origins, headers, exposed headers)
    • HTTP method checkboxes (GET, POST, PUT, DELETE, HEAD)
    • MaxAgeSeconds number input with validation (0-86400)
    • Optional rule ID input (max 255 chars)
    • Real-time Zod validation with error messages
    • Save/Cancel actions
  • DeleteCorsModal.tsx: Confirmation modal for deleting entire CORS configuration
  • TagInput.tsx: Reusable component for managing string arrays:
    • Add tags via input + Enter/Add button
    • Remove tags via X button
    • Validation (required arrays must have at least one item)
    • Used for AllowedOrigins, AllowedHeaders, ExposeHeaders
  • BucketModals.tsx: Orchestration component that manages which modal is open (CORS, Policy, Delete, etc.)
  • BucketToastNotifications.tsx: Toast notifications for CORS operations (success/error)

Component Updates

  • BucketHeader.tsx: Added CORS button to bucket detail header actions
  • BucketHeaderActions.tsx: Wired CORS modal open handler
  • useBucketInfo.ts: Added CORS data query to consolidated bucket info hook:
    • Fetches CORS configuration alongside versioning, policy, and bucket metadata
    • 5-minute cache, no retry (404 is normal for no CORS)
    • Used to show CORS status in bucket details

Test Updates

  • Get CORS: existing config, no config (null), bucket not found
  • Set CORS: create new, replace existing, validation errors, bucket not found
  • Delete CORS: existing config, no config (idempotent), bucket not found
  • S3 error mapping for all operations

Related Issues

None

Key Technical Details

CORS Rule Validation

  • AllowedMethods: 1-5 methods per rule (GET, POST, PUT, DELETE, HEAD)
  • AllowedOrigins: At least 1 origin required (supports wildcards like * or https://*.example.com)
  • AllowedHeaders / ExposeHeaders: Optional arrays
  • MaxAgeSeconds: 0-86400 (0-24 hours), controls browser preflight cache
  • ID: Optional, max 255 characters, must be unique per bucket
  • Total rules: 1-100 per bucket (S3/Ceph limit)

Architecture Pattern

Follows aurora-dashboard's standard tRPC + React Query pattern:

  • Backend: cephProtectedProcedure enforces EC2 credentials, uses AWS SDK v3 S3 commands
  • Frontend: trpcReact hooks with React Query for caching/invalidation
  • Error handling: S3 errors mapped to tRPC errors via mapS3ErrorToTRPCError
  • Idempotent operations: get returns null for no config, delete succeeds if already deleted (NoSuchCORSConfiguration is not an error)
  • Validation: Zod schemas enforce constraints on both client and server

UX Decisions

  • NoSuchCORSConfiguration is not an error: No CORS config is a valid state, not a 404
  • Full replacement on save: PutBucketCorsCommand replaces entire configuration (S3 behavior)
  • Per-rule editing: Users can edit individual rules inline without affecting others
  • Tag-based input: Origins/headers use TagInput component for better UX than comma-separated text
  • 5-minute cache: CORS config rarely changes, so longer cache than object listings (30s)

Testing Instructions

  1. pnpm i
  2. pnpm run test
  3. Manual testing:
    • Navigate to Storage > Ceph > Buckets > (select a bucket)
    • Click "CORS" button in bucket header
    • View tab: Should show "No CORS configuration" if none exists
    • Add tab: Create a CORS rule with:
      • Allowed Origins: https://example.com
      • Allowed Methods: GET, POST
      • Max Age Seconds: 3600
    • Save and verify rule appears in View tab
    • Edit the rule: add another origin, save
    • Delete the CORS configuration via Delete button
    • Test with different permission levels (read-only should disable edit/delete)

Checklist

  • I have performed a self-review of my code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • I have made corresponding changes to the documentation (if applicable).
  • My changes generate no new warnings or errors.

Summary by CodeRabbit

  • New Features

    • Added CORS configuration management for Ceph buckets.
    • View, add, edit, and delete CORS rules from the bucket interface.
    • Configure origins, methods, headers, exposed headers, rule IDs, and cache duration.
    • Added validation, wildcard-origin security warnings, and success/error notifications.
    • Added localized labels, guidance, and error messages for CORS workflows.
  • Bug Fixes

    • Improved handling of missing or malformed CORS configurations and related storage errors.
  • Tests

    • Added coverage for retrieving, saving, deleting, validating, and handling errors for CORS configurations.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3b8d1b11-d5ad-4323-8168-c4dc9b208b94

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds Ceph bucket CORS schemas, get/set/delete APIs, validation, rule-management components, bucket actions, modal flows, notifications, status loading, tests, and English/German translation entries.

Changes

Ceph bucket CORS management

Layer / File(s) Summary
CORS API contract and procedures
packages/aurora/src/server/Storage/types/ceph.ts, packages/aurora/src/server/Storage/routers/ceph/*, packages/aurora/src/server/Storage/helpers/s3ErrorMapper.ts
Adds validated CORS schemas and Ceph S3 procedures for retrieving, applying, and deleting bucket CORS configurations, with mapped error handling and router tests.
Rule editor and viewer
packages/aurora/src/client/routes/.../Ceph/Buckets/{TagInput,CorsRuleForm,CorsRulesViewer}.tsx
Adds tag entry, URL/header validation, CORS rule creation/editing, rule display, wildcard warnings, and per-rule actions.
CORS configuration and deletion modals
packages/aurora/src/client/routes/.../Ceph/Buckets/{CorsModal,DeleteCorsModal}.tsx
Adds modal state management for loading, editing, saving, deleting, resetting, and invalidating CORS configuration.
Bucket controls, data loading, and notifications
packages/aurora/src/client/routes/.../Ceph/{Buckets/*,hooks/useBucketInfo.ts}, packages/aurora/src/locales/{en,de}/*
Adds CORS status badges, bucket actions, modal wiring, toast helpers, query loading, and localized CORS UI strings.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant BucketHeader
  participant CorsModal
  participant corsRouter
  participant CephS3
  User->>BucketHeader: Open or edit CORS
  BucketHeader->>CorsModal: Open modal
  CorsModal->>corsRouter: Load CORS rules
  corsRouter->>CephS3: GetBucketCorsCommand
  CephS3-->>corsRouter: CORS configuration
  corsRouter-->>CorsModal: Validated rules
  User->>CorsModal: Save or delete configuration
  CorsModal->>corsRouter: Set or delete CORS
  corsRouter->>CephS3: PutBucketCorsCommand or DeleteBucketCorsCommand
  CephS3-->>corsRouter: Operation result
  corsRouter-->>CorsModal: Success or mapped error
Loading

Possibly related issues

Possibly related PRs

Suggested labels: enhancement, object storage, ux improvement

Suggested reviewers: andypf

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding CORS configuration management for Ceph/S3 buckets.
Description check ✅ Passed The description covers the required summary, changes, related issues, testing, and checklist; screenshots are the only missing optional section.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch kiryl-ceph-cors

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.

@KirylSAP KirylSAP self-assigned this Jul 24, 2026
@KirylSAP KirylSAP added aurora-portal pr-build Triggers Docker image build for PR preview push image to GHCR. Ceph labels Jul 24, 2026
@KirylSAP
KirylSAP marked this pull request as ready for review July 24, 2026 12:35
@KirylSAP
KirylSAP requested a review from a team as a code owner July 24, 2026 12:35
@KirylSAP
KirylSAP marked this pull request as draft July 24, 2026 12:35

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 8

🧹 Nitpick comments (1)
packages/aurora/src/server/Storage/routers/ceph/corsRouter.test.ts (1)

70-190: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a get test for a stored rule that violates the write schema. All get tests feed rules that satisfy corsRuleSchema. There's no case where S3 returns e.g. MaxAgeSeconds > 86400, which currently makes get fail (see the read-validation issue in corsRouter.ts). A test here would pin down the intended behavior once that's resolved.

🤖 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 `@packages/aurora/src/server/Storage/routers/ceph/corsRouter.test.ts` around
lines 70 - 190, The get test suite lacks coverage for stored CORS rules that
violate corsRuleSchema, such as a rule with MaxAgeSeconds greater than 86400.
Add a caller.get test using mockSend with an otherwise valid rule containing
this out-of-range value, and assert the intended successful read behavior after
the corsRouter.ts validation fix.
🤖 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
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/storage/-components/Ceph/Buckets/BucketModals.tsx:
- Around line 137-150: Update the CorsModal callback handling in BucketModals so
save and delete operations select distinct success and error toasts. Split the
callbacks or propagate the completed operation from CorsModal, then use
deletion-specific toast helpers for deleting all rules while preserving the
existing save toasts for save operations.

In
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/storage/-components/Ceph/Buckets/CorsModal.tsx:
- Around line 113-115: Update the CorsRuleForm rendering to use a React key
based on the current editing target, such as the editing rule index or null
state, so canceling via handleCancelEdit remounts the form and clears stale
values. Apply the change at the <CorsRuleForm> usage rather than modifying
handleCancelEdit.

In
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/storage/-components/Ceph/Buckets/CorsRuleForm.tsx:
- Around line 16-40: The CorsRuleForm retains stale edit values after
cancellation because its defaultValues are only applied on initial mount. Update
the CorsRuleForm usage or its lifecycle so canceling and setting editingRule to
null remounts it with a distinct key or resets all fields to blank defaults,
while preserving the existing values when editing a rule.

In
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/storage/-components/Ceph/Buckets/CorsRulesViewer.tsx:
- Around line 36-39: Replace the manual rule/rules ternary in the surrounding
Trans block with Lingui’s Plural macro/component, passing rules.length as the
plural count so translators control localized wording and word order; apply the
same change to the minute/minutes message around the related duration display,
and preserve the existing counts and surrounding UI behavior.
- Around line 59-65: Update the warning Message in CorsRulesViewer to use the
useLingui() t macro for its title, matching the translated title behavior in
CorsModal.tsx while preserving the existing warning content and styling.

In
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/storage/-components/Ceph/Buckets/DeleteCorsModal.tsx:
- Around line 129-135: Update the CORS count message in DeleteCorsModal to use
Lingui’s Plural component instead of the inline rule/rules ternary. Bind the
corsRules.length value through the count binding and provide locale-aware
one/other plural forms without interpolating the count directly into the one
prop.

In
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/storage/-components/Ceph/Buckets/TagInput.tsx:
- Around line 97-111: The urlValidator currently rejects wildcard-subdomain
origins because URL parsing does not accept * in hostnames. Update urlValidator
to validate http://*.example.com and https://*.example.com forms using
wildcard-safe parsing, while continuing to require only http: or https:
protocols and preserving existing validation results for ordinary URLs.

In `@packages/aurora/src/server/Storage/routers/ceph/corsRouter.ts`:
- Around line 44-67: Update the CORS read path around the rawCorsRules mapping
so GET operations do not parse stored rules with the write-only corsRuleSchema.
Use a lenient read schema or safe per-rule parsing that accepts valid S3/Ceph
values, including larger MaxAgeSeconds and longer AllowedMethods collections,
while preserving the existing null and error handling behavior.

---

Nitpick comments:
In `@packages/aurora/src/server/Storage/routers/ceph/corsRouter.test.ts`:
- Around line 70-190: The get test suite lacks coverage for stored CORS rules
that violate corsRuleSchema, such as a rule with MaxAgeSeconds greater than
86400. Add a caller.get test using mockSend with an otherwise valid rule
containing this out-of-range value, and assert the intended successful read
behavior after the corsRouter.ts validation fix.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 97cfcf9f-87c6-476a-9e46-739e2df9a805

📥 Commits

Reviewing files that changed from the base of the PR and between 24a187e and 1f7a0d9.

📒 Files selected for processing (20)
  • packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/BucketHeader.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/BucketHeaderActions.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/BucketModals.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/BucketToastNotifications.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/CorsModal.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/CorsRuleForm.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/CorsRulesViewer.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/DeleteCorsModal.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/TagInput.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/hooks/useBucketInfo.ts
  • packages/aurora/src/locales/de/messages.po
  • packages/aurora/src/locales/de/messages.ts
  • packages/aurora/src/locales/en/messages.po
  • packages/aurora/src/locales/en/messages.ts
  • packages/aurora/src/server/Storage/helpers/s3ErrorMapper.ts
  • packages/aurora/src/server/Storage/routers/ceph/corsRouter.test.ts
  • packages/aurora/src/server/Storage/routers/ceph/corsRouter.ts
  • packages/aurora/src/server/Storage/routers/ceph/index.ts
  • packages/aurora/src/server/Storage/routers/index.ts
  • packages/aurora/src/server/Storage/types/ceph.ts

Comment on lines +137 to +150
<CorsModal
isOpen={activeModal === "cors"}
bucketName={bucketName}
onClose={onClose}
onSuccess={(bucketName) => {
const { message, ...options } = getCorsSavedToast(bucketName)
toast.success(message, options)
onClose()
}}
onError={(bucketName, errorMessage) => {
const { message, ...options } = getCorsSaveErrorToast(bucketName, errorMessage)
toast.error(message, options)
onClose()
}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Report editor-initiated deletion as deletion.

CorsModal calls these callbacks for both saving and deleting all rules. Its delete path therefore displays “CORS Configuration Saved” or a save-error toast. Split save/delete callbacks, or pass the completed operation, and select the matching toast.

🤖 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
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/storage/-components/Ceph/Buckets/BucketModals.tsx
around lines 137 - 150, Update the CorsModal callback handling in BucketModals
so save and delete operations select distinct success and error toasts. Split
the callbacks or propagate the completed operation from CorsModal, then use
deletion-specific toast helpers for deleting all rules while preserving the
existing save toasts for save operations.

Comment on lines +113 to +115
const handleCancelEdit = () => {
setEditingRuleIndex(null)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Root cause of stale form values is in CorsRuleForm.tsx.

See the review comment on CorsRuleForm.tsx (lines 16-40): calling onCancel here changes editingRule from a rule object to null without remounting CorsRuleForm, so the form doesn't reset. Consolidated fix is to key <CorsRuleForm> by the editing target (line 232).

🤖 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
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/storage/-components/Ceph/Buckets/CorsModal.tsx
around lines 113 - 115, Update the CorsRuleForm rendering to use a React key
based on the current editing target, such as the editing rule index or null
state, so canceling via handleCancelEdit remounts the form and clears stale
values. Apply the change at the <CorsRuleForm> usage rather than modifying
handleCancelEdit.

Comment on lines +16 to +40
export const CorsRuleForm = ({ editingRule, onSubmit, onCancel, isSaving }: CorsRuleFormProps) => {
const { t } = useLingui()

const form = useForm({
defaultValues: {
ID: editingRule?.ID || "",
AllowedOrigins: editingRule?.AllowedOrigins || [],
AllowedMethods: (editingRule?.AllowedMethods || []) as string[],
AllowedHeaders: editingRule?.AllowedHeaders || [],
ExposeHeaders: editingRule?.ExposeHeaders || [],
MaxAgeSeconds: editingRule?.MaxAgeSeconds?.toString() || "",
},
onSubmit: async ({ value }) => {
const newRule: CorsRule = {
ID: value.ID || undefined,
AllowedOrigins: value.AllowedOrigins,
AllowedMethods: value.AllowedMethods as ("GET" | "PUT" | "POST" | "DELETE" | "HEAD")[],
AllowedHeaders: value.AllowedHeaders.length > 0 ? value.AllowedHeaders : undefined,
ExposeHeaders: value.ExposeHeaders.length > 0 ? value.ExposeHeaders : undefined,
MaxAgeSeconds: value.MaxAgeSeconds ? parseInt(value.MaxAgeSeconds, 10) : undefined,
}

onSubmit(newRule)
},
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Locate relevant files =="
fd -a 'Cors(RuleForm|Modal)\.tsx$|CorsRuleForm\.tsx$|CorsModal\.tsx$' . || true

echo "== Search for CorsRuleForm definition and CorsModal usages =="
rg -n "CorsRuleForm|handleCancelEdit|editingRule|activeTab|Tab\.ADD" -S . --glob '*.tsx' --glob '*.ts' | head -200

echo "== Git diff stat/name-only (context) =="
git diff --stat || true
git diff --name-only || true

Repository: cobaltcore-dev/aurora-dashboard

Length of output: 10006


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== CorsModal outline/contents =="
wc -l packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/CorsModal.tsx
sed -n '1,270p' packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/CorsModal.tsx

echo "== CorsRuleForm relevant contents =="
sed -n '1,80p' packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/CorsRuleForm.tsx
sed -n '160,185p' packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/CorsRuleForm.tsx

echo "== Package lock files/deps mentioning `@tanstack/react-form` =="
rg -n '"`@tanstack/react-form`"|`@tanstack/react-form`' package.json pnpm-lock.yaml package-lock.json yarn.lock 2>/dev/null || true

Repository: cobaltcore-dev/aurora-dashboard

Length of output: 244


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== CorsModal contents =="
wc -l 'packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/CorsModal.tsx'
sed -n '1,270p' 'packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/CorsModal.tsx'

echo "== CorsRuleForm relevant contents =="
sed -n '1,80p' 'packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/CorsRuleForm.tsx'
sed -n '160,185p' 'packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/CorsRuleForm.tsx'

echo "== Package lock files/deps mentioning `@tanstack/react-form` =="
rg -n '"`@tanstack/react-form`"|`@tanstack/react-form`' package.json pnpm-lock.yaml package-lock.json yarn.lock 2>/dev/null || true

echo "== Behavioral/static verifier: tab-cancel flow and Form call site =="
python3 - <<'PY'
from pathlib import Path
p = Path('packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/CorsModal.tsx')
text = p.read_text()
checks = {
    'handleCancelEdit': 'const handleCancelEdit' in text,
    'sets editingRuleIndex null': 'setEditingRuleIndex(null)' in text,
    'activeTab unchanged in handleCancelEdit': 'handleCancelEdit' in text and text.split('const handleCancelEdit')[1].split('};')[0].lsplit('const handleDelete')[0] is not None and 'setActiveTab' not in text.split('const handleCancelEdit')[1].split('};')[0],
    'CorsRuleForm mounted in activeTab ADD': '<CorsRuleForm' in text and 'activeTab === Tab.ADD' in text,
    'editingRule prop keyed by nullity only': 'editingRule={editingRuleIndex !== null ? currentRules[editingRuleIndex] : null}' in text,
    'form key absent': 'key=' not in text.split('<CorsRuleForm', 1)[1].split('/')[0],
}
for k,v in checks.items():
    print(f'{k}: {v}')
PY

Repository: cobaltcore-dev/aurora-dashboard

Length of output: 12151


🌐 Web query:

TanStack Form v1 useForm defaultValues prop changes do not reset form unless remount reset

💡 Result:

In TanStack Form v1, issues where defaultValues prop changes do not automatically reset the form or where form.reset() behaves inconsistently often stem from how the library manages internal state and options synchronization, particularly within the React adapter [1][2][3]. If you find that your form is not resetting as expected when defaultValues change, consider the following points: 1. Reactivity and useMemo: The documentation emphasizes that if the defaultValues object changes, the form state will be reset to the new values [4]. To prevent unnecessary or incorrect resets, it is critical to use React.useMemo when defining your defaultValues object, ensuring it only changes when the underlying data actually changes [4]. 2. Reset Behavior Issues: Historically, there have been issues where calling form.reset(values) inside asynchronous functions (such as onSubmit) or when prop-driven updates occur caused the form to revert to the initial defaultValues instead of the new ones provided [1][2][3]. These behaviors have been addressed in various patch releases (e.g., v1.14.1 and subsequent fixes) aimed at ensuring reset() and update() honor the intended values [1][2][3]. If you are experiencing this, ensure you are using the latest version of the library. 3. Using form.reset() explicitly: If you need to force a reset to new values, the form.reset(newValues) method is the intended API [5]. If you want to change the defaults but maintain the current state, or vice versa, you can provide options to reset(values, { keepDefaultValues: ... }) [5]. 4. Workarounds: If your form remains "stuck" due to stale options during re-renders, some developers have found that ensuring the form instance is properly updated or, in extreme cases, remounting the component (using a key prop) forces a clean initialization [1][3]. However, with recent fixes to FormApi—such as the introduction of internal flags to track when default values have been overridden—the library should now correctly preserve values even when subsequent re-renders occur [3]. If you are still facing persistent issues, check the TanStack Form repository for the latest bug reports related to your specific framework adapter, as these issues are often environment-specific [6][7].

Citations:


🌐 Web query:

TanStack Form @tanstack/react-form 1.29.0 source defaultValues useEffect useDefaultValues change form reset

💡 Result:

In TanStack Form (including version 1.29.0), managing defaultValues and form state requires careful handling to ensure reactivity and state consistency [1][2]. When you need to update the default values of a form (e.g., after a successful API save to reset the "dirty" state), you can use the form.reset method [3]. Calling form.reset(newDefaultValues) updates the form's internal baseline values to those provided [3]. If you are experiencing issues where default values appear to be "lagging" or reverting, consider the following technical nuances: 1. Reactivity and Rerenders: TanStack Form uses a store-based reactivity model [1]. If you need a component to react to updates in default values or dirty state, ensure you are utilizing the provided subscription methods (like form.Subscribe or useStore) rather than relying on direct access to formApi.options [1]. 2. Race Conditions: Issues have been observed where calling form.reset inside an onSubmit handler can lead to race conditions where the internal state is overwritten by subsequent updates [2]. If form.reset is failing to reflect your new values, a common workaround identified by the community is to wrap the reset call in a setTimeout, though this is a symptom of internal state synchronization and not a permanent architectural solution [2]. 3. Updating Options Safely: If you use form.update to change default values, be aware that it replaces the entire options object [4]. To safely update defaultValues without losing other configurations like onSubmit or validators, you must spread the existing options: form.update({...form.options, defaultValues: newValues }) [4] The concept of a useDefaultValues hook does not exist as a native feature in the public TanStack Form API; instead, the lifecycle of default values is managed directly via the form instance methods reset and update [3][4]. If you find your values are not updating as expected, ensure you are not triggering form updates in a way that conflicts with ongoing submission processes [2].

Citations:


🌐 Web query:

github TanStack form packages react form useForm defaultValues remount source

💡 Result:

In TanStack Form, the defaultValues provided to the useForm hook are intended to be the initial state of the form [1][2]. If these defaultValues change, the form's internal state will typically be reset to the new values [3]. Regarding behavior during remounts and updates: 1. Initialization: When the component calling useForm mounts, the form is initialized with the provided defaultValues [4][3]. 2. Updating defaultValues: Because the useForm hook integrates with React's lifecycle, if the defaultValues object passed to the hook changes (e.g., due to a parent re-render with new props or fetched data), TanStack Form detects this change and updates the form's state accordingly [3]. It is recommended to use useMemo for the defaultValues object to prevent unnecessary re-initializations [3]. 3. Remounting: If the component calling useForm unmounts and remounts, the useForm hook will run again, creating a fresh FormApi instance [5]. Consequently, the form state will be reset to the current defaultValues. If you need to "reset" a form while keeping it mounted, you should use the form.reset() method [3][6]. 4. Known Issues and Workarounds: - A common issue arises when form.reset(newValues) is called (e.g., inside an onSubmit handler), but the form subsequently reverts to the original defaultValues due to a re-render where the component's useForm options are re-evaluated [6][7]. This is because the component closure may still hold the "stale" original defaultValues. - Recent patches (e.g., PR #2218) have introduced internal flags (like _defaultValuesOverridden) to prevent this regression, ensuring that explicit calls to reset(newValues) are preserved even when the component re-renders with stale options [7]. - If you are experiencing unexpected resets, ensure you are not passing a non-memoized object as defaultValues and consider using the { keepDefaultValues: true } option if you wish to treat the new values as the new base defaults [8]. For handling asynchronous data, TanStack documentation suggests avoiding manual useEffect synchronizations where possible and instead leveraging the defaultValues prop directly or coordinating with data-fetching libraries like TanStack Query [9][4].

Citations:


🌐 Web query:

site:github.com/tanstack/form@1.29.0 packages/react-form src useForm.tsx

💡 Result:

The file packages/react-form/src/useForm.tsx is a core component of the TanStack Form library for React [1][2]. It defines the useForm custom React hook, which serves as the primary entry point for managing form state, handling submissions, and interacting with form fields within a React application [3][4]. Key technical aspects of this file include: 1. Functionality: The hook returns an extended instance of the FormApi class, which encapsulates the form's logic, including state management and field interactions [3][4]. 2. Implementation: It leverages TanStack Form's core functionality (from @tanstack/form-core) and provides React-specific extensions, such as integration with React's lifecycle and rendering patterns (e.g., useMemo, useState, and useIsomorphicLayoutEffect) [1][5]. 3. API: It accepts optional form configuration (FormOptions) and returns a ReactFormExtendedApi, which includes utilities like handleSubmit, formId, and the current form state [1][3][6]. This file is central to the library's architecture, bridging the framework-agnostic core logic with React-specific components like Field and Subscribe [7][5].

Citations:


🌐 Web query:

@tanstack/form-core 1.29.0 defaultValues sync formApi.options defaultValues reset

💡 Result:

In @tanstack/form-core version 1.29.0, the relationship between defaultValues, formApi.options, and the reset method is governed by a prioritize-field-over-form strategy and specific mechanisms to ensure state consistency [1][2]. Core Reset Behavior When you call form.reset(values, opts), the behavior depends on the parameters provided: - If values are passed and keepDefaultValues is false (default), the form updates its internal formApi.options.defaultValues to the new values and resets the form state to those new values [3][4]. - If no values are passed, the form resets to the current baseline defaults [3][4]. Field-over-Form Prioritization As of the 1.29.0 release cycle, TanStack Form employs a prioritized default system [1][2]: - When calculating default values (e.g., for form.reset or deriving isDefaultValue), the library prioritizes field-level default values (defined on individual field instances) over form-level default values (defined in form options) [2]. - This ensures that when the form resets, it respects the specific default values configured at the field level, preventing "reset betrayal" where form-level defaults unexpectedly override field-specific ones [2]. State Synchronization and Overwrite Prevention TanStack Form includes mechanisms to prevent stale component props from overriding a manual reset: - When reset(newValues) is called, it sets an internal flag (e.g., _defaultValuesOverridden) to signal that the user has explicitly changed the baseline defaults [5]. - During subsequent updates (often triggered by framework re-renders), the form checks this flag to prevent the adapter from overwriting the recently reset values with stale ones from the original component props [6][5]. This ensures that values set via reset persist even if the component re-renders with its original default prop [6][5]. If you find that defaultValues are "lagging" or reverting unexpectedly, it is typically due to React (or other framework) adapters re-rendering with stale options, which the library actively attempts to guard against in recent patches like 1.29.0 [6][5][7].

Citations:


Reset CorsRuleForm when canceling edit.

handleCancelEdit only clears editingRuleIndex; activeTab stays on Tab.ADD, so the <CorsRuleForm> mount with stale defaultValues is reused. The form continues showing the edited rule’s values after canceled edit. Add a key on the <CorsRuleForm> based on the edit target, or reset the form to blank values when editingRule becomes null.

🤖 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
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/storage/-components/Ceph/Buckets/CorsRuleForm.tsx
around lines 16 - 40, The CorsRuleForm retains stale edit values after
cancellation because its defaultValues are only applied on initial mount. Update
the CorsRuleForm usage or its lifecycle so canceling and setting editingRule to
null remounts it with a distinct key or resets all fields to blank defaults,
while preserving the existing values when editing a rule.

Comment on lines +36 to +39
<Trans>
{rules.length} {rules.length === 1 ? "rule" : "rules"} configured
</Trans>
)}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Hardcoded pluralization words bypass i18n (see also line 206-210).

rules.length === 1 ? "rule" : "rules" is passed as an interpolated value inside <Trans>, so translators only ever see the generic "{0} {1} configured" message and the English words "rule"/"rules" are baked in verbatim for every locale — German (and others) can't render correct pluralization/word order. Use Lingui's Plural macro/component instead of a manual ternary. The same pattern recurs for "minute"/"minutes" at lines 206-210; see consolidated note.

Static analysis also flags this line ("Should be ${variable}, not ${object.property} or ${myFunction()}"), and the same rule additionally flags index + 1 at line 132 (lower-impact, since that's just a numeric placeholder).

🧰 Tools
🪛 GitHub Check: lint

[warning] 37-37:
Should be ${variable}, not ${object.property} or ${myFunction()}


[warning] 37-37:
Should be ${variable}, not ${object.property} or ${myFunction()}

🤖 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
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/storage/-components/Ceph/Buckets/CorsRulesViewer.tsx
around lines 36 - 39, Replace the manual rule/rules ternary in the surrounding
Trans block with Lingui’s Plural macro/component, passing rules.length as the
plural count so translators control localized wording and word order; apply the
same change to the minute/minutes message around the related duration display,
and preserve the existing counts and surrounding UI behavior.

Source: Linters/SAST tools

Comment on lines +59 to +65
<Message variant="warning" title="Security Warning" className="mb-4">
<Trans>
One or more rules use wildcard (*) for AllowedOrigins, which allows any website to access your bucket. Only
use this for truly public resources.
</Trans>
</Message>
)}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Hardcoded title="Security Warning" is never translated.

Unlike the equivalent warning in CorsModal.tsx (title={t\Security Warning`}), this titleis a plain string literal, so it will always render in English regardless of locale even though a "Security Warning" catalog entry exists. Use theuseLingui() t` macro here too.

🌐 Proposed fix
+import { Trans, useLingui } from "`@lingui/react/macro`"
...
 export const CorsRulesViewer = ({...}: CorsRulesViewerProps) => {
+  const { t } = useLingui()
   const hasWildcardOrigin = rules.some((rule) => rule.AllowedOrigins.includes("*"))
   ...
-        <Message variant="warning" title="Security Warning" className="mb-4">
+        <Message variant="warning" title={t`Security Warning`} className="mb-4">
📝 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
<Message variant="warning" title="Security Warning" className="mb-4">
<Trans>
One or more rules use wildcard (*) for AllowedOrigins, which allows any website to access your bucket. Only
use this for truly public resources.
</Trans>
</Message>
)}
<Message variant="warning" title={t`Security Warning`} className="mb-4">
<Trans>
One or more rules use wildcard (*) for AllowedOrigins, which allows any website to access your bucket. Only
use this for truly public resources.
</Trans>
</Message>
)}
🤖 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
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/storage/-components/Ceph/Buckets/CorsRulesViewer.tsx
around lines 59 - 65, Update the warning Message in CorsRulesViewer to use the
useLingui() t macro for its title, matching the translated title behavior in
CorsModal.tsx while preserving the existing warning content and styling.

Comment on lines +129 to +135
{corsData?.corsRules && (
<p className="text-theme-default text-sm">
<Trans>
Current configuration has {corsData.corsRules.length} CORS{" "}
{corsData.corsRules.length === 1 ? "rule" : "rules"}.
</Trans>
</p>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="packages/aurora/src/client/routes/_auth/projects/\$projectId/storage/-components/Ceph/Buckets/DeleteCorsModal.tsx"
echo "== file exists =="
if [ -f "$file" ]; then echo yes; wc -l "$file"; else echo no; fi

echo "== relevant file excerpt =="
sed -n '1,190p' "$file"

echo "== Lingui plural usage in same repo =="
rg -n "Plural|count=|pluralized|i18n|macro" packages/aurora/src -g '*.tsx' -g '*.ts' | head -200

echo "== inline ternary message occurrences =="
rg -n '\? ".*\. ?" : ".*\. ?"|rule" : "rules|Plural' packages/aurora/src/client/routes/_auth/projects/"$projectId"/storage/-components/Ceph/Buckets/DeleteCorsModal.tsx packages/aurora/src/client/routes/_auth/projects -g '*.tsx' -g '*.ts' | head -200 || true

Repository: cobaltcore-dev/aurora-dashboard

Length of output: 32298


🌐 Web query:

Lingui Plural component documentation value one other

💡 Result:

In LinguiJS, the Plural component is used to handle quantity-dependent messages [1][2]. It requires a value prop and an other prop, which serves as the required catch-all plural form [3][1]. When using the Plural component: - value: Specifies the number used to determine the plural form [3][1]. - one: Represents the singular form (e.g., used for "1") [3][1]. - other: The required general plural form (used if no other specific rule matches) [3][1]. - #: A special placeholder that is automatically replaced with the actual value of the value prop [2]. Example usage: Beyond one and other, you may use other CLDR-compliant categories such as zero, two, few, or many, depending on the requirements of the target language [3][4]. Additionally, you can handle exact numeric matches by using props prefixed with an underscore, such as _0 for "0" or _1 for "1" [1][2]. Exact matches take precedence over category-based plural forms [2][5].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== lint config references for lingui/localized strings =="
rg -n "localized|localization|lingui|I18n|Plural|rule" -g '*lint*' -g '*eslintrc*' -g '*.json' -g '*.js' -g '*.mjs' . \
  | head -300 || true

echo "== examples of Plural with bound count in storage components =="
sed -n '1,40p' "packages/aurora/src/client/routes/_auth/projects/\$projectId/storage/-components/Swift/Objects/index.tsx"
sed -n '100,118p' "packages/aurora/src/client/routes/_auth/projects/\$projectId/storage/-components/Swift/Objects/DeleteObjectsModal.tsx"
sed -n '105,120p' "packages/aurora/src/client/routes/_auth/projects/\$projectId/storage/-components/Swift/Containers/ContainerToastNotifications.tsx"

echo "== deterministic Lingui Plural semantic probe =="
node - <<'JS'
const pluralForm = {
  one: "rule",
  other: "rules",
}
for (const n of [0, 1, 2, 3]) {
  const form = n === 1 ? "one" : "other"
  // Lingui's simplest English Plural behavior for one/other.
  console.log(`${n}: ${form === "one" ? pluralForm.one : pluralForm.other}`)
}
JS

Repository: cobaltcore-dev/aurora-dashboard

Length of output: 35739


Use Lingui pluralization for the CORS rule count.

The inline English ternary won’t use locale-specific plural forms. Bind the count and replace it with Plural; avoid interpolating the count directly into the one prop so the value is handled by Plural.

Proposed fix
-import { Trans, useLingui } from "`@lingui/react/macro`"
+import { Plural, Trans, useLingui } from "`@lingui/react/macro`"
...
+  const corsRuleCount = corsData?.corsRules?.length ?? 0
...
-                  Current configuration has {corsData.corsRules.length} CORS{" "}
-                  {corsData.corsRules.length === 1 ? "rule" : "rules"}.
+                  Current configuration has {corsRuleCount} CORS{" "}
+                  <Plural value={corsRuleCount} one="rule" other="rules" />.
📝 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
{corsData?.corsRules && (
<p className="text-theme-default text-sm">
<Trans>
Current configuration has {corsData.corsRules.length} CORS{" "}
{corsData.corsRules.length === 1 ? "rule" : "rules"}.
</Trans>
</p>
{corsData?.corsRules && (
<p className="text-theme-default text-sm">
<Trans>
Current configuration has {corsRuleCount} CORS{" "}
<Plural value={corsRuleCount} one="rule" other="rules" />.
</Trans>
</p>
🧰 Tools
🪛 GitHub Check: lint

[warning] 133-133:
Should be ${variable}, not ${object.property} or ${myFunction()}


[warning] 132-132:
Should be ${variable}, not ${object.property} or ${myFunction()}

🤖 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
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/storage/-components/Ceph/Buckets/DeleteCorsModal.tsx
around lines 129 - 135, Update the CORS count message in DeleteCorsModal to use
Lingui’s Plural component instead of the inline rule/rules ternary. Bind the
corsRules.length value through the count binding and provide locale-aware
one/other plural forms without interpolating the count directly into the one
prop.

Source: Linters/SAST tools

Comment on lines +97 to +111
export const urlValidator = (url: string): { valid: boolean; error?: string } => {
if (url === "*") {
return { valid: true }
}

try {
const urlObj = new URL(url)
if (!["http:", "https:"].includes(urlObj.protocol)) {
return { valid: false, error: "URL must use http or https protocol" }
}
return { valid: true }
} catch {
return { valid: false, error: "Invalid URL format. Expected: https://example.com" }
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Locate file =="
fd -a 'TagInput.tsx$' . || true

echo "== Relevant file excerpt =="
if [ -f packages/aurora/src/client/routes/_auth/projects/\$projectId/storage/-components/Ceph/Buckets/TagInput.tsx ]; then
  sed -n '1,180p' packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/TagInput.tsx | cat -n
fi

echo "== Search urlValidator usages =="
rg -n "urlValidator|AllowedOrigin|CORS|cors" packages/aurora/src || true

echo "== JS URL behavior for wildcard candidates =="
node - <<'JS'
const cases = [
  "*",
  "http://*.example.com",
  "https://*.example.com",
  "http://example.com",
  "file://*.example.com",
  "http://a*b.example.com"
];
for (const url of cases) {
  let status = "allowed";
  try {
    const u = new URL(url);
    if (!["http:", "https:"].includes(u.protocol)) {
      status = { status:"disallowed", reason:`protocol ${u.protocol}` };
    } else {
      status = { status:"allowed", url: u.href };
    }
  } catch (e) {
    status = { status:"throws", message: e.message };
  }
  console.log(JSON.stringify({ input:url, status }));
}
JS

Repository: cobaltcore-dev/aurora-dashboard

Length of output: 382


🌐 Web query:

AWS S3 CORS AllowedOrigin wildcard * allowed http://*.example.com documentation

💡 Result:

In AWS S3 CORS configuration, you can use the asterisk () character in the AllowedOrigins element to define allowed origins [1]. There are two primary ways the wildcard is used: 1. Subdomain Wildcards: You can specify a pattern to allow multiple subdomains, such as http://.example.com [1]. According to official AWS documentation, the origin string in the AllowedOrigins element is permitted to contain exactly one asterisk () wildcard character [1]. 2. All Origins: You can specify a single asterisk () as the origin to enable all origins to send cross-origin requests [1][2]. When S3 processes a request, it evaluates the CORS configuration and uses the first rule that matches the incoming request's Origin header [3]. It is important to note that if you use the wildcard (*) as the origin, it may affect the ability to perform credentialed requests, as browsers typically do not allow an Access-Control-Allow-Origin response header of * when credentials (such as cookies or authorization headers) are involved [4].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE='packages/aurora/src/client/routes/_auth/projects/$projectId/storage/-components/Ceph/Buckets/TagInput.tsx'

echo "== Relevant file excerpt =="
sed -n '1,200p' "$FILE" | cat -n

echo "== Search urlValidator usages =="
rg -n "urlValidator|AllowedOrigin|CORS|cors|TagInput" packages/aurora/src || true

echo "== JS URL behavior for wildcard candidates =="
node - <<'JS'
const cases = ["*", "http://*.example.com", "https://*.example.com", "http://example.com", "file://*.example.com", "http://a*b.example.com"];
for (const url of cases) {
  let status = "allowed";
  try {
    const u = new URL(url);
    status = ["http:", "https:"].includes(u.protocol) ? { status:"allowed", url: u.href } : { status:"disallowed", reason: `protocol ${u.protocol}` };
  } catch (e) {
    status = { status:"throws", message: e.message };
  }
  console.log(JSON.stringify({ input: url, status }));
}
JS

Repository: cobaltcore-dev/aurora-dashboard

Length of output: 50388


Allow wildcard-subdomain origins in urlValidator.

S3/Ceph CORS accepts origins like http://*.example.com, but new URL(url) rejects any hostname containing *. This form validates AllowedOrigins with urlValidator, so wildcard-subdomain origins cannot be added through the UI even though the CORS backend schema allows string origins. Use a wildcard-safe URL check while still requiring http:/https:.

🤖 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
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/storage/-components/Ceph/Buckets/TagInput.tsx
around lines 97 - 111, The urlValidator currently rejects wildcard-subdomain
origins because URL parsing does not accept * in hostnames. Update urlValidator
to validate http://*.example.com and https://*.example.com forms using
wildcard-safe parsing, while continuing to require only http: or https:
protocols and preserving existing validation results for ordinary URLs.

Comment on lines +44 to +67

// S3 returns CORSRules array or undefined
const rawCorsRules = response.CORSRules ?? null

if (!rawCorsRules) {
return { corsRules: null }
}

// Validate and type-check the CORS rules using Zod schema
const corsRules = rawCorsRules.map((rule) => corsRuleSchema.parse(rule))

return { corsRules }
} catch (error) {
// NoSuchCORSConfiguration is not an error - it means no CORS config set
const s3Error = error as { name?: string; Code?: string }
if (s3Error.name === "NoSuchCORSConfiguration" || s3Error.Code === "NoSuchCORSConfiguration") {
return { corsRules: null }
}

throw mapS3ErrorToTRPCError(error, {
operation: "get CORS configuration",
bucket: bucketName,
})
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Does Ceph RGW or AWS S3 PutBucketCors reject MaxAgeSeconds greater than 86400?

💡 Result:

No, Ceph RGW (RADOS Gateway) does not have a hard-coded rejection of MaxAgeSeconds values greater than 86400 (one day) in its PutBucketCors implementation [1]. Technical details: 1. Implementation: In the Ceph RGW codebase, MaxAgeSeconds is processed as an unsigned integer [1]. The function RGWCORSRule::create_rule parses the provided MaxAgeSeconds string using strtoul without imposing an upper-bound check against the value 86400 [1]. 2. Validation: While Ceph RGW validates that the provided input is a valid integer, it does not enforce specific constraints on the magnitude of that integer beyond what the data type supports [2][3][1]. 3. Context: The number 86400 is often associated with the maximum cache time in various web contexts, but it is not a protocol-level limit enforced by S3 specifications or Ceph's current implementation for this parameter [4][5][1]. If you encounter an error when attempting to set a high value, it is likely due to either a malformed XML structure or an invalid integer format (e.g., non-numeric characters) rather than the size of the number itself [2][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Tracked files matching corsRouter/cors:"
git ls-files | rg '(^|/)corsRouter\.ts$|cors|CORS' || true

echo
echo "Candidate file lines:"
fd -a 'corsRouter\.ts$' . | while read -r f; do
  rel="${f#"$PWD"/}"
  echo "--- $rel"
  wc -l "$rel"
  sed -n '1,180p' "$rel" | cat -n
done

echo
echo "Schema references:"
rg -n "corsRuleSchema|MaxAgeSeconds|AllowedMethods|ID|Zod|safeParse|catch" -S .

Repository: cobaltcore-dev/aurora-dashboard

Length of output: 50388


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "CORS schema files:"
rg -n "corsRuleSchema|corsSchema|Zod|z\.object|MaxAgeSeconds|AllowedMethods|ID" -S --glob '!node_modules' --glob '!dist' --glob '!build' . | head -200

echo
echo "Exact target file:"
target="$(git ls-files | rg '(^|/)packages/aurora/src/server/Storage/routers/ceph/corsRouter\.ts$' | head -1)"
if [ -n "${target:-}" ]; then
  wc -l "$target"
  sed -n '1,180p' "$target" | cat -n
fi

Repository: cobaltcore-dev/aurora-dashboard

Length of output: 33328


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Locate ceph type definitions:"
fd -a 'ceph\.(ts|tsx|js|jsx)$' . | while read -r f; do
  rel="${f#"$PWD"/}"
  echo "--- $rel ($(wc -l < "$rel") lines)"
  rg -n "corsRuleSchema|corsConfigurationSchema|setCorsInputSchema|getCorsInputSchema|deleteCorsInputSchema|z\.object|MaxAgeSeconds|AllowedMethods" "$rel" || true
done

echo
echo "Search exact schema files in packages/aurora/src/server/types:"
for f in packages/aurora/src/server/types/ceph.ts packages/aurora/src/server/types/ceph/ceph.ts; do
  if [ -f "$f" ]; then
    echo "--- $f ($(wc -l < "$f") lines)"
    nl -ba "$f" | sed -n '1,260p'
  fi
done

Repository: cobaltcore-dev/aurora-dashboard

Length of output: 2212


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Relevant ceph schema definitions:"
nl -ba packages/aurora/src/server/Storage/types/ceph.ts | sed -n '488,570p'

echo
echo "Relevant response mapper behavior:"
fd -a 's3ErrorMapper\.(ts|tsx|js|jsx)$' . | while read -r f; do
  rel="${f#"$PWD"/}"
  echo "--- $rel ($(wc -l < "$rel") lines)"
  nl -ba "$rel" | sed -n '1,220p'
done

Repository: cobaltcore-dev/aurora-dashboard

Length of output: 243


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Relevant ceph schema definitions:"
sed -n '488,570p' packages/aurora/src/server/Storage/types/ceph.ts | awk '{printf "%4d  %s\n", NR+487, $0}'

echo
echo "Relevant response mapper behavior:"
while read -r f; do
  rel="${f#"$PWD"/}"
  echo "--- $rel"
  sed -n '1,220p' "$f" | awk '{printf "%4d  %s\n", NR, $0}'
done < <(git ls-files | rg '(^|/)s3ErrorMapper\.(ts|tsx|js|jsx)$')

Repository: cobaltcore-dev/aurora-dashboard

Length of output: 3641


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "List s3ErrorMapper candidates:"
git ls-files | grep -E '(^|/)s3ErrorMapper\.(ts|tsx|js|jsx)$' || true

echo
while IFS= read -r f; do
  rel="${f#"$PWD"/}"
  echo "--- $rel"
  sed -n '1,240p' "$f" | awk '{printf "%4d  %s\n", NR, $0}'
done < <(git ls-files | grep -E '(^|/)s3ErrorMapper\.(ts|tsx|js|jsx)$')

Repository: cobaltcore-dev/aurora-dashboard

Length of output: 314


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "--- packages/aurora/src/server/Storage/helpers/s3ErrorMapper.ts"
sed -n '1,240p' packages/aurora/src/server/Storage/helpers/s3ErrorMapper.ts | awk '{printf "%4d  %s\n", NR, $0}'

Repository: cobaltcore-dev/aurora-dashboard

Length of output: 4064


Don’t validate S3 read responses with the write-only CORS schema.

corsRuleSchema enforces write/UI-only constraints such as MaxAgeSeconds ≤ 86400 and AllowedMethods ≤ 5, while Ceph/S3 can store integer MaxAgeSeconds values and longer IDs. A bucket CORS config created outside this UI therefore makes get parse a returned rule and throw; that ZodError is caught as a generic error and mapped to INTERNAL_SERVER_ERROR, breaking view/edit/delete for the existing config. Use a lenient read schema or .safeParse/.catch per rule so reads can still load and delete existing configurations.

🧰 Tools
🪛 ast-grep (0.44.1)

[error] 33-67: Avoid SQL injection
Context: cephProtectedProcedure.input(getCorsInputSchema).query(async ({ ctx, input }): Promise => {
const s3 = ctx.getCephClient()
const { bucketName } = input

try {
  const response = await s3.send(
    new GetBucketCorsCommand({
      Bucket: bucketName,
    })
  )

  // S3 returns CORSRules array or undefined
  const rawCorsRules = response.CORSRules ?? null

  if (!rawCorsRules) {
    return { corsRules: null }
  }

  // Validate and type-check the CORS rules using Zod schema
  const corsRules = rawCorsRules.map((rule) => corsRuleSchema.parse(rule))

  return { corsRules }
} catch (error) {
  // NoSuchCORSConfiguration is not an error - it means no CORS config set
  const s3Error = error as { name?: string; Code?: string }
  if (s3Error.name === "NoSuchCORSConfiguration" || s3Error.Code === "NoSuchCORSConfiguration") {
    return { corsRules: null }
  }

  throw mapS3ErrorToTRPCError(error, {
    operation: "get CORS configuration",
    bucket: bucketName,
  })
}

})
Note: [CWE-89] Improper Neutralization of Special Elements used in an SQL Command ('SQL Injection').

(sql-injection-typescript)

🤖 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 `@packages/aurora/src/server/Storage/routers/ceph/corsRouter.ts` around lines
44 - 67, Update the CORS read path around the rawCorsRules mapping so GET
operations do not parse stored rules with the write-only corsRuleSchema. Use a
lenient read schema or safe per-rule parsing that accepts valid S3/Ceph values,
including larger MaxAgeSeconds and longer AllowedMethods collections, while
preserving the existing null and error handling behavior.

Source: Linters/SAST tools

@martawright

martawright commented Jul 29, 2026

Copy link
Copy Markdown

Updated modal flow for CORS Rules modal

-> TBC
Save or Add New Rule - which shoud be primary on all modal instances
see CORS Rules / Display and Save for the context
Add/Edit forms replace the "view" content in the existing Modal, and are in turn replaced by the "view" content on cancel/save

CORS Rules / empty
empty

CORS Rules / view
view

CORS Rules / configure add new rule
configure add new rule

CORS Rules / Save rule
Save rule

CORS Rules / Display and Save
Display and Save

@KirylSAP
KirylSAP force-pushed the kiryl-ceph-cors branch 2 times, most recently from 872612a to 2c4d996 Compare July 31, 2026 10:14
KirylSAP added 6 commits July 31, 2026 12:17
Add tRPC router with three endpoints (get/set/delete) for managing CORS configurations on S3/Ceph buckets. Includes Zod schemas for validation, comprehensive test coverage, and S3 error mapping. NoSuchCORSConfiguration is treated as a normal state (returns null), not an error.

Signed-off-by: KirylSAP <kiryl.mishchuk@sap.com>
Add modal components for viewing, creating, editing, and deleting CORS rules on Ceph buckets. CorsModal provides tabbed interface (View/Add), CorsRuleForm handles rule editing with TagInput for string arrays (origins, headers), CorsRulesViewer displays existing rules, DeleteCorsModal confirms deletion. All components use React Hook Form with Zod validation.

Signed-off-by: KirylSAP <kiryl.mishchuk@sap.com>
Add CORS badge to bucket header, CORS button to header actions, wire modals into BucketModals orchestrator, add toast notifications for CORS operations. Extend useBucketInfo hook to fetch CORS configuration with 5-minute cache and retry: false (no CORS is a normal state). Follows existing patterns from bucket policy and versioning features.

Signed-off-by: KirylSAP <kiryl.mishchuk@sap.com>
Add German and English translations for CORS modal titles, form labels, validation messages, toast notifications, and guidance text. Includes descriptions for CORS concepts (origins, methods, headers, MaxAgeSeconds) and wildcard security warnings.

Signed-off-by: KirylSAP <kiryl.mishchuk@sap.com>
Signed-off-by: KirylSAP <kiryl.mishchuk@sap.com>
Signed-off-by: KirylSAP <kiryl.mishchuk@sap.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

aurora-portal Ceph pr-build Triggers Docker image build for PR preview push image to GHCR. pr-preview

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants