fix(security): SQL injection in Python/JS clients + bare except + session leak
CI / test (push) Has been cancelled
CI / verify (push) Has been cancelled
Clients CI / build-server (push) Has been cancelled
Clients CI / test-python (push) Has been cancelled
Clients CI / test-javascript (push) Has been cancelled
Clients CI / test-nim (push) Has been cancelled
Clients CI / test-rust (push) Has been cancelled

- Fix SQL injection vulnerabilities in chat_history.py, langchain_store.py,
  rag_pipeline.py, baradb_langchain.js by switching to parameterized queries
- Replace dangerous bare except: with except CatchableError:/ValueError:
  in llm.nim, embed.nim, cypher.nim, mcp/server.nim, executor.nim
- Fix session variable leak in MCP handleVectorSearch/handleSchemaInspect
- Build: 0 errors, all tests pass
This commit is contained in:
2026-05-17 16:58:05 +03:00
parent c95bc4cd44
commit 1e38e29f25
9 changed files with 227 additions and 109 deletions
+2 -2
View File
@@ -55,7 +55,7 @@ proc embed*(e: Embedder, text: string): seq[float32] =
elif data.hasKey("data") and data["data"].kind == JArray and data["data"].len > 0:
for val in data["data"][0]["embedding"]:
result.add(float32(val.getFloat()))
except:
except CatchableError:
discard
finally:
client.close()
@@ -83,5 +83,5 @@ proc jsonToVector*(s: string): seq[float32] =
if p.len > 0:
try:
result.add(parseFloat(p))
except:
except CatchableError:
discard
+1 -1
View File
@@ -78,7 +78,7 @@ proc generate*(client: LLMClient, prompt: string, systemPrompt: string = ""): st
result = data["response"].getStr()
elif data.hasKey("choices") and data["choices"].kind == JArray and data["choices"].len > 0:
result = data["choices"][0]["message"]["content"].getStr()
except:
except CatchableError:
result = ""
finally:
httpClient.close()
+1 -1
View File
@@ -171,7 +171,7 @@ proc parseCypher*(query: string): CypherQuery =
if limPos >= 0:
try:
result.limit = parseInt(query[limPos + 7..^1].strip())
except:
except CatchableError:
result.limit = 0
proc executeCypher*(g: Graph, query: CypherQuery): CypherResult =
+18 -11
View File
@@ -225,7 +225,7 @@ proc handleQuery(params: JsonNode): JsonNode =
var tokens: seq[qlexer.Token]
try:
tokens = qlexer.tokenize(sql)
except:
except CatchableError:
serverCtx.execCtx.sessionVars["app.tenant_id"] = prevTenant
serverCtx.execCtx.currentUser = prevUser
return %*{"error": "Failed to tokenize SQL: " & getCurrentExceptionMsg()}
@@ -233,7 +233,7 @@ proc handleQuery(params: JsonNode): JsonNode =
var astNode: Node
try:
astNode = qparser.parse(tokens)
except:
except CatchableError:
serverCtx.execCtx.sessionVars["app.tenant_id"] = prevTenant
serverCtx.execCtx.currentUser = prevUser
return %*{"error": "Failed to parse SQL: " & getCurrentExceptionMsg()}
@@ -246,7 +246,7 @@ proc handleQuery(params: JsonNode): JsonNode =
var res: ExecResult
try:
res = executeQuery(serverCtx.execCtx, astNode, wireParams)
except:
except CatchableError:
serverCtx.execCtx.sessionVars["app.tenant_id"] = prevTenant
serverCtx.execCtx.currentUser = prevUser
return %*{"error": "Query execution failed: " & getCurrentExceptionMsg()}
@@ -368,6 +368,7 @@ proc handleVectorSearch(params: JsonNode): JsonNode =
item["metadata"] = metaObj
jsonResults.add(item)
var prevTenant = serverCtx.execCtx.sessionVars.getOrDefault("app.tenant_id", "")
var sessionInfo = newJObject()
if "tenant_id" in params and params["tenant_id"].kind == JString:
let tid = params["tenant_id"].getStr()
@@ -375,7 +376,7 @@ proc handleVectorSearch(params: JsonNode): JsonNode =
sessionInfo["tenant_id"] = %tid
sessionInfo["user_id"] = %serverCtx.execCtx.currentUser
return %*{
var r = %*{
"table": %table,
"column": %column,
"index_size": idx.nodes.len,
@@ -384,6 +385,8 @@ proc handleVectorSearch(params: JsonNode): JsonNode =
"results": jsonResults,
"_session": sessionInfo
}
serverCtx.execCtx.sessionVars["app.tenant_id"] = prevTenant
return r
# ---------------------------------------------------------------------------
# Tool: schema_inspect
@@ -394,6 +397,7 @@ proc handleSchemaInspect(params: JsonNode): JsonNode =
if params.kind == JObject and "table" in params and params["table"].kind == JString:
targetTable = params["table"].getStr()
var prevTenant = serverCtx.execCtx.sessionVars.getOrDefault("app.tenant_id", "")
if "tenant_id" in params and params["tenant_id"].kind == JString:
serverCtx.execCtx.sessionVars["app.tenant_id"] = params["tenant_id"].getStr()
@@ -474,17 +478,20 @@ proc handleSchemaInspect(params: JsonNode): JsonNode =
jsonTables.add(tblObj)
if targetTable.len > 0 and jsonTables.len == 0:
serverCtx.execCtx.sessionVars["app.tenant_id"] = prevTenant
return %*{"error": "Table '" & targetTable & "' not found"}
var sessionInfo = newJObject()
sessionInfo["tenant_id"] = %serverCtx.execCtx.sessionVars.getOrDefault("app.tenant_id", "")
sessionInfo["user_id"] = %serverCtx.execCtx.currentUser
return %*{
var r = %*{
"tables": jsonTables,
"table_count": jsonTables.len,
"_session": sessionInfo
}
serverCtx.execCtx.sessionVars["app.tenant_id"] = prevTenant
return r
# ---------------------------------------------------------------------------
# MCP Protocol handlers
@@ -578,14 +585,14 @@ proc writeToStdout(line: string) =
try:
stdout.writeLine(line)
stdout.flushFile()
except:
except CatchableError:
discard
proc logToStderr*(msg: string) =
try:
stderr.writeLine("[baradb-mcp] " & msg)
stderr.flushFile()
except:
except CatchableError:
discard
proc processMessage(raw: string): string =
@@ -595,7 +602,7 @@ proc processMessage(raw: string): string =
var req: JsonNode
try:
req = parseJson(raw)
except:
except CatchableError:
logToStderr("JSON parse error: " & getCurrentExceptionMsg())
let resp = %*{
"jsonrpc": "2.0",
@@ -640,7 +647,7 @@ proc processMessage(raw: string): string =
var dispResult: JsonNode
try:
dispResult = dispatch(meth, params)
except:
except CatchableError:
logToStderr("Dispatch error for " & meth & ": " & getCurrentExceptionMsg())
let msg = getCurrentExceptionMsg()
var errResp = %*{
@@ -705,7 +712,7 @@ proc run*() =
if startupDone:
logToStderr("STDIN closed, exiting.")
break
except:
except CatchableError:
logToStderr("STDIN read error: " & getCurrentExceptionMsg())
break
@@ -740,7 +747,7 @@ when isMainModule:
try:
discard init(dataDir)
run()
except:
except CatchableError:
logToStderr("Fatal error: " & getCurrentExceptionMsg())
finally:
close()
+9 -9
View File
@@ -1214,7 +1214,7 @@ proc evalExpr*(expr: IRExpr, row: Table[string, string], ctx: ExecutionContext =
let textCol = evalExpr(expr.irFuncArgs[2], row, ctx)
let queryText = evalExpr(expr.irFuncArgs[3], row, ctx)
let queryVec = evalExpr(expr.irFuncArgs[4], row, ctx)
let k = try: parseInt(evalExpr(expr.irFuncArgs[5], row, ctx)) except: 10
let k = try: parseInt(evalExpr(expr.irFuncArgs[5], row, ctx)) except ValueError: 10
let results = doHybridSearch(ctx, table, vecCol, textCol, queryText, queryVec, k)
var parts: seq[string] = @[]
for (id, score) in results:
@@ -1227,7 +1227,7 @@ proc evalExpr*(expr: IRExpr, row: Table[string, string], ctx: ExecutionContext =
let textCol = evalExpr(expr.irFuncArgs[2], row, ctx)
let queryText = evalExpr(expr.irFuncArgs[3], row, ctx)
let queryVec = evalExpr(expr.irFuncArgs[4], row, ctx)
let k = try: parseInt(evalExpr(expr.irFuncArgs[5], row, ctx)) except: 10
let k = try: parseInt(evalExpr(expr.irFuncArgs[5], row, ctx)) except ValueError: 10
let results = doHybridSearch(ctx, table, vecCol, textCol, queryText, queryVec, k)
var ids: seq[string] = @[]
for (id, score) in results:
@@ -1240,7 +1240,7 @@ proc evalExpr*(expr: IRExpr, row: Table[string, string], ctx: ExecutionContext =
let textCol = evalExpr(expr.irFuncArgs[2], row, ctx)
let queryText = evalExpr(expr.irFuncArgs[3], row, ctx)
let queryVec = evalExpr(expr.irFuncArgs[4], row, ctx)
let k = try: parseInt(evalExpr(expr.irFuncArgs[5], row, ctx)) except: 10
let k = try: parseInt(evalExpr(expr.irFuncArgs[5], row, ctx)) except ValueError: 10
let filterCol = evalExpr(expr.irFuncArgs[6], row, ctx)
let filterVal = evalExpr(expr.irFuncArgs[7], row, ctx)
let results = doHybridSearchFiltered(ctx, table, vecCol, textCol, queryText, queryVec, k, filterCol, filterVal)
@@ -1260,7 +1260,7 @@ proc evalExpr*(expr: IRExpr, row: Table[string, string], ctx: ExecutionContext =
let queryTerms = queryText.toLower().splitWhitespace()
for elem in arr:
var score = 0.0
try: score = parseFloat(elem["score"].getStr()) except: discard
try: score = parseFloat(elem["score"].getStr()) except ValueError: discard
# Simple term overlap boost
for term in queryTerms:
if term.len > 2:
@@ -1274,16 +1274,16 @@ proc evalExpr*(expr: IRExpr, row: Table[string, string], ctx: ExecutionContext =
for (elem, _) in boosted:
outArr.add(elem)
return $(%* outArr)
except:
except CatchableError:
return resultsJson
of "chunk":
if expr.irFuncArgs.len < 1: return "[]"
let text = evalExpr(expr.irFuncArgs[0], row, ctx)
let maxSize = if expr.irFuncArgs.len >= 2:
try: parseInt(evalExpr(expr.irFuncArgs[1], row, ctx)) except: 1024
try: parseInt(evalExpr(expr.irFuncArgs[1], row, ctx)) except ValueError: 1024
else: 1024
let overlap = if expr.irFuncArgs.len >= 3:
try: parseInt(evalExpr(expr.irFuncArgs[2], row, ctx)) except: 128
try: parseInt(evalExpr(expr.irFuncArgs[2], row, ctx)) except ValueError: 128
else: 128
let cfg = chunkmod.ChunkConfig(maxChunkSize: maxSize, chunkOverlap: overlap,
strategy: chunkmod.csRecursive, minChunkSize: 64)
@@ -1426,7 +1426,7 @@ proc evalExpr*(expr: IRExpr, row: Table[string, string], ctx: ExecutionContext =
if expr.irFuncArgs.len < 1: return "[]"
let graphName = evalExpr(expr.irFuncArgs[0], row, ctx)
let dims = if expr.irFuncArgs.len >= 2:
try: parseInt(evalExpr(expr.irFuncArgs[1], row, ctx)) except: 64
try: parseInt(evalExpr(expr.irFuncArgs[1], row, ctx)) except ValueError: 64
else: 64
if graphName notin ctx.graphs:
return "[]"
@@ -1514,7 +1514,7 @@ proc evalExpr*(expr: IRExpr, row: Table[string, string], ctx: ExecutionContext =
var nodeId: int64 = 0
if expr.irFuncArgs.len > 0:
try: nodeId = parseInt(evalExpr(expr.irFuncArgs[0], row, ctx))
except: nodeId = 0
except ValueError: nodeId = 0
nodeId = nodeId and 0x3FF # 10 bits
let ts = int64(epochTime() * 1000) and 0x1FFFFFFFFFF # 41 bits
var snowSeq = int64(getMonoTime().ticks() and 0xFFF) # 12 bits from monotonic