Bonnes pratiques d’ingénierie HexVM

Créer un contrôle de conformité des manifestes de confidentialité iOS

Créer un contrôle de conformité des manifestes de confidentialité iOS

Après la migration d’un projet iOS vers un Mac dans le cloud, la réussite de la compilation ne garantit pas que les déclarations de confidentialité soient complètes. Une mise à jour de dépendance peut ajouter un fichier PrivacyInfo.xcprivacy, tandis qu’une mauvaise configuration de la copie des ressources peut empêcher le manifeste du projet principal d’être intégré à l’app finale. L’approche la plus fiable consiste à traiter les manifestes de confidentialité comme des artefacts de compilation : inspecter d’abord les sources, vérifier ensuite le produit généré, puis utiliser une référence versionnée pour bloquer toute modification qui n’a pas encore été examinée.

Définir le périmètre du contrôle

Un contrôle exploitable doit couvrir au moins quatre niveaux : vérifier que les manifestes peuvent être analysés comme des plist, valider le type des champs de premier niveau, inclure tous les manifestes des dépendances dans le périmètre d’analyse et confirmer que les manifestes attendus sont présents dans l’app finale. Il ne suffit pas de rechercher un seul fichier à la racine du dépôt : les dépendances sources, les frameworks précompilés et les composants récupérés par les gestionnaires de paquets peuvent chacun fournir leurs propres déclarations.

Commencez par créer un index des manifestes dans un espace de travail propre :

find . \
  -path './.git' -prune -o \
  -path './DerivedData' -prune -o \
  -name PrivacyInfo.xcprivacy -print \
  | LC_ALL=C sort > privacy-manifests.current

Il est recommandé de consigner dans privacy-manifests.baseline les chemins explicitement suivis par le projet. Le pipeline compare alors les deux fichiers. Tout ajout, toute suppression ou toute modification de chemin provoque d’abord un échec ; la référence n’est mise à jour qu’après validation par les responsables du code. Les changements de déclaration introduits par une mise à jour de dépendance ne passent ainsi pas inaperçus.

La référence ne constitue pas une preuve de conformité. Elle indique uniquement que la modification actuelle a fait l’objet d’un examen explicite et ne remplace pas l’évaluation du comportement réel du code.

Valider les plist et leur structure de premier niveau

plutil -lint permet de détecter une plist XML ou binaire endommagée, mais ne vérifie pas le type des champs. La bibliothèque standard de Python suffit pour ajouter un contrôle structurel léger, sans installer de dépendance supplémentaire :

import pathlib
import plistlib
import sys

allowed = {
    "NSPrivacyTracking": bool,
    "NSPrivacyTrackingDomains": list,
    "NSPrivacyCollectedDataTypes": list,
    "NSPrivacyAccessedAPITypes": list,
}

failed = False
files = sorted(pathlib.Path(".").rglob("PrivacyInfo.xcprivacy"))

if not files:
    print("No privacy manifest found")
    sys.exit(1)

for path in files:
    try:
        with path.open("rb") as stream:
            data = plistlib.load(stream)
        if not isinstance(data, dict):
            raise TypeError("Root must be a dictionary")
        for key, value in data.items():
            expected = allowed.get(key)
            if expected is None:
                raise KeyError(f"Unknown top-level key: {key}")
            if not isinstance(value, expected):
                raise TypeError(f"{key} must be {expected.__name__}")
        print(path)
    except Exception as error:
        failed = True
        print(f"{path}: {error}")

sys.exit(1 if failed else 0)

Enregistrez le script sous Scripts/validate_privacy_manifests.py et exécutez-le avant la compilation. Si le projet utilise des champs d’extension internes à l’organisation, n’autorisez pas globalement toutes les clés inconnues : ajoutez-les une par une à la liste des valeurs admises et documentez leur usage.

Créer une grille de vérification des Required Reason APIs

Une structure valide ne garantit pas que les motifs déclarés soient corrects. Les appels liés notamment à l’horodatage des fichiers, au temps écoulé depuis le démarrage du système, à l’espace disque et aux préférences doivent tous entrer dans le périmètre de la revue du code. Une analyse automatisée peut fournir des indices, mais les macros, les couches d’abstraction et les dépendances binaires entraînent des faux négatifs comme des faux positifs. Une simple recherche textuelle ne doit donc jamais déterminer directement le code de motif à déclarer.

Conservez une ligne d’audit pour chaque déclaration :

Élément contrôlé Informations à consigner Condition d’échec
Catégorie d’API Identifiant de catégorie indiqué dans le manifeste Aucun chemin de code correspondant à la catégorie
Emplacement d’utilisation Module, fichier et responsable Source de l’appel impossible à localiser
Finalité de l’utilisation Fonctionnalité réelle proposée à l’utilisateur Motif incompatible avec le comportement
Origine de la dépendance Code interne ou composant précis Origine du binaire indéterminée
Déclencheur de révision Modification du code ou de la version d’une dépendance Absence de nouvelle validation après la modification

Pour les composants binaires propriétaires, consignez au minimum la version du composant, le hachage du manifeste et la fonctionnalité métier qui justifie son intégration. Une déclaration dont l’origine ne peut pas être expliquée ne doit pas être complétée par supposition : demandez confirmation au responsable de la dépendance.

Vérifier de nouveau l’app finale

Un fichier présent dans les sources peut ne pas être copié en raison d’une mauvaise configuration de la target membership ou de la phase de ressources. Le pipeline doit effectuer une compilation Release non signée, puis inspecter l’artefact réel :

rm -rf .build/privacy

xcodebuild \
  -scheme "$SCHEME" \
  -configuration Release \
  -sdk iphoneos \
  -derivedDataPath .build/privacy \
  CODE_SIGNING_ALLOWED=NO \
  build

APP_PATH="$(find .build/privacy/Build/Products -type d -name '*.app' -print -quit)"
test -n "$APP_PATH"
find "$APP_PATH" -name PrivacyInfo.xcprivacy -print | LC_ALL=C sort

L’app principale, les frameworks intégrés et les extensions doivent chacun apparaître conformément à la structure prévue du projet. N’inscrivez pas en dur un répertoire aléatoire de DerivedData et ne vous contentez pas de compter les manifestes dans les sources : plusieurs manifestes sources peuvent être fusionnés, remplacés ou omis pendant la compilation.

Enregistrer l’inventaire des artefacts

Enregistrez comme pièce jointe du pipeline le chemin relatif et le SHA-256 de chaque manifeste contenu dans l’artefact :

find "$APP_PATH" -name PrivacyInfo.xcprivacy -print0 |
  while IFS= read -r -d '' file; do
    relative="${file#"$APP_PATH"/}"
    digest="$(shasum -a 256 "$file" | awk '{print $1}')"
    printf '%s  %s
' "$digest" "$relative"
  done | LC_ALL=C sort > privacy-artifact.sha256

En cas de problème lors d’un audit, cet inventaire permet de répondre précisément à la question « que contenait réellement cette compilation ? ». Il est plus fiable que l’examen de l’état actuel de la branche.

Intégrer les conditions d’échec au workflow quotidien

Il est recommandé de répartir les contrôles entre un niveau rapide et un niveau complet. Les demandes de fusion exécutent d’abord la découverte des manifestes, l’analyse des plist, la vérification des types de champs et la comparaison avec la référence. La branche principale effectue ensuite la compilation Release et le contrôle des artefacts de l’app. Un contrôle rapide en échec indique généralement le chemin concerné en quelques dizaines de secondes, tandis que le contrôle complet détecte les problèmes liés à la phase de ressources et aux frameworks intégrés.

Conservez les contrôles suivants avant la mise en production :

  • L’index des manifestes correspond à la référence approuvée.
  • Chaque fichier passe avec succès plutil -lint et le script de validation structurelle.
  • Toute nouvelle catégorie d’API est associée à un emplacement dans le code, à un responsable et à une finalité réelle.
  • Les différences de manifestes après une mise à jour de dépendance ont été examinées manuellement.
  • Les manifestes de l’app finale, des extensions et des frameworks intégrés correspondent aux attentes.
  • Les chemins et les hachages des artefacts ont été archivés avec cette compilation.

L’objectif de ce contrôle n’est pas de prendre automatiquement des décisions de conformité à la place de l’équipe. Il doit transformer toute omission en échec de compilation explicite et permettre de relier chaque modification de déclaration au code, aux dépendances et aux traces de sa revue.

Questions fréquentes

La présence de PrivacyInfo.xcprivacy dans le dépôt suffit-elle ?

Non. Le fichier doit être lisible, employer les bons types de champs et être intégré à l’application ou au framework réellement compilé.

Un script peut-il confirmer chaque justification de Required Reason API ?

Pas entièrement. Il détecte les absences, les structures invalides et les changements non examinés, mais le propriétaire du code doit confirmer le motif réel.

Pourquoi renouveler la référence après la mise à jour d’une dépendance ?

Une dépendance peut ajouter ou modifier son manifeste. La référence ne doit changer qu’après examen du diff et validation des déclarations.

Apple Silicon dédié

Choisissez un Mac dans le cloud dédié pour votre prochaine compilation

Choisissez le modèle de Mac mini, la durée de location et la région selon votre charge de travail. Chaque commande correspond à un nœud physique dédié ; la disponibilité réelle est indiquée en temps réel dans la console.

Choisir une solution Mac dans le cloud