Parallax

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

CheckReads
Secrets and personal data written to the app's logsThe log feed
Tokens and personal data in URLs and query stringsThe network panel's requests
Android backup of app data allowedAndroidManifest.xml
Cleartext HTTP allowediOS Info.plist (ATS), Android usesCleartextTraffic and the network security config
Exported Android components without a permissionAndroidManifest.xml
Hardcoded keys and internal or test URLs in the release bundleThe 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 checks FLAG_SECURE on 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 setting FLAG_SECURE on 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-allow set to true in the configuration's entitlements;
  • ENABLE_TESTABILITY = YES;
  • DEBUG=1 in 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." } ] }

On this page