Files
PleinR/tests/site-url.test.ts
Claude 17511a73c3 Connexion réseaux : adresse de retour déduite de la requête, CSP sans upgrade en HTTP
Cliquer sur « Connecter » sans avoir enregistré l'URL publique du site
renvoyait une erreur, et la redirection retombait sur https://0.0.0.0 :
la directive upgrade-insecure-requests était posée même sur une page
servie en HTTP, le navigateur basculait donc la navigation vers un
https:// inexistant.

- `publicBaseUrl()` : réglage `site_public_url`, variables d'environnement,
  puis origine de la requête (X-Forwarded-Host / X-Forwarded-Proto). L'adresse
  de retour OAuth et le lien des publications l'utilisent ; `seo-server`
  s'appuie dessus au lieu de dupliquer la déduction.
- `src/lib/site-url.ts` : déduction pure et testée de l'origine, HTTP par
  défaut pour localhost et les IP nues, HTTPS pour un nom de domaine.
- Middleware : `upgrade-insecure-requests` uniquement quand la page est servie
  en HTTPS.
- Page Réseaux : l'adresse détectée est annoncée comme valeur par défaut, avec
  le rappel que Facebook et LinkedIn exigent du HTTPS sur un domaine.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QXNXRC4j5VLfKvpyyisrnb
2026-09-06 10:11:06 +00:00

42 lines
1.9 KiB
TypeScript

import { test } from "node:test";
import assert from "node:assert/strict";
import { defaultProtocol, originFromHeaders } from "../src/lib/site-url";
function headersOf(entries: Record<string, string>) {
const map = new Map(Object.entries(entries).map(([k, v]) => [k.toLowerCase(), v]));
return { get: (name: string) => map.get(name.toLowerCase()) ?? null };
}
test("l'origine suit les en-têtes du reverse proxy", () => {
assert.equal(
originFromHeaders(headersOf({ host: "pleinr-app:3000", "x-forwarded-host": "pleinr.example.fr", "x-forwarded-proto": "https" })),
"https://pleinr.example.fr"
);
});
test("une liste d'hôtes transmis retient le premier", () => {
assert.equal(
originFromHeaders(headersOf({ "x-forwarded-host": "pleinr.example.fr, proxy.interne", "x-forwarded-proto": "https, http" })),
"https://pleinr.example.fr"
);
});
test("un accès direct en HTTP sur une IP reste en HTTP", () => {
assert.equal(originFromHeaders(headersOf({ host: "0.0.0.0:3000" })), "http://0.0.0.0:3000");
assert.equal(originFromHeaders(headersOf({ host: "192.168.1.20:8413" })), "http://192.168.1.20:8413");
assert.equal(originFromHeaders(headersOf({ host: "localhost:3000", "x-forwarded-proto": "http" })), "http://localhost:3000");
});
test("un nom de domaine sans indication de protocole est présumé en HTTPS", () => {
assert.equal(originFromHeaders(headersOf({ host: "pleinr.example.fr" })), "https://pleinr.example.fr");
assert.equal(defaultProtocol("pleinr.example.fr:8443"), "https");
assert.equal(defaultProtocol("[::1]:3000"), "http");
});
test("un en-tête absent ou malformé ne produit rien", () => {
assert.equal(originFromHeaders(headersOf({})), "");
assert.equal(originFromHeaders(headersOf({ host: "bad host/with path" })), "");
assert.equal(originFromHeaders(headersOf({ host: "pleinr.example.fr", "x-forwarded-proto": "ftp" })), "https://pleinr.example.fr");
});