fix: strip NUL bytes from content before PostgreSQL search indexing (#592)

Signed-off-by: phernandez <paul@basicmachines.co>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Paul Hernandez
2026-02-20 18:11:04 -06:00
committed by GitHub
parent d7faeb754e
commit edb7991ccf
10 changed files with 221 additions and 18 deletions
@@ -24,6 +24,15 @@ from basic_memory.repository.semantic_errors import SemanticDependenciesMissingE
from basic_memory.schemas.search import SearchItemType, SearchRetrievalMode
def _strip_nul_from_row(row_data: dict) -> dict:
"""Strip NUL bytes from all string values in a row dict.
Secondary defense: PostgreSQL text columns cannot store \\x00.
Primary sanitization happens in SearchService.index_entity_markdown().
"""
return {k: v.replace("\x00", "") if isinstance(v, str) else v for k, v in row_data.items()}
class PostgresSearchRepository(SearchRepositoryBase):
"""PostgreSQL tsvector implementation of search repository.
@@ -92,6 +101,7 @@ class PostgresSearchRepository(SearchRepositoryBase):
# Serialize JSON for raw SQL
insert_data = search_index_row.to_insert(serialize_json=True)
insert_data["project_id"] = self.project_id
insert_data = _strip_nul_from_row(insert_data)
# Use upsert to handle race conditions during parallel indexing
# ON CONFLICT (permalink, project_id) matches the partial unique index
@@ -533,7 +543,7 @@ class PostgresSearchRepository(SearchRepositoryBase):
for row in search_index_rows:
insert_data = row.to_insert(serialize_json=True)
insert_data["project_id"] = self.project_id
insert_data_list.append(insert_data)
insert_data_list.append(_strip_nul_from_row(insert_data))
# Use upsert to handle race conditions during parallel indexing
# ON CONFLICT (permalink, project_id) matches the partial unique index