diff --git a/internal/vaultwarden/api_client.go b/internal/vaultwarden/api_client.go index 5f823cb..498e39c 100644 --- a/internal/vaultwarden/api_client.go +++ b/internal/vaultwarden/api_client.go @@ -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 { diff --git a/internal/vaultwarden/client.go b/internal/vaultwarden/client.go index 308fcfe..d6759e8 100644 --- a/internal/vaultwarden/client.go +++ b/internal/vaultwarden/client.go @@ -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 }