feat(sync): http:// limite au reseau local, Basic des la 1re requete

APK : le clair est permis par Android et trie dans TSyncClient.RefusalReason
(IsLocalHost : RFC 1918, 127.x, .local, .lan, nom sans point) - chacun pointe
vers son propre WebDAV sans IP connue a la compilation ; vers Internet, HTTPS.
PC : Authorization Basic envoye d'emblee, plus de 401 anonyme ni de PUT du
coffre envoye deux fois.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-10-02 23:26:17 +01:00
parent 3051c3c392
commit 0604344c60
4 changed files with 79 additions and 32 deletions
+54 -4
View File
@@ -56,6 +56,11 @@ type
Error: string; // message brut, pour le journal
end;
/// Vrai pour une adresse du reseau local (RFC 1918, boucle, .local, .lan,
/// nom sans point) : le seul endroit ou le HTTP en clair est accepte.
function IsLocalHost(const AHost: string): Boolean;
type
TSyncClient = class
private
FBaseUrl: string;
@@ -64,6 +69,7 @@ type
/// Attente de la POIGNEE DE MAIN, distincte de celle de la reponse.
FConnectMs: Integer;
FPrivacyChecked: Boolean;
function PublicRepoWarning: string;
public
constructor Create(const ABaseUrl, AToken: string);
@@ -82,11 +88,12 @@ type
/// l'ecriture (412 = un autre appareil a publie entre-temps).
function IsWebDav: Boolean;
/// Refus LOCAL, avant tout envoi : http:// hors reseau local, puis depot public.
/// UN DEPOT PUBLIC EST REFUSE (Gitea comme GitHub) : chaque synchro est un
/// commit, et l'historique garde tous les instantanes, attaquables hors
/// ligne par quiconque peut les lire. Message a afficher, ou '' si le depot
/// est prive ou si on n'a pas pu le savoir. Verifie une fois par instance.
function PublicRepoWarning: string;
function RefusalReason: string;
/// POURQUOI UN 404 OU UN 401, en clair pour l'utilisateur. Gitea repond 404
/// aussi bien pour un fichier absent que pour un depot ou un proprietaire
@@ -113,7 +120,7 @@ type
implementation
uses
System.JSON, System.NetEncoding;
System.JSON, System.NetEncoding, System.StrUtils;
function TSyncClient.IsGitea: Boolean;
begin
@@ -207,6 +214,49 @@ begin
if LP > 0 then Result := Copy(AContentsUrl, 1, LP - 1) else Result := '';
end;
/// HTTP EN CLAIR : RESEAU LOCAL SEULEMENT. Android le permet partout
/// (network_security_config) pour que chacun pointe vers SON serveur WebDAV,
/// a une adresse qu'on ne connait pas a la compilation ; le tri se fait donc
/// ici. Vers Internet, le jeton ne part qu'en HTTPS.
/// ponytail: un Wi-Fi etranger qui reutilise la meme plage recevrait le jeton
/// en clair - le coffre, lui, reste chiffre. HTTPS chez soi supprime le risque.
function IsLocalHost(const AHost: string): Boolean;
var
LParts: TArray<string>;
A, B: Integer;
begin
var LHost := AHost.ToLower;
if (LHost = 'localhost') or LHost.EndsWith('.local') or LHost.EndsWith('.lan') or
(LHost.IndexOf('.') < 0) and (LHost <> '') then
Exit(True);
LParts := LHost.Split(['.']);
if (Length(LParts) <> 4) or not TryStrToInt(LParts[0], A) or
not TryStrToInt(LParts[1], B) then
Exit(False);
for var LPart in LParts do
if not TryStrToInt(LPart, A) then Exit(False);
A := StrToInt(LParts[0]);
Result := (A = 10) or (A = 127) or ((A = 192) and (B = 168)) or
((A = 172) and (B >= 16) and (B <= 31));
end;
function TSyncClient.RefusalReason: string;
var
LHost: string;
begin
if StartsText('http://', FBaseUrl) then
begin
try
LHost := TURI.Create(FBaseUrl).Host;
except
LHost := '';
end;
if not IsLocalHost(LHost) then
Exit('http:// is only allowed on the local network (192.168.x.x, 10.x.x.x, .local, .lan) - use https:// for ' + LHost);
end;
Result := PublicRepoWarning;
end;
function TSyncClient.PublicRepoWarning: string;
var
LHttp: THTTPClient;
@@ -389,7 +439,7 @@ begin
Result.Error := 'Ecriture disponible seulement vers Gitea, GitHub ou WebDAV (jeton utilisateur:mot-de-passe)';
Exit;
end;
Result.Error := PublicRepoWarning;
Result.Error := RefusalReason;
if Result.Error <> '' then
begin
Result.Status := ssRefused; // HttpCode 0 : refus local, pas du serveur
@@ -522,7 +572,7 @@ begin
Result.Error := 'URL ou jeton non configure';
Exit;
end;
Result.Error := PublicRepoWarning;
Result.Error := RefusalReason;
if Result.Error <> '' then
begin
Result.Status := ssRefused; // HttpCode 0 : refus local, pas du serveur