FraudShield
A security and fraud-risk scanner for our mobile apps. Point it at source code (Flutter, Kotlin, SwiftUI, KMP) or a built app (.apk, .aab, .ipa). It reports weaknesses that attackers and scammers abuse, explains how each one gets abused, and tells you how to fix it.
- Pure Python 3.8+, no dependencies, no internet (except the optional
--depscheck). Your code never leaves your computer. - Results are printed in the terminal and saved to an HTML report you can share. JSON and SARIF output are available for CI.
- Every finding is mapped to the OWASP MASVS mobile security standard.
Install
brew install pipx && pipx ensurepath # once, if you don't have pipx
pipx install fraudshield-mobile
Update later with pipx upgrade fraudshield-mobile.
Quick start
# Source code: one app, or a folder full of apps
fraudshield ~/code/wallet-app
cd ~/code/wallet-app && fraudshield .
# Built apps: scan what you are about to ship
fraudshield build/app/outputs/flutter-apk/app-release.apk
fraudshield build/ios/ipa/Wallet.ipa
# Everything at once, plus a dependency check
fraudshield ~/code/wallet-app app-release.apk Wallet.ipa --deps
Open fraudshield-report.html to see the full report. You can filter it by severity and search it. It also includes a checklist of server-side fraud controls to confirm with the backend team.
Why scan both source and the build?
| Source scan finds | Build scan finds |
|---|---|
| Insecure code: trust-all TLS, OTP/PIN checked in the app, tokens in SharedPreferences/UserDefaults, logging of PINs, weak crypto, WebView holes, bypass/test codes | What actually ships: keys injected by CI or --dart-define, .env files bundled into Flutter assets, debug builds, the debug signing key, development provisioning profiles, missing obfuscation |
| Config: AndroidManifest, network security config, Info.plist, Gradle, CI build commands | The compiled manifest and Info.plist, DEX strings, Flutter libapp.so, the iOS binary, bundled JSON/env files |
Run the source scan on every pull request. Run the build scan on the release artifact before uploading it to the Play Store or App Store.
What it checks
| Area | Examples |
|---|---|
| Secrets | AWS keys, private keys, Paystack/Stripe sk_live_, Flutterwave secret keys, MTN MoMo / Hubtel / M-Pesa credentials, FCM server keys, JWTs, Google API keys, signing passwords, .env and key.properties contents |
| Network | Trust-all TrustManager or HostnameVerifier, badCertificateCallback => true, iOS trust-all challenge handlers, cleartext HTTP, ATS disabled, user CAs trusted, verbose HTTP logging (OkHttp, Ktor, Dio), staging URLs in release builds |
| Storage | Tokens or PINs in SharedPreferences, DataStore, UserDefaults, @AppStorage, multiplatform-settings, shared_preferences, GetStorage or Hive; weak Keychain protection; secrets copied to the clipboard; world-readable files |
| Fraud logic | OTP/PIN compared on the device, master/test OTPs and bypassOtp switches, KYC level or limits read from local storage, fees or totals computed in the app, WebViews opening URLs taken from deep links, custom-scheme deep links, intent redirection |
| Auth | Biometric login that is only a yes/no (Frida-bypassable), PIN typed into a visible TextField |
| Crypto | ECB/DES, MD5/SHA-1, hard-coded keys and IVs, predictable random OTPs |
| Platform | Debuggable builds, allowBackup, exported components, risky permissions (READ_SMS, SYSTEM_ALERT_WINDOW…), UIFileSharingEnabled, old TLS |
| Missing defences | Screenshot/screen-recording protection, root/jailbreak detection, certificate pinning, Play Integrity / App Attest / App Check, overlay (tap-jacking) protection, R8 or --obfuscate |
Dependencies (--deps) |
Known vulnerabilities from OSV.dev in pubspec.lock, Gradle / libs.versions.toml and Package.resolved |
Options
| Option | What it does |
|---|---|
--fail-on high |
Exits with code 1 if any finding is high or worse. Use it in CI to block a release. |
--min-severity medium |
Hides findings below medium. |
--json out.json / --sarif out.sarif |
Machine-readable output. SARIF shows up in GitHub code scanning. |
--html path.html / --no-html |
Changes where the report is saved, or turns the report off. |
--deps |
Checks dependencies against OSV.dev. This sends package names and versions, nothing else. |
--exclude "legacy/*" |
Skips paths. The option can be repeated. |
--include-tests |
Also reports issues in test code. Secrets in tests are always reported. |
--write-baseline fs-baseline.json / --baseline fs-baseline.json |
Accepts today's findings, then reports only new ones. Useful when adopting the tool on an older app. |
Silencing a false positive
Add a comment on the line, or on the line above it. Always say why:
Log.d(TAG, "token refreshed") // fraudshield:ignore LOG001 - no value logged
Debug-only code is skipped automatically: if (BuildConfig.DEBUG) { … }, if (kDebugMode) { … } and #if DEBUG … #endif.
Using it in CI
GitHub Actions:
- run: pipx install fraudshield-mobile
- name: FraudShield (source)
run: fraudshield . --fail-on high --sarif fraudshield.sarif --html fraudshield.html
- name: FraudShield (release build)
run: fraudshield build/app/outputs/flutter-apk/app-release.apk build/ios/ipa/*.ipa --fail-on high --no-html
- uses: github/codeql-action/upload-sarif@v3
if: always()
with: { sarif_file: fraudshield.sarif }
- uses: actions/upload-artifact@v4
if: always()
with: { name: fraudshield-report, path: fraudshield.html }
Codemagic or a shell script: pipx install fraudshield-mobile && fraudshield . --fail-on high || exit 1
Reading the grade
| Grade | Meaning |
|---|---|
| F | At least one critical finding: attackers can exploit it now. Don't release. |
| D | Three or more high findings. |
| C | At least one high finding. Fix before release. |
| B | Only medium or lower: hardening needed. Most apps without anti-tamper protection land here. |
| A | No significant issues found. |
Limits (please read)
- The scanner matches patterns. It catches the common mistakes quickly, but it's not a penetration test. Some findings will need a human to confirm them, and some problems can't be seen by pattern matching.
- It can't see your backend, and most real fraud losses come from missing server-side controls: amount and fee recomputation, idempotency, OTP limits, IDOR checks, SIM-swap checks, velocity rules. The HTML report ends with a checklist for these.
- For built apps:
- Native
FLAG_SECUREis a numeric constant, so it can't be seen in a build. Confirm it with a source scan. - App Store-downloaded IPAs are encrypted. Scan the IPA exported from Xcode or CI instead.
- For
.aabfiles the manifest isn't decoded. Usebundletool build-apks --mode=universaland scan the APK too.
- Native
- Secret values are masked in all reports, so reports are safe to share.
Code layout
| File | What's in it |
|---|---|
fraudshield/cli.py |
Command line: arguments, filtering, baseline, exit codes |
fraudshield/rules.py |
Every detection rule, with its severity, explanation, fix and MASVS mapping, plus the defence markers. Most changes happen here. |
fraudshield/scanner.py |
Rule engine, source-folder scanning, AndroidManifest / network-security-config / Info.plist / Gradle / CI checks, missing-defence checks, OSV dependency check |
fraudshield/binary_scanner.py |
.apk / .aab / .ipa decoding: Android binary XML, DEX strings, Mach-O, signing certificate and provisioning profile, bundled assets |
fraudshield/report.py |
Risk grade, terminal output, SARIF, HTML report, backend checklist |
Development
python3 -m unittest discover -s tests -v # 18 tests
python3 tests/make_test_apps.py # builds vulnerable test APK/IPA into tests/build/
python3 fraudshield.py path/to/app # run from this folder without installing
tests/fixtures/ contains small, deliberately vulnerable Flutter, Kotlin, SwiftUI and KMP apps, plus one clean app that must produce zero findings. The fake keys are inserted at test time, so the repo never contains anything that looks like a real secret and GitHub push protection won't block it. Binary tests build real APKs and an IPA with the Android SDK and Xcode, and are skipped on machines without them.
To add a rule, add an R(...) entry to RULES in fraudshield/rules.py, add a vulnerable line to a fixture, and assert it in tests/test_fraudshield.py. Keep the clean app clean.
Metadata
Release files for fraudshield-mobile 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| fraudshield_mobile-1.0.0.tar.gz | 51.7 kB | Without provenance | Details for fraudshield_mobile-1.0.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| fraudshield_mobile-1.0.0-py3-none-any.whl | Python 3 | none | any | Details for fraudshield_mobile-1.0.0-py3-none-any.whl |
Total release size: 101.8 kB
Release files / fraudshield_mobile-1.0.0.tar.gz
| Download URL | fraudshield_mobile-1.0.0.tar.gz |
|---|---|
| Size | 51.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
3062e4fa2902ba9518869d4a60694ff224b62e101daa6dd59ef19107ac622afc
|
|
BLAKE2b-256 checksum How to use checksums |
151d55daa9ddc32c591dfa36b0c7fe36a4feacc1831e59baee857009a857fe74
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.9
|
Release files / fraudshield_mobile-1.0.0-py3-none-any.whl
| Download URL | fraudshield_mobile-1.0.0-py3-none-any.whl |
|---|---|
| Size | 50.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1ef80d35c585837bb105479cdffc0295450fbd438b34250671369e2b22e17709
|
|
BLAKE2b-256 checksum How to use checksums |
9a00d70290192ff3aeabd736d234b7b29aecddb5fad8723b780c700ea11d114d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.9.9
|
Security for fraudshield-mobile 1.0.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.