适用于 Apple App Store 和 Google Play 的提交前合规扫描器。对代码、隐私声明、Android 文件和 IPA/APK/AAB 二进制文件进行扫描,检查是否符合相关标准。
适用于 Apple App Store 和 Google Play 的提交前合规扫描器。对代码、隐私声明、Android 文件和 IPA/APK/AAB 二进制文件进行扫描,检查是否符合相关标准。
Know before you submit. Pre-submission compliance scanner for the Apple App Store and Google Play.
Greenlight reads your app (source code, privacy manifests, Android manifests and Gradle builds, IPA binaries, App Store Connect metadata) and checks it against Apple's Review Guidelines and Google Play's Developer Program Policies. Every finding cites the rule it comes from. No account, no uploads, no network. A full preflight on a mid-size project finishes in single-digit milliseconds.
A static scan can only prove a flow exists. greenlight verify proves it works: it runs your account-deletion, restore-purchases, and Sign in with Apple flows on a cloud device and fails the build if any of them dead-ends. That tier is powered by Revyl and needs a free account. Everything else on this page runs offline. See greenlight verify.
# Homebrew (macOS)
brew install revylai/tap/greenlight
# Go
go install github.com/RevylAI/greenlight/cmd/greenlight@latest
# Build from source
git clone https://github.com/RevylAI/greenlight.git
cd greenlight && make build
# Binary at: build/greenlight
# Everything, one command, zero uploads
greenlight preflight /path/to/your/project
# Include an IPA for binary analysis
greenlight preflight . --ipa build.ipa
# Gate CI on it
greenlight preflight . --exit-code
Four levels. The top two fail a CI gate; the bottom two are advisory.
Level Meaning CRITICAL Rejection or install failure is near-certain. Fix before you submit. HIGH A published deadline, a required declaration, or a check that fails at runtime. WARN Likely to draw reviewer attention or an information request. INFO Best practice.--exit-code trips on CRITICAL and HIGH, and on a scanner that crashed, so an incomplete scan never reports as a pass.
greenlight preflight [path]Runs every applicable scanner in parallel. Android and iOS projects in one repo are both picked up, so a cross-platform app is checked against both stores in a single pass.
greenlight preflight . # scan current directory
greenlight preflight ./my-app --ipa build.ipa # with binary inspection
greenlight preflight . --format json # JSON for CI
greenlight preflight . --format sarif --output greenlight.sarif
greenlight preflight . --exit-code # non-zero on CRITICAL/HIGH
Scanner
Checks
metadata
app.json / Info.plist: name, version, bundle ID format, icon, privacy policy URL, purpose strings
codescan
24 rules over Swift, Objective-C, React Native, and Expo source
privacy
PrivacyInfo.xcprivacy completeness, Required Reason APIs, tracking SDKs vs ATT
playscan
Google Play policy, target API deadline, restricted permissions, foreground service types, Play Billing version (Android projects only)
ipa
Binary: Info.plist keys, launch storyboard, icons, app size, framework privacy manifests
Adding --verify extends the same command into the runtime tier, so one invocation covers static and runtime:
greenlight preflight . --verify --build-name "My App" \
--var [email protected] --var password=secret --exit-code
--verify accepts the same targeting flags as the standalone command: --artifact, --build-name, --device-model, --os-version, --var.
greenlight codescan [path]greenlight codescan /path/to/project
greenlight codescan . --config path/to/.greenlight.yml
24 rules, roughly 60 patterns, over Swift, Objective-C, React Native, and Expo.
CRITICAL:
HIGH:
WARN:
INFO: debug logging left in production code (§2.1), and no encryption export-compliance declaration.
Rules that describe a project-level fact, such as "no account deletion anywhere", report once for the whole project rather than once per file that triggers them.
greenlight playscan [path]greenlight playscan /path/to/project
greenlight playscan --apk app-release.apk # built artifact: merged manifest
greenlight playscan --aab app-release.aab # app bundle
greenlight playscan . --format json
greenlight playscan . --exit-code
Checks an Android app against Google Play's Developer Program Policies and its published distribution deadlines. Every finding links the policy page it comes from.
Deadlines:
version.ref are resolved. HIGHRestricted permissions, each of which needs an approved use case or a declaration form:
READ_CALL_LOG useMANAGE_EXTERNAL_STORAGE (All files access)QUERY_ALL_PACKAGES, REQUEST_INSTALL_PACKAGESACCESS_BACKGROUND_LOCATION, which needs a declaration and a demo videoandroid:permission rather than a <uses-permission>Manifest and build:
FOREGROUND_SERVICE permission, or a declared type missing its FOREGROUND_SERVICE_* permission. Both throw at startForeground(). CRITICALspecialUse foreground services, which need a Console justificationandroid:exported missing on components with an intent filter (API 31+), which makes the package fail to install. CRITICALandroid:debuggable="true". CRITICALandroid:usesCleartextTraffic="true"com.google.android.gms.permission.AD_ID, which silently returns a zeroed advertising ID--apk and --aab read the merged manifest, so they see permissions contributed by library manifests that a source scan structurally cannot. Every manifest and permission check above runs against a built artifact, plus the native code checks below.
The three checks that read the Gradle model (Play Billing version, ads-SDK detection, auth-SDK detection) cannot run on an archive, because a built artifact does not carry one. The scan reports that gap as a finding rather than staying silent, so a clean artifact scan is never mistaken for a clean scan of everything. Source and artifact are complementary; preflight accepts both at once:
greenlight preflight . --aab app-release.aab
Native code checks:
LOAD segment alignment on arm64-v8a libraries, 16 KB zip alignment of uncompressed libraries, and GNU_RELRO presence. CRITICAL / HIGHarmeabi-v7a without arm64-v8a, or x86 without x86_64, is flagged. CRITICALIn an AAB, native code is read from every module rather than base/ alone, so a library that ships in a feature module is checked like any other.
Both formats are decoded in pure Go, an APK's compiled binary XML and an AAB's protobuf manifest alike, so no Android SDK, aapt2, or bundletool is required.
On scope: a source scan reads the AndroidManifest.xml and Gradle files in your repo, which is the pre-merge manifest. Permissions contributed by library manifests only appear once the build merges them, so a clean source scan is not proof of a clean merged manifest. Scan the built artifact to close that gap. targetSdk is resolved from the app module, convention plugins (build-logic/, buildSrc/, build-plugin/), version catalogs, gradle.properties, and named constants; when it cannot be resolved, the scan says so instead of reporting a pass.
greenlight privacy [path]greenlight privacy /path/to/project
Checks that PrivacyInfo.xcprivacy exists and is filled in, finds Required Reason APIs used in code and compares them against what the manifest declares, and cross-references detected tracking SDKs against your ATT implementation.
greenlight ipa <path.ipa>greenlight ipa /path/to/build.ipa
Plists are parsed for real, binary and XML both, rather than string-matched. Inspects:
greenlight scan --app-id <ID>greenlight auth setup # one-time: configure API key
greenlight auth login # or: sign in with Apple ID
greenlight scan --app-id 6758967212 # run all tiers
API-based checks against your app in App Store Connect: metadata completeness, screenshots for required device sizes, build processing status, age rating and encryption compliance, and content analysis for platform references and placeholders.
greenlight verify [path]Static checks confirm a flow exists in your source. verify confirms it works, by handing flow-dependent guidelines to the Revyl CLI and running them on a cloud device.
This is the gap static analysis cannot close. A "Delete Account" button wired to nothing passes codescan: the string deleteAccount is in the source, §5.1.1 is suppressed, and you get GREENLIT. At runtime it dead-ends, and Apple rejects under §5.1.1(v). verify taps the button.
greenlight verify . --dry-run # show claimed flows + generated tests, no device
greenlight verify . --build-name "My App" \
--var [email protected] --var password=secret
greenlight verify . --build-name "My App" --flows account-deletion --os-version "iOS 26.2"
greenlight verify . --artifact build/MyApp.app --build-name "My App" # upload, then run
greenlight verify . --platform android --artifact app-release.apk --build-name "My App"
Only flows your app actually claims are run:
Flow Guideline What runtime proves that static can'taccount-deletion
§5.1.1
暂无开放 Issues,或尚未同步最近议题。