shared/platform-tools was a git submodule. On this branch it is a plain
directory of tracked files, frozen at ea3616abae30106df283e73a3e2f9e5bb0e6ae90
— the same commit fotografia.andmore.pt pins. Contents are byte-identical to
that tree, verified blob by blob.
The submodule caused every deployment failure this site has had:
- .gitmodules named no `branch`, so the update fell back to
refs/remotes/origin/HEAD — a ref a plain server-side clone never writes.
"Atualizar submódulo" failed with "Unable to find refs/remotes/origin/HEAD
revision in submodule path".
- Its URL was http://, which the origin answers with 523.
- This install is deployed by uploading files, and a submodule is invisible
to that. The tree moved forward while shared/platform-tools stayed on the
old pin, config/functions_secrets.php died on a missing compat_secrets.php,
and the whole site — public pages included — went down.
Each of those was separately fixable. Together they mean a second repository
nobody can see from the deployment path, gating whether the first one boots. A
plain directory deploys like every other file and cannot fall out of step.
The cost is that platform-tools updates no longer arrive by bumping a pin; they
are a copy-over-and-commit. shared/platform-tools/VENDORED.md records the
upstream SHA, the procedure, and which four files differ from upstream in line
endings only (social/*.js, .css, functions_publications.php — CRLF upstream,
normalised to LF here; content otherwise identical).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
39 lines
1.5 KiB
PHP
39 lines
1.5 KiB
PHP
<?php
|
|
// ─── CSRF helpers (shared) ───────────────────────────────────────────────────
|
|
//
|
|
// Self-contained double-submit-free session token for every POST form rendered
|
|
// by the platform-tools pages (platform_update.php, backups.php) and verified
|
|
// by their handlers. Deliberately independent of any per-host csrf helper —
|
|
// the only contract is the same one the flash messages already rely on: the
|
|
// stub's bootstrap has a PHP session available.
|
|
//
|
|
// Public API:
|
|
// platform_csrf_token(): string — session token (created on first use)
|
|
// platform_csrf_field(): string — ready-to-echo hidden <input>
|
|
// platform_csrf_ok(): bool — validate the token sent with a POST
|
|
|
|
function platform_csrf_token(): string {
|
|
if (session_status() !== PHP_SESSION_ACTIVE) {
|
|
@session_start();
|
|
}
|
|
if (empty($_SESSION['platform_tools_csrf'])) {
|
|
$_SESSION['platform_tools_csrf'] = bin2hex(random_bytes(32));
|
|
}
|
|
return (string) $_SESSION['platform_tools_csrf'];
|
|
}
|
|
|
|
function platform_csrf_field(): string {
|
|
return '<input type="hidden" name="_pt_csrf" value="'
|
|
. htmlspecialchars(platform_csrf_token())
|
|
. '">';
|
|
}
|
|
|
|
function platform_csrf_ok(): bool {
|
|
if (session_status() !== PHP_SESSION_ACTIVE) {
|
|
@session_start();
|
|
}
|
|
$sent = (string) ($_POST['_pt_csrf'] ?? '');
|
|
$have = (string) ($_SESSION['platform_tools_csrf'] ?? '');
|
|
return $sent !== '' && $have !== '' && hash_equals($have, $sent);
|
|
}
|