feat: support all cipher types (Login/SecureNote/Card/Identity)
CI / test (push) Has been cancelled
CI / docker (push) Has been cancelled

- decryptCipher: full decrypt for Card (number/cvv/exp/brand) and Identity (all fields)
- extractSecret: type-aware output (notes for SecureNote, JSON fields for Card/Identity)
- tested: TEST_NOTE (SecureNote) and login secrets resolve by name
This commit is contained in:
Hermes committed 2026-08-02 14:01:32 +02:00
1 parent 4408999bb9
commit 7934e58c37
2 files changed
+107 -1

No files matched your search

+89
View File
@@ -81,6 +81,7 @@ type SyncCipher struct {
Notes *string `json:"notes"`
Login *SyncLogin `json:"login"`
Card *SyncCard `json:"card"`
Identity *SyncIdentity `json:"identity"`
Fields []SyncField `json:"fields"`
}
@@ -99,6 +100,31 @@ type SyncCard struct {
CardholderName *string `json:"cardholderName"`
Number *string `json:"number"`
Code *string `json:"code"`
Brand *string `json:"brand"`
ExpMonth *string `json:"expMonth"`
ExpYear *string `json:"expYear"`
}
// SyncIdentity contains encrypted identity data.
type SyncIdentity struct {
Title *string `json:"title"`
FirstName *string `json:"firstName"`
MiddleName *string `json:"middleName"`
LastName *string `json:"lastName"`
Address1 *string `json:"address1"`
Address2 *string `json:"address2"`
Address3 *string `json:"address3"`
City *string `json:"city"`
State *string `json:"state"`
PostalCode *string `json:"postalCode"`
Country *string `json:"country"`
Company *string `json:"company"`
Email *string `json:"email"`
Phone *string `json:"phone"`
SSN *string `json:"ssn"`
Username *string `json:"username"`
PassportNumber *string `json:"passportNumber"`
LicenseNumber *string `json:"licenseNumber"`
}
// SyncField contains encrypted custom field data.
@@ -562,6 +588,69 @@ func decryptCipher(c SyncCipher, key SymmetricKey) (DecryptedItem, error) {
}
}
if c.Card != nil {
if c.Card.CardholderName != nil {
if v, err := DecryptStr(*c.Card.CardholderName, key); err == nil && v != "" {
item.Fields["cardholder_name"] = v
}
}
if c.Card.Number != nil {
if v, err := DecryptStr(*c.Card.Number, key); err == nil && v != "" {
item.Fields["number"] = v
}
}
if c.Card.Code != nil {
if v, err := DecryptStr(*c.Card.Code, key); err == nil && v != "" {
item.Fields["code"] = v
}
}
if c.Card.Brand != nil {
if v, err := DecryptStr(*c.Card.Brand, key); err == nil && v != "" {
item.Fields["brand"] = v
}
}
if c.Card.ExpMonth != nil {
if v, err := DecryptStr(*c.Card.ExpMonth, key); err == nil && v != "" {
item.Fields["exp_month"] = v
}
}
if c.Card.ExpYear != nil {
if v, err := DecryptStr(*c.Card.ExpYear, key); err == nil && v != "" {
item.Fields["exp_year"] = v
}
}
}
if c.Identity != nil {
idFields := map[string]*string{
"title": c.Identity.Title,
"first_name": c.Identity.FirstName,
"middle_name": c.Identity.MiddleName,
"last_name": c.Identity.LastName,
"address1": c.Identity.Address1,
"address2": c.Identity.Address2,
"address3": c.Identity.Address3,
"city": c.Identity.City,
"state": c.Identity.State,
"postal_code": c.Identity.PostalCode,
"country": c.Identity.Country,
"company": c.Identity.Company,
"email": c.Identity.Email,
"phone": c.Identity.Phone,
"ssn": c.Identity.SSN,
"username": c.Identity.Username,
"passport_number": c.Identity.PassportNumber,
"license_number": c.Identity.LicenseNumber,
}
for name, ptr := range idFields {
if ptr != nil {
if v, err := DecryptStr(*ptr, key); err == nil && v != "" {
item.Fields[name] = v
}
}
}
}
for _, f := range c.Fields {
var name, value string
if f.Name != nil {
+18 -1
View File
@@ -3,6 +3,7 @@
package vaultwarden
import (
"encoding/json"
"fmt"
"maps"
"strings"
@@ -229,8 +230,24 @@ func (c *Client) backgroundSync() {
}
// extractSecret extracts the most relevant secret value from a decrypted item.
// Priority: password > field named "value"/"secret"/"api_key" > notes > first field.
// For Login: password > field named "value"/"secret"/"api_key" > notes > first field.
// For SecureNote: notes (the whole note body).
// For Card / Identity: a compact JSON of all decrypted fields.
func extractSecret(item DecryptedItem) string {
switch item.Type {
case CipherTypeSecureNote:
if item.Notes != "" {
return item.Notes
}
case CipherTypeCard, CipherTypeIdentity:
if len(item.Fields) > 0 {
b, err := json.Marshal(item.Fields)
if err == nil {
return string(b)
}
}
}
if item.Password != "" {
return item.Password
}