Skip to content

fix(memory): escape record_ids in lancedb delete filter expression#4930

Closed
wishhyt wants to merge 1 commit intocrewAIInc:mainfrom
wishhyt:fix/lancedb-delete-escape
Closed

fix(memory): escape record_ids in lancedb delete filter expression#4930
wishhyt wants to merge 1 commit intocrewAIInc:mainfrom
wishhyt:fix/lancedb-delete-escape

Conversation

@wishhyt
Copy link
Copy Markdown
Contributor

@wishhyt wishhyt commented Mar 18, 2026

Summary

  • The delete() method in LanceDBStorage interpolated record_ids directly into a filter expression without escaping single quotes, while touch_records(), get_record(), and update() in the same file all correctly apply .replace("'", "''"). This was a copy-paste omission.
  • A crafted record_id containing a single quote could break out of the string literal in the filter expression.

Changes

  • Added safe_ids = [str(rid).replace("'", "''") for rid in record_ids] before building the IN (...) expression, matching the pattern used by adjacent methods.

Test plan

  • Existing unit tests for LanceDBStorage.delete() should continue to pass
  • Verify with a record_id containing ' that the filter is correctly escaped

Note

Medium Risk
Adjusts how record_ids are interpolated into LanceDB delete filter expressions to prevent malformed queries or injection via unescaped quotes. Low functional risk, but impacts deletion behavior for IDs containing special characters.

Overview
Fixes LanceDBStorage.delete() to escape single quotes in record_ids before constructing the id IN (...) filter expression.

This aligns deletion-by-ID behavior with other methods (e.g. touch_records, get_record, update) and prevents crafted IDs from breaking the LanceDB filter syntax.

Written by Cursor Bugbot for commit 8e8fb7a. This will update automatically on new commits. Configure here.

The `delete()` method interpolated `record_ids` directly into a
LanceDB filter string without escaping single quotes, while every
other method that builds id-based filters (`touch_records`,
`get_record`, `update`) already applied `.replace("'", "''")`
correctly. This was a copy-paste omission that could allow a
crafted record_id to break out of the string literal.

Made-with: Cursor
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

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

before = int(self._table.count_rows())
ids_expr = ", ".join(f"'{rid}'" for rid in record_ids)
safe_ids = [str(rid).replace("'", "''") for rid in record_ids]
ids_expr = ", ".join(f"'{rid}'" for rid in safe_ids)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Incomplete fix: second delete path still unescaped

Medium Severity

The delete method has two code paths that build IN (...) filter expressions. The PR escapes record_ids in the first branch (line 408–409), but the second branch at line 431 builds ids_expr directly from to_delete (which contains record.id values read from the database) without applying the same .replace("'", "''") escaping. A record previously stored with a single quote in its ID would break the filter expression in this path.

Additional Locations (1)
Fix in Cursor Fix in Web

@greysonlalonde
Copy link
Copy Markdown
Contributor

Duplicate of #4835. Closing.

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