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
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:
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user