Add composer.json with league/commonmark, guzzlehttp/guzzle, phpmailer/phpmailer; wire autoloader into bootstrap; document de-librairisation strategy and PHP extension setup

This commit is contained in:
Pontoporeia
2026-05-20 00:53:37 +02:00
parent 728f05502c
commit 4683ba4116
7 changed files with 4210 additions and 2 deletions

87
docs/de-librairisation.md Normal file
View File

@@ -0,0 +1,87 @@
# De-librairisation Plan
## Why
XAMXAM currently contains ~3,300 lines of custom code implementing
common infrastructural concerns that well-maintained ecosystem libraries
have already solved — correctly, securely, and with years of security
audits and edge-case hardening behind them. By replacing these bespoke
implementations with off-the-shelf packages we:
- **Eliminate attack surface** — we stop maintaining our own SMTP
client, HTTP client, and Markdown parser.
- **Reduce maintenance burden** — each line of infrastructure code we
own is a line we must understand, debug, and keep secure. Off-the-shelf
libs shift that to dedicated maintenance teams.
- **Gain features for free** — DKIM signing, TLS 1.3, connection pooling,
async I/O, proper content security — all things we would never build
ourselves.
- **Make the codebase smaller and more readable** — the remaining code
is *actual application logic*, not protocol plumbing.
## What we replace
| # | Component | Current size | Replaced by | Why |
|---|-----------|-------------|--------------|-----|
| 1 | Markdown parser | ~1770 lines | `league/commonmark` 2.x | Parsedown 1.8.0 is unmaintained since 2019. It has known XSS vulnerabilities fixed in later versions that never shipped. `league/commonmark` is the de-facto standard, actively maintained, security-audited, and supports GFM extensions (tables, strikethrough, autolinks). |
| 2 | SMTP client | ~680 lines | `phpmailer/phpmailer` 6.x | Raw socket SMTP with manual STARTTLS negotiation is one of the hardest things to get right in application code. PHPMailer handles TLS 1.3, DKIM, MIME encoding, character sets, connection pooling — all things our custom code does not. |
| 3 | HTTP client | ~200 lines | `guzzlehttp/guzzle` 7.x | `PeerTubeService::httpRequest()` uses `file_get_contents()` with `stream_context_create()`. Manual JSON parsing, no retry logic, no connection reuse, fragile error handling. Guzzle is the PHP HTTP standard. |
| 4 | Encryption | ~86 lines | `defuse/php-encryption` 2.x | Our AES-256-GCM implementation is actually correct, but home-rolled crypto is never recommended. `defuse/php-encryption` is the recommended library by the PHP security community. **Requires migration of existing encrypted data.** |
## What we keep (and why)
| Component | Why keep |
|-----------|----------|
| `password_hash()` / `password_verify()` | Already the correct approach — PHP's built-in bcrypt. No library needed. |
| CSRF (`App.php`) | Implementation is correct: 256-bit random token, `hash_equals()` verification, rotated after mutations. A Symfony CSRF component would be an upgrade but not urgent. |
| Rate limiter (`RateLimit.php`) | Adequate for current scale. A concurrent-safe Symfony rate-limiter would be better but the file race condition is low-risk at our traffic levels. |
| PHP templates | Plain PHP `include` with `extract()` is fast, simple, and well-understood. Auto-escaping (Twig) would be a security upgrade but the migration cost is high and content is mostly admin-controlled. |
| Logging | `error_log()` with JSON-lines is sufficient. Monolog would be cleaner but adds no security benefit. |
## Composer setup
The project currently has no `composer.json`. PHP CS Fixer and PHPStan
were installed manually into `vendor/bin/`. We will:
1. Create `composer.json` with the four packages above.
2. Run `composer install` to populate `vendor/`.
3. Keep existing PHP CS Fixer and PHPStan configs — they already work.
## Migration order (by risk)
### Phase 1: Markdown parser (low risk, high payoff)
- **Surface**: 4 files import Parsedown
- **API similarity**: `$pd->text($input)``$converter->convert($input)`
- **SafeMode equivalent**: `league/commonmark` is safe by default (no raw HTML in safe mode)
- **No data migration needed**: input is Markdown strings, output is HTML — both are ephemeral and regenerated on each page load
- **Files to change**:
- `app/src/Controllers/AboutController.php`
- `app/src/Controllers/LicenceController.php`
- `app/templates/partials/form/form-help-block.php`
- `app/public/admin/form-help-inline-fragment.php`
### Phase 2: HTTP client (low risk, PeerTube-specific)
- **Surface**: `app/src/PeerTubeService.php` only
- **No data migration needed**: purely a transport layer replacement
- **Can be done independently of Phase 1**
### Phase 3: SMTP client (medium risk, needs testing)
- **Surface**: `app/src/SmtpRelay.php`
- **API change**: `SmtpRelay::send($db, $to, $subject, $htmlBody)` signature stays, but internals replaced with PHPMailer
- **No data migration needed**: SMTP settings in DB are read identically
- **Must test**: actual email sending to a real SMTP server before deploying
### Phase 4: Encryption (highest risk, requires migration)
- **Surface**: `app/src/Crypto.php`, `app/src/ShareLink.php`, `app/src/AdminAuth.php`
- **Affects**: encrypted passwords in `share_links.encrypted_password`, SMTP password in `site_settings`
- **Migration required**: decrypt all values with old Crypto, re-encrypt with defuse/php-encryption, write migration script
- **Do last**, and only if the risk/reward is worth it (our current implementation is actually correct)
## Target state
After all phases, the codebase loses ~2,700 lines of infrastructure code and gains
four well-maintained dependencies with known security postures and upgrade paths.

127
docs/system-setup.md Normal file
View File

@@ -0,0 +1,127 @@
# System Setup — PHP Extensions
## Required extensions (in `composer.json`)
| Extension | Used for | Enabled? |
|-----------|----------|----------|
| `pdo` | Database abstraction | ✅ |
| `pdo_sqlite` | SQLite driver | ✅ |
| `sqlite3` | Direct SQLite (migrations) | ✅ |
| `openssl` | AES-256-GCM encryption, TLS, CSRNG | ✅ |
| `json` | API responses, config, logging | ✅ |
| `ctype` | Character type checks (Composer/vendor) | ✅ (polyfill) |
| `filter` | Input validation (`filter_var`) | ✅ |
| `hash` | Password hashing, checksums | ✅ |
| `mbstring` | Multibyte string handling | ✅ (polyfill) |
| `iconv` | Character encoding conversion (vendor) | ⚠️ (polyfill) |
| `tokenizer` | PHP-CS-Fixer, PHPStan | ✅ |
| `fileinfo` | MIME type detection (FilePond uploads) | ✅ |
| `curl` | HTTP requests (PeerTube, external APIs) | ✅ |
| `zip` | ZIP export (ExportController), vendor packages | ✅ |
| `dom` | HTML/XML parsing (vendor, Parsedown→CommonMark) | ✅ |
| `libxml` | XML parsing | ✅ |
| `session` | Admin auth, CSRF, flash messages | ✅ |
| `zlib` | Compression, vendor packages | ✅ |
## Recommended extensions (not required, but useful)
| Extension | Purpose | Add? |
|-----------|---------|------|
| `gd` | Image resizing/thumbnails (cover images) | ⬜ Future |
| `exif` | Image metadata extraction | ⬜ Future |
| `sodium` | Modern crypto primitives (alternative to openssl) | ⬜ Optional |
| `intl` | Unicode collation, date formatting (locale-aware) | ⬜ Optional |
## Production server (nginx + PHP-FPM)
The production server runs PHP 8.4 FPM. The nginx config references:
```
fastcgi_pass unix:/var/run/php/php8.4-fpm.sock;
```
### Production extension list
Same as above, plus:
- `php8.4-fpm` (the FPM SAPI itself)
### Enabling an extension on Arch Linux
Extensions are compiled as shared objects (`.so` files) and live in
`/usr/lib/php/modules/`. To enable one:
```bash
# 1. Verify the .so file exists
ls /usr/lib/php/modules/iconv.so
# 2. Add to /etc/php/php.ini
echo "extension=iconv" >> /etc/php/php.ini
# 3. Verify it loaded
php -m | grep iconv
# 4. Restart PHP-FPM (production)
sudo systemctl restart php8.4-fpm
```
### Checking the production server
```bash
# SSH into the server
ssh xamxam
# List loaded extensions
php -m
# List available (compiled but not loaded) extensions
ls /usr/lib/php/modules/
# Check PHP-FPM status
sudo systemctl status php8.4-fpm
```
## Required shared objects on current dev machine
The following `.so` files exist in `/usr/lib/php/modules/` but are **not loaded**:
| Extension | `.so` present? | Currently loaded? | Needed? |
|-----------|---------------|-------------------|---------|
| `iconv` | ✅ `iconv.so` | ❌ (polyfill) | For production — enable native to avoid polyfill overhead |
| `gd` | ❌ | ❌ | Not yet, but useful for cover image thumbnails |
| `exif` | ✅ `exif.so` | ❌ | Not yet |
| `intl` | ✅ `intl.so` | ❌ | Not yet |
| `sodium` | ❌ | ❌ | Not yet |
| `bcmath` | ✅ `bcmath.so` | ❌ | No |
| `bz2` | ✅ `bz2.so` | ❌ | No |
| `gmp` | ✅ `gmp.so` | ❌ | No |
| `ldap` | ✅ `ldap.so` | ❌ | No — future LDAP auth planned |
## Quick enable for production parity
To match what composer expects natively (no polyfills needed on server):
```bash
# On the server
echo "extension=iconv" | sudo tee -a /etc/php/php.ini
echo "extension=mbstring" | sudo tee -a /etc/php/php.ini
sudo systemctl restart php8.4-fpm
```
`iconv` and `mbstring` are currently satisfied by Symfony polyfills on the dev
machine. Enabling them natively on the server is a free performance improvement
and avoids a class of polyfill edge cases.
## composer.json platform requirements
```json
"require": {
"php": ">=8.4",
"ext-json": "*",
"ext-pdo": "*",
"ext-openssl": "*"
}
```
These three are declared explicitly because the application cannot function
without them. All other extensions (curl, zip, dom, etc.) are required
transitively by vendor packages and will cause a `composer install` failure
if missing.