-
Notifications
You must be signed in to change notification settings - Fork 532
fix: Resolve #1528 - fix: enforce ALLOWED_UPDATE_FIELDS in update_memory (#770) #1529
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,36 @@ | ||||||||||||||||||||||||||||||
| import logging | ||||||||||||||||||||||||||||||
| from memanto.app.constants import ALLOWED_UPDATE_FIELDS | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| logging.basicConfig(level=logging.INFO) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
Comment on lines
+1
to
+5
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win Add missing imports for
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
| def update_memory(memory_id, namespace, updates): | ||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||
| Updates an existing memory record, restricting changes to ALLOWED_UPDATE_FIELDS. | ||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||
| # 1. Align with get_memory contract and handle missing records | ||||||||||||||||||||||||||||||
| memory = get_memory(memory_id, namespace) | ||||||||||||||||||||||||||||||
| if memory is None: | ||||||||||||||||||||||||||||||
| raise ValueError(f"Memory record {memory_id} not found in namespace {namespace}") | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # 2. Log fields removed by the whitelist (names only, not values) | ||||||||||||||||||||||||||||||
| dropped_fields = [k for k in updates if k not in ALLOWED_UPDATE_FIELDS] | ||||||||||||||||||||||||||||||
| if dropped_fields: | ||||||||||||||||||||||||||||||
| logging.warning(f"Update dropped protected fields: {', '.join(dropped_fields)}") | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # 3. Filter updates | ||||||||||||||||||||||||||||||
| allowed_updates = {k: v for k, v in updates.items() if k in ALLOWED_UPDATE_FIELDS} | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # 4. Reconstruct the complete MemoryRecord, not a partial record | ||||||||||||||||||||||||||||||
| # Normalize the existing record to a dictionary to preserve all fields (agent_id, created_at, etc.) | ||||||||||||||||||||||||||||||
| if hasattr(memory, 'model_dump'): # Pydantic v2 | ||||||||||||||||||||||||||||||
| existing_data = memory.model_dump() | ||||||||||||||||||||||||||||||
| elif hasattr(memory, 'dict'): # Pydantic v1 | ||||||||||||||||||||||||||||||
| existing_data = memory.dict() | ||||||||||||||||||||||||||||||
| else: # Standard object | ||||||||||||||||||||||||||||||
| existing_data = vars(memory).copy() | ||||||||||||||||||||||||||||||
|
Comment on lines
+25
to
+30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win Handle According to the Since 🐛 Proposed fix- if hasattr(memory, 'model_dump'): # Pydantic v2
- existing_data = memory.model_dump()
- elif hasattr(memory, 'dict'): # Pydantic v1
- existing_data = memory.dict()
- else: # Standard object
- existing_data = vars(memory).copy()
+ if isinstance(memory, dict):
+ existing_data = memory.copy()
+ elif hasattr(memory, 'model_dump'): # Pydantic v2
+ existing_data = memory.model_dump()
+ elif hasattr(memory, 'dict'): # Pydantic v1
+ existing_data = memory.dict()
+ else: # Standard object
+ existing_data = vars(memory).copy()📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Overlay only allowed updates | ||||||||||||||||||||||||||||||
| existing_data.update(allowed_updates) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # Return the reconstructed record | ||||||||||||||||||||||||||||||
| return MemoryRecord(**existing_data) | ||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Remove module-level
logging.basicConfig().Calling
logging.basicConfig()at the module level is an anti-pattern as it forces a global logging configuration on the entire application when this module is imported. The application's entry point should be responsible for configuring logging.♻️ Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents