Fixes the nine findings of security audit run 1 on 2.4.1 (T1-T11).
F1: Uploads that are not complete now count only the bytes the server
received, and a chunk that goes above the storage quota gets 422. An
anonymous uploader can no longer fill the quota with declared sizes.
F2: The share password and the admin login have a second limit without
the address (20 attempts per minute per share and per e-mail), because
a forged X-Forwarded-For changes the address.
F3: The system password gate and the admin check now also run on
Livewire updates, so an old uploader snapshot cannot register files.
F4: The session keeps a fingerprint of the system password's hash.
When the admin changes the system password, earlier verifications end.
F5: The page after the upload opens only in the session that created
the share, and only while recipients can still reach the share.
F6: The share password limit counts an attempt before the password is
checked, so parallel attempts cannot pass it.
F7: The system password has the same two limits (5 per address, 20 in
total per minute).
F8: A password change or reset signs the user's other sessions out.
F9: The only admin cannot delete the account, which would open /setup
to every visitor.
The setup wizard also holds the admin password to the password
defaults, and Livewire applies EnsureAdmin to updates.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Config: auth, services, logging, queue and database only repeated the
framework's own files and are gone; the others keep only the keys that
differ (app version, cache serializable_classes, session cookie name,
Markdown mail theme, the shares disk, three Octane values, Livewire's
pagination theme and payload guards).
- Email verification is removed: User never implemented MustVerifyEmail,
so it was never enforced, and SealShare has a single admin and no
registration. CreateNewUser goes with it.
- FileEncryptionService::encryptFile() and generateSalt() were only used
by tests; tests build files with encryptTestFile() in tests/Pest.php.
- The expiration options are defined once, as Share::EXPIRATIONS. "30 Days"
now lasts 30 days instead of a calendar month, and Admin settings only
save a default expiration that is one of the options.
- One-caller helpers are inlined, the uploader reads chunk responses with
XHR's responseType, and starter-kit leftovers are removed.
- Docker: PHP reads the PHP_* limits from the environment itself
(${VAR:-default} in uploads.ini); both entrypoints stop writing the ini.
docker-compose.yml shares the app and scheduler variables through one
anchor. The dev image installs gd for the screenshot publisher and fake
test images.
- Development runs in Docker only: the composer dev script, concurrently,
laravel/pail, laravel/sail, autoprefixer and the shell-quote override
are gone.
- phpunit.xml forces the test environment with <server> entries, so tests
run in the dev container no longer use its real database.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The Laravel 13 upgrade pulled Pint 1.27.1 to 1.29.3, which changed the
laravel preset's default rules. composer lint and the CI lint job fail
without this, so it is required rather than cosmetic.
Formatting only, applied by `vendor/bin/pint`. The rules that fired were
fully_qualified_strict_types, ordered_imports, single_blank_line_at_eof,
unary_operator_spaces, not_operator_with_successor_space, braces_position,
single_line_empty_body, single_line_after_imports and no_extra_blank_lines.
Kept separate from the upgrade commit to keep that diff reviewable.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>