feat(android): read the snapshot from a Gitea repo, and close cleartext

PMA.SyncClient now speaks two transports, told apart by the URL:
/api/v1/repos/ means the Gitea contents API, anything else the old PHP
endpoint. The PHP path stays until the Windows side publishes to the repo
too - without it the migration would need both ends flipped at once, with
no way back.

Three things differ on the Gitea side and nothing else: the header is
"token <PAT>" not "Bearer" (swapping them gives a silent 401), the body is
JSON carrying base64 content, and the fingerprint comparison happens in the
client since the API has no If-None-Match. The body is re-downloaded, which
is cheap; the four-second key derivation is still avoided. The result's
ETag field carries the sha, so the caller cannot tell which transport
answered.

usesCleartextTraffic is replaced by a network config that permits cleartext
to 127.0.0.1 and localhost BY NAME and forbids it everywhere else. The
attribute drew no such line: a mistyped URL sent the bearer token over the
network in the clear with nothing to signal it. Now it fails immediately,
which is the behaviour worth having.

Verified in the built APK: res/xml/network_security_config.xml is present
and the manifest attribute resolves to a real resource id. NOT verified
against a real repo - that needs a private repo, a published
vault-sync.json and a token, none of which exist yet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-08-17 11:55:07 +01:00
parent b3fbaf44bd
commit 68df2dd7e7
5 changed files with 207 additions and 31 deletions
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Le trafic en clair n'est permis que vers la BOUCLE LOCALE, et interdit
partout ailleurs.
Il l'etait auparavant partout, via usesCleartextTraffic="true". C'etait pour
une seule raison : l'essai local passe par "adb reverse", qui fait voir le
poste comme 127.0.0.1 et parle HTTP. Mais l'attribut ne distingue pas la
boucle locale du reste du monde - une URL mal saisie, ou un serveur repassant
en HTTP, envoyait le jeton en clair sur le reseau sans que rien ne le signale.
Ici la permission est nominative. Le jeton ne peut plus sortir qu'en TLS, et
une URL "http://" vers un vrai hote echoue tout de suite, ce qui est
exactement le comportement voulu : bruyant, pas silencieux.
cleartextTrafficPermitted="false" a la racine n'est pas redondant avec le
defaut d'Android 9+ : il vaut aussi pour les versions anterieures, et il dit
l'intention noir sur blanc.
-->
<network-security-config>
<base-config cleartextTrafficPermitted="false" />
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="false">127.0.0.1</domain>
<domain includeSubdomains="false">localhost</domain>
</domain-config>
</network-security-config>