Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 41 additions & 10 deletions .github/workflows/pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,31 @@ concurrency:
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
codeql:
name: CodeQL JavaScript/TypeScript
runs-on: ubuntu-24.04
timeout-minutes: 20
permissions:
contents: read
actions: read
security-events: write
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
persist-credentials: false
- uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370
with:
languages: javascript-typescript
build-mode: none
queries: security-extended
- uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370
with:
category: drive-test-javascript

verify:
name: Vérification complète
needs: codeql
runs-on: ubuntu-24.04
timeout-minutes: 30
# Même version que package-lock.json : navigateurs et bibliothèques déjà présents.
Expand All @@ -43,10 +66,10 @@ jobs:
env:
BUILD_SHA: ${{ github.sha }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 — SHA vérifié le 23/10/2026
with:
persist-credentials: false
- uses: actions/setup-node@v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 — SHA vérifié le 23/10/2026
with:
node-version: 22
cache: npm
Expand Down Expand Up @@ -90,32 +113,40 @@ jobs:
CI: '1'
- name: Captures d'écran des parcours (données fictives uniquement)
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 — SHA vérifié le 23/10/2026
with:
name: captures-parcours
path: screenshots/
retention-days: 30
- name: Rapport Playwright
if: always()
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 — SHA vérifié le 23/10/2026
with:
name: rapport-playwright
path: playwright-report/
retention-days: 14
# L'artefact publié est celui-ci : construit puis testé ci-dessus, pour ce commit.
- name: Artefact vérifié (Cloudflare)
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 — SHA vérifié le 23/10/2026
with:
name: site-verifie-${{ github.sha }}
path: dist-cloudflare/
include-hidden-files: true
retention-days: 7
# Même archive Linux que upload-pages-artifact v3, sans son appel
# transitif mobile à upload-artifact@v4. Les octets de dist/ restent ceux testés.
- name: Archive vérifiée (GitHub Pages)
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
run: tar --dereference --hard-dereference -C dist --exclude=.git --exclude=.github -cvf "$RUNNER_TEMP/artifact.tar" .
- name: Artefact vérifié (GitHub Pages)
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
uses: actions/upload-pages-artifact@v3
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 — SHA vérifié le 23/10/2026
with:
path: dist/
name: github-pages
path: ${{ runner.temp }}/artifact.tar
retention-days: 1
if-no-files-found: error

gate:
name: Porte de publication
Expand Down Expand Up @@ -144,7 +175,7 @@ jobs:
cancel-in-progress: false
steps:
- id: deployment
uses: actions/deploy-pages@v4
uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 — SHA vérifié le 23/10/2026
- name: Le site sert bien ce commit
run: |
url="${{ steps.deployment.outputs.page_url }}"
Expand All @@ -170,13 +201,13 @@ jobs:
cancel-in-progress: false
steps:
- name: Récupération de l'artefact vérifié de ce run
uses: actions/download-artifact@v4
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 — SHA vérifié le 23/10/2026
with:
name: site-verifie-${{ github.sha }}
path: dist
- name: L'artefact correspond au commit
run: grep -q "\"sha\":\"${{ github.sha }}\"" dist/version.json
- uses: actions/setup-node@v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 — SHA vérifié le 23/10/2026
with:
node-version: 22
- name: Projet Cloudflare en envoi direct (créé une seule fois, sans connexion Git)
Expand Down
47 changes: 47 additions & 0 deletions docs/DRIVE_ACCOUNT_BOUNDARY_20261023.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Frontière compte et destination — NOESIS, 23 octobre 2026

Statut : préparation sensible, revue humaine obligatoire, aucune fusion ni activation cloud autorisée par cette PR. Cette note ne modifie pas les contrats normatifs V1/A1/A2.

## Défauts reproduits avant correction

Deux contre-tests du fournisseur, exécutés sur la version antérieure, échouent : un dossier personnel simplement accessible en écriture est accepté ; un fichier déplacé hors du dossier sélectionné peut encore être téléchargé et annoncé vérifié si ses empreintes concordent.

## Correction limitée au périmètre

Le choix du compte reste dans l'interface officielle Google, avec `prompt: select_account`, le seul scope `drive.file` et aucun consentement automatique. Après le retour Google, l'application lit seulement l'identité du compte courant (`about.get`, permissionId/emailAddress), l'affiche en mémoire et exige une confirmation explicite avant Picker, réservation d'ID ou accès aux fichiers. Ni compte, ni adresse, ni jeton ne sont persistés ou consignés. L'application ne peut pas déterminer seule si le compte est personnel : l'utilisateur doit choisir un compte de test dédié. La configuration de production reste absente/désactivée.

Le dossier est neuf, créé par l'application avec un ID réservé et la marque privée `rc-folder-v1`, puis relu. Sa sélection reste explicite dans Google Picker. Le fournisseur exige son identité, sa propriété, son type, l'absence de corbeille et le droit d'ajout. Aucune modification, suppression, exploration générale ou labellisation d'un dossier existant. Un dossier ancien non marqué est refusé : aucune migration implicite. Le même dossier marqué peut être choisi sur l'autre appareil avec le même projet/client OAuth autorisé.

Listes et relectures sont limitées au dossier choisi, aux marques `rc-v1`/`rc-key-a1` et aux métadonnées attendues. Le parent du fichier est vérifié avant sa lecture. Une empreinte SHA-256 reste une preuve d'octets, pas une authentification GCM : celle-ci appartient uniquement à Race Control. Un ajout extérieur au dossier ou une suppression par un utilisateur autorisé n'est pas empêché par cette marque.

Le jeton est exclusivement en mémoire. Expiration, déconnexion et révocation connue invalident la sélection. Les callbacks d'un ancien compte ou d'un ancien Picker ne réactivent pas la nouvelle session. Un changement de destination invalide le bail réseau précédent. Une opération persistée conserve sa liaison opaque compte/client/dossier et ne peut être reprise ailleurs. Le marquage du dossier ne remplace ni l'autorisation Google ni la comparaison de cette liaison.

La création du dossier réutilise l'ID réservé pendant la session si une réponse est perdue. Après fermeture complète avant confirmation du dossier, il faut retrouver explicitement le dossier existant dans Picker ; ne pas annoncer une reprise persistante de cette préparation de dossier. Les files d'archives conservent déjà durablement leurs octets et identifiants, séparément.

Race Control suspend son moteur avant un changement de destination, attend la fin de l'ancienne exécution, et ne valide pas son résultat tardif. La file reste conservée ; un paquet éventuellement créé est relu avec son ID initial lors d'une reprise autorisée dans sa destination initiale. Les données métier restent exclusivement dans Race Control.

Control Vault affiche le compte et le dossier sélectionnés, la date d'opération, la file connue et les états distincts attente/envoi/envoyé/octets vérifiés/blocage/erreur. Les têtes A2 listées restent déclaratives avant authentification dans Race Control. Aucun déchiffrement ou arbitrage métier dans Vault.

## Reproduction et vérifications

- Vault : `npm ci`, `npm run typecheck`, `npm test`, `npm run build`, `npm run security`, `CI=1 npm run test:e2e` ; les deux moteurs et les quatre projets de viewport restent requis.
- Contre-tests : `tests/unit/drive-boundary.test.ts` (dossier non dédié, parent divergent, confirmation, changement de compte, callbacks tardifs, création idempotente et révocation).
- Parcours navigateur : `tests/e2e/connected-drive.spec.ts` (Google entièrement intercepté, données fictives, confirmation avant création/Picker, relecture et téléchargement, rejet du clair, oubli du jeton au rechargement).
- Race Control : `node tests/run-ci.js --lane unit`, `BROWSER=chromium node tests/e2e85-drive-runtime.js`, puis `BROWSER=webkit node tests/e2e85-drive-runtime.js` ; suite CI complète avant toute décision de fusion. `tests/test_drive_sync.js` vérifie aussi le changement de destination pendant l'envoi.
- Reproduction statique du fournisseur dans Race Control : `node tools/vendor-vault-drive.js ../Control-Vault`. Les SHA-256 des quatre sources sont inscrits dans l'en-tête généré. Le module GIS est identique entre dépôts, hormis l'export ES de Vault.

Ces preuves sont unitaires et d'intégration avec réseau Google simulé. Aucun appel Google réel, aucun compte personnel, aucune validation sur un iPhone physique. VALIDATION PHYSIQUE EN ATTENTE.

## Environnement réel à autoriser séparément

Deux origines HTTPS de test indépendantes, un client OAuth Web de test commun à ces seules origines, une clé Picker restreinte, un compte Google de test confirmé par l'utilisateur et un dossier neuf sont nécessaires. La création de ressources administratives et leur publication nécessitent un GO ciblé distinct. Le schéma public reste exactement `approved`, `clientId`, `apiKey`, `appId`, `raceOrigin`, `vaultOrigin`. Aucun compte/dossier/jeton/secret ajouté au schéma. Ne jamais configurer un secret client OAuth dans une application statique.

L'autorisation `approved: true` est réservée au futur environnement de test isolé après validation de ses ressources. Aucun changement des variables ou secrets de production. Aucun accès réel depuis `ipoower.github.io`. Les essais réels doivent démontrer les droits effectifs sur les fichiers des deux applications : Picker seul ne prouve pas ces droits.

## Risques, dépendances et récupération

Modification sensible commune aux deux clients : relire les deux PR et la compatibilité du fournisseur reproduit. La PR de sécurité CI/CD Control Vault #16 reste séparée et non fusionnée. Aucun changement de workflow, permission, secret, données existantes, contrat normatif, service worker ou animation.

Le retour arrière concerne le code uniquement, par PR et après accord. Ne jamais effacer la file, les bases locales, le dossier ou les archives pour faciliter un retour arrière. Une installation ancienne non marquée reste disponible ; son export chiffré et sa restauration isolée doivent être validés avant toute migration personnelle explicite.

Aucune promesse de synchronisation permanente d'une PWA fermée sur iOS. Reprise seulement application ouverte, autorisée, connectée et coffre déverrouillé ; expiration Google implique une action humaine. Un succès HTTP ne constitue pas une sauvegarde vérifiée, et les octets vérifiés par Vault ne constituent pas une restauration réussie.
21 changes: 21 additions & 0 deletions docs/DRIVE_REAL_TEST_20261025.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Candidat Google Drive TEST — 25 octobre 2026

Cette branche est réservée aux projets Cloudflare existants `rc-drive-test-ipoower` et `cv-drive-test-ipoower`, branche de publication `noesis-test`. Aucun main, environnement de production, projet Reconversion ou travail Garage/Climatisation n'est modifié. Les PR temporaires #239/#20 restent des preuves et ne sont ni fusionnées ni publiées.

Le candidat réunit les PR sources pertinentes sans intégrer les commits des branches temporaires. Les moteurs métier/chiffrement restent ceux des sources, avec les correctifs de sécurité/IDB et de récupération déjà testés. RC accepte désormais une base HTTPS à la racine, auparavant refusée par le validateur de build ; les URL avec identifiants, requête ou fragment restent refusées.

Les scripts `tools/build-drive-test.cjs` (RC) et `scripts/build-drive-test.mjs` (Vault) exigent `BUILD_SHA` à 40 caractères. Avec `GOOGLE_DRIVE_CONFIG` absent, ils produisent un profil fermé, `googleEnabled:false`, configuration `null`. Ce n'est pas une activation Google. Un client réel ne sera accepté qu'avec les six champs existants et les deux origines TEST exactes ; aucune clé secrète, phrase, dossier, email ou jeton dans cette configuration publique.

RC construit exclusivement depuis `tests/fixtures/preset.fake.json` et un relais fictif sans notification, avec les clés publiques de test du harnais. Il déchiffre et compare le préréglage produit. Les sorties sont limitées aux assets publics et données synthétiques ; aucun fichier chiffré du dossier personnel `encrypted/`, préréglage clair, artifact privé ou code du relais n'est publié. Géolocalisation interdite. Les deux PWA portent TEST, un bandeau visible et un lien réciproque TEST ; les empreintes de tous les fichiers figurent dans `fixture-manifest.json`.

Le numéro RC `prod-999` est réservé à la fixture de recette ; ses copies ne doivent jamais être utilisées comme progression personnelle ou importées dans la production. Chaque construction utilise les sels et IV aléatoires normaux : les octets chiffrés changent légitimement entre deux builds. Vérifier le manifeste du ZIP effectivement publié, sans prétendre à un chiffré déterministe.

Les nouvelles preuves Node couvrent 128 révisions A2 valides, 129 refusées avant HTTP et le budget 128 Mio. La borne mémoire utilise des métadonnées HTTP simulées et n'annonce aucun transfert Google de 128 Mio. Aucun historique n'est supprimé. Les copies sont indépendantes ; la limite demeure une restriction de disponibilité à traiter avant une promesse de récupération durable pour de gros historiques.

Après vérification des SHA/CI, dépôt uniquement dans le projet TEST correspondant, jamais dans `race-control-ipoower` / `control-vault-ipoower`. Le libellé Cloudflare « production » de ces deux projets isolés désigne leurs seules URL TEST. Contrôler version, octets, CSP et origine effective avant OAuth. Une connexion Cloudflare sécurisée est actuellement nécessaire car le connecteur refuse le téléversement des assets avec ses permissions présentes. Ne pas extraire ou élargir les secrets de production pour contourner ce refus.

Google Cloud doit utiliser un projet TEST dédié, Drive API + Picker, client Web commun, `drive.file`, clé Picker limitée aux référents TEST et à Picker API, aucun billing. L'administrateur et l'identité Drive de test sont deux rôles distincts ; ne jamais choisir automatiquement un compte personnel pour les essais. L'absence d'accès console ne constitue pas une configuration exécutée.

La recette réelle requiert : envoi de fixture, relecture exacte des octets, reçu, édition pendant envoi, perte de réponse après écriture, reprise du même identifiant, hors ligne, deux contextes indépendants, conflit sans écrasement, installation vierge, fermeture/réouverture et égalité exhaustive des valeurs. Distinguer fournisseur Google simulé, Google hébergé et iPhone physique. Aucun mot de passe, code, jeton ou HAR brut dans les preuves.

Rollback TEST : sélectionner le déploiement TEST précédent si nécessaire et autorisé, sans supprimer de déploiement, fichier Drive, cache, coffre, outbox ou historique. Un rollback logiciel ne restaure pas des données. Ne pas réintroduire une version ignorant un journal de récupération en cours.
56 changes: 56 additions & 0 deletions docs/DRIVE_RECOVERY_HANDOFF_20261025.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Reçus et récupération RC — 25 octobre 2026

Auteur : Bryan Fiarda. Correction sensible, non fusionnée, non déployée.
Base dépendante : #19, qui inclut #17. #16 et #18 restent séparées ; #20 est
une preuve temporaire à ne pas fusionner. Aucune branche source modifiée.

## Défaut reproduit et correction

Lors d'un téléchargement, #19 relit le reçu durable et s'assure que le job
d'upload concurrent est intact. La comparaison par JSON.stringify transforme
son Uint8Array chiffré entier en objet JSON numérique, deux fois. Avec la limite
contractuelle de fichier, ces chaînes peuvent occuper bien plus de mémoire que
le fichier. Aucune panne physique ni perte personnelle n'est supposée.

Le nouveau contre-test place un upload synthétique de 2 Mio en attente et refuse
sa sérialisation JSON. Il échoue sur #19 (null au lieu des octets téléchargés),
puis passe avec sameJob. La comparaison vérifie la forme connue, la liaison,
l'identifiant réservé, l'empreinte, la longueur et chaque octet sans conversion
JSON. Une seule différence d'octet interdit VERIFIED_BYTES. Les quatre tests
antérieurs restent intacts ; le journal avant correction est conservé.

La copie Uint8Array reste gérée par les mêmes archives et transactions IDB.
Aucune réduction de taille du ciphertext, aucune suppression ou nouvelle
compression n'est revendiquée. Ce changement évite une allocation inutile ; il
ne chiffre pas un gain de RAM d'un iPhone réel non mesuré.

## Frontière de confiance et lien de récupération

L'écran Drive garde le parcours d'import dans Race Control et ajoute le lien
`<raceOrigin>/recovery.html`. Cette page appartient au candidat RC de récupération
A2 du 25 octobre. Publier les deux candidats compatibles après leurs GO séparés.
Ne pas présenter ce lien comme un parcours disponible dans la production actuelle.

Control Vault ne demande jamais le code, ne déchiffre pas et ne choisit pas une
tête A2. Le code et la comparaison métier restent dans RC. Le lien utilise
uniquement l'origine validée de la configuration, aucun paquet, code, jeton ou
identifiant privé dans l'URL, avec noopener/noreferrer. Les tests de l'interface
vérifient le lien et l'absence de champ mot de passe.

## Validation et limites

Reproduire : `npm run typecheck`, `npm test`, `npm run build`, `npm run security`,
`CI=1 npm run test:e2e`. Le workflow Chromium/WebKit existant reste inchangé.
Vérifier la CI sur le SHA exact de cette PR ; les anciennes CI ne la valident pas.

Les réponses Google des tests sont fictives. Les comptes, restrictions de clé
Picker, appProperties et visibilité entre les origines doivent être vérifiés
sur un compte de test autorisé, puis sur iPhone physique. Aucun essai réel,
configuration Google, déploiement ou transmission personnelle dans ce lot.

## Impact et retour arrière

Changement local au contrôle de reçu et à un lien explicite ; aucun format,
scope, secret, schéma IDB, cache, contrat V1/A1/A2 ou CI modifié. Ne jamais fusionner
la PR temporaire #20. Revert logiciel par PR après GO, sans purger les jobs,
archives, reçus, fichiers Drive ou stockages d'autres applications.
Loading
Loading