3 Commits

Author SHA1 Message Date
ignacionelson 7c7ba7cd53 Stop a typed-in storage quota from 500ing when a client is created
Filling the "Storage quota (MB)" field on the new-client form raised a
TypeError and the request died with a 500. Leaving it blank worked, which
is why it reached a release: that path goes through `null ?? 0`, and the
0 is an int.

The `integer` validation rule checks that a value looks like an integer.
It does not convert it. `$request->validate()` returns the raw input, so
the form field arrives as the string "2048" -- and the create form types
that field as a string in React, so it is a string even over JSON. Both
controllers declare strict_types, so handing it to
`ClientAccounts::create()`'s `int $storageQuotaMb` is a TypeError.

Fixed on both surfaces that call create(): the staff screen and
/api/v1/clients. The API twin had the same defect, reachable by sending
the quota as a quoted JSON value or a form-encoded body -- its own create
test only ever sent a JSON number.

Two more call sites had the same shape and are cast too, though nothing
sends them a string today: the share-link download cap and a comment's
reply_to. Both are safe only because a frontend file happens to call
Number() first, which is a fact about that file rather than anything the
signature guarantees. The null in each is preserved rather than collapsed
to 0 -- "no cap" is not a cap of zero.

`storage_quota_mb` is also cast on User and Invitation. The column is an
unsignedInteger and both docblocks already promise int; it is read
straight into provision()'s typed parameter when an invitation is
redeemed, and which type a driver hands back is not something that call
site should depend on.

Found on the new files-test rehearsal instance, on its first real use,
against the same build the whole fleet is running.
2026-09-12 21:16:49 -03:00
ignacionelson 6560346280 Mark the first administrator's address verified, as intended
The last two paths that passed email_verified_at into User::create() and
lost it: the setup screen, and projectsend:admin for a container that
comes up from environment variables. It is deliberately absent from
$fillable, so mass assignment drops it without a word, and both meant to
set it.

The intent is plain in both cases — the first administrator typed their
own address into the form in front of them, and whoever provisioned the
container supplied it themselves. There is nobody to confirm it to.

Inert today, since MustVerifyEmail is not enabled on the model, but the
column is what a later switch would read: turning verification on would
have locked out the one account that cannot be helped by another
administrator.

Both are now pinned by a test that fails when the forceFill is removed.
StaffAccounts had already fixed this for staff and named the rest; with
client accounts done earlier today, that list is empty.

Also says on User::$fillable what absence from it buys and what it does
not. It stops a request smuggling a value in; it does not tell code that
meant to set the value that it failed. Four separate paths made the same
mistake against the same comment.
2026-09-08 17:07:35 -03:00
ignacionelson 6e47d76ba6 ProjectSend 2.0.0
Client file sharing, rebuilt from the ground up: a private area per
client, resumable uploads, folders, groups and categories, sharing with
expiry dates and download limits, comments, file versions, an activity
log, a REST API, and sixteen languages.

This repository begins here. ProjectSend 2 was developed privately, and
that development history is not published — the previous generation
remains available, with its own history, at projectsend/legacy.

Free software under the GNU General Public License v2, or (at your
option) any later version.
2026-08-14 01:38:12 -03:00