Skip to content

[security] Sanitize project_id in conversations_db.rs to prevent path traversal#494

Closed
iberi22 wants to merge 1 commit into
mainfrom
fix/security-path-traversal-conversations-db-31314974923277825
Closed

[security] Sanitize project_id in conversations_db.rs to prevent path traversal#494
iberi22 wants to merge 1 commit into
mainfrom
fix/security-path-traversal-conversations-db-31314974923277825

Conversation

@iberi22
Copy link
Copy Markdown
Owner

@iberi22 iberi22 commented Jun 4, 2026

Fixed a critical path traversal vulnerability in db_path function by sanitizing the project_id and verifying the final resolved path. Added unit tests for sanitization and edge cases.

Fixes #428


PR created automatically by Jules for task 31314974923277825 started by @iberi22

… traversal

- Implemented project_id sanitization allowing only alphanumeric, hyphens, and underscores.
- Added assertion to prevent empty project_id after sanitization.
- Added path validation using canonicalize to ensure the DB file is within the expected conversations directory.
- Added unit tests test_db_path_sanitization and test_db_path_empty_after_sanitization.
@google-labs-jules
Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jun 4, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2b5d63f2-948f-4c9f-a52d-0e622a2c02ec

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/security-path-traversal-conversations-db-31314974923277825

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request updates the default port configuration from 8006 to 8003 across several files, configuration templates, and documentation. It also introduces several code changes that degrade robustness and performance. Specifically, graceful error handling for JSON serialization and lock acquisition has been replaced with .unwrap() calls, which can cause runtime panics. Additionally, A2AClient now instantiates a new reqwest::Client on every initialization instead of reusing a shared client, risking socket exhaustion. The code-graph indexer has been modified to parse all supported file types using the RustParser, which will fail on non-Rust files. Finally, an unused state variable was introduced in the agent HTTP handler, and descriptive .expect() calls were replaced with .unwrap() in the query engine, removing valuable debugging context.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread src/a2a/mod.rs
Ok(value) => success_response(request.id, value),
Err(e) => internal_error(request.id, format!("serialization error: {}", e)),
},
Ok(task) => success_response(request.id, serde_json::to_value(task).unwrap()),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Using .unwrap() on serde_json::to_value(task) introduces a potential runtime panic if serialization fails. In production code, serialization errors should be handled gracefully (e.g., by returning an internal error response) rather than crashing the server. Please restore the previous error handling logic.

            Ok(task) => match serde_json::to_value(task) {
                Ok(value) => success_response(request.id, value),
                Err(e) => internal_error(request.id, format!("serialization error: {}", e)),
            },

Comment thread src/a2a/mod.rs
Ok(value) => success_response(request.id, value),
Err(e) => internal_error(request.id, format!("serialization error: {}", e)),
},
Ok(task) => success_response(request.id, serde_json::to_value(task).unwrap()),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Using .unwrap() on serde_json::to_value(task) introduces a potential runtime panic if serialization fails. In production code, serialization errors should be handled gracefully (e.g., by returning an internal error response) rather than crashing the server. Please restore the previous error handling logic.

            Ok(task) => match serde_json::to_value(task) {
                Ok(value) => success_response(request.id, value),
                Err(e) => internal_error(request.id, format!("serialization error: {}", e)),
            },

Comment thread code-graph/src/db/mod.rs
.conn
.lock()
.map_err(|e| GraphError::Database(format!("lock poisoned: {}", e)))?;
let conn = self.conn.lock().unwrap();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Replacing graceful error propagation with .unwrap() on lock acquisition is a regression in robustness. If the mutex is poisoned (e.g., due to a panic in another thread holding the lock), calling .unwrap() will panic and crash the entire process. In production code, lock poisoning should be handled gracefully by propagating the error as a Result (using GraphError::Database). Please restore the previous error handling pattern.

        let conn = self
            .conn
            .lock()
            .map_err(|e| GraphError::Database(format!("lock poisoned: {}", e)))?;

Comment thread code-graph/src/db/mod.rs
.map_err(|e| GraphError::Database(format!("lock poisoned: {}", e)))?;
let symbol = normalize_symbol_for_insert(symbol);
let stable_id = symbol.stable_id.clone().unwrap_or_default();
let conn = self.conn.lock().unwrap();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

Replacing graceful error propagation with .unwrap() on lock acquisition is a regression in robustness. If the mutex is poisoned, calling .unwrap() will panic and crash the entire process. Please restore the previous error handling pattern.

        let conn = self
            .conn
            .lock()
            .map_err(|e| GraphError::Database(format!("lock poisoned: {}", e)))?;

Comment on lines +73 to +74
let mut parser = RustParser::new();
match parser.parse(&source, &relative_path) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The indexer collects files with various extensions (such as .py, .ts, .go, .java, etc.) but attempts to parse all of them using RustParser (which uses tree-sitter-rust). This is a major bug: parsing non-Rust files with a Rust parser will result in syntax errors and failed parsing (logged as warnings), or worse, extract incorrect/garbage symbols.

Instead, the indexer should use the language-appropriate parser (e.g., by calling parse_source from crate::parser::parse_source which dispatches based on Language), or restrict indexing only to .rs files if other languages are not yet supported.

Suggested change
let mut parser = RustParser::new();
match parser.parse(&source, &relative_path) {
let lang = crate::types::Language::from_extension(ext);
match crate::parser::parse_source(&source, &lang, &relative_path) {


pub async fn agent_push_context_handler(
State(_state): State<AppState>,
State(state): State<AppState>,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The extracted state variable is unused in the function body. Under strict compiler/clippy settings (such as #![deny(warnings)]), this will trigger an unused_variables compilation error. If the state is not needed, please prefix it with an underscore (e.g., _state) to suppress the warning.

Suggested change
State(state): State<AppState>,
State(_state): State<AppState>,

/// Get cached result if still valid
pub fn get(&self, query: &str) -> Option<QueryResult> {
let cache = self.cache.read().expect("RwLock not poisoned");
let cache = self.cache.read().unwrap();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Replacing .expect("RwLock not poisoned") with .unwrap() removes valuable debugging context. If the lock is poisoned, the process will panic, but without the descriptive message, it is much harder to identify the root cause of the panic from logs. Please retain the descriptive .expect(...) calls.

Suggested change
let cache = self.cache.read().unwrap();
let cache = self.cache.read().expect("RwLock not poisoned");

Comment thread src/a2a/mod.rs
pub fn new() -> Self {
Self {
http_client,
http_client: reqwest::Client::new(),
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Instantiating a new reqwest::Client on every A2AClient::new() call can lead to socket exhaustion and poor performance, as reqwest::Client is designed to be shared and reused to benefit from connection pooling. Previously, the shared DEFAULT_HTTP_CLIENT was cloned (which is cheap and reuses the same connection pool). Please consider restoring the use of the shared client or passing it as a dependency.

Suggested change
http_client: reqwest::Client::new(),
http_client: crate::utils::http::DEFAULT_HTTP_CLIENT.clone(),

@iberi22
Copy link
Copy Markdown
Owner Author

iberi22 commented Jun 4, 2026

Too far behind main after large refactor (multiple modify/delete conflicts with #489, #492, #493). The security fix (sanitize project_id) needs to be re-applied fresh onto current main. Creating new PR shortly.

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.

[security] Path traversal critico en conversations_db.rs - sanitizar project_id

1 participant