github nilsteampassnet/TeamPass 3.2.2.4

3 hours ago

What's Changed

This is a maintenance release on the 3.2.2 line. It closes two access-control flaws: the REST API ignored item-level restrictions (GHSA-gxc6-rgv6-wx99), and a user granted access to a subfolder could read the items of its parent folder (GHSA-jr9q-x7pj-8qh9). It also adds idempotent item creation and deletion to the REST API, a self-service trial of the browser extension licence, and fixes the Monitoring log filters and purges, the folder special options, and folder trees that ignored a newly created folder until refreshed. Upgrading is required for every installation.

This release changes the database schema. UPGRADE_MIN_DATE is raised, so every installation goes through the upgrade wizard once. The migration only adds: one table, one nullable column on the items table, and four settings. Nothing existing is rewritten.

🔒 Security fixes

  • The REST API ignored item-level restrictions (GHSA-gxc6-rgv6-wx99) - an item can be narrowed after creation to a subset of users or roles. The web refuses a caller outside that subset, but the API authorized on folder membership and on the presence of a sharekey, and narrowing an item never revokes the sharekey an excluded user already holds. item/get, item/inFolders and item/changes therefore returned the password and the TOTP secret of an item the web refused to open, item/getOtp handed out a live code, and PUT /item/update and DELETE /item/delete let the excluded user overwrite or delete the item with an ordinary folder right. The decision now lives in one place, shared by the web, Security Posture and the API: restricted items are omitted from every read (and from X-Total-Count), the delta feed reports them once as removed with reason: out_of_scope so an offline client drops its copy, and both mutations answer 403. Reported by Santosh Kumar Puppala.

  • A user granted access to a subfolder could read the items of its parent folder (GHSA-jr9q-x7pj-8qh9) - the folder tree shows the parent of an accessible folder as a blocked node, and that node is also stored in the user's cached list of visible folders. The item access check accepted any folder present in that list, so a user granted subfolder B got read access to the items of its parent A, passwords included. Access is now granted only for a folder of the user's scope, freshly resolved for the request: accessible folders and the user's own personal folders, minus explicit denials and other users' personal folders. Being listed in the cached tree is never enough on its own.

  • The API tag list disclosed the tags of every folder - GET /item/allTags is documented as returning the tags accessible to the user, but it ran an unscoped query over the whole tags table. Any authenticated API user got the tags of every folder in the instance, including folders they cannot see and items they are restricted from. Tag names routinely carry organisational information, such as a client name or a project codename. The list now applies the same authorization as the item reads. A sharekey is deliberately not required, so the list does not flicker while the keys of a new item are being distributed.

✨ New features

  • Self-service trial of the browser extension licence - administrators can request an evaluation licence for the browser extension from Settings → API → Licence, instead of writing to the maintainer. The licence server confirms the request by e-mail. The tab follows the pending confirmation and warns that requesting a new message invalidates the previous link. It then shows the licence status, which the dashboard widget also displays. A trial has no grace period, so both warn from seven days before it ends. Because a trial is granted once per instance and per product, forever, the request is refused before anything is sent when the FQDN is not a public host name (localhost or an IP address) or when the extension key is missing. An instance with no Internet access gets an offline path: a link to a confirmation page on the licence server, delivered by e-mail through the instance's own SMTP relay (customizable template), copied to the clipboard or shown as a QR code. The extension validates its licence from the browser, so a trial activated this way works even though the server never sees the activation. The licence server's answers are RSA-signed and verified on the raw response, and the calls go through the proxy configured in the settings.

  • Idempotent item creation and conditional deletion in the REST API (PR #5358, @guerricv) - POST /item/create and DELETE /item/delete accept an optional Idempotency-Key header. A client that lost the response can retry safely: an identical replay returns the original result with Idempotency-Replayed: true and repeats no write or side effect, reusing a key for a different request answers 409, and replaying a completed delete never deletes an item that was restored in the meantime. DELETE /item/delete also accepts an optional revision precondition and answers 409 when the item changed since. This is the groundwork for bidirectional offline synchronization. Only HMACs and replay-safe response metadata are stored, never the raw key, the request body or a credential value, and records are kept for the offline synchronization window (offline_sync_window_days, 90 days by default). Requests without the header behave exactly as before.

  • Search in the LDAP synchronization user list (PR #5362, @guerricv) - a search field placed before the Refresh and Add role actions filters the loaded users by login, display name, first name, last name and e-mail address, shows an explicit empty result, and keeps the filter across refreshes. No LDAP query changes.

🛠️ Improvements

  • Third-party libraries updated - 20 production dependencies are refreshed, among them phpseclib 3.0.57, Guzzle 7.15.5, the Symfony 6.4.45 components and TCPDF 6.11.4. Composer reports no known vulnerability in the resulting set.

  • The Docker image now applies Alpine security updates - the image installed its packages but never refreshed the ones shipped by the php:8.3-fpm-alpine3.24 base image. openssl and curl therefore stayed at the versions frozen in it, which accumulated 50 Trivy alerts for five packages Alpine had already fixed. They are now upgraded at build time, and the image is rebuilt every week so an unchanged release keeps receiving them.

🐛 Bug fixes

  • Monitoring → Logs: the column filter was ignored, and some purges ignored the selected account (PR #5368, @guerricv, issue #5363) - the Items log sent a search column the server ignored: searching alice in User also returned Bob's activity on an item named alice portal. The search now honours the selected column, matches the displayed user names and translated actions, and no longer searches raw timestamps; Date, API and Personal are removed from the column selector, and a saved selection of one of them falls back to All columns. The User list in the footer is a deletion criterion, which the Errors, Copy, Admin and Failed logins purges ignored, so a purge restricted to one account deleted the entries of every account. It is now applied everywhere, the final calendar day of the range is included, and the confirmation is cleared when the criteria or the tab change. Knowledge Base logs now translate their action codes and show the user's full name.

  • A new folder did not appear in the Items tree until the tree was refreshed (PR #5371, @guerricv, discussion #5330) - folder creation appended the folder to part of the cached tree and marked the whole cache as current, so the displayed tree stayed stale, even after reloading the page or signing in again. Creating a folder now invalidates the cache of every affected user, including the creator, direct grants and administrators, and the next tree load rebuilds it through the normal permission logic.

  • Editing a folder from the Items page reset its special options and its renewal period (PR #5374, @guerricv, issue #5372) - renaming a folder, changing its icon or moving it from the Items page disabled both Special options (create or edit items without meeting the folder's minimum password strength) and set the renewal period to 0, because that form does not send those fields. Omitted fields are now preserved; an explicit value still applies. The API checked the creation exception when validating a password update, instead of the editing one. New subfolders now copy both options from their parent, whether created from the web, the API, a CSV or a KeePass import, and the creation form prefills them when a parent is selected. The copy is made once: later changes to the parent do not propagate.

  • The admin dashboard reported an enabled scheduled backup as missing (PR #5360, @guerricv) - the check read the scheduled backup flag from the wrong settings namespace, so the "configure a scheduled backup" recommendation was shown even when a scheduled backup was enabled.

  • "New version available" kept showing after an upgrade - the latest-release check cached its verdict together with the GitHub data and replayed it for up to 12 hours. The database survives a container image upgrade, so an instance that had just installed the latest release kept announcing it as available. The verdict is now recomputed against the running version every time it is shown, and the sidebar cache is invalidated by an upgrade.

  • The file integrity check kept warning about teampass_background_tasks.lock (PR #5366, @guerricv, issue #5364) - the runtime lock and signal files were written with too permissive modes. They are now repaired to owner read and write without granting any group or other access, symbolic links and non-regular files are refused, and the permissions audit still reports any runtime file it cannot fix.

  • The background task log was never written, and two task handlers could run at once (PR #5370, @guerricv) - LOG_TASKS_FILE became an absolute path with the storage/ layout, but the task logger kept resolving it against app/scripts/, so enabling the task log wrote nothing. The path is fixed, the log is created with mode 0640 instead of 0644, and entries are appended under an exclusive lock. The background handler also deleted the lock file it was holding: one handler could then still lock the deleted file while another created and locked a new one, and both ran. The lock file is now kept and reused.

  • The login form accepted overlapping submissions (PR #5367, @guerricv) - repeated Enter presses, clicks or YubiKey events started a new login request while the previous one was still pending. The form now allows one submission at a time, shows a busy state, and restores itself after a refusal, a network error or an MFA challenge. Server-side validation and brute-force protection are unchanged.

⬆️ Upgrade notes

  • Schema. The upgrade creates the api_idempotency table and adds a nullable, uniquely indexed api_idempotency_id column to the items table, in a single table rebuild that can take a moment on a large vault. It also seeds the four licence-trial settings. UPGRADE_MIN_DATE is raised, so every installation goes through the upgrade wizard once; the Docker entrypoint runs the migration on start.
  • Background tasks. Let running background tasks finish before replacing the files, for instance by pausing the cron job during the upgrade. The previous code deletes the handler lock file when it exits, and the new code keeps it: running both side by side can briefly start two handlers.
  • REST API clients. Items the caller is restricted from are no longer returned, PUT /item/update and DELETE /item/delete answer 403 for them, and the delta feed reports them once as removed with reason: out_of_scope. GET /item/allTags only lists the tags of items the caller can read. A folder created through the API without the two special options now inherits them from its parent; send 0 explicitly to disable them. Idempotency-Key and the revision precondition on delete are optional, so existing clients are unaffected.
  • Folder special options. Options already reset by an earlier edit from the Items page are not restored, because a stored 0 cannot be told apart from a deliberate choice. Re-check the Special options and the renewal period of the folders you renamed or moved from the Items page.
  • Folder trees. A tree cache that was already stale before the upgrade may need the tree refresh button once.
  • Back up your database before upgrading, as always.

Full Changelog: 3.2.2.3...3.2.2.4

Important

  • Requires at least PHP 8.2

Languages

Please join Teampass v3 translation project on Poeditor and translate it for your language.

Installation

Follow instructions from Documentation.

Upgrade

Follow instructions from Documentation.

Ideas and comments

Are welcome ... please use Discussions.

Download TeamPass

Don't miss a new TeamPass release

NewReleases is sending notifications on new releases.