mcpField
G
gpc-preflight

gpc-preflight

github.com/yasserstudioTesting & quality139 installsUpdated 1w ago
Repository

About gpc-preflight

GPC Preflight Scanner is a skill for checking Android app bundles and APKs against Google Play requirements before release. It describes nine scanners, configurable findings, signing consistency checks, and CI usage.

Key features

  1. 01Scan AAB and APK files
  2. 02Check manifest and permissions
  3. 03Inspect native libraries and alignment
  4. 04Detect secrets and billing SDKs
  5. 05Validate listing metadata and policy
  6. 06Check signing certificates via Play API

Use cases

  1. 01Find policy issues before Play upload
  2. 02Gate releases in CI with scan findings
  3. 03Investigate signing certificate mismatches

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/yasserstudio/gpc-skills --skill gpc-preflight

After installing, type/gpc-preflightin Claude Code to confirm it loaded.

Details

From the project README

GPC Preflight Scanner

When to use

Use this skill when the task involves:

  • Scanning an AAB or APK file before uploading to Google Play
  • Checking target SDK version compliance
  • Auditing permissions against Google Play policies
  • Verifying 64-bit native library support
  • Scanning source code for hardcoded secrets or credentials
  • Detecting non-Play billing SDKs
  • Checking store listing metadata compliance
  • CI/CD quality gates based on policy compliance

Quick reference

# Full scan (AAB or APK)
gpc preflight app.aab
gpc preflight app.apk

# With metadata and source scanning
gpc preflight app.aab --metadata fastlane/metadata/android --source app/src

# Specific scanners only
gpc preflight manifest app.aab
gpc preflight permissions app.aab
gpc preflight metadata ./metadata
gpc preflight codescan ./src

# CI mode
gpc preflight app.aab --fail-on error --json

9 scanners

ScannerChecksSeverity
manifesttargetSdk >= 36 (v0.9.79+), debuggable, testOnly (reads from <application> element since v0.9.80), cleartext, missing exported, FGS types, geofencing foreground service (v0.9.65+)critical/error/warning
permissions18 restricted permissions, contacts broad-access, Health Connect granular (v0.9.65+), Data Safety reminderscritical/error/warning/info
native-libs64-bit ARM compliance, ABI detection, 16KB page alignment (AAB + APK since v0.9.80, ELF header read 4096 bytes, at most 512 libraries per scan)critical/warning
metadataListing character limits, screenshots, privacy policy URLerror/warning
secretsAWS keys, Google API keys, Stripe keys, private keyscritical/warning
billingStripe, Braintree, PayPal, Razorpay SDK detectionwarning
privacyTracking SDKs, Advertising ID, data collection cross-referencewarning/info
policyFamilies/COPPA, financial, health, UGC, overlaywarning/info
sizeEstimated per-device download (AAB: largest ABI only, no bundle metadata or on-demand/fast-follow modules; APK: whole file), large native libs, large assetswarning/info

Configuration (.preflightrc.json)

{
  "failOn": "error",
  "targetSdkMinimum": 36,
  "maxDownloadSizeMb": 200,
  "allowedPermissions": ["android.permission.READ_SMS"],
  "disabledRules": ["cleartext-traffic"],
  "severityOverrides": { "billing-stripe-sdk": "info" }
}

API level 36 (Android 16) is required by August 31, 2026 for all new apps and updates on Google Play. The targetSdkMinimum default in GPC preflight was updated to 36 in v0.9.79 to reflect this deadline.

Download size (v0.9.99+)

For an AAB, size-over-limit estimates what one device downloads, the way Google Play serves the bundle: only the largest ABI's native libraries, no BUNDLE-METADATA/ (debug symbols, R8 mapping) or bundle signature, and no on-demand or fast-follow modules or asset packs. Every density and language is still counted, so it errs high. For an APK the whole file counts. The default maxDownloadSizeMb is 200, the size at which Play shows users on mobile data a large-download dialog. Before v0.9.99 the whole .aab was summed, so multi-ABI Flutter/React Native bundles were flagged at 3-4x their real download (GH #116); if a user on an older version hits that, upgrade, or add "disabledRules": ["size-over-limit"]. For an exact figure, bundletool get-size total on the bundle.

native-libs-large (native-libs scanner) and size-large-native likewise measure the largest single ABI for an AAB.

APK scans: manifest checks (manifest, permissions, policy, privacy scanners) currently run on AABs only. An APK's binary-XML manifest is not decoded yet, so an APK scan reports "Manifest could not be fully parsed" and runs the rest. Scan the AAB you upload for full coverage.

Exit codes

  • 0 — all checks passed
  • 1 — runtime error
  • 6 — findings at or above --fail-on severity

Key rules

Rule IDSeverityWhat
targetSdk-below-minimumcriticaltargetSdkVersion < 36
debuggable-truecriticalandroid:debuggable="true"
testOnly-truecriticalandroid:testOnly="true"
missing-arm64critical32-bit ARM without 64-bit
missing-exportederrorComponent with intent-filter but no exported attr
foreground-service-type-missingwarningApp requests FOREGROUND_SERVICE but no service declares foregroundServiceType (API 34+); reported once (v0.9.99+, was one error per service)
secret-aws-keycriticalAWS access key in source
secret-stripe-keycriticalStripe secret key in source
contacts-permission-broadwarningREAD_CONTACTS / WRITE_CONTACTS (v0.9.65+, April 2026 policy)
geofencing-foreground-servicewarningLocation FGS + ACCESS_BACKGROUND_LOCATION (v0.9.65+, April 2026 policy)
health-connect-granularwarning/infoREAD_ALL_HEALTH_DATA; warning on targetSdk >= 36, info otherwise (v0.9.65+, April 2026 policy)
policy-app-content-declarationinfoAny FOREGROUND_SERVICE* permission; reminder that Play Console -> Policy -> App content needs the matching declaration (v0.9.94+)
native-libs-16kb-uncheckedwarningNot every native library could be checked for 16 KB alignment (v1.0.0-rc.1+)

native-libs-16kb-unchecked (v1.0.0-rc.1+): the reader buffers at most 512 .so headers, which keeps memory flat on a bundle carrying thousands of native libraries. When a bundle has more than that, the alignment check covers only the first 512, so preflight says so rather than let a truncated check read as a clean one. The finding names how many were skipped. To cover the whole bundle, check it with Android Studio's APK Analyzer or zipalign -c -P 16 -v 4 app.apk on a universal APK. Most apps never see this rule.

Procedures

Running a full preflight scan
  1. Build your AAB or APK: ./gradlew bundleRelease (or assembleRelease for APK)
  2. Run: gpc preflight app/build/outputs/bundle/release/app-release.aab Or for APK: gpc preflight app/build/outputs/apk/release/app-release.apk
  3. Fix any critical/error findings
  4. Add a .preflightrc.json to allow approved permissions or disable false positives
  5. Re-run until clean

Note: After the scan, GPC shows a reminder about Android developer verification requirements (September 30, 2026 enforcement for BR, ID, SG, TH). Run gpc verify for details.

Since v0.9.80, the result JSON includes a skippedScanners array when scanners are filtered out (e.g., manifest-dependent scanners skipped due to manifest parse failure). This replaces the previous behavior where skipped scanners were silently omitted.

Signing key consistency (v0.9.66+)
gpc preflight signing                    # Check cert consistency across two most recent bundles
gpc preflight signing --json             # JSON output for CI
gpc preflight signing --app com.example.app  # Override package name

Compares signing certificates across your two most recent bundle versions via the Play API (generatedApks.list). Requires auth (service account or OAuth). Exit code 6 on mismatch (same as other preflight threshold breaches). Exit code 4 on API errors.

This is NOT an offline scan. It calls the Play API to create an edit, list bundles, fetch generated APKs for the top two version codes, compare certificateSha256Fingerprint, then delete the edit.

April 2026 policy rules (v0.9.65+)

Three rules added for Google Play's April 15, 2026 policy batch. Compliance deadline: May 15, 2026.

  1. Contacts broad access (contacts-permission-broad): Flags READ_CONTACTS / WRITE_CONTACTS. Google now requires the Android Contact Picker instead of broad access. Emits a single finding even when both permissions are present. Suppress via allowedPermissions for dialer/messaging apps.

  2. Geofencing foreground service (geofencing-foreground-service): Fires when a service has foregroundServiceType containing "location" AND the app declares ACCESS_BACKGROUND_LOCATION. Google removed geofencing as an approved foreground service use case. For legitimate background location tracking (navigation, fitness), suppress via "disabledRules": ["geofencing-foreground-service"].

  3. Health Connect granular permissions (health-connect-granular): Flags READ_ALL_HEALTH_DATA. Severity is warning when targetSdk >= 36 (Android 16 requirement), info otherwise. Replace with granular permissions like health.READ_STEPS, health.READ_HEART_RATE, etc.

App content declaration advisory (v0.9.94+)

policy-app-content-declaration fires at info severity whenever the AAB requests any FOREGROUND_SERVICE* permission, listing which ones. It is a reminder that Play Console → Policy → App content needs the matching "Foreground service permissions" declaration completed before Google will accept a release.

Why it can only advise: the declaration is stored in Play Console and is not exposed through the Publisher API or readable from the bundle. Preflight can see that your app requests the permissions; it cannot see whether you filled in the form. It therefore never fails a run, regardless of --fail-on.

This is not the same check as foreground-service-type-missing. That one looks at android:foregroundServiceType on your <service> elements, which is a manifest requirement for services that call startForeground(). You can have every service correctly typed and still be blocked at upload by the missing Console declaration — that combination is exactly what motivated this rule.

If the declaration is incomplete, the upload fails with API_DECLARATION_REQUIRED (v0.9.94+). Before v0.9.94 the same failure was reported as a service account permission problem, which sent people to Users and permissions instead of App content.

One finding is emitted per scan, not one per permission.

False-negative fixes (v0.9.80)

Four scanner accuracy improvements shipped in v0.9.80:

  1. testOnly attribute source: The testOnly scanner now reads android:testOnly from the <application> element of the manifest. Previously it was incorrectly reading from the <manifest> root, causing the check to always report no finding even when testOnly="true" was set on the application.

  2. 16KB alignment scanner scope: The 16KB page alignment scanner now checks native libraries inside APKs in addition to AABs. Previously it only inspected AAB artifacts, leaving APK native libraries unscanned.

  3. ELF header read size: The ELF header reader was increased from 256 bytes to 4096 bytes. Small reads were insufficient for some native library formats, causing alignment checks to be skipped silently.

  4. skippedScanners in results: Scanners that are disabled via skippedScanners (or skipped due to missing inputs) are now reported in the scan result JSON under a skippedScanners array. This makes it visible in CI when a scanner did not run.

Adding to CI
# GitHub Actions
- name: Preflight
  run: gpc preflight app.aab --fail-on error --json > preflight.json
- name: Upload report
  if: always()
  uses: actions/upload-artifact@v4
  with:
    name: preflight-report
    path: preflight.json

Related skills

  • gpc-release-flow — uploading and releasing after preflight passes
  • gpc-ci-integration — CI/CD patterns including preflight gates
  • gpc-troubleshooting — exit code 6 handling
  • gpc-security — credential handling and key rotation

Comments

Markdown supported