From 01fbcda75d462ae15da337100116c88dbb6f5789 Mon Sep 17 00:00:00 2001 From: johnnyq Date: Mon, 26 Oct 2026 19:54:36 -0400 Subject: [PATCH] Security sweep 2: API archive, unarchive, resolve and close now check the client named in the call the same as create, update and delete do (every call except read.php and time.php counts as a write); API asset, contact, network and document create/update keep linked contacts, locations, vendors, networks and folders within the record's own client; linking a contact to an asset refuses another client's contact, and unlinking only clears the contact it was linked to; agents limited to certain clients no longer see other clients' names in the asset import template file name, export filter summaries, or the messages from file moves, unlinks, ticket asset removal, bulk project add and bulk interface network; guest quote Accept and Decline post instead of being plain links and check the key with hash_equals; exports that match nothing say so instead of showing a blank page; about 30 handlers now stop with 'no longer exists' when their record was just deleted, instead of showing warnings or an error page and writing history for a record that is gone; restore_contact_note reads its own id parameter; ticket prefix pattern escaped for the v flag; missing user agent and array key parameters handled; CHANGELOG updated --- CHANGELOG.md | 13 +++++ admin/post/users.php | 2 + agent/post/asset.php | 63 +++++++++++++++++++++---- agent/post/asset_interface.php | 8 +++- agent/post/certificate.php | 4 +- agent/post/client.php | 2 + agent/post/contact.php | 73 +++++++++++++++++++++++++---- agent/post/credential.php | 4 +- agent/post/domain.php | 6 ++- agent/post/expense.php | 2 + agent/post/file.php | 6 +++ agent/post/income.php | 2 + agent/post/invoice.php | 30 +++++++++++- agent/post/location.php | 4 +- agent/post/network.php | 6 ++- agent/post/network_ip.php | 2 + agent/post/payment.php | 8 ++++ agent/post/product.php | 4 +- agent/post/project.php | 2 +- agent/post/quote.php | 30 ++++++++++++ agent/post/recurring_invoice.php | 24 +++++++++- agent/post/service.php | 2 +- agent/post/software.php | 4 +- agent/post/ticket.php | 15 ++++-- agent/post/transaction.php | 2 + agent/post/trip.php | 2 + agent/post/vendor.php | 4 +- api/v1/assets/asset_model.php | 6 +++ api/v1/contacts/contact_model.php | 3 ++ api/v1/documents/document_model.php | 3 ++ api/v1/enforce_api_rbac.php | 5 +- api/v1/networks/network_model.php | 5 +- api/v1/validate_api_key.php | 2 +- client/join.php | 3 +- client/login_microsoft.php | 2 +- functions/contract_signature.php | 2 +- functions/export.php | 38 +++++++++++++++ functions/portal_invite.php | 2 +- functions/request.php | 2 +- functions/settings.php | 2 +- guest/guest_post.php | 20 ++++---- guest/guest_view_contract.php | 2 +- guest/guest_view_quote.php | 15 +++--- guest/includes/inc_all_guest.php | 2 +- includes/load_user_session.php | 2 +- 45 files changed, 373 insertions(+), 67 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d620d780b..2f3c4cb14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1411,6 +1411,15 @@ If you are coming from 26.08 or earlier, the database update still has to be run - Clients: a client with a contract couldn't be deleted, because the database refused it. - Contract templates: the after-hours rate of a new template was never saved, rates were cut to whole amounts, and net terms written as "Net 30", as the field suggested, were saved as 0. - Contract templates: Edit showed the rates empty, so saving a template set them to 0. +- API: a key acting as an agent limited to certain clients could archive or restore another client's contacts, credentials, documents, domains, locations and networks, archive or restore the client itself, and resolve or close its tickets, by naming that client in the call - only creating, editing and deleting checked the client named. Archiving a contact that way also archived its portal login. +- API: an asset, contact, network or document made or edited through the API could be given another client's contact, location, vendor, network or folder, which then showed on this client's record. +- Assets: linking a contact to an asset from the contact's page took another client's contact, whose name and details then showed on the asset to anyone who could open it. +- Agents limited to certain clients could see the names of other clients' records: in the file name of the asset import template, the filter line of an export, and the messages after moving files to a folder, unlinking a credential, contact or asset, removing an asset from a ticket, and moving tickets into a project or interfaces onto a network in bulk. +- Quotes: Accept and Decline on the page a client opens from a quote email were plain links, so a mail scanner or link preview that opens every link could answer the quote. They are now buttons. +- Exports: exporting with filters that match nothing left a blank page. It now says nothing matches and stays on the list. +- Assets: unlinking a contact from an asset with a page opened earlier took off whichever contact the asset had been given since. +- Archiving, restoring or deleting an asset, contact, note, invoice, quote, payment or recurring invoice, or saving one, that someone else had just deleted showed an error page or warnings and wrote history for a record that was gone. It now says the record no longer exists. +- Settings: the ticket prefix's letters-and-hyphens check didn't run in the browser - its pattern is refused by current browsers. ITFlow still checked it on saving. ### Developer Updates @@ -2016,6 +2025,10 @@ If you are coming from 26.08 or earlier, the database update still has to be run - Tags' look: `.itflow-tag` draws `--itflow-tag-color` as a `color-mix()` wash and edge with the icon in it and the name in `--bs-body-color` (`--itflow-tag-ink`, 75% or 55% under `[data-bs-theme="dark"]`, is how much of the color the icon keeps), with neutral fallbacks before each `color-mix()`; `--itflow-tag-text` is no longer used by pills. `.itflow-search-tag-icon` and the new `.itflow-select-badge.is-soft` follow it. New `tagOptionLook($color, $icon)` in `functions/tags.php` (`selectOptionColor()` plus `data-soft="1"`) for a tag's `