github paolostivanin/OTPClient v5.2.1
5.2.1

3 hours ago

5.2.1 is a bug-fix release. A post-release audit of 5.2.0 turned up a batch of defects across the database lifetime, the GUI, the importers and exporters, the command line and the search provider, and this release closes them. A separate report against 5.2.0 is fixed here too: a window left in the tray raised itself, and took focus, on every desktop unlock (#473).

The worst of the audit findings could destroy data or block saving altogether: a database replaced while an unlock was still running, a token silently omitted from a save that then committed, and an external-modification baseline armed with a pre-write hash that refused every later save with "Database changed on disk". Alongside those, a failed password prompt gave no reason why, a lock and unlock cycle recorded an empty group filter as the user's choice, a delayed refresh could leave an expired code on screen, and several importer and command-line error paths reported the wrong thing, in the wrong place, or nothing at all.

There is no on-disk format or settings change; existing databases and preferences are untouched.

Database and storage

  • FIX: replacing the active database while an unlock was still running could free the state the worker was reading. The worker holds a raw pointer to the database it was started for, and opening or creating another one freed it underneath. Replacement is now refused until the unlock finishes, replies from the keyring for a database that is no longer active are discarded rather than starting a second unlock, and a second concurrent unlock worker is refused
  • FIX: a token could be silently lost to an out-of-memory condition during a save. Merging the quarantined tokens into the database ignored a failed append, so the serialization succeeded without the token and the atomic replace made the omission permanent. The append is now checked and the save aborts before anything reaches disk; the same rule now applies to imports and to the extraction of invalid tokens, where removing a token from the source before the append had succeeded destroyed it outright and still counted it as moved
  • FIX: the external-modification guard could be armed with a stale hash and then refuse every save with "Database changed on disk". After a successful atomic write the baseline was recomputed by reopening the destination, which could observe a concurrent writer's bytes, and a failed hash left the pre-write value in place while marking it fresh. The baseline is now derived from the exact bytes the commit wrote, and where hashing is somehow unavailable the guard is disabled for that generation with a journal warning rather than left stale
  • FIX: moving a token to another database could delete it instead. If staging the token in the target failed, which any allocation on the way there can cause, the target was not saved but a later success branch still removed the token from the source database by position. The move now verifies the source database and its generation, and the token's own contents, before removing anything

Import and export

  • FIX: the importers and exporters stopped trusting sizes and lengths they could not verify. Aegis, Authenticator Pro, 2FAS and FreeOTP imports reject a failed file-size probe instead of feeding it into the secure-memory budget, Authenticator Pro verifies the backup's header, period-less 2FAS tokens get the 30-second default, the second JSON dump must return the exact length before a database is replaced or an export is called successful, and Base32 validation rejects a secret made only of padding

Security

  • SECURITY: a crafted Aegis backup could force a 1 GiB allocation and a long scrypt run before authentication had a chance to reject it. The scrypt parameters are now bounded (n up to 2^17, and n times p up to 2^20)
  • SECURITY: the search provider's rate limit was a single global bucket, so a local peer that knew the trigger keyword could keep it drained and starve the real user. The buckets are now keyed on the D-Bus sender, with a bounded map and least-recently-seen eviction so fabricated sender names cannot grow it without limit

Desktop search

  • FIX: a wedged Secret Service held the search provider's entry reload open forever, with every later query queued behind it. Every keyring lookup now carries a cancellable 30-second deadline, after which the reload moves on
  • FIX: activating a search result resolved it by position, so an entry list that changed between the search and the activation could copy another account's code. Results are now resolved by a stable token identity (issuer, label and contents), and an ambiguous match is refused rather than guessed

GUI

  • FIX: a rejected password showed no reason why. The unlock dialog cleared its fields before displaying the error, and clearing a field re-fires the change handler that hides the error label, so the message was hidden in the same breath as it was shown: a synchronous rejection, whether a wrong current password, secure-memory exhaustion or a concurrent unlock, flashed by unseen. The error is now set after the fields are cleared and the label survives, and a retry after a failed unlock arrives with the reason the last attempt failed instead of a blank prompt
  • FIX: a delayed TOTP refresh tick could hide a freshly generated code or leave an expired one on screen. The code and the step recorded for it came from two separate clock reads, so a period boundary falling between them made the refresh believe the new code belonged to the next step and skip regeneration for the whole period. Generation and the recorded step now share one sampled timestamp, and cross-database search results are included in that bookkeeping
  • FIX: the validity countdown's colors bled across rows. Every recycled row registered its own display-wide CSS provider at application priority, so the color that won was whichever row bound or recycled last rather than each row's remaining time, and warning colors could appear on every row or none. The two configured colors now live in one shared provider and each row carries a class
  • FIX: locking and unlocking the database filtered every grouped token out of the list. Locking splices the group dropdown's model to zero, which GtkDropDown answers with an invalid selection, and comparing that value against the now-empty last index underflowed to true and recorded the empty-string "Ungrouped" sentinel as the user's choice, which the rebuild then faithfully restored. An invalid selection now means "All"
  • FIX: a drag-reorder that failed to commit could leave the on-screen row bound to another entry, because the reorder carried the sorted position rather than the store's; the list is now rebuilt from the database when a reorder fails. A fatal unlock failure now refreshes the content page instead of leaving it stale, and import and file-chooser callbacks are bound to the database and session they were started from
  • FIX: a discarded QR result is now wiped, per-row CSS providers and combo row string lists are released, the clipboard timer is cleared, an export to a non-native path is rejected, and parse-uri wipes its split-line copies of the secret

Tray

  • FIX: with minimize-to-tray enabled, unlocking the desktop brought the window back and gave it focus even though it had been left in the tray. Locking the session makes the tray host drop the icon, and the fail-safe that shows a hidden window when its icon does not come back read that as a tray that had gone away, so the window was raised three seconds into every lock and was sitting on screen at unlock. Tray recovery is now suspended for as long as the desktop is locked, however many locks come and go, and resumes at unlock with a fresh grace period only if the icon still has not returned. The desktop's lock state is now tracked for this regardless of whether Auto-Lock is enabled (#473)

Command line

  • FIX: the command line's password prompt treated a read error as the end of input, passing a partial password on to surface later as a baffling "Incorrect password". A failed read now names the error and stops. A keyring password that cannot be copied into secure memory no longer records itself as stored while holding nothing, which later reported that no password was set; it fails with the memlock message instead. The database-list JSON output no longer prints "(null)" and exits successfully when serialization fails, and a settings export whose second JSON dump fails is no longer returned as an empty success

Packaging

Nothing changed in the build, its dependencies, or the on-disk format, so distribution packagers have nothing to adjust for this release.


sha256: bdd49352c58e33627ba6afcb93d4e533ef93fcac65cb74abd94761026ef17e51

Don't miss a new OTPClient release

NewReleases is sending notifications on new releases.