Skip to content

v1.0.1 — security and performance hardening

Latest

Choose a tag to compare

@tnandla tnandla released this 06 Aug 08:44
· 5 commits to main since this release

A security and performance pass over the whole app. No schema change — upgrade by uploading the new code and rebuilt assets. Full detail in CHANGELOG.md.

Security

  • Decrypted credential secrets no longer persist in Livewire component state. Revealing a secret stored the plaintext in a public property, which Livewire serialises into wire:snapshot and echoes back to the browser on every later interaction with that page. Only the credential id is kept now.
  • Closed cross-project reads through client-controlled state. A partner could change userId on their own statement to read another partner's ledger; the approval queue rendered work items from unassigned projects; the links screen exposed budget totals for unauthorised projects.
  • Staff can no longer reassign tasks without tasks.assign, including through the bulk bar.
  • Exported CSVs neutralise spreadsheet formulas. A description starting =, +, - or @ executed on open in Excel and Sheets.
  • /_ops hardened: tokens shorter than 32 characters are refused with a 404, responses are no-store / no-referrer / noindex, cache-clear no longer rewrites layouts, and the asset recovery action discards downloads that are not plausibly the asset.
  • Livewire is served from the app, not a third-party CDN.
  • Attachment downloads are authorised and never renderable. Files are served against the permission of the record they hang off, as an attachment with an opaque content type, so an uploaded .html or .svg cannot execute on the app's origin.

Money integrity

  • Distribution shares add up exactly. Rounding each share half-up independently could pay out more than the profit (101 paisa split 50/50 paid 102).
  • Concurrent approvals can no longer double-credit the partner ledger, and an approved run keeps the ownership snapshot it was computed from.
  • Auto-expenses from article and link approval are idempotent under double-submit and concurrent requests, and a deliberately deleted recurring expense is not resurrected.

Fixed

  • Attachments can be downloaded at all. Task evidence, project files and expense receipts could be uploaded and deleted but never read back, so an approver could not open the evidence they were approving against.

Performance

The sixteen main screens went from roughly 3,100 queries to 170, displaying the same thing. Causes were per-request permission lookups, repeated settings reads against the database cache store, per-row aggregates called from Blade, a P&L report running three queries per project, and shared-expense allocations rebuilt (and rewritten) on read.

Lazy loading now throws outside production so the next N+1 fails in CI.

Honesty fix

Two-factor authentication is columns and a settings toggle only — no enrolment, no login challenge. The settings screen and SECURITY.md now say so, because the toggle could previously be mistaken for a control.

Docs

README rewritten around what the app does; setup moved to docs/INSTALL.md.


103 tests green on PHP 8.3, 8.4 and 8.5.