fix(security): validate file_id and partition names to block Milvus filter injection - #470
Conversation
…ilter injection file_id only forbade '/', and partition names were never validated. Both are interpolated into Milvus filter expression strings (file_id == "...", partition == "..."), so a value containing quotes/brackets could break out of the literal and inject boolean logic that escapes the partition scope (e.g. file_id = 'x" or partition=="other'). - Restrict both to a safe identifier allowlist ([A-Za-z0-9._:-]). - Enforce the partition allowlist in ensure_partition_role (every partition-scoped op) and create_partition. - Apply validate_file_id to the delete/get/ancestors/file-search endpoints that previously took a raw file_id path param.
📝 WalkthroughWalkthroughIntroduces a shared allowlist-based regex ( ChangesAllowlist Identifier Validation
Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
openrag/routers/indexer.py (1)
401-402:⚠️ Potential issue | 🟠 Major
source_file_idbypasses validation and reaches Milvus filter expressions, creating an injection vulnerability.The
source_file_idForm parameter is passed directly toindexer.copy_file.remote()without validation. The copy operation callsvectordb.get_file_chunks(file_id, partition), which constructs a Milvus filter expression at line 938 of vectordb.py:filter_expr = f'partition == "{partition}" and file_id == "{file_id}"'Since
source_file_idis unvalidated, it can reach this f-string filter and be exploited for injection attacks. The destinationfile_idparameter on line 399 correctly usesDepends(validate_file_id), butsource_file_idlacks the same validation.Apply the validation pattern used for the destination file_id:
async def copy_file_between_partitions( partition: str, file_id: str = Depends(validate_file_id), metadata: Any | None = Depends(validate_metadata), source_partition: str = Form(...), - source_file_id: str = Form(...), + source_file_id: str = Depends(validate_file_id), indexer=Depends(get_indexer),🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@openrag/routers/indexer.py` around lines 401 - 402, The `source_file_id` parameter lacks validation and is passed directly to the copy operation, eventually reaching an unvalidated Milvus filter expression that constructs an f-string, creating an injection vulnerability. Apply the same validation pattern used for the destination file_id parameter by adding `Depends(validate_file_id)` to the `source_file_id` Form parameter definition, ensuring the input is properly validated before reaching the filter expression construction in vectordb operations.
🧹 Nitpick comments (1)
openrag/routers/indexer.py (1)
43-43: 💤 Low valueDead code:
FORBIDDEN_CHARS_IN_FILE_IDappears unused.This constant was part of the previous validation approach that only forbade specific characters. Now that validation uses the allowlist regex in
utils.py, this constant is no longer referenced.🧹 Suggested removal
-FORBIDDEN_CHARS_IN_FILE_ID = set("/") # set('"<>#%{}|\\^`[]')🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@openrag/routers/indexer.py` at line 43, Remove the unused constant FORBIDDEN_CHARS_IN_FILE_ID from openrag/routers/indexer.py. This constant represents the old blacklist-based validation approach and is no longer referenced now that validation has been refactored to use the allowlist regex pattern in utils.py. Simply delete the line containing this constant definition.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@openrag/routers/indexer.py`:
- Around line 401-402: The `source_file_id` parameter lacks validation and is
passed directly to the copy operation, eventually reaching an unvalidated Milvus
filter expression that constructs an f-string, creating an injection
vulnerability. Apply the same validation pattern used for the destination
file_id parameter by adding `Depends(validate_file_id)` to the `source_file_id`
Form parameter definition, ensuring the input is properly validated before
reaching the filter expression construction in vectordb operations.
---
Nitpick comments:
In `@openrag/routers/indexer.py`:
- Line 43: Remove the unused constant FORBIDDEN_CHARS_IN_FILE_ID from
openrag/routers/indexer.py. This constant represents the old blacklist-based
validation approach and is no longer referenced now that validation has been
refactored to use the allowlist regex pattern in utils.py. Simply delete the
line containing this constant definition.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: d1b7b39f-aef8-4c3d-800f-41cfb4306256
📒 Files selected for processing (4)
openrag/routers/indexer.pyopenrag/routers/partition.pyopenrag/routers/search.pyopenrag/routers/utils.py
Issue (High)
file_idonly forbade/(FORBIDDEN_CHARS_IN_FILE_ID = set("/")) and partition names were not validated at all. Both are interpolated by f-string into Milvus filter expressions:A
file_idsuch asx" or partition == "other(no/, previously valid) breaks out of the quoted literal and injects boolean logic into delete/query/relationship-expansion filters — reading or deleting chunks outside the intended partition. A maliciously named partition gives the same primitive.Fix
file_idand partition names to a safe identifier allowlist[A-Za-z0-9._:-].ensure_partition_role(covers every role-gated partition operation) and increate_partition.validate_file_iddependency to thedelete_file,get_file,get_file_ancestors, andsearch_fileendpoints, which previously accepted a rawfile_idpath param.Notes
Defense-in-depth follow-up: convert the remaining f-string Milvus filters to parameterized expressions (
filter_params=), the pattern already used bylist_all_chunk/get_file_chunk_ids. Validation here closes the injection; parameterization would make it structurally impossible.Summary by CodeRabbit
Release Notes