Vérifier l’accès aux fichiers d’une app macOS après relancement

Security ·~6 min de lecture

Vérifier l’accès aux fichiers d’une app macOS après relancement

Une fonction de lecture de fichiers macOS développée sur un Mac distant peut fonctionner sans problème dans un répertoire de test, puis afficher « Accès refusé » chez les utilisateurs. Souvent, le fichier de test se trouve justement dans le conteneur de l’app, ou le test s’arrête à la lecture juste après la sélection sans vérifier ce qui se passe une fois l’app relancée. Pour valider cette fonction, utilisez un fichier situé hors du conteneur et sélectionné par l’utilisateur. La vérification doit aller jusqu’à sa réouverture après relancement de l’app.

Définir d’abord le périmètre du test

L’App Sandbox limite l’accès aux fichiers situés hors du conteneur de l’app. Le fait que l’utilisateur accorde l’accès au moyen du sélecteur de fichiers système ne permet pas à l’app de lire indéfiniment le fichier à partir de son chemin initial. Si le produit doit retrouver ce fichier, l’app doit enregistrer un signet à portée de sécurité. Au lancement suivant, elle doit résoudre ce signet et activer l’accès pendant la lecture ou l’écriture effective.

Préparez un fichier texte ordinaire, au contenu facilement reconnaissable, hors du conteneur de l’app. Évitez les répertoires soumis à des autorisations de confidentialité supplémentaires du système : vous introduiriez un autre mécanisme d’autorisation dans ce test. Préparez également un fichier dans le conteneur à titre de comparaison. S’il est lisible, cela prouve seulement que le code de traitement des fichiers fonctionne, pas que l’accès aux fichiers externes est correct.

Le critère de validation n’est pas « aucune erreur après la fermeture du sélecteur », mais « après relancement, l’app peut toujours lire le fichier désigné dans les limites de l’autorisation accordée et propose une action permettant de rétablir l’accès si cette autorisation n’est plus utilisable ».

Vérifier les droits déclarés par l’app livrée

Examinez d’abord le .app réellement livré, et pas seulement le fichier de droits modifié dans le projet. Une différence dans la configuration de compilation ou la signature peut changer les droits déclarés dans le produit final. Remplacez APP ci-dessous par le chemin réel du produit compilé pour ce test :

APP="$PWD/build/Reader.app"
codesign --verify --strict --verbose=2 "$APP"
codesign -d --entitlements :- "$APP"

Vérifiez que le droit relatif à la sandbox est activé. Pour un import en lecture seule, contrôlez le droit de lecture des fichiers sélectionnés par l’utilisateur. N’envisagez le droit de lecture et d’écriture que si l’app doit réellement modifier le fichier d’origine. Si l’accès doit être conservé d’un lancement à l’autre, vérifiez aussi le droit relatif aux signets à portée de l’app. N’étendez pas les droits d’accès à des répertoires sans rapport avec le test dans le seul but de le faire réussir.

L’examen de la signature indique quels droits sont déclarés, mais pas si l’utilisateur a sélectionné un fichier ni si le signet peut être restauré. Ces deux points doivent être vérifiés en exécutant l’app.

Enregistrer le signet et encadrer chaque accès

Une fois que NSOpenPanel a renvoyé l’URL du fichier sélectionné par l’utilisateur, créez un signet à portée de sécurité et stockez les Data obtenues dans l’espace de stockage persistant de l’app. Ne conservez pas uniquement url.path : le chemin indique l’emplacement, mais ne contient pas les informations nécessaires pour récupérer l’accès.

Pour la lecture, regroupez la résolution du signet, l’activation de l’accès et sa désactivation dans une même fonction afin de ne pas oublier le nettoyage en cas d’erreur :

enum FileAccessError: Error {
    case staleBookmark
    case accessDenied
}

func readSelectedFile(bookmark: Data) throws -> Data {
    var stale = false
    let url = try URL(
        resolvingBookmarkData: bookmark,
        options: .withSecurityScope,
        relativeTo: nil,
        bookmarkDataIsStale: &stale
    )
    guard !stale else { throw FileAccessError.staleBookmark }
    guard url.startAccessingSecurityScopedResource() else {
        throw FileAccessError.accessDenied
    }
    defer { url.stopAccessingSecurityScopedResource() }
    return try Data(contentsOf: url)
}

La création du signet doit elle aussi utiliser l’option de portée de sécurité, et les données renvoyées doivent être conservées de façon fiable. Dans cet exemple, la lecture s’arrête si le signet est périmé. L’interface du produit doit alors permettre de sélectionner à nouveau le fichier, puis de créer un nouveau signet une fois la nouvelle autorisation obtenue. Il faut vérifier le résultat de startAccessingSecurityScopedResource() et ne pas attendre la fermeture de l’app pour appeler stopAccessingSecurityScopedResource().

Tester le relancement dans une session graphique

Sur un Mac dans le cloud, la signature peut être contrôlée en ligne de commande. En revanche, valider l’interaction avec NSOpenPanel exige une session graphique macOS utilisable. Le fait qu’une tâche s’exécute par SSH ne garantit pas que le sélecteur de fichiers puisse s’afficher. Pour les tests automatisés aussi, assurez-vous que le processus de test dispose d’une session graphique.

Effectuez les étapes suivantes avec le même fichier de test et consignez, à chaque fois, le résultat attendu et le résultat obtenu :

  1. Au premier lancement, tentez de lire le fichier externe à partir d’un chemin préenregistré, sans ouvrir le sélecteur. Vérifiez que l’app ne contourne pas l’autorisation prévue par le seul recours à ce chemin.
  2. Sélectionnez vous-même le fichier dans la fenêtre, lisez son contenu, vérifiez-le et enregistrez le signet.
  3. Quittez complètement l’app, puis relancez le même produit livré. Résolvez le signet, relisez le fichier et vérifiez son contenu.
  4. Déplacez le fichier de test, puis réessayez. L’app doit afficher une erreur compréhensible et proposer de sélectionner à nouveau le fichier, sans présenter à tort l’absence du fichier comme un défaut de signature.
  5. Testez le cas d’erreur avec des données de signet corrompues. L’app doit arrêter la lecture et demander une nouvelle sélection, sans afficher de fenêtres en boucle ni réessayer indéfiniment.

La troisième étape est la plus facile à oublier : une lecture limitée à un seul cycle de vie du processus peut masquer le fait que le signet n’a jamais été enregistré de manière persistante. Si le test prévoit de réécrire le fichier, répétez séparément cette série d’étapes avec le droit de lecture et d’écriture. Un test en lecture seule ne suffit pas.

Diagnostiquer les échecs à partir des éléments observés

Si la lecture échoue immédiatement après la sélection, vérifiez d’abord que l’URL renvoyée par NSOpenPanel est utilisée correctement et que les droits déclarés correspondent aux besoins de lecture ou d’écriture. Si elle fonctionne aussitôt, mais échoue après relancement, vérifiez en priorité que le signet a réellement été conservé et que l’app relancée possède toujours la même identité et la même configuration de signature. Si seuls certains répertoires posent problème, écartez d’abord la possibilité qu’ils soient soumis à des autorisations de confidentialité supplémentaires, plutôt que d’assouplir directement les droits de la sandbox.

Le compte rendu du test doit au minimum conserver la version de l’app livrée, ses droits définitifs, la catégorie du répertoire contenant le fichier de test, les résultats avant et après la sélection ainsi qu’après relancement, et le type d’erreur. Ne consignez que les états et erreurs nécessaires, sans afficher les données brutes du signet ni le contenu du fichier. Si une compilation ultérieure échoue, l’équipe pourra ainsi distinguer une modification des droits déclarés, un problème lié au cycle de vie du signet ou l’absence de session graphique dans l’environnement de test, au lieu de repartir de « ça s’ouvre sur ma machine ».

Questions fréquentes

Pourquoi conserver un signet après la sélection du fichier ?

Si l’app doit rouvrir le fichier après relancement, elle doit enregistrer un signet à portée de sécurité, le résoudre, activer l’accès pendant la lecture, puis libérer cet accès.

Une session SSH suffit-elle pour ce test ?

Non. Elle permet de contrôler la signature et les droits déclarés, mais la sélection du fichier et les tests interactifs exigent une session graphique macOS utilisable.

Intégrez un Mac dans le cloud à votre flux de travail

VPSPush propose des Mac physiques dédiés accessibles à distance. Choisissez un emplacement et une durée, puis vérifiez les informations de connexion et le montant final dans votre commande.

Découvrir les offres de Mac dans le cloud et commander