Step 1
Log in to your app in the Simulator or emulator
A refresh token in UserDefaults or AsyncStorage, a JWT in the URL cache, a session in shared_preferences or DataStore: logout bugs are invisible in the UI and show up later in pentest reports. OWASP lists the weakness as MASWE-0024 (sensitive data accessible after session termination), but no MASTG test automates it.
Capture before, log out, capture after. Each secret is matched by a keyed hash, even if it moved to another file or address, and reported once: every place it survived, the store and key that hold it, and a fix. A capture that read too little is INCOMPLETE, never a silent PASS.
Step 1
Log in to your app in the Simulator or emulator
Step 2
Capture: storage, caches, files and memory
Step 3
Log out the way a user would
Step 4
Capture again: PASS, FAIL or INCOMPLETE, per secret
“3 secrets survived logout in 7 places.” One group per secret (the same token in memory and on disk is one result), with survived, appeared and released matched by keyed hash.
Key-aware extraction from plists, SQLite, JSON, XML preferences, MMKV and DataStore names the exact key, e.g. SDK:redux-persist › persist:root › auth › refreshToken. Values seen before logout are looked up again after it, so opaque tokens no regex matches are found too (as written, not re-encoded).
PASS, FAIL or INCOMPLETE. Below 90 % coverage it's INCOMPLETE. Memory-only survivors fail the gate only where that's reliable (native code with MallocScribble on); in React Native and Flutter heaps they're reported, not failed.
Swift, React Native, Flutter and KMP debug builds on the iOS Simulator. Android debuggable builds over adb run-as (no root), from the CLI on Linux or macOS.
Survivors map to MASWE-0024 (MASVS-AUTH-3); data on disk to MASVS-STORAGE-1 and MASTG-TEST-0302 (iOS) or MASTG-TEST-0207 (Android). Evidence for your review, not a certificate.
JWTs, bearer headers, AWS/GitHub/Slack/Google/Stripe keys, private keys, Luhn- and issuer-checked cards, IBAN (mod-97), T.C. Kimlik No (checksum), e-mail and phone, plus refresh, access and session tokens recognised by their key.
Access token (JWT)
Expected after logout: gone
Actual: still in Library/Caches/…/Cache.db (URLCache) and in memory
High · fails the gate (disk) · MASWE-0024
The token survived logout in the URLSession cache. Use an ephemeral session (or Cache-Control: no-store) for authenticated calls and clear URLCache on logout. The memory copy is reported too; on its own it would not fail the gate unless MallocScribble is on.
1
Before and after logout: storage, caches and files, plus memory on iOS. Every capture states its coverage.
2
Each secret is matched by keyed hash across captures and grouped, with the store, the key and the blocking reason.
3
Apply the fix for that store, then run the check again until it says PASS.
Open WatchMemory and run the System check (Xcode, Simulator, developer mode)
Log in to your app in the Simulator and start a residue check
Log out when asked: get PASS, FAIL or INCOMPLETE, with a fix for each surviving secret
Export a self-contained HTML report (print it to PDF) for a residue check or a CI run. It states what was scanned, how much, and on which platform and build, so a PASS means something.
Run the same check in CI after your UI tests. With Pro or Team it fails the build when a canary or a high-risk secret survives logout. Free runs report-only with the same, truthful verdict.
# the app generates its own canary; freed native memory is scribbled
SIMCTL_CHILD_WM_CANARY_MODE=1 SIMCTL_CHILD_MallocScribble=1 \
SIMCTL_CHILD_MallocNanoZone=0 xcrun simctl launch booted com.acme.app
# …log in and out with your UI test, then:
watchmemory ci --process MyApp --bundle-id com.acme.app \
--scan-profile full --baseline .watchmemory-baseline.json \
--sarif wm.sarif --junit wm.xmlAn agent can write a logout fix but can't see whether the token is really gone. WatchMemory's MCP server lets Claude Code, Cursor and other agents check the running Simulator app.
$0
$12 / month · or $99 / year
$1,500 / app / year
Founding price: $990 for the first 20 apps
Founding price: $990/yr for the first 20 apps, first app per organisation, locked for 24 months, in exchange for a named reference or case study and a quarterly feedback call. Manual pre-sale: contact us.
From $6,000 / year
Coming with v1.0. Talk to us.
Prices in USD. Pro is sold through Lemon Squeezy (merchant of record), which calculates taxes at checkout. Team is invoiced. Scans always run locally.
A local-first data-residue tester for mobile apps. It captures your app's storage, caches, files and (on iOS) memory before and after logout and tells you which tokens and personal data survived, with a PASS, FAIL or INCOMPLETE verdict.
Yes, on the iOS Simulator with debug builds (Flutter runs only in debug mode on the Simulator). Findings are labelled with the store and key, such as AsyncStorage, MMKV or flutter.* preferences, and the runtime is stamped on every report. Memory-only hits in the JavaScript (Hermes) or Dart heap are reported but don't fail the gate, because freed strings there wait for garbage collection. Storage results gate as usual.
In beta. The CLI reads a debuggable build's private data over adb run-as (no root) on Linux or macOS, including CI runners. It covers disk only: SharedPreferences, DataStore, Room/SQLite, WebView data, the OkHttp cache, MMKV and files. There are no Android memory results yet.
No, by design. iOS and Android only let a debugger or run-as reach debug (debuggable) builds. WatchMemory checks your own debug build; release builds can't be inspected, by us or by anyone without the same access.
No SDK needed. Optional: the WatchMemoryDebugAgent Swift package (DEBUG builds only) lets the app generate a WMCANARY_ canary for zero-false-positive checks.
Not yet. Keychain items live outside the app container, so a token left in the Keychain after logout isn't reported today. Delete Keychain items in your logout code; a Keychain lifecycle check is on the roadmap.
Not yet. iOS scans target Simulator apps on your Mac. Physical-device support is on the roadmap.
No tool can: OWASP doesn't certify apps or tools. WatchMemory automates the OWASP MASWE-0024 check and maps results to MASVS-STORAGE and MASVS-AUTH-3, so you have evidence for a review or a pentest.
The check runs and reports a truthful verdict, labelled report-only. A FAIL still exits 0, so the build isn't failed. Pro enforces for one app; Team covers every developer and runner of an app.
Mac app: macOS 14+ and Xcode (it brings the iOS Simulator and LLDB); the scan engine is bundled. CLI: Python 3.9+, plus Xcode for iOS or adb for Android (Linux or macOS).
No. Scans run locally, values are masked and hashed with a per-install key in every report by default, and the MCP server never sends raw values to AI models. The only network calls are the Pro licence check and a daily update check (version number only).
A local-first data-residue tester: it proves that logout removes tokens and personal data from a mobile app's storage, caches and memory, with a PASS / FAIL / INCOMPLETE verdict.
Mobile teams shipping apps with accounts, payments or health data: native iOS, React Native, Flutter and KMP on the iOS Simulator, and Android (beta) for debuggable builds.
Those find leaked objects and measure memory. WatchMemory searches for secret values and checks whether they are gone after logout. Use them together.
OWASP MAS v2: MASWE-0024 (sensitive data accessible after session termination), MASVS-STORAGE-1/2 and MASVS-AUTH-3. It automates the MASWE-0024 check; it doesn't certify compliance.
Practical guides on clearing tokens and personal data on logout, for iOS, React Native, Flutter and Android.
Read all articlesRun a residue check on your next build. Free, local, no account.
Team pricing, invoices or a founding-price pilot? Email us
WatchMemory's Mac app is local-first: scans and reports stay on your machine. Web analytics on this site are privacy-friendly and only used to improve the website.