Skip to content

Fixes #20464: [BugFix] Restrict rename replacement field size for snapshot restore#20465

Merged
cwperks merged 1 commit intoopensearch-project:mainfrom
meet-v25:main
Jan 23, 2026
Merged

Fixes #20464: [BugFix] Restrict rename replacement field size for snapshot restore#20465
cwperks merged 1 commit intoopensearch-project:mainfrom
meet-v25:main

Conversation

@meet-v25
Copy link
Copy Markdown
Contributor

@meet-v25 meet-v25 commented Jan 23, 2026

Description

Fixes an issueupon snapshot restore functionality. An attacker with snapshot restore permissions can crash cluster nodes by exploiting unbounded string expansion in the renameIndex() method. More details in Issue #20464 .

Related Issues

Resolves #20464

Check List

  • [ y ] Functionality includes testing.
  • [ na ] API changes companion pull request created, if applicable.
  • [ na ] Public documentation issue/PR created, if applicable.
  • ITs Success -> ./gradlew :server:internalClusterTest --tests "org.opensearch.snapshots.RestoreSnapshotIT"

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

@meet-v25 meet-v25 requested a review from a team as a code owner January 23, 2026 09:06
@github-actions github-actions bot added bug Something isn't working Storage:Snapshots labels Jan 23, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Jan 23, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

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.

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

📝 Walkthrough

Walkthrough

This PR addresses a denial-of-service vulnerability in snapshot restore operations by adding validation to limit the renameReplacement field size. The validation enforces a maximum byte size matching the index name limit in RestoreSnapshotRequest.validate(), preventing unbounded string expansion during index renaming. A corresponding test verifies that oversized replacement strings trigger a ValidationException.

Changes

Cohort / File(s) Summary
Validation logic
server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java
Added import for MetadataCreateIndexService and extended validate() method to enforce maximum byte size on renameReplacement field using MAX_INDEX_NAME_BYTES limit. Validation error is added if replacement is non-empty and exceeds the limit.
Test coverage
server/src/internalClusterTest/java/org/opensearch/snapshots/RestoreSnapshotIT.java
Added import for ValidationException and new test method testInvalidRenameReplacementPattern() that constructs a 100000-character replacement string and asserts ValidationException is thrown. Note: Test method appears twice in the file with identical signatures.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The code changes address all primary objectives: validation enforces max 255-byte limit on rename_replacement in RestoreSnapshotRequest, tests verify the fix prevents DoS attacks, matching Issue #20464 requirements.
Out of Scope Changes check ✅ Passed All changes are directly scoped to the CVE fix: adding validation to RestoreSnapshotRequest and tests in RestoreSnapshotIT, with no unrelated modifications.
Title check ✅ Passed The title clearly and concisely summarizes the main change: restricting rename replacement field size for snapshot restore to fix a security vulnerability.
Description check ✅ Passed The PR description addresses the core issue, mentions the related issue #20464, includes testing confirmation, and follows the template structure mostly correctly.

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


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.

Copy link
Copy Markdown
Contributor

@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

🤖 Fix all issues with AI agents
In
`@server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java`:
- Around line 262-271: The byte-length check for renameReplacement uses
getBytes() which depends on the platform charset; change it to compute UTF-8
bytes explicitly (use StandardCharsets.UTF_8) when comparing against
MetadataCreateIndexService.MAX_INDEX_NAME_BYTES so the validation matches
MetadataCreateIndexService.validateIndexOrAliasName; update the check in
RestoreSnapshotRequest where renameReplacement is validated to call
renameReplacement.getBytes(StandardCharsets.UTF_8) (or equivalent UTF-8 charset
API) and keep the existing validationException handling.
🧹 Nitpick comments (2)
server/src/internalClusterTest/java/org/opensearch/snapshots/RestoreSnapshotIT.java (1)

1367-1385: Test coverage looks good; consider adding boundary test cases.

The test correctly validates that oversized replacement strings are rejected. For completeness, consider adding boundary tests at exactly 255 bytes and 256 bytes to ensure the boundary condition is handled correctly.

Also, the method name testInvalidRenameReplacementPattern suggests testing the pattern, but it's actually testing the replacement string. A name like testInvalidRenameReplacementSize or testRenameReplacementExceedsMaxSize would be more precise.

💡 Suggested boundary test
public void testRenameReplacementBoundarySize() {
    setupSnapshotRestore();
    
    // Test at exactly MAX_INDEX_NAME_BYTES (255) - should pass validation
    String validReplacement = "a".repeat(255);
    // This should not throw ValidationException for size
    // (may fail for other reasons like index already exists)
    
    // Test at MAX_INDEX_NAME_BYTES + 1 (256) - should fail validation  
    String invalidReplacement = "a".repeat(256);
    ValidationException exception = expectThrows(
        ValidationException.class,
        () -> client().admin()
            .cluster()
            .prepareRestoreSnapshot(snapshotRepo, snapshotName1)
            .setWaitForCompletion(false)
            .setIndices(index)
            .setRenamePattern(index)
            .setRenameReplacement(invalidReplacement)
            .get()
    );
    assertTrue(exception.getMessage().contains("rename_replacement string size exceeds max allowed size"));
}
server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java (1)

262-271: Add length validation for renameAliasReplacement to prevent DoS via oversized alias names.

The renameAliasReplacement field uses the same replaceAll mechanism for alias renaming as renameReplacement does for index renaming. Since alias names are subject to the same 255-byte limit as index names (enforced by MetadataCreateIndexService.validateIndexOrAliasName), the resulting alias name after applying the replacement pattern could exceed this limit. Add equivalent validation for renameAliasReplacement length in the validate() method to match the protection already provided for renameReplacement.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5906e7b and 9aee9b8.

📒 Files selected for processing (2)
  • server/src/internalClusterTest/java/org/opensearch/snapshots/RestoreSnapshotIT.java
  • server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-01-13T17:40:27.167Z
Learnt from: reta
Repo: opensearch-project/OpenSearch PR: 20411
File: server/src/main/java/org/opensearch/index/codec/CodecService.java:112-133
Timestamp: 2026-01-13T17:40:27.167Z
Learning: Avoid capturing or evaluating a supplier (e.g., this::defaultCodec) upfront when passing it to a registry during object construction. If registries may replace defaults during iteration (as in EnginePlugin.getAdditionalCodecs), pass the supplier itself and only resolve it at use time. This ensures dynamic behavior is preserved during initialization and prevents premature binding of defaults in codecs/registry setup. This pattern should apply to similar initialization paths in Java server code where registries may mutate defaults during construction.

Applied to files:

  • server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java
🧬 Code graph analysis (1)
server/src/main/java/org/opensearch/action/admin/cluster/snapshots/restore/RestoreSnapshotRequest.java (1)
server/src/main/java/org/opensearch/cluster/metadata/MetadataCreateIndexService.java (1)
  • MetadataCreateIndexService (171-2093)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (20)
  • GitHub Check: gradle-check
  • GitHub Check: Analyze (java)
  • GitHub Check: assemble (21, windows-latest)
  • GitHub Check: detect-breaking-change
  • GitHub Check: precommit (25, ubuntu-24.04-arm)
  • GitHub Check: precommit (25, macos-15)
  • GitHub Check: precommit (21, windows-latest)
  • GitHub Check: assemble (21, ubuntu-24.04-arm)
  • GitHub Check: assemble (25, windows-latest)
  • GitHub Check: precommit (21, macos-15-intel)
  • GitHub Check: assemble (25, ubuntu-latest)
  • GitHub Check: assemble (21, ubuntu-latest)
  • GitHub Check: precommit (25, macos-15-intel)
  • GitHub Check: precommit (21, macos-15)
  • GitHub Check: precommit (25, ubuntu-latest)
  • GitHub Check: precommit (21, ubuntu-latest)
  • GitHub Check: assemble (25, ubuntu-24.04-arm)
  • GitHub Check: precommit (21, windows-2025, true)
  • GitHub Check: precommit (21, ubuntu-24.04-arm)
  • GitHub Check: precommit (25, windows-latest)
🔇 Additional comments (1)
server/src/internalClusterTest/java/org/opensearch/snapshots/RestoreSnapshotIT.java (1)

50-50: LGTM!

The import for ValidationException is correctly added to support the new test.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@github-actions
Copy link
Copy Markdown
Contributor

❌ Gradle check result for 52a49a4: FAILURE

Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change?

@meet-v25 meet-v25 force-pushed the main branch 2 times, most recently from b80d9b0 to 20e0866 Compare January 23, 2026 09:53
@github-actions
Copy link
Copy Markdown
Contributor

❌ Gradle check result for 20e0866: FAILURE

Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change?

@meet-v25 meet-v25 force-pushed the main branch 2 times, most recently from 668682a to eeaff26 Compare January 23, 2026 10:35
@github-actions
Copy link
Copy Markdown
Contributor

✅ Gradle check result for eeaff26: SUCCESS

@codecov
Copy link
Copy Markdown

codecov bot commented Jan 23, 2026

Codecov Report

❌ Patch coverage is 20.00000% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.34%. Comparing base (967c809) to head (a785f6d).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
...ster/snapshots/restore/RestoreSnapshotRequest.java 20.00% 3 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main   #20465      +/-   ##
============================================
+ Coverage     73.23%   73.34%   +0.11%     
- Complexity    71953    72024      +71     
============================================
  Files          5795     5795              
  Lines        329248   329254       +6     
  Branches      47410    47413       +3     
============================================
+ Hits         241122   241505     +383     
+ Misses        68841    68404     -437     
- Partials      19285    19345      +60     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@github-actions
Copy link
Copy Markdown
Contributor

❌ Gradle check result for 9b90c6c: FAILURE

Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change?

@cwperks cwperks changed the title Fixes #20464: [BugFix] Restrict rename replacement field size for snapshot restore DOS CVE Fixes #20464: [BugFix] Restrict rename replacement field size for snapshot restore Jan 23, 2026
@cwperks
Copy link
Copy Markdown
Member

cwperks commented Jan 23, 2026

@meet-v25 can you please fix the precommit issues?

@github-actions
Copy link
Copy Markdown
Contributor

❌ Gradle check result for a785f6d: FAILURE

Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change?

@github-actions
Copy link
Copy Markdown
Contributor

❕ Gradle check result for a785f6d: UNSTABLE

Please review all flaky tests that succeeded after retry and create an issue if one does not already exist to track the flaky failure.

@github-project-automation github-project-automation bot moved this to 👀 In review in Storage Project Board Jan 23, 2026
@cwperks cwperks merged commit 147ae50 into opensearch-project:main Jan 23, 2026
33 of 36 checks passed
@github-project-automation github-project-automation bot moved this from 👀 In review to ✅ Done in Storage Project Board Jan 23, 2026
tanyabti pushed a commit to tanyabti/OpenSearch that referenced this pull request Feb 24, 2026
…pensearch-project#20465)

Signed-off-by: meetvm <meetvm@amazon.com>
Co-authored-by: meetvm <meetvm@amazon.com>
tanyabti pushed a commit to tanyabti/OpenSearch that referenced this pull request Feb 24, 2026
…pensearch-project#20465)

Signed-off-by: meetvm <meetvm@amazon.com>
Co-authored-by: meetvm <meetvm@amazon.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working Storage:Snapshots

Projects

Status: ✅ Done

Development

Successfully merging this pull request may close these issues.

[BUG] Snapshot rename replacement unbounded length rename

2 participants