Clears RUSTSEC-2026-0367, RUSTSEC-2026-0368 and RUSTSEC-2026-0369 on zip 4.6.1, which turned the daily audit.yml red at run 558 (2026-10-17) without any lockfile change. zip was compiled only for Windows, through the default `zip` feature of tauri-plugin-updater 2.10.0. It is fixed only in zip >= 9.0.0, and tauri-plugin-updater 2.14.0 still requires zip ^4, so no cargo update can reach a fix. That feature only unpacks archived update packages (.nsis.zip, .msi.zip, .AppImage.tar.gz, produced by createUpdaterArtifacts "v1Compatible"); this app ships createUpdaterArtifacts: true, whose packages are never archives. The updater is now declared without its default features, keeping rustls-tls. The lockfile loses zip, arbitrary and derive_arbitrary (666 -> 663 packages, nothing added), and tar leaves the Linux graph. tar was the canary of the suppression guard in check-rust.yml. It is replaced by webkit2gtk, Tauri's Linux webview, which keeps the property that made tar useful: a dependency present on x86_64-unknown-linux-gnu only, so finding it also proves that --target resolves conditional edges. ADR 0018, architecture.md, qa-update-cycle.md and the release skill are updated to match. Verified locally: cargo-audit 0.22.2 exits 1 on the old lock with the three IDs and 0 on the new one (663 crates, the same 12 allowed warnings); the suppression guard is green, and red with the canary swapped for tar or zip; windows-sys guard 9/9; cargo check and cargo test (267 passed) on Linux; cargo xwin check for x86_64-pc-windows-msvc is green on main (zip compiled) and on this branch (zip gone). Refs #455 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
12 KiB
ADR 0018 — Suppression d'advisories RustSec non atteignables : liste par ID, preuve par cible livrée, garde-fou anti-péremption
- Status: Accepted
- Date: 2026-07-27
- Issues: #310 (les 9 advisories découvertes, cette décision), #312 (déclencheur de retrait des entrées
quick-xml), #314 (le cron deaudit.ymln'a jamais démarré), #455 (canaritar→webkit2gtk) - S'appuie sur #232 (split de
check.yml, création deaudit.yml, retrait du|| truequi masquait la sortie decargo audit)
Amendement du 2026-07-27 (#312/#313) — la décision et ses trois règles sont inchangées ; seules les entrées ont bougé. Les deux advisories
quick-xmlont quitté la liste le jour même :plist1.10.0 tirequick-xml0.41.0, qui porte le correctif, dans la borne existante detauri. Elles sont donc résolues, pas acceptées, et le mécanisme a fonctionné comme prévu — la vérification par cible a montré qu'un correctif était devenu atteignable, et l'entrée est partie. Il ne reste queRUSTSEC-2023-0071(rsa), et le garde-fou ne boucle plus que sur ce crate.Trois passages ci-dessous sont datés du 2026-07-27 avant ce bump et à lire comme historiques :
quick-xmlcomme exemple de l'insuffisance de--target all(règle 1), l'entréequick-xmldu tableau de contexte, et l'alternative « override /[patch.crates-io]» — cette dernière n'est plus vraie,plist1.10.0 déclarant désormaisquick-xml ^0.41.0. L'argument de fond qu'elle illustre ([patch.crates-io]ne franchit pas une frontière semver-incompatible) reste correct, seul le cas d'espèce a disparu.
Amendement du 2026-10-17 (#455) — le canari du garde-fou change ; la décision et ses trois règles restent. Pour retirer
zip4.6.1 (RUSTSEC-2026-0367 à -0369, sans version corrigée compatible avec l'updater),tauri-plugin-updaterest compilé sans sa featurezip, qui tirait aussitar:tara quitté le graphe Linux, et le canari d'origine aurait rougi par construction. Il est remplacé parwebkit2gtk, la webview Linux de Tauri (wry), qu'un build Linux de Tauri v2 ne peut pas perdre. Le choix garde la propriété qui faisait la valeur detar: une dépendance présente surx86_64-unknown-linux-gnuet absente dex86_64-pc-windows-msvc, dont la présence prouve aussi que--targetrésout les arêtes conditionnelles sur lesquelles repose la règle 1.tauri, envisagé à l'analyse de #455, est inconditionnel et n'aurait prouvé que l'affichage decargo tree.
Contexte
#232 a retiré le || true qui avalait la sortie de cargo audit et créé audit.yml, un job quotidien (06:00 UTC) volontairement bloquant : il est le canal de notification pour les advisories publiées entre deux PR Rust, check-rust.yml ne tournant que sur ~1 PR sur 40.
Le retrait du masque a découvert 9 advisories RustSec préexistantes dans src-tauri/Cargo.lock. Six sont atteignables et corrigeables par un simple cargo update (rustls-webpki ×4, tar ×2, tous sur le chemin de tauri-plugin-updater). Les trois restantes ne le sont pas :
| Crate | Advisories | Situation |
|---|---|---|
quick-xml 0.38.4 |
RUSTSEC-2026-0194, RUSTSEC-2026-0195 (7.5 high) | Tiré par plist, dont tauri ne dépend que pour le bundling Apple. Absent des arbres x86_64-unknown-linux-gnu et x86_64-pc-windows-msvc ; présent seulement sur x86_64-apple-darwin, cible que le projet ne livre pas. Corrigé en >= 0.41.0 alors que plist exige ^0.38 — frontière semver-incompatible que [patch.crates-io] ne peut pas franchir. |
rsa 0.9.10 |
RUSTSEC-2023-0071 (Marvin, 5.9 medium) | Aucun correctif publié (liste patched vide). Seul parent dans le lock : sqlx-mysql, artefact du graphe multi-backend de sqlx ; le projet parle à SQLite via tauri-plugin-sql. cargo tree -i rsa --target all ne retourne rien. |
Le problème n'est donc pas de corriger ces trois advisories — c'est impossible — mais de décider ce que devient le gate quotidien. Le laisser rouge en permanence reproduit exactement le défaut que #232 venait de supprimer : une alarme qui sonne tous les jours sans qu'on puisse rien y faire finit ignorée, et la prochaine advisory réelle se noie dedans. C'est la même perte de signal que le || true, obtenue par un autre chemin.
Décision
Une liste de suppressions par ID d'advisory, dans un .cargo/audit.toml versionné à la racine du dépôt, encadrée par trois règles.
Règle 1 — critère d'admission
Une advisory ne peut être listée que si son crate est absent du graphe de dépendances de toutes les cibles livrées (Windows et Linux), ou si aucun correctif n'a été publié. Une advisory atteignable dont le correctif existe se corrige, elle ne se supprime jamais.
La preuve est mécanique et reproductible, et doit être refaite contre le lock courant :
cargo tree --manifest-path src-tauri/Cargo.toml -i <crate> --target x86_64-unknown-linux-gnu
cargo tree --manifest-path src-tauri/Cargo.toml -i <crate> --target x86_64-pc-windows-msvc
Sortie vide sur les deux cibles = entrée justifiée. --target all ne suffit pas : il répond « présent » pour des crates conditionnels Apple qui ne sont compilés nulle part chez nous, ce qui est précisément le cas de quick-xml.
Règle 2 — clé par ID, jamais par crate
Les entrées portent un ID d'advisory (RUSTSEC-YYYY-NNNN), jamais un nom de crate. Une nouvelle advisory déposée contre un crate déjà listé repasse le gate au rouge, volontairement : elle est examinée pour elle-même. C'est ce qui distingue une suppression justifiée d'un silence permanent, et un élargissement au crate entier suffirait à annuler le bénéfice du gate.
Règle 3 — garde-fou anti-péremption
L'atteignabilité est une propriété du graphe résolu aujourd'hui, pas une propriété permanente. Un bump tauri/plist rendant quick-xml inconditionnel laisserait la suppression cacher une advisory devenue vivante, et l'audit resterait vert : l'inverse exact du rouge permanent que cette décision cherche à éviter.
check-rust.yml porte donc une étape bloquante qui rejoue la règle 1 pour chaque crate supprimé, sur les deux cibles livrées, et échoue si l'un d'eux entre dans un arbre. Elle est placée après cargo check (index de registre déjà chaud) et passe --locked pour que cargo tree ne réécrive pas le lock contre lequel l'audit a été pris. Le scénario qui périmerait la justification est lui-même une modification de src-tauri/, soit exactement ce qui déclenche ce workflow.
Trois détails la rendent fiable plutôt que décorative :
- le code de sortie de
cargo treeest testé séparément de sa sortie standard — un crate absent sort en 0 avec un stdout vide, tandis qu'un échec decargo treesort en non-zéro avec un stdout vide lui aussi ; sans cette distinction, une panne de l'outil se lirait comme une preuve d'absence ; - un canari (
webkit2gtk, dépendance réellement présente, et seulement sous Linux ;tarjusqu'à #455) doit être trouvé à chaque exécution, faute de quoi le silence de la boucle ne prouve rien ; .cargo/**est ajouté auxpathsdecheck-rust.ymlpour qu'une PR ne touchant que la liste déclenche bien la vérification.
Le pendant — une entrée devenue inutile, que rien ne signale — est couvert par une issue de suivi portant la condition de retrait : #312 l'a joué pour quick-xml (et a servi : le déclencheur avait sauté sans que personne le remarque), #319 le fait pour rsa. Toute entrée de la liste doit en avoir une, sinon le garde-fou ne couvre qu'une moitié du risque.
Alternatives considérées
- Laisser
audit.ymlrouge en permanence. Rejeté : c'est la perte de signal que #232 venait de corriger, obtenue autrement. Une alarme toujours rouge n'est plus une alarme. - Remettre
continue-on-errorou|| truesur le job quotidien. Rejeté pour la même raison, en pire : cela supprime le signal pour toutes les advisories, pas seulement pour les trois inévitables. - Dupliquer des
--ignoredansaudit.ymletcheck-rust.yml. Rejeté : deux listes à maintenir en phase, sans endroit naturel où écrire la justification. Le fichier.cargo/audit.tomlest lu par les deux workflows sans qu'aucun n'ait à le savoir, et il porte les preuves à côté des entrées. - Ignorer au niveau du crate plutôt que de l'advisory. Rejeté — voir la règle 2.
- Un override /
[patch.crates-io]pourquick-xml. Impossible : le correctif est en0.41.0etplistexige^0.38.[patch.crates-io]ne franchit pas une frontière semver-incompatible. (Périmé le jour même —plist1.10.0 déclarequick-xml ^0.41.0, et un simplecargo update -p plista suffi ; voir l'amendement en tête. Le principe reste, le cas d'espèce a disparu.) - Retirer
sqlx-mysqldu graphe pour éliminerrsa. Écarté :rsan'étant compilé sur aucune cible, l'opération serait un contorsionnement du manifeste pour un gain nul.
Conséquences
Positives
audit.ymlredevient un signal exploitable : rouge veut dire « quelque chose de nouveau et d'actionnable », et non « les trois mêmes advisories qu'hier ».- Les six advisories réellement atteignables sont corrigées, sans toucher
Cargo.toml(rustls-webpki0.103.9 → 0.103.13,tar0.4.44 → 0.4.46, dans les bornes existantes). - La justification de chaque suppression vit à côté de l'entrée, avec les commandes pour la rejouer — un lecteur futur n'a pas à re-dériver l'analyse ni à re-litiguer la branche « override ».
Négatives
- Un audit vert ne signifie plus « zéro advisory », mais « zéro advisory hors de la liste ». Le contrat du gate a changé et doit être lu comme tel — d'où la mention dans
audit.yml, dansdocs/architecture.mdet dansCLAUDE.md. - La liste et la boucle du garde-fou sont couplées à la main : ajouter une entrée dans
.cargo/audit.tomlsans ajouter son crate danscheck-rust.ymllaisse la nouvelle entrée sans couverture. Les deux fichiers portent le rappel réciproque, mais rien ne l'impose mécaniquement. - Le garde-fou ne tourne que sur les PR touchant
src-tauri/ou.cargo/. C'est le bon déclencheur pour le scénario visé, mais ce n'est pas une vérification continue.
Neutre
- Les 23
warning(bindings gtk-rs GTK3 non maintenus,fxhash, crates yanked) ne sont pas concernés : ils ne comptent pas dans le code de sortie decargo auditet ne sont donc pas ce qui rend le gate vert ou rouge. Le crate yankedtauri-plugin-deep-link 2.4.8, dépendance directe, est traité séparément en #313. - Un
workflow_dispatchvert prouve que la commande sort en 0, pas que l'alarme quotidienne existe : au moment de cette décision,audit.ymln'a encore jamais été déclenché par sonschedule(#314).
Liens
.cargo/audit.toml— la liste, ses preuves et ses critères de retrait.forgejo/workflows/check-rust.yml— étape « Verify suppressed advisories are still unreachable ».forgejo/workflows/audit.yml— le gate quotidien bloquant- ADR 0009 — récupération de cours via
reqwest, l'un des deux chemins qui rendentrustls-webpkiatteignable - Issues #310 (cette décision), #312 (retrait
quick-xml, fait le 2026-07-27), #313 (tauri-plugin-deep-linkyanked, fait), #314 (cron jamais déclenché), #315 (smoke-test du cycle de mise à jour), #319 (déclencheur de retraitrsa, la dernière entrée restante), #455 (updater sans sa featurezip, canaritar→webkit2gtk)