Security hints
Cheap checks of the project's files, the release bundle, the app's logs and requests, with masked evidence.
Hints from a development tool, not an audit: an empty list does not mean the app is secure.
What is checked
| Check | Reads |
|---|---|
| Secrets and personal data written to the app's logs | The log feed |
| Tokens and personal data in URLs and query strings | The network panel's requests |
| Android backup of app data allowed | AndroidManifest.xml |
| Cleartext HTTP allowed | iOS Info.plist (ATS), Android usesCleartextTraffic and the network security config |
| Exported Android components without a permission | AndroidManifest.xml |
| Hardcoded keys and internal or test URLs in the release bundle | The bundle you name |
| Secrets and personal data stored in plain text (ST-2) | The app's own storage on a simulator or emulator (see below) |
| Sensitive screens not protected from screenshots and the app switcher (ST-3) | Android: the app's window while such a screen is shown. iOS: the project (see below) |
| A debuggable release build (CD-2) | Android: the manifest and the Gradle release build type. iOS: the Xcode project, and with --app the built app's entitlements (see below) |
Every finding has an id, a category (MASVS), a severity, the place (file and line, or the request), masked evidence (the type and the first and last two characters, never the value) and a hint.
Run it
npx -y @parallax-rn/mcp@0.1.0-beta.2 security --project <your app> --release-bundle <bundle file>--device <udid or serial> --app-id <bundle id or package> adds the app's stored data (ST-2). --app <path to a built .app> also checks the built iOS app's entitlements with codesign. --json prints the findings as JSON. doctor has a Security (hints) row with the static checks. The agent reads every finding, the runtime ones included, with app_data { source: "security" }.
Secrets stored in plain text
ST-2 reads the app's own storage from the Mac, read only: on an iOS simulator the app's data container, on an Android emulator through run-as (so a development, debuggable build). It never reads a physical device and never writes anything. When it cannot read, for example on a physical device or a release build where run-as is refused, the report says so.
- Read: AsyncStorage, MMKV, UserDefaults (the plists in
Library/Preferences), SharedPreferences, and SQLite databases as text. Caches and the development bundle are skipped. - Not read, and counted as protected: the Keychain, EncryptedSharedPreferences and other encrypted stores.
- Flagged: values that look like tokens or keys, and personal data such as emails and phone numbers. The place is the file inside the app's container and the key; the evidence is masked (the type, the length, the first and last two characters), never the value.
The hint: keep tokens in the Keychain or the Keystore (for example react-native-keychain or expo-secure-store); AsyncStorage, MMKV and UserDefaults can be read by anyone with the device or a backup.
Without the Parallax app running there is no Parallax core, and the agent's report and doctor say the stored files were not read ("no Parallax core: open the Parallax app"): an empty list there does not mean clean. Key names that themselves look like a secret, an email or a phone number are masked like values.
It runs when asked (Re-run in the Security tab, app_data { source: "security" }, doctor), not on a timer; a read takes about 0.15 to 0.3 s. From the command line, security --device <udid or emulator serial> --app-id <bundle id or package> reads it for one app.
Sensitive screens and screenshots
ST-3 treats a screen as sensitive when the focused screen's component tree has a TextInput with secureTextEntry, or fields whose names, testIDs or placeholders look like a password, PIN, card number, CVV or IBAN. Field values are never read. Inactive tabs and screens that React Navigation keeps mounted below the focused one are ignored.
- Android, at run time. While such a screen is shown, Parallax reads
dumpsys window(read only) and checksFLAG_SECUREon the app's focused window; other or closing windows of the app do not count, and older Android versions' flag format is read too. If the flags cannot be read or the app's window is not found (another app in front, a different package name), the report gives a note, never a finding. When the flag is not set, a medium finding names the screen and suggests settingFLAG_SECUREon the window or using a screenshot-prevention library. When no sensitive screen is shown, the report says "no sensitive screen was on screen when the check ran": open the screen and press Re-run. - iOS, a static hint. There is no run-time signal without sending input, so it is an info hint: the project has secure inputs and no sign of screenshot or app-switcher protection. A protection library counts only when it is a real dependency (not a dev dependency) and is imported; native protection counts when a cover, blur or snapshot API is used as the app resigns active.
The screen result is kept until Re-run: the Security tab shows when the screen was checked ("screen checked at hh:mm:ss; Re-run while the screen is open"), and doctor and the agent's read reuse the last result. The agent asks for a fresh one with app_data { source: "security", refresh: true }.
Nothing is sent to the device. The result is in the same report: the Security tab, app_data { source: "security" } and doctor.
A debuggable iOS release build
CD-2 reads the Xcode project's app target in every release-like configuration (any configuration whose name does not contain "debug") and reports:
get-task-allowset to true in the configuration's entitlements;ENABLE_TESTABILITY = YES;DEBUG=1in the preprocessor definitions;- a development signing identity on the app target with manual signing.
A target's setting overrides the project's. The stock React Native and Expo templates trigger none of them. Settings from .xcconfig files are not read; the report says so.
In the window
The Device panel's Security tab shows the same report as app_data { source: "security" }, for the app on the selected device. It starts with the line above, then the counts by severity, a Re-run button and the findings grouped as HIGH, MEDIUM, LOW and INFO. Each finding shows its check id and MASVS category, the place (a file:line opens in your editor; a request id selects the request in the Network tab), the masked evidence and the hint.
- Attach adds the finding to your message to the agent.
- Accept… asks for a reason (1 to 500 characters) and writes it to the project's
.parallax/security.json. Accepted findings fold into "Accepted (n)".
A shut-down device shows no findings.
Accept a finding
A finding you have decided to keep goes into .parallax/security.json in the project, with its id and a reason. It then appears under "accepted" with your reason instead of among the open hints. An id stays the same from run to run.
{ "accepted": [ { "id": "ST-4-1a2b3c", "reason": "Backups are wanted for this app." } ] }