libkeepass / pykeepass

Python library to interact with keepass databases (supports KDBX3 and KDBX4)
https://pypi.org/project/pykeepass/
GNU General Public License v3.0
403 stars 96 forks source link

[4.0.7] regression -> gnome secrets crashes on expiration date check with missmatch of date format #382

Closed fabiscafe closed 4 months ago

fabiscafe commented 4 months ago

Secrets is a password manager.

After importing https://github.com/libkeepass/pykeepass/pull/378 to make secrets even build and work (see the PR), secrets now crashes when trying to unlock the .kdbx. It looks like at least one of my expiration dates does have an incompatible format. This shouldn't happen inside a minor release. Rolling back to pykeepass 4.0.6 makes secrets work again.

This is the output I get when starting it from the terminal.

Traceback (most recent call last):
  File "/usr/lib/python3.11/site-packages/gsecrets/unlock_database.py", line 212, in _unlock_callback
    database_manager.unlock_finish(result)
  File "/usr/lib/python3.11/site-packages/gsecrets/database_manager.py", line 124, in unlock_finish
    self.entries.splice(0, 0, [SafeEntry(self, e) for e in db.entries])
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/gsecrets/database_manager.py", line 124, in <listcomp>
    self.entries.splice(0, 0, [SafeEntry(self, e) for e in db.entries])
                               ^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/gsecrets/safe_element.py", line 545, in __init__
    self._check_expiration()
  File "/usr/lib/python3.11/site-packages/gsecrets/safe_element.py", line 603, in _check_expiration
    if self.props.expired:
       ^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/gi/_propertyhelper.py", line 394, in obj_get_property
    return getattr(self, name, None)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/gi/_propertyhelper.py", line 207, in __get__
    value = self.fget(instance)
            ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/gsecrets/safe_element.py", line 919, in expired
    return self.entry.expired
           ^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/pykeepass/baseelement.py", line 120, in expired
    self.expiry_time
  File "/usr/lib/python3.11/site-packages/pykeepass/baseelement.py", line 127, in expiry_time
    return self._get_times_property('ExpiryTime')
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/pykeepass/baseelement.py", line 94, in _get_times_property
    return self._kp._decode_time(prop.text)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/pykeepass/pykeepass.py", line 824, in _decode_time
    return datetime.strptime(text, DT_ISOFORMAT).replace(tzinfo=timezone.utc)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/_strptime.py", line 567, in _strptime_datetime
    tt, fraction, gmtoff_fraction = _strptime(data_string, format)
                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/_strptime.py", line 349, in _strptime
    raise ValueError("time data %r does not match format %r" %
ValueError: time data '2024-01-29T23:00:00+00:00' does not match format '%Y-%m-%dT%H:%M:%S%fZ'
fabiscafe commented 4 months ago

This might be related to https://github.com/libkeepass/pykeepass/commit/a2da6854af4c45487f6788fd693d921c26ca8eb5

However this can't be reverted on top of current stable.

City-busz commented 4 months ago

A simple fix could be to replace +00:00 with Z on line 824: return datetime.strptime(text.replace('+00:00','Z'), DT_ISOFORMAT).replace(tzinfo=timezone.utc)

City-busz commented 4 months ago

Or maybe just replace the Z parameter with %z on line 31: DT_ISOFORMAT = "%Y-%m-%dT%H:%M:%S%f%z"

fabiscafe commented 4 months ago

DT_ISOFORMAT = "%Y-%m-%dT%H:%M:%S%f%z"

@City-busz This works for one case, thanks. But I just hit on another one.

% secrets
Traceback (most recent call last):
  File "/usr/lib/python3.11/site-packages/gi/overrides/__init__.py", line 351, in wrap
    return func(a, b, *user_data)
           ^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/gsecrets/sorting.py", line 48, in sort_by_ctime_asc
    if ele1.ctime is None or ele2.ctime is None:
       ^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/gsecrets/safe_element.py", line 328, in ctime
    time = self._element.ctime
           ^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/pykeepass/baseelement.py", line 136, in ctime
    return self._get_times_property('CreationTime')
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/pykeepass/baseelement.py", line 94, in _get_times_property
    return self._kp._decode_time(prop.text)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/pykeepass/pykeepass.py", line 824, in _decode_time
    return datetime.strptime(text, DT_ISOFORMAT).replace(tzinfo=timezone.utc)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/_strptime.py", line 567, in _strptime_datetime
    tt, fraction, gmtoff_fraction = _strptime(data_string, format)
                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/_strptime.py", line 349, in _strptime
    raise ValueError("time data %r does not match format %r" %
ValueError: time data '2019-02-22T15:04:26.027609+00:00' does not match format '%Y-%m-%dT%H:%M:%S%f%z'

So far I have

City-busz commented 4 months ago

Ah, just replace the line 824 with: return datetime.fromisoformat(text).replace(tzinfo=timezone.utc)

It should work in all cases. :)

City-busz commented 4 months ago

I submitted https://github.com/libkeepass/pykeepass/pull/383, which should fix this problem.

fabiscafe commented 4 months ago

LGTM - With #383 I can open, edit and save both of my files :+1: