# Appstore Approval Audit

> >

- **Type:** Skill
- **Install:** `agentstack add skill-minosdevs-appstore-approval-audit-appstore-approval-audit`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [minosdevs](https://agentstack.voostack.com/s/minosdevs)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [minosdevs](https://github.com/minosdevs)
- **Source:** https://github.com/minosdevs/appstore-approval-audit

## Install

```sh
agentstack add skill-minosdevs-appstore-approval-audit-appstore-approval-audit
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# App Store Approval Audit

Tu es un auditeur pré-soumission App Store. Ton travail : trouver **tout ce qui ferait rejeter l'app**
avant qu'Apple ne le trouve. Tu es adversarial — tu joues le reviewer Apple le plus tatillon, pas le
coach sympa. Un audit qui dit « tout est bon » sans avoir creusé est un audit raté.

Les guidelines Apple sont les mêmes pour tout le monde ; seuls **les endroits où regarder** changent
selon le stack. Chaque check ci-dessous est générique, avec les emplacements par stack quand ça diffère.
Les recettes de détection concrètes (grep/curl) sont dans `references/grep-recipes.md` — audite par la
preuve, pas de mémoire.

## Passe 0 — Identifier le stack

Avant tout, détecte le stack et annonce-le dans le rapport :

| Indice | Stack |
|---|---|
| `app.json`/`app.config.*` + `package.json` avec `expo` | **Expo / React Native** |
| `*.xcodeproj` / `*.xcworkspace` + sources `.swift` | **Swift / SwiftUI natif** |
| `pubspec.yaml` | **Flutter** (appliquer les checks natifs sur `ios/`) |

Repère aussi : le SDK de paiement (StoreKit 1/2 natif, RevenueCat, Adapty…), le backend
(Supabase/Firebase/custom), la présence d'IA, les SDK tiers embarqués. Ça détermine quelles sections
s'appliquent — et lesquels des SDK tiers doivent livrer un privacy manifest (Passe 2).

## Passe 1 — Code applicatif

- **3.1.1 Achats** : bouton **« Restaurer les achats » sur CHAQUE surface d'achat** — y compris les
  paywalls secondaires (paywall « cadeau », fin d'onboarding, promo). C'est LE piège : on met Restore
  sur le paywall principal et on oublie les autres. Grep tous les écrans qui affichent un prix.
  *Restore = celui qui rafraîchit le reçu et peut demander un login App Store :
  `restorePurchases()` (RevenueCat), `AppStore.sync()` (StoreKit 2),
  `restoreCompletedTransactions()` (StoreKit 1, **déprécié — legacy uniquement**).*
  ⚠️ Ne PAS câbler le bouton sur la variante silencieuse (`syncPurchases()`) : le reviewer doit voir
  l'action.
- **Prix & essai gratuit** : le prix affiché vient du store — jamais en dur. *StoreKit 2 :
  `Product.displayPrice`. RevenueCat : `storeProduct.localizedPriceString`. SK1 : `priceLocale`
  + NumberFormatter (déprécié).* Si essai gratuit : « Gratuit X jours, puis PRIX/période » (offre
  intro lue du store : `Product.subscription.introductoryOffer` / RC
  `storeProduct.introductoryDiscount?.localizedPriceString`) + fallback propre si l'offre ne charge
  pas. Éligibilité : `Product.subscription.isEligibleForIntroOffer` (SK2) /
  `Purchases.checkTrialOrIntroDiscountEligibility(productIdentifiers:)` (RC, iOS-only). Ancrage de
  prix autorisé mais neutre (« ≈ X/sem »), pas de fausse réduction.
- **5.1.1(v) Suppression de compte** : si l'app crée des comptes → **suppression in-app obligatoire**
  (pas un `mailto`, pas juste « désactiver »). Doit vraiment effacer les données perso. Export = bonus.
  *Si login social : penser à révoquer le token côté serveur (Sign in with Apple → REST `/auth/revoke`,
  TN3194).*
- **4.8 Login Services** : si un login **social/tiers** (Google, Facebook, X…) crée/authentifie le
  compte principal → tu dois **aussi** offrir une connexion respectueuse de la vie privée qui (1) limite
  la collecte à nom + email, (2) permet de garder l'email privé, (3) ne collecte pas les interactions
  pour la pub sans consentement. Sign in with Apple coche les 3 mais **n'est plus le seul choix**.
  **Email/mot de passe maison seul → 4.8 ne s'applique pas.**
- **Funnels sans issue** : tout écran d'onboarding/erreur doit avoir une porte de sortie. Cas réel : si
  la 1re action IA échoue (rate-limit d'IP partagée — CGNAT mobile, wifi public : fréquent), l'inscription
  ne doit PAS devenir impossible. Chercher les écrans où le seul bouton est « Réessayer ».
- **Timeouts** : tout appel réseau long (IA…) a un timeout → jamais de spinner infini devant le
  reviewer. *JS : `AbortSignal.timeout`. Swift : `URLRequest.timeoutInterval` / `Task` + délai.*
  Erreurs à messages dédiés (rate-limit ≠ réseau ≠ serveur).
- **Reset de mot de passe** : le flux marche **en build de prod, sur device**. Pièges vécus :
  Site URL resté sur `http://localhost` ; allow-list de redirect vide ; **le deep-link/universal link
  est gravé au build** (invisible avant rebuild). Vérifier : redirect posé, scheme déclaré (*Expo :
  `scheme` dans app.json ; Swift : `CFBundleURLTypes` ou Associated Domains*), handler du lien, écran
  de nouveau mot de passe existant.
- **iPad & rotations** : le reviewer teste souvent sur iPad. Si `supportsTablet: true`, l'UI ne doit
  pas casser ; si `false`, vérifier que l'app tourne quand même correctement en mode compatibilité.
  Un crash ou un layout brisé sur iPad = rejet 2.1.

## Passe 2 — Config & bundle

- **Zéro secret dans le bundle** : aucune clé API sensible (OpenAI, Stripe secret…) embarquée — elles
  vivent côté serveur (fonction/proxy). Clés publiques OK : URL backend, anon/publishable key, clé SDK
  de paiement. *Expo : tout `EXPO_PUBLIC_*` part en clair dans le bundle (grep + `extra` de app.json).
  Swift : littéraux `sk-`/`key` dans les sources, `Info.plist`, `.xcconfig`, plists de config.*
- **Privacy Manifest** (`PrivacyInfo.xcprivacy`) — **obligatoire depuis mai 2024**, blocker silencieux :
  toute *Required Reason API* utilisée doit déclarer une raison, sinon **rejet à l'upload** (`ITMS-91053`).
  Le cas quasi universel : `UserDefaults`/AsyncStorage → catégorie `NSPrivacyAccessedAPICategoryUserDefaults`
  raison `CA92.1`. Les SDK tiers « à impact vie privée » doivent livrer leur propre manifest signé, sinon
  `ITMS-91061` (appliqué depuis févr. 2025) → upgrade du SDK. *Expo : `app.json → ios.privacyManifests`
  (SDK 50+). Swift/Flutter : fichier dans le target, vérité via « Generate Privacy Report » sur l'archive.*
  **Détail complet (codes de raison, catégories, ITMS, par stack) : `references/privacy-manifest.md`.**
- **Export compliance réglé** : chiffrement standard iOS = exempté → déclaration posée dans la config.
  *Expo canonique : `ios.config.usesNonExemptEncryption: false` (le passthrough
  `ios.infoPlist.ITSAppUsesNonExemptEncryption: false` marche aussi). Swift : `ITSAppUsesNonExemptEncryption`
  dans l'Info.plist du target.* Plus de question à chaque upload.
- **`UIBackgroundModes` parasites** : un mode déclaré non utilisé = rejet 2.5.4.
  ⚠️ Lire l'**Info.plist FINAL** : *Expo : celui du build généré — `npx expo prebuild --clean` si `ios/`
  n'est pas commité, sinon c'est le plist commité qui gagne, pas app.json. Swift : l'Info.plist du target
  + les Capabilities Xcode.*
- **Permissions** : chaque `NS*UsageDescription` correspond à une feature réelle, avec une phrase qui
  explique le POURQUOI utilisateur (pas « l'app a besoin de la caméra »). Si l'app fait du **tracking**
  → `NSUserTrackingUsageDescription` requis (ATT) **ET** `NSPrivacyTracking: true` + `NSPrivacyTrackingDomains`
  dans le manifest (deux exigences distinctes). Attention aux descriptions par défaut injectées par des libs.
- **Version/build number** cohérents et incrémentés. *Expo : `build..ios.autoIncrement` dans
  eas.json (+ `appVersionSource`). Swift : `CURRENT_PROJECT_VERSION`/agvtool ou fastlane.*

## Passe 3 — Backend & vérité serveur

(S'applique quel que soit le stack — c'est du serveur.)

- **Quotas/premium côté serveur** : la limite du freemium est vérifiée et incrémentée PAR LE SERVEUR,
  jamais par le client (sinon contournable → le paywall devient décoratif).
- **Webhook de paiement** (RevenueCat webhook / App Store Server Notifications **V2** — V1 dépréciée) :
  fait un **UPSERT**, pas un UPDATE — sinon un payant dont le profil manque reste bloqué en gratuit
  (vécu). Idéal : repli de revalidation par l'API du provider (App Store Server API / RC) avant de
  refuser un accès. Vérifier la signature du webhook (HMAC).
- **5.1.2(i) — Partage avec une IA tierce** (guideline durcie ~nov. 2025) : si un input utilisateur part
  chez un fournisseur IA (OpenAI…), la guideline exige de le **divulguer ET d'obtenir un consentement
  explicite AVANT** de transmettre. Ce n'est plus « optionnel » : nommer le tiers, et prévoir une porte
  de consentement / opt-in in-app près de la feature IA (au minimum une divulgation claire). La clé API
  IA doit être **server-only** (jamais dans le bundle) — à préciser dans les notes reviewer.
- **Anti prompt-injection** si IA : les entrées utilisateur ne pilotent pas le système ; sources/citations
  contrôlées serveur (forcer `sources: []` si l'app ne doit jamais citer de texte).
- **Comptes démo reviewer** existent en base, testés, mot de passe posé, backend allumé.

## Passe 4 — Nature de l'app & contenu

Rejets sur ce que l'app **est**, pas seulement ce qu'elle fait. Souvent sous-estimés.

- **4.2 Minimum Functionality** : pas un simple wrapper de site web / web-clipping ; l'app apporte une
  vraie valeur native et fonctionne sans dépendre d'une autre app. Les apps « template » générées sont
  rejetées sauf soumises par le propriétaire du contenu.
- **4.3 Spam / duplicate** (durci juin 2026) : pas un clone indistinct d'apps existantes, pas de variantes
  multiples du même app avec des Bundle IDs différents (villes, marques). Une app IA « générique » de plus
  dans une catégorie saturée est un risque réel.
- **4.7 Contenu généré / chatbots / UGC** : si l'app génère du contenu ou héberge de l'UGC public, prévoir
  **filtrage de contenu + signalement + blocage d'utilisateur**, et un gating d'âge pour le contenu qui
  dépasse la note de l'app. Un chatbot IA ouvert sans garde-fou = rejet.
- **UGC privé non partagé ≠ UGC public** : répondre honnêtement au questionnaire (voir âge, Passe 5).

## Passe 5 — Métadonnées & hors-code (interview + vérifs en ligne)

Ce qui bloque le plus en vrai n'est PAS du code — et c'est identique pour tous les stacks :

- **Pages légales EN LIGNE** : `/privacy`, `/terms`, `/support` répondent **HTTPS 200** (le reviewer
  clique ; un 404 = rejet). La privacy policy **nomme les tiers** (fournisseur IA, backend, gestionnaire
  d'abonnements) — cohérente avec le App Privacy label. Terms (EULA) + Privacy **liés depuis le paywall**
  (exigence 3.1.2).
- **App Privacy label** : chaque donnée = Linked to identity **Yes**, Tracking **No** (sauf SDK de pub
  réel). User Content « shared with third parties » si IA. **Ne JAMAIS cocher Tracking/Advertising** sans
  SDK de pub. Cohérent avec le privacy manifest (Passe 2) et la privacy policy.
- **Age Rating — nouveau système (depuis 24 juil. 2025)** : bandes **4+, 9+, 13+, 16+, 18+** (12+ et 17+
  supprimés). Le **nouveau questionnaire est désormais obligatoire** pour soumettre (échéance passée du
  31 janv. 2026 : ne plus l'avoir rempli bloque les updates). Il couvre l'**IA/chatbots** (les prendre en
  compte pour la fréquence de contenu sensible) + contrôles in-app, capacités, thèmes médicaux, violence.
  IA → minimum souvent relevé : normal, pas un blocker.
- **Abonnements** : statut « Ready to Submit » — sinon piège **« Missing Metadata »** : il manque
  (a) le screenshot de review de CHAQUE abo ET (b) la **localisation du GROUPE d'abonnement** (display
  name localisé). Tant que c'est bloqué, les produits chargés par le SDK reviennent VIDES → paywall
  reviewer cassé. Et **sélectionner les abos AVEC la version** à la 1re soumission (champ « Optional »
  trompeur mais obligatoire).
- **Comptes démo** : compte **premium** dans les champs login + compte **gratuit qui atteint le paywall**
  décrit dans les notes (le reviewer teste l'abo en sandbox). Backend allumé, données peuplées (app vide
  = mauvaise review). *Un « demo mode » à la place d'un vrai compte nécessite l'accord préalable d'Apple.*
- **Métadonnées honnêtes** : pas de promesse de revenus, pas de feature absente, pas de fausse
  authenticité de contenu. **2.3.10 : aucune mention/visuel d'une autre plateforme** (Android, Play Store)
  dans la fiche/les screenshots. Content Rights cohérent.
- **Paiement externe** : par défaut, pas de lien de paiement hors IAP. **Nuance US** : depuis l'injonction
  Epic (avr. 2025), le storefront US autorise des liens externes sans entitlement (une commission Apple
  pourrait être réintroduite, cf. déc. 2025) ; **hors US**, ça exige l'entitlement External Purchase Link.
  Donc « lien externe = rejet » n'est plus absolu — le qualifier par région.
- **Testé sur TestFlight** (le vrai build) avant submit : IAP sandbox, reset mdp, essai gratuit affiché,
  suppression de compte.

## Rapport (format imposé)

Rends le rapport dans ce format (modèle complet : `references/rapport-audit.md`) :

1. **Stack détecté** + sections N/A explicites.
2. **Verdict** : `GO` / `GO CONDITIONNEL` / `NO-GO` + une phrase.
3. **Findings** classés :
   - `BLOCKER` — rejet quasi certain ou soumission impossible. Bloque le submit.
   - `HIGH` — risque réel de rejet ou casse un flux que le reviewer teste.
   - `MEDIUM` — risque modéré / à corriger vite après launch.
   - `LOW` — polish.
4. Chaque finding : **guideline Apple** (ex. 3.1.1) ou **code ITMS** (ex. ITMS-91053), **fichier:ligne**
   si code, **preuve** (ce que tu as observé/grepé/curlé), **fix concret** (le vrai fix, estimé en minutes
   si possible — pas « améliorer X »).
5. Séparer **findings CODE** / **findings HORS-CODE** (App Store Connect, pages web, comptes démo) —
   le chemin critique est souvent hors-code.
6. Terminer par la **checklist des 10 dernières minutes avant submit** (voir références).

## Règles d'audit

- Vérifie par la **preuve** : ouvre les fichiers, grep les patterns (`references/grep-recipes.md`), teste
  les URLs. Jamais de finding « il faudrait vérifier que… » sans avoir essayé de vérifier toi-même.
- Un doute sérieux se classe au niveau de sévérité supérieur (le reviewer ne donne pas le bénéfice du doute).
- Si l'app n'a pas d'IAP, saute les sections paiement ; pas d'IA → saute 5.1.2 ; pas de compte → saute
  4.8/5.1.1(v). Dis-le explicitement (« N/A », jamais de silence).
- Les guidelines et les dates bougent (Apple met à jour la page en continu). Si un point est daté/juridique
  (liens externes US, âges, IA tierce), signale-le comme dépendant de la région/date plutôt que comme absolu.

## Références

- `references/guidelines-rejets.md` — guideline par guideline, piège par piège, avec dates de changement.
- `references/privacy-manifest.md` — Required Reason APIs, codes de raison, ITMS, par stack.
- `references/grep-recipes.md` — recettes de détection concrètes (grep/curl) par check.
- `references/asc-checklist.md` — App Store Connect champ par champ + modèle de notes reviewer.
- `references/rapport-audit.md` — format de sortie du rapport.

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [minosdevs](https://github.com/minosdevs)
- **Source:** [minosdevs/appstore-approval-audit](https://github.com/minosdevs/appstore-approval-audit)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-minosdevs-appstore-approval-audit-appstore-approval-audit
- Seller: https://agentstack.voostack.com/s/minosdevs
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
