diff --git a/delphi-backend/Handlers/PM.Handler.Entries.pas b/delphi-backend/Handlers/PM.Handler.Entries.pas index af612e9..f412029 100644 --- a/delphi-backend/Handlers/PM.Handler.Entries.pas +++ b/delphi-backend/Handlers/PM.Handler.Entries.pas @@ -118,14 +118,9 @@ begin begin LObj := TJSONObject.Create; LObj.AddPair('id', TJSONNumber.Create(LQ.FieldByName('id').AsInteger)); - LObj.AddPair('site', LQ.FieldByName('site').AsString); + // site + username cleartext columns were dropped — client reads them + // from *_enc only (decryptEntryMeta). title/tags cleartext still exist. LObj.AddPair('title', LQ.FieldByName('title').AsString); - // Cleartext username: '' for migrated rows (ciphertext lives in - // username_enc). Old rows still carry it until the client sweep. - LObj.AddPair('username', LQ.FieldByName('username').AsString); - // Encrypted username (AES-GCM under the vault key). NULL → JSON null - // so the client knows the row isn't migrated yet and falls back to - // the cleartext `username` above. if LQ.FieldByName('username_enc').IsNull then LObj.AddPair('username_enc', TJSONNull.Create) else @@ -404,20 +399,17 @@ begin LQ.Connection := DB.Connection; LQ.SQL.Text := 'INSERT INTO vault_entries ' + - '(user_id, site, title, username, username_enc, username_iv, ' + + '(user_id, title, username_enc, username_iv, ' + ' site_enc, site_iv, title_enc, title_iv, tags_enc, tags_iv, ' + ' encrypted_password, iv, encryption_method, ' + ' folder, tags, totp_secret, totp_iv, kind, custom_fields, custom_fields_iv,' + ' icon_b64, template, uuid, created_at, updated_at, password_changed_at) ' + - 'VALUES (:uid, :s, :tt, :u, :uenc, :uiv, :senc, :siv, :tenc, :tiv2, :genc, :giv, ' + + 'VALUES (:uid, :tt, :uenc, :uiv, :senc, :siv, :tenc, :tiv2, :genc, :giv, ' + ' :e, :i, ''client'', :f, :t, :ts, :tiv, :k, ' + ' :cf, :cfiv, :ic, :tpl, :uuid, :c, :c2, :c)'; LQ.ParamByName('uid').AsInteger := LUserId; - LQ.ParamByName('s').AsString := LSite; LQ.ParamByName('tt').AsString := LTitle; - LQ.ParamByName('u').AsString := LUser; - // Encrypted metadata: NULL when not supplied (pre-migration client or a - // row with no value) so GET emits JSON null and the client falls back. + // site/username cleartext columns dropped — only *_enc are written. BindNullable(LQ, 'uenc', LUserEnc); BindNullable(LQ, 'uiv', LUserIv); BindNullable(LQ, 'senc', LSiteEnc); @@ -596,7 +588,7 @@ begin if LHasTemplate then LTemplateSet := ', template=:tpl'; LQ.SQL.Text := 'UPDATE vault_entries ' + - 'SET site=:s, title=:tt, username=:u, username_enc=:uenc, username_iv=:uiv, ' + + 'SET title=:tt, username_enc=:uenc, username_iv=:uiv, ' + ' site_enc=:senc, site_iv=:siv, title_enc=:tenc, title_iv=:tiv2, ' + ' tags_enc=:genc, tags_iv=:giv, ' + ' encrypted_password=:e, iv=:i, ' + @@ -607,9 +599,7 @@ begin ' THEN :c ELSE password_changed_at END' + LTemplateSet + ' ' + 'WHERE id=:id AND user_id=:uid'; - LQ.ParamByName('s').AsString := LSite; LQ.ParamByName('tt').AsString := LTitle; - LQ.ParamByName('u').AsString := LUser; BindNullable(LQ, 'uenc', LUserEnc); BindNullable(LQ, 'uiv', LUserIv); BindNullable(LQ, 'senc', LSiteEnc); @@ -1243,12 +1233,12 @@ begin 'WHERE user_id = :uid AND uuid = :uuid'; LQ.SQL.Text := 'INSERT INTO vault_entries ' + - '(user_id, site, title, username, username_enc, username_iv, ' + + '(user_id, title, username_enc, username_iv, ' + ' site_enc, site_iv, title_enc, title_iv, tags_enc, tags_iv, ' + ' encrypted_password, iv, encryption_method, ' + ' folder, tags, totp_secret, totp_iv, kind, custom_fields, custom_fields_iv,' + ' icon_b64, template, uuid, created_at, updated_at) ' + - 'VALUES (:uid, :s, :tt, :u, :uenc, :uiv, :senc, :siv, :tenc, :tiv2, :genc, :giv, ' + + 'VALUES (:uid, :tt, :uenc, :uiv, :senc, :siv, :tenc, :tiv2, :genc, :giv, ' + ' :e, :i, ''client'', :f, :t, :ts, :tiv, :k, ' + ' :cf, :cfiv, :ic, :tpl, :uuid, :c, :c2)'; // Declare optional param types ONCE — the prepared statement is @@ -1316,9 +1306,7 @@ begin // row carries site_enc); the client validated before import. LQ.ParamByName('uid').AsInteger := LUserId; - LQ.ParamByName('s').AsString := LSite; LQ.ParamByName('tt').AsString := LTitle; - LQ.ParamByName('u').AsString := LUser; BindNullable(LQ, 'uenc', LUserEnc); BindNullable(LQ, 'uiv', LUserIv); BindNullable(LQ, 'senc', LSiteEnc); diff --git a/js/app.js b/js/app.js index bf23858..b2875be 100644 --- a/js/app.js +++ b/js/app.js @@ -1482,6 +1482,8 @@ async function decryptEntryMeta(list) { if (enc && iv) { const v = await decryptPwd(enc, iv); if (v !== '[ERROR]') e[f] = v; + } else if (e[f] == null) { + e[f] = ''; // site/username cleartext columns dropped → default } } }