Affected versions: confirmed against the current default branch as of 2026-09-16
Summary
POST /sql-driver/{conversationId}/execute accepts a raw SQL statement and executes it against a configured data source, gated only by [Authorize] (any logged-in user, no role check). It performs no check that the caller owns conversationId, and the underlying ConversationService.SetConversationId call has no ownership check either, silently creating a new conversation using the caller's own user id if the given id doesn't already exist. Any authenticated user, regardless of role, can therefore execute arbitrary SQL against any configured data source by supplying a conversation id of their own choosing.
Details
[Authorize]
[ApiController]
public class SqlDriverController : ControllerBase
{
[HttpPost]
[Route("/sql-driver/{conversationId}/execute")]
public async Task<IActionResult> ExecuteSqlQuery([FromRoute] string conversationId, [FromBody] SqlQueryRequest sqlQueryRequest)
{
...
var conv = _services.GetRequiredService<IConversationService>();
await conv.SetConversationId(conversationId, [...]);
...
var result = await fn.InvokeFunction("execute_sql", msg);
ConversationService.SetConversationId:
public async Task SetConversationId(string conversationId, List<MessageState> states, bool isReadOnly = false)
{
_conversationId = conversationId;
await _state.Load(_conversationId, isReadOnly);
states.ForEach(x => _state.SetState(...));
}
No ownership check exists here. By contrast, ConversationController.GetConversation (which only reads a conversation's own dialog, a far less sensitive operation) already enforces the caller is an admin or the conversation's owner:
var (isAdmin, user) = await userService.IsAdminUser(_user.Id);
var filter = new ConversationFilter
{
Id = conversationId,
UserId = !isAdmin ? user?.Id : null,
...
};
SqlDriverController has no equivalent check anywhere. The identical gap exists on POST /sql-driver/{conversationId}/result.
POC
(available upon request)
Impact
Any authenticated user of a BotSharp instance, regardless of intended role, can execute arbitrary SQL statements against any data source configured in SqlDriverSetting.Connections, by calling this endpoint with a conversation id of their own choosing (no leaked or real conversation id is required). In a deployment using this plugin's intended "chat with your data" feature with real database connections, this grants every logged-in chat user unrestricted database access, bypassing whatever scoping the AI agent's own system prompt was meant to enforce.
Affected versions: confirmed against the current default branch as of 2026-09-16
Summary
POST /sql-driver/{conversationId}/executeaccepts a raw SQL statement and executes it against a configured data source, gated only by[Authorize](any logged-in user, no role check). It performs no check that the caller ownsconversationId, and the underlyingConversationService.SetConversationIdcall has no ownership check either, silently creating a new conversation using the caller's own user id if the given id doesn't already exist. Any authenticated user, regardless of role, can therefore execute arbitrary SQL against any configured data source by supplying a conversation id of their own choosing.Details
ConversationService.SetConversationId:No ownership check exists here. By contrast,
ConversationController.GetConversation(which only reads a conversation's own dialog, a far less sensitive operation) already enforces the caller is an admin or the conversation's owner:SqlDriverControllerhas no equivalent check anywhere. The identical gap exists onPOST /sql-driver/{conversationId}/result.POC
(available upon request)
Impact
Any authenticated user of a BotSharp instance, regardless of intended role, can execute arbitrary SQL statements against any data source configured in
SqlDriverSetting.Connections, by calling this endpoint with a conversation id of their own choosing (no leaked or real conversation id is required). In a deployment using this plugin's intended "chat with your data" feature with real database connections, this grants every logged-in chat user unrestricted database access, bypassing whatever scoping the AI agent's own system prompt was meant to enforce.