-
Notifications
You must be signed in to change notification settings - Fork 0
Wip better search #24
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
Conversation
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
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.
Pull Request Overview
This PR implements an enhanced search functionality for the image gallery, replacing simple filename search with comprehensive filtering capabilities. The changes enable users to search across multiple image attributes including metadata, content type, and upload information.
- Replaces basic filename search with a multi-field search system supporting wildcards
- Adds server-side search filtering with database query optimization
- Introduces a unified metadata field handling approach using both
metadata
andmetadata_
properties
Reviewed Changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
File | Description |
---|---|
frontend/src/components/ImageMetadata.js | Updates metadata handling to support both metadata and metadata_ fields consistently |
frontend/src/components/ImageGallery.js | Implements advanced search UI with field selection, wildcard support, and dynamic metadata key options |
frontend/src/Project.js | Passes search parameters to the backend API for server-side filtering |
frontend/src/App.css | Adds styling for new search interface components and custom metadata table |
backend/utils/crud.py | Implements database-level search filtering with support for JSON metadata queries |
backend/routers/images.py | Adds search parameter handling and cache key updates for the images API |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
backend/utils/crud.py
Outdated
elif search_field == 'metadata': | ||
# Search across all metadata values using JSON text search | ||
# This uses PostgreSQL's jsonb operators | ||
query = query.where(text("metadata_::text ILIKE :search_value")).params(search_value=search_value_lower) |
Copilot
AI
Sep 17, 2025
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.
Using raw SQL with text() and user input creates potential SQL injection vulnerability. Consider using SQLAlchemy's cast() function or proper JSON operators instead of string interpolation in raw SQL.
Copilot uses AI. Check for mistakes.
backend/utils/crud.py
Outdated
else: | ||
# Search specific metadata key using JSON path | ||
# This searches for the specific key in the metadata JSON | ||
query = query.where(text("metadata_ ->> :key ILIKE :search_value")).params(key=search_field, search_value=search_value_lower) |
Copilot
AI
Sep 17, 2025
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.
Similar SQL injection risk as above. The search_field parameter is passed directly to raw SQL. Use SQLAlchemy's proper JSON path operators or validate/sanitize the key parameter.
query = query.where(text("metadata_ ->> :key ILIKE :search_value")).params(key=search_field, search_value=search_value_lower) | |
import re | |
# Only allow alphanumeric and underscore keys (adjust as needed) | |
if re.match(r'^[a-zA-Z0-9_]+$', search_field): | |
# Safe to interpolate the key name directly | |
query = query.where(text(f"metadata_ ->> '{search_field}' ILIKE :search_value")).params(search_value=search_value_lower) | |
else: | |
# Invalid key, skip filtering | |
pass |
Copilot uses AI. Check for mistakes.
useEffect(() => { | ||
if (refreshProjectImages) { | ||
refreshProjectImages({ | ||
searchField: searchValue ? searchField : null, | ||
searchValue: searchValue || null | ||
}); | ||
} | ||
}, [searchField, searchValue]); |
Copilot
AI
Sep 17, 2025
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.
This useEffect will trigger API calls on every keystroke when typing in the search field. Consider debouncing the search to avoid excessive API requests.
Copilot uses AI. Check for mistakes.
const filename = (image.filename || '').toLowerCase(); | ||
// Support wildcard search with * | ||
if (searchLower.includes('*')) { | ||
const regex = new RegExp(searchLower.replace(/\*/g, '.*'), 'i'); |
Copilot
AI
Sep 17, 2025
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.
Creating regex from user input without escaping special characters can cause ReDoS (Regular Expression Denial of Service) attacks. Consider escaping regex special characters or using a safer wildcard implementation.
const regex = new RegExp(searchLower.replace(/\*/g, '.*'), 'i'); | |
// Escape regex special characters except for '*', then replace '*' with '.*' | |
const escapeRegExp = (str) => str.replace(/[-[\]/{}()+?.\\^$|]/g, '\\$&'); | |
const safePattern = escapeRegExp(searchLower).replace(/\\\*/g, '.*'); | |
const regex = new RegExp('^' + safePattern + '$', 'i'); |
Copilot uses AI. Check for mistakes.
import uuid | ||
from sqlalchemy import select, update, delete, and_ | ||
import re | ||
from sqlalchemy import select, update, delete, and_, text, or_, cast, String |
Check notice
Code scanning / CodeQL
Unused import Note
Import of 'or_' is not used.
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI about 1 month ago
The best way to fix the problem is to remove the unnecessary text
import from the import statement on line 3 in backend/utils/crud.py
. Keep all other imported symbols intact, as they may be used elsewhere in the code. The change should only modify the import line, specifically deleting text
from the list of symbols imported from sqlalchemy
. No other changes are required to existing functionality—just a precise edit to the import.
-
Copy modified line R3
@@ -1,6 +1,6 @@ | ||
import uuid | ||
import re | ||
from sqlalchemy import select, update, delete, and_, text, or_, cast, String | ||
from sqlalchemy import select, update, delete, and_, or_, cast, String | ||
from sqlalchemy.ext.asyncio import AsyncSession | ||
from sqlalchemy.orm import selectinload | ||
from core import models, schemas |
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.
Pull Request Overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
backend/utils/crud.py
Outdated
query = query.where(models.DataInstance.metadata_[search_field].astext.ilike(search_value_lower)) | ||
else: | ||
# Invalid key format, skip filtering for security | ||
logger.warning(f"Invalid metadata key format rejected: {search_field}") |
Copilot
AI
Sep 17, 2025
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.
The logger
is not imported or defined in this file. This will cause a NameError when an invalid metadata key format is encountered.
Copilot uses AI. Check for mistakes.
// Support wildcard search with * (with ReDoS protection) | ||
if (searchLower.includes('*')) { | ||
// Escape all regex special characters except * | ||
const escapeRegExp = (str) => str.replace(/[-[\]/{}()+?.\\^$|]/g, '\\$&'); |
Copilot
AI
Sep 17, 2025
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.
[nitpick] The escapeRegExp function is defined inline within the filter logic. Consider extracting this utility function outside the component or to a utils module for better maintainability and potential reuse.
Copilot uses AI. Check for mistakes.
else: | ||
# Search specific metadata key using JSON path with input validation | ||
# Only allow alphanumeric characters, underscores, and hyphens for security | ||
if re.match(r'^[a-zA-Z0-9_-]+$', search_field): |
Copilot
AI
Sep 17, 2025
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.
[nitpick] The regex pattern for validating metadata keys is a magic string that could benefit from being defined as a constant at the module level with a descriptive name like VALID_METADATA_KEY_PATTERN
.
Copilot uses AI. Check for mistakes.
No description provided.