Skip to content

fix: file edit operation by adding bounds check #20

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/fs_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,21 @@ impl FileSystemService {

let mut match_found = false;

for i in 0..=content_lines.len() - old_lines.len() {
// skip when the match is impossible:
if old_lines.len() > content_lines.len() {
let error_message = format!(
"Cannot apply edit: the original text spans more lines ({}) than the file content ({}).",
old_lines.len(),
content_lines.len()
);

return Err(RpcError::internal_error()
.with_message(error_message)
.into());
}

let max_start = content_lines.len().saturating_sub(old_lines.len());
for i in 0..=max_start {
let potential_match = &content_lines[i..i + old_lines.len()];

// Compare lines with normalized whitespace
Expand Down
27 changes: 27 additions & 0 deletions tests/test_fs_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -896,3 +896,30 @@ async fn test_preserve_unix_line_endings() {
let updated = std::fs::read_to_string(&file).unwrap();
assert_eq!(updated, "updated1\nupdated2\n"); // Still uses \n endings
}

#[tokio::test]
// Issue #19: https://github.com/rust-mcp-stack/rust-mcp-filesystem/issues/19
async fn test_panic_on_out_of_bounds_edit() {
let (temp_dir, service) = setup_service(vec!["dir1".to_string()]);

// Set up an edit that expects to match 5 lines
let edit = EditOperation {
old_text: "line e\n".repeat(41).to_string(),
new_text: "replaced content".to_string(),
};

// Set up your file content with only 2 lines
let file_content = "line A\nline B\n";
let test_path = create_temp_file(
&temp_dir.as_path().join("dir1"),
"test_input.txt",
file_content,
);

let result = service
.apply_file_edits(&test_path, vec![edit], Some(true), None)
.await;

// It should panic without the fix, or return an error after applying the fix
assert!(result.is_err());
}