Skip to content

Conversation

yileicn
Copy link
Collaborator

@yileicn yileicn commented Oct 14, 2025

PR Type

Enhancement


Description

  • Added MongoDB support to SQL Driver plugin with query execution capabilities

  • Implemented MongoDB query parsing with support for find, projection, sort, and limit operations

  • Updated agent instructions and configuration to handle both SQL and MongoDB queries

  • Added MongoDB.Driver package dependency and connection string configuration


Diagram Walkthrough

flowchart LR
  A["SQL Driver"] --> B["Database Type Switch"]
  B --> C["MySQL/SQL Server/Redshift"]
  B --> D["MongoDB (New)"]
  D --> E["Parse MongoDB Query"]
  E --> F["Apply Filters & Operations"]
  F --> G["Return Results"]
Loading

File Walkthrough

Relevant files
Enhancement
SqlSelect.cs
Implement MongoDB query execution and parsing logic           

src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlSelect.cs

  • Added MongoDB query execution method RunQueryInMongoDb with
    regex-based query parsing
  • Implemented support for MongoDB find operations with projection, sort,
    and limit chaining
  • Added parameter substitution for MongoDB queries
  • Integrated MongoDB case in database type switch statement
  • Added StopCompletion flag for MongoDB query results
+74/-0   
Configuration changes
SqlDriverSetting.cs
Add MongoDB connection string configuration property         

src/Plugins/BotSharp.Plugin.SqlDriver/Settings/SqlDriverSetting.cs

  • Added MongoDbConnectionString property to configuration settings
+1/-0     
agent.json
Update agent metadata for MongoDB support                               

src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json

  • Updated agent description to include MongoDB query execution
  • Modified routing rules to accept both SQL statements and MongoDB
    queries
+2/-2     
sql_select.json
Update function definition for MongoDB query support         

src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/functions/sql_select.json

  • Updated function description to support both SQL and MongoDB queries
+1/-1     
Documentation
instruction.liquid
Update agent instructions for MongoDB query support           

src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/instructions/instruction.liquid

  • Updated agent description from SQL-only to support both SQL and
    MongoDB
  • Added MongoDB query format instructions and examples
  • Emphasized preservation of original query format without conversion
+5/-2     
Dependencies
BotSharp.Plugin.SqlDriver.csproj
Add MongoDB.Driver NuGet package dependency                           

src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj

  • Added MongoDB.Driver package reference to project dependencies
+1/-0     

Copy link

qodo-merge-pro bot commented Oct 14, 2025

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Injection via substitution

Description: Parameter substitution for MongoDB queries uses raw string Replace without
quoting/escaping, enabling crafted values to break JSON structure and potentially inject
unintended operators or alter query semantics.
SqlSelect.cs [132-137]

Referred Code
private string ApplyParameters(string query, Models.SqlParameter[] parameters)
{
    foreach (var p in parameters)
        query = query.Replace($"@{p.Name}", p.Value?.ToString() ?? "null");
    return query;
}
No operator allowlist

Description: MongoDB query parsing relies on broad regular expressions and BsonDocument.Parse of
user-influenced strings, which may allow operator injection like $where or use of
server-side JavaScript if not restricted, lacking allowlist/validation of allowed
operators.
SqlSelect.cs [103-160]

Referred Code
var match = Regex.Match(statement, 
    @"^([^.]+)\.([^.]+)\.find\s*\((.*?)\)(.*)?$", 
    RegexOptions.Singleline);

if (!match.Success)
    return ["Invalid MongoDB query format. Expected: database.collection.find({query})"];

var queryJson = ApplyParameters(match.Groups[3].Value.Trim(), args.Parameters);

try
{
    var database = client.GetDatabase(match.Groups[1].Value);
    var collection = database.GetCollection<BsonDocument>(match.Groups[2].Value);

    var filter = string.IsNullOrWhiteSpace(queryJson) || queryJson == "{}" 
        ? Builders<BsonDocument>.Filter.Empty 
        : BsonDocument.Parse(queryJson);

    var findFluent = collection.Find(filter);
    findFluent = ApplyChainedOperations(findFluent, match.Groups[4].Value);



 ... (clipped 37 lines)
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
No custom compliance provided

Follow the guide to enable custom compliance check.

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

Copy link

qodo-merge-pro bot commented Oct 14, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Security
Fix NoSQL injection vulnerability

To prevent a NoSQL injection vulnerability in ApplyParameters, replace the
direct string replacement with JsonSerializer.Serialize to ensure parameter
values are correctly escaped and formatted before being inserted into the query.

src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlSelect.cs [132-137]

 private string ApplyParameters(string query, Models.SqlParameter[] parameters)
 {
     foreach (var p in parameters)
-        query = query.Replace($"@{p.Name}", p.Value?.ToString() ?? "null");
+    {
+        // Serialize the parameter value to its JSON representation to prevent NoSQL injection
+        // and ensure correct formatting for different data types.
+        var jsonValue = JsonSerializer.Serialize(p.Value);
+        query = query.Replace($"@{p.Name}", jsonValue);
+    }
     return query;
 }
  • Apply / Chat
Suggestion importance[1-10]: 10

__

Why: The suggestion correctly identifies a critical NoSQL injection vulnerability in the ApplyParameters method and provides a robust fix using JsonSerializer.Serialize to safely handle parameter values.

High
High-level
Replace fragile regex parsing with structured JSON

The current MongoDB query implementation uses fragile regex parsing and unsafe
string replacement for parameters, creating a NoSQL injection vulnerability. It
should be replaced with a structured JSON schema for queries to be built safely
using the MongoDB driver's features.

Examples:

src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlSelect.cs [94-160]
    private IEnumerable<dynamic> RunQueryInMongoDb(SqlStatement args)
    {
        var settings = _services.GetRequiredService<SqlDriverSetting>();
        var client = new MongoClient(settings.MongoDbConnectionString);
        
        // Normalize multi-line query to single line
        var statement = Regex.Replace(args.Statement.Trim(), @"\s+", " ");
        
        // Parse MongoDB query: database.collection.find({query}).projection({}).sort({}).limit(100)
        var match = Regex.Match(statement, 

 ... (clipped 57 lines)

Solution Walkthrough:

Before:

private IEnumerable<dynamic> RunQueryInMongoDb(SqlStatement args)
{
    // statement is like "db.users.find({'name': '@name'}).limit(10)"
    var statement = Regex.Replace(args.Statement.Trim(), @"\s+", " ");
    
    // Parse with regex
    var match = Regex.Match(statement, @"^([^.]+)\.([^.]+)\.find\s*\((.*?)\)(.*)?$");
    
    // Unsafe parameter replacement
    var queryJson = ApplyParameters(match.Groups[3].Value, args.Parameters);
    
    // Parse potentially injected JSON, creating a vulnerability
    var filter = BsonDocument.Parse(queryJson);
    var findFluent = collection.Find(filter);
    
    // More regex parsing for chained methods
    findFluent = ApplyChainedOperations(findFluent, match.Groups[4].Value);
    
    return findFluent.ToList();
}

After:

// The LLM would generate a structured JSON string, deserialized here.
class MongoDbQuery {
    public string Database { get; set; }
    public string Collection { get; set; }
    public BsonDocument Filter { get; set; }
    public BsonDocument Projection { get; set; }
    public BsonDocument Sort { get; set; }
    public int? Limit { get; set; }
}

private IEnumerable<dynamic> RunQueryInMongoDb(SqlStatement args)
{
    // No more regex parsing or string replacement for parameters.
    var mongoQuery = JsonSerializer.Deserialize<MongoDbQuery>(args.Statement);

    var database = client.GetDatabase(mongoQuery.Database);
    var collection = database.GetCollection<BsonDocument>(mongoQuery.Collection);

    var findFluent = collection.Find(mongoQuery.Filter ?? Builders<BsonDocument>.Filter.Empty);

    if (mongoQuery.Projection != null) findFluent = findFluent.Project(mongoQuery.Projection);
    if (mongoQuery.Sort != null) findFluent = findFluent.Sort(mongoQuery.Sort);
    if (mongoQuery.Limit.HasValue) findFluent = findFluent.Limit(mongoQuery.Limit);

    return findFluent.ToList();
}
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a critical NoSQL injection vulnerability and a fragile regex-based parsing mechanism, proposing a much more robust and secure structured data approach.

High
General
Improve parsing of chained operations

Refactor ApplyChainedOperations to use a single regex that captures all chained
MongoDB operations. This fixes a bug by ensuring operations are applied in the
correct order and improves parsing efficiency.

src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlSelect.cs [139-160]

 private IFindFluent<BsonDocument, BsonDocument> ApplyChainedOperations(
     IFindFluent<BsonDocument, BsonDocument> findFluent, string chainedOps)
 {
     if (string.IsNullOrWhiteSpace(chainedOps)) return findFluent;
 
-    // Apply projection
-    var projMatch = Regex.Match(chainedOps, @"\.projection\s*\((.*?)\)", RegexOptions.Singleline);
-    if (projMatch.Success)
-        findFluent = findFluent.Project<BsonDocument>(BsonDocument.Parse(projMatch.Groups[1].Value.Trim()));
+    // Use a single regex to match all chained operations in order.
+    var chainedOpsRegex = new Regex(@"\.(\w+)\s*\((.*?)\)", RegexOptions.Singleline);
+    var matches = chainedOpsRegex.Matches(chainedOps);
 
-    // Apply sort
-    var sortMatch = Regex.Match(chainedOps, @"\.sort\s*\((.*?)\)", RegexOptions.Singleline);
-    if (sortMatch.Success)
-        findFluent = findFluent.Sort(BsonDocument.Parse(sortMatch.Groups[1].Value.Trim()));
+    foreach (Match match in matches)
+    {
+        var operation = match.Groups[1].Value;
+        var args = match.Groups[2].Value.Trim();
 
-    // Apply limit
-    var limitMatch = Regex.Match(chainedOps, @"\.limit\s*\((\d+)\)");
-    if (limitMatch.Success && int.TryParse(limitMatch.Groups[1].Value, out var limit))
-        findFluent = findFluent.Limit(limit);
+        switch (operation)
+        {
+            case "projection":
+                findFluent = findFluent.Project<BsonDocument>(BsonDocument.Parse(args));
+                break;
+            case "sort":
+                findFluent = findFluent.Sort(BsonDocument.Parse(args));
+                break;
+            case "limit":
+                if (int.TryParse(args, out var limit))
+                {
+                    findFluent = findFluent.Limit(limit);
+                }
+                break;
+        }
+    }
 
     return findFluent;
 }
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a bug where chained MongoDB operations are not processed in order, and provides a more robust and efficient implementation using a single regex to parse and apply them sequentially.

Medium
Optimize regex by using source-generation

Improve performance by replacing the on-the-fly Regex.Match with a pre-compiled,
source-generated regex using the [GeneratedRegex] attribute.

src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlSelect.cs [103-105]

-var match = Regex.Match(statement, 
-    @"^([^.]+)\.([^.]+)\.find\s*\((.*?)\)(.*)?$", 
-    RegexOptions.Singleline);
+var match = MongoDbFindQueryRegex().Match(statement);
  • Apply / Chat
Suggestion importance[1-10]: 5

__

Why: The suggestion offers a valid performance optimization by proposing the use of source-generated regex, which pre-compiles the expression at build time, avoiding runtime compilation overhead.

Low
Learned
best practice
Add null checks after deserialization

Add null-checks for deserialization result and for nested properties to avoid
NullReferenceExceptions, and return a safe message when inputs are missing.

src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlSelect.cs [23-29]

 var args = JsonSerializer.Deserialize<SqlStatement>(message.FunctionArgs);
+if (args == null)
+{
+    message.Content = "Invalid or missing query arguments.";
+    return false;
+}
 
 if (args.GeneratedWithoutTableDefinition)
 {
-    message.Content = $"Get the table definition first.";
+    message.Content = "Get the table definition first.";
     return false;
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 5

__

Why:
Relevant best practice - Ensure nullability guards before property access and default to safe fallbacks when inputs can be missing.

Low
  • Update

@JackJiang1234
Copy link
Contributor

reviewed

@yileicn yileicn merged commit 61b3279 into SciSharp:master Oct 14, 2025
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants