fix: критични бъгове в storage, MVCC, auth, Raft, query executor

Поправени проблеми:
- MVCC: aborted транзакции вече не стават видими (добавено abortedTxns множество)
- LSM-Tree: поправена загуба на данни при immutable memtable overwrite
- LSM-Tree: поправена SSTable search order (сортиране по id вместо minKey)
- WAL: sync()/close() вече извикват posix.fsync()
- Auth: simpleHash (djb2) заменен с HMAC-SHA256
- Recovery: summary() вече не мутира базата данни
- Raft: поправена majority calculation за четен брой нодове
- Query: EXISTS subqueries вече изпълняват подзаявката
- Nimble: поправен build (-d:ssl) и bench task

Добавен BUG_AUDIT.md с пълен доклад и план за подобрения.

292 теста, 0 failure-а.
This commit is contained in:
2026-05-12 22:45:50 +03:00
parent 3396ba4d63
commit 131541a0e5
10 changed files with 288 additions and 26 deletions
+32 -10
View File
@@ -2,6 +2,7 @@
import std/strutils
import std/base64
import std/tables
import checksums/sha2
type
AuthMethod* = enum
@@ -51,12 +52,33 @@ proc base64UrlDecode(data: string): string =
s &= "="
return decode(s)
proc simpleHash(data: string, key: string): string =
var prefix = data & key
var h: uint64 = 5381
for ch in prefix:
h = ((h shl 5) + h) + uint64(ord(ch))
return $h
proc hmacSha256(key, message: string): string =
var k = key
if k.len > 64:
var ctx = initSha_256()
ctx.update(k.toOpenArray(0, k.len-1))
let hash = ctx.digest()
k = $hash
while k.len < 64:
k &= "\x00"
var ipad = newString(64)
var opad = newString(64)
for i in 0..<64:
ipad[i] = chr(ord(k[i]) xor 0x36)
opad[i] = chr(ord(k[i]) xor 0x5c)
var innerCtx = initSha_256()
innerCtx.update(ipad.toOpenArray(0, ipad.len-1))
innerCtx.update(message.toOpenArray(0, message.len-1))
let innerHash = innerCtx.digest()
var outerCtx = initSha_256()
outerCtx.update(opad.toOpenArray(0, opad.len-1))
outerCtx.update(innerHash.toOpenArray(0, innerHash.len-1))
let outerHash = outerCtx.digest()
return $outerHash
proc createToken*(am: AuthManager, claims: JWTClaims): string =
let header = base64UrlEncode("{\"alg\":\"HS256\",\"typ\":\"JWT\"}")
@@ -64,7 +86,7 @@ proc createToken*(am: AuthManager, claims: JWTClaims): string =
"{\"sub\":\"" & claims.sub & "\",\"role\":\"" & claims.role &
"\",\"database\":\"" & claims.database & "\"}")
let data = header & "." & payload
let signature = simpleHash(data, am.secretKey)
let signature = hmacSha256(am.secretKey, data)
am.tokens.add(data & "." & base64UrlEncode(signature))
return am.tokens[^1]
@@ -73,7 +95,7 @@ proc verifyToken*(am: AuthManager, token: string): (bool, JWTClaims) =
if parts.len != 3:
return (false, JWTClaims())
let data = parts[0] & "." & parts[1]
let sig = simpleHash(data, am.secretKey)
let sig = hmacSha256(am.secretKey, data)
if base64UrlEncode(sig) != parts[2]:
return (false, JWTClaims())
# Parse payload
@@ -132,8 +154,8 @@ proc validateCredentials*(am: AuthManager, creds: AuthCredentials): AuthResult =
if creds.username in am.users:
let stored = am.users[creds.username]
# SCRAM-SHA-256: client sends SHA-256(password) as payload
let clientHash = if creds.payload.len > 0: creds.payload else: simpleHash("", am.secretKey)
if stored == clientHash or stored == simpleHash(creds.payload, am.secretKey):
let clientHash = if creds.payload.len > 0: creds.payload else: hmacSha256(am.secretKey, "")
if stored == clientHash or stored == hmacSha256(am.secretKey, creds.payload):
return AuthResult(authenticated: true, username: creds.username,
role: "user", database: "default")
return AuthResult(authenticated: false, error: "Invalid SCRAM credentials")