(self, unmask=False)
| 226 | return [] |
| 227 | |
| 228 | def display(self, unmask=False): |
| 229 | print('') |
| 230 | print('{0:>20s}: {1:<20s}'.format('UID', self.record_uid)) |
| 231 | print('{0:>20s}: {1:<20s}'.format('Type', self.record_type if self.record_type else '')) |
| 232 | if self.title: print('{0:>20s}: {1:<20s}'.format('Title', self.title)) |
| 233 | if self.login: |
| 234 | login_label = self.login_label or 'Login' |
| 235 | print('{0:>20s}: {1:<20s}'.format(login_label, self.login)) |
| 236 | if self.password: |
| 237 | display_password = (self.unmasked_password or self.password) if unmask else '********' |
| 238 | password_label = self.password_label or 'Password' |
| 239 | print('{0:>20s}: {1:<20s}'.format(password_label, display_password)) |
| 240 | if self.login_url: |
| 241 | url_label = self.url_label or 'URL' |
| 242 | print('{0:>20s}: {1:<20s}'.format(url_label, self.login_url)) |
| 243 | # print('{0:>20s}: https://keepersecurity.com/vault#detail/{1}'.format('Link',self.record_uid)) |
| 244 | |
| 245 | if len(self.custom_fields) > 0: |
| 246 | for c in self.custom_fields: |
| 247 | if not 'value' in c: c['value'] = '' |
| 248 | if not 'name' in c: c['name'] = c['type'] if 'type' in c else '' |
| 249 | field_name = str(c['name']) |
| 250 | # Skip fileRef fields - they're shown in Attachments section |
| 251 | # Check for both 'fileRef' and 'fileRef:' (with trailing colon when no label) |
| 252 | if field_name.rstrip(':') in ('fileRef', 'addressRef', 'cardRef'): |
| 253 | continue |
| 254 | # Special handling for passkey fields - display nicely instead of raw JSON |
| 255 | if field_name.rstrip(':').lower() == 'passkey': |
| 256 | pk_value = c['value'] |
| 257 | # Handle both single passkey and list of passkeys |
| 258 | if isinstance(pk_value, list): |
| 259 | pk_value = pk_value[0] if pk_value else {} |
| 260 | if isinstance(pk_value, dict): |
| 261 | print('{0:>20s}:'.format('Passkey')) |
| 262 | # Format created date |
| 263 | created_ts = pk_value.get('createdDate', 0) |
| 264 | if created_ts: |
| 265 | created_dt = datetime.datetime.fromtimestamp(created_ts / 1000) |
| 266 | created_str = created_dt.strftime('%m/%d/%Y, %I:%M %p') |
| 267 | print('{0:>28s}: {1}'.format('Created', created_str)) |
| 268 | username = pk_value.get('username', '') |
| 269 | if username: |
| 270 | print('{0:>28s}: {1}'.format('Username', username)) |
| 271 | relying_party = pk_value.get('relyingParty', '') |
| 272 | if relying_party: |
| 273 | print('{0:>28s}: {1}'.format('Relying Party', relying_party)) |
| 274 | continue |
| 275 | # Strip type prefixes from field names (e.g., "text:Sign-In Address" -> "Sign-In Address") |
| 276 | field_type_prefixes = ('text:', 'multiline:', 'url:', 'phone:', 'email:', 'secret:', 'date:', 'name:', 'host:', 'address:') |
| 277 | display_name = field_name |
| 278 | # v2 records carry the real type in c['type']; v3 encode it as "type:label" in the name. |
| 279 | # Keep this list in sync with _MASKED_FIELD_TYPES in commands/record.py. |
| 280 | # 'note' = Secured Note — sensitive, masked by design. |
| 281 | # 'passkey' omitted: early-exit handler above renders only non-sensitive sub-fields. |
| 282 | _MASKED_TYPES = frozenset({ |
| 283 | 'secret', 'pinCode', 'note', 'json', 'oneTimeCode', |
| 284 | 'paymentCard', 'bankAccount', 'keyPair', 'securityQuestion', |
| 285 | }) |
nothing calls this directly
no test coverage detected