# Can You Trust a Super-App? Part 1

<details>
  <summary><strong>TL;DR — the one-paragraph version</strong></summary>
<p>Every super-app, WeChat, Telegram, Alipay, Max holds near-total power over the mini-apps running inside it: it can read their screens, their storage and their traffic, inject code into them,
and hand them your identity. This is architectural, not a Max bug and not a Russian peculiarity; it falls out of the way Android hosts web content, and the paper I'm responding to says so plainly.
So the headline is deflating: there is nothing special or suspicious in Max having these powers. What I did was test whether they are really there on the current build, and what they amount to
in practice.</p>
<ul>
<li>I re-tested all eight of the paper's claims on a build four days old. Every one held up — the only one I could not fully settle is whether the host actually <em>blocks</em> a mini-app's navigation in
practice, as opposed to merely being able to.</li>
<li>One finding is mine, not theirs: the "block screenshots" protection a mini-app can switch on hides its screen from <em>you</em>, while leaving it fully readable by Max. A privacy control that only ever
faces the user.</li>
<li>So why single out Max at all? Because the same power reads very differently in a messenger you can walk away from than in an app being built into national infrastructure — one that already ships
a government identity document as a first-party mini-app. Nobody has to be acting in bad faith for an invisible, unauditable capability to matter.</li>
</ul>
  </details>

On 10 September a team from Michigan, Calgary, Georgia Tech and IIT Delhi published *"Don't Trust the Super-App: A Case Study of Russia's Max"* (arXiv:2609.11814). It is a good paper (14k words is readable), and its argument is not the one the title suggests to a casual reader. It is not an accusation that Max is spying on anyone, but an argument about architecture: that a decade of super-app security research has audited the mini-apps while quietly assuming the host app could be trusted, and that this assumption was never justified for any super-app, anywhere.

Their case study is Max, and they say plainly in their conclusion that they did not observe it exercising these capabilities maliciously. I will come back to that sentence more than once, because it is the most important thing in the paper and the easiest thing to misread.

This is the first of two posts. This one covers what Max can do to the mini-apps it hosts and reports what happened when I tested every claim on a newly released build. The second covers the layer above it: what Max's servers decide about your particular copy of the app, which is where I think the harder question lives, and which is also where the "we saw no abuse" sentence gets its proper context.

They tested Max 25.15 in a lab. I tested 26.32.1, released on 17 September 2026 (four days old when I started).

The short version: I adjudicate eight claims below. All five of the paper's core capability findings reproduce on the current build. A sixth, whether the host can block or redirect a mini-app's navigation, I could only partially test. The last two the paper argues from Android's design rather than measuring, and I was able to measure one of them.

The most interesting result is not on their list at all. Max sets Android's screenshot-blocking flag while a mini-app is open, so your screenshot of that screen comes out solid black, while Max itself reads the same pixels from inside its own process without obstruction.

## **What the paper actually says**

If you have not met the term, a **super-app** is an app that hosts other apps. Messaging is the shell; inside it sits a platform of *mini-apps* such as web bundles, written in ordinary HTML and JavaScript, that load in an in-app browser view and reach native device capability through a bridge the host provides. WeChat popularised the model. Telegram, Alipay and TikTok all run versions of it. Max runs one, and ships first-party mini-apps of its own, including a digital identity document.

The paper is good on why this model spread, and the incentives are worth understanding because they explain the shape of everything that follows. For the mini-app developer: instant distribution to the host's entire user base, with no per-release app-store review, and access to cameras, contacts and payments through the bridge. For the host: engagement that does not leak to other apps, and a cut of every payment that flows through its rails. And one I had not considered until I read it, a super-app that you open every day keeps permissions alive that Android would otherwise restrict to foreground-only use, or revoke entirely after disuse. Regular use sustains the app's own privilege.

The argument the paper makes is aimed squarely at its own field. A decade of super-app research has found real problems, identity confusion, undocumented APIs, permission bypasses, privilege escalation, and UI deception reproduced across apps with billions of users. That work was disclosed responsibly, and it earned vendor patches, bug bounties and fixes. It was valuable. But almost all of it treated the host app as a trusted intermediary: something to be helped, hardened, made safer for the mini-apps running inside it. The question nobody had systematically asked was what happens when that trust is misplaced? when the host itself is the adversary, whether by intent or under pressure.

The authors situate Max among other state-aligned super-apps to argue this is not hypothetical. They are also careful to note it is not only an authoritarian concern, pointing to legislative pressure on end-to-end encryption in Western democracies. I think that framing is correct, and I will not dwell on it here, because the technical findings do not depend on it: nothing below is specific to Max, or to Russia.

Their method deserves a mention, because it is more thorough than most work of this kind. They instrumented Max on rooted physical hardware rather than an emulator, using Frida with more than eighty hooks armed simultaneously, enough that it destabilised Android's just-in-time compiler, and they had to force methods through the interpreter to keep the process alive. They compared behaviour from a Russian vantage point and a US network, and they worked with two different builds of the app: one sourced in Russia and one from the Play Store, which turn out to differ in their cryptographic layer. That last detail seems minor here but returns in part two.

What they found is five distinct capabilities that together let the host act as a man-in-the-middle for every interaction a mini-app has. In plain terms, Max can capture what is on the mini-app's screen, read and write everything the mini-app stores, inject its own code into the page, see and rewrite the mini-app's network traffic, and tell the mini-app who you are.

## **Why I'm checking it rather than summarising it**

You can read the paper yourself as it is open access, and clearly written.

What I can add is three things. First, I have been taking Max apart on this blog since last year, and in June I published [a map of this exact bridge](https://devsecmike.hashnode.dev/how-much-of-your-phone-can-a-max-mini-app-reach) — independently, and on a newer build. When a paper lands on ground you have already covered, the useful contribution is not agreement but it is verification.

Also, a second pass over the same ground finds things the first one missed. The most interesting result in this post is one the authors did not test for, and I only found it because I had spent June looking at a feature they had no particular reason to care about.

## **The claims, and where they landed**

| # | Claim | Verdict |
| --- | --- | --- |
| 1 | The host can capture mini-app UI | **Confirmed (and extended)** |
| 2 | The host can read and write mini-app local storage | **Confirmed** |
| 3 | The host can inject JavaScript into a mini-app | **Confirmed** |
| 4 | The host can observe and rewrite mini-app network traffic | **Confirmed** |
| 5 | The host can issue the user's identity to a mini-app | **Confirmed** |
| 6 | The host can block or redirect mini-app navigation | **Capability proven, use not observed** |
| 7 | Android's multi-party permission model collapses inside a mini-app | **Confirmed** |
| 8 | Mini-app code is loaded dynamically and cannot be attested | **Confirmed** |

## **Claim 1: capturing the screen**

The paper shows that a super-app can capture the pixels of a mini-app it hosts. The mini-app's WebView renders into a window the host owns, and although the web content executes in an isolated renderer process, the GPU services that produce the final image run inside the host's process. Renderer isolation does not make the rendered result opaque to the app doing the hosting. They measured two capture paths: Android's `PixelCopy` API succeeded in 31 of 35 attempts (88.5%), while the older `View.draw(Canvas)` route managed 73%.

I can reproduce that. But there is a question their paper does not ask, and it is the one that makes this concrete for a user.

Android has a flag called `FLAG_SECURE`. When an app sets it, the system refuses to screenshot that window you get a black rectangle, and the screen is excluded from screen recording and the app switcher. It is the flag your banking app uses. In Max, a mini-app can ask for it through the bridge, with a command called `WebAppSetupScreenCaptureBehavior`.

Max's own Digital ID mini-app asks for it on launch. I watched it happen:

```js
// injected into the mini-app's page by Max, on load
WebApp.sendEvent("WebAppSetupScreenCaptureBehavior",
  "{\"requestId\":\"f8e8c9af-…\",\"isScreenCaptureEnabled\":false}");
```

So the mini-app has requested that its screen be protected. Does that protect it?

With Digital ID open, I took a screenshot the ordinary way, over ADB. The result is solid black, the flag is working exactly as designed.

Then, from inside Max's own process, I tried both capture paths against the same window at the same moment. The window's flags confirmed `FLAG_SECURE` was set (`0x81812100`). `View.draw(Canvas)` returned a bitmap with 61 distinct colours. `PixelCopy` returned `SUCCESS` and a bitmap with 67 distinct colours. Both are full renders of a screen that the operating system had just refused to give me.

I saved the host-side capture to a file to be sure I was not reading noise. It is the Digital ID onboarding screen, legible, in colour. At the bottom of it, above the button, there is a line of reassurance: *"Данные зашифрованы и надёжно защищены"* — the data is encrypted and securely protected.

The result is this: `disable-screenshots` protects the mini-app from the user, not from the app hosting it. A mini-app that asks Max to protect its screen gets protection from everyone except Max.

I want to be careful about what that does and does not mean. It does not mean Max is capturing anybody's screen. Nothing I saw suggests that, and the paper's authors say the same about everything they tested. What it means is that the control which looks like a privacy feature is, in this architecture, a user-facing one only and neither the mini-app developer who requested it nor the person looking at the screen has any way to know that.

## **Claim 2: reading the mini-app's storage**

The paper's point here is structural and correct. A mini-app's cookies, `localStorage`, `sessionStorage` and IndexedDB are stored by the WebView inside the host app's own private data directory. They are Max's files, sitting under Max's UID. There is no Android boundary to breach, because there is no boundary.

Max also offers bridge-mediated storage, `DeviceStorage` and `SecureStorage` which mini-apps can call instead. My June post covered those commands. The sharper framing, which the paper gets right, is that the bridge is a convenience rather than a boundary: a mini-app that avoids it and writes to `localStorage` directly has not put its data anywhere less accessible to the host.

I can now add a live observation to what was previously a static claim. Opening Digital ID, one of the first things Max does is answer a `WebAppSecureStorageGetKey` request from the page, during start-up, before the user has touched anything.

## **Claim 3: injecting code into the page**

The bridge is asymmetric, and this is the part most people miss.

Going inward, mini-app to host, the surface is tiny and fixed. Two JavaScript interfaces were injected in the session I captured, exposing three callable methods between them. (A third, more privileged interface exists and is injected only for mini-apps the backend marks as privileged; it did not appear here, and I covered its gating in the June post.) Going outward, host to mini-app, Android offers `evaluateJavascript`, which runs arbitrary code in the page with the same authority as the page's own scripts, with no limit on content, timing or frequency.

Hooking that method on 26.32.1, opening a single mini-app produced six injections. One is a polyfill that reroutes the Web Share API through Max:

```js
// polyfill for web share api
if (window.navigator && !window.navigator.canShare) {
    window.navigator.canShare = () => true;
}
if (window.navigator && !window.navigator.share) { … }
```

The other five are bridge responses, delivered as injected code: `WebAppGetLaunchContext`, `WebAppNfcGetInfo`, `WebAppBiometryGetInfo`, `WebAppSecureStorageGetKey` and the screen-capture command above.

Every one of those is mundane, the mechanism a host uses to answer a routine bridge call is the same mechanism it would use to do anything else to the page, and from the page's perspective the two are indistinguishable.

## **Claim 4: watching the traffic**

A mini-app's HTTP requests do not travel through the host's networking stack, so instrumenting the host's own network layer would tell you the mini-app's traffic is invisible. It isn't. The WebView's callbacks, `shouldInterceptRequest`, `onPageStarted`, `onReceivedError`, `onConsoleMessage` all execute inside the host process.

Opening one mini-app generated 51 such callbacks in my capture: 31 resource requests and 20 console messages. The requests covered four hosts:

| Host | Requests seen | What it is |
| --- | --- | --- |
| [`ext-bots.max.ru`](http://ext-bots.max.ru) | 14 | mini-app analytics events |
| [`digital-id.max.ru`](http://digital-id.max.ru) | 8 | the mini-app's own assets |
| [`sdk-api.apptracer.ru`](http://sdk-api.apptracer.ru) | 5 | third-party crash/telemetry SDK |
| [`ext-api.max.ru`](http://ext-api.max.ru) | 4 | Max platform API |

The 20 console messages are the page's own internal logging, routing changes, start-up state, and NFC availability delivered straight into the host process, which is not something the page has any way to opt out of.

`shouldInterceptRequest` is not a passive observer. Its contract is that the host may inspect a request and optionally supply its own response, so the same callback that observes can also block, rewrite or substitute. One thing I should be exact about, because it is the difference between a capability and its use: Max's own WebView clients do not override this callback. They override navigation and page-lifecycle callbacks, but not this one. So what I am showing is that the request passes through a hook that runs in Max's process and is readable by anything running there my Frida script, or the paper's injected code, not that Max's own shipping code reads or rewrites it. InterSecLab, working from the same application, reached the same conclusion independently: Max does not override the interception callback in any of its WebView client classes.

The apptracer line is worth a note of its own. [`sdk-api.apptracer.ru`](http://sdk-api.apptracer.ru) is the third-party diagnostics SDK I wrote about in [an earlier post](https://devsecmike.hashnode.dev/inside-max-s-telemetry-the-apptracer-sdk). It turns out to be reachable inside the mini-app surface as well, which is not something the paper covers and not something I had looked for.

## **Claim 5: issuing your identity**

Every mini-app load arrives with a fragment that Max constructs and appends to the URL, before the page runs. On 26.32.1 it still carries the user's public IP address and an identity object with the numeric user id and name fields. The paper enumerates six fields in total, including an HMAC tag and a `query_id`.

I found this in June on 26.18.2 and it is unchanged. The mini-app does not authenticate you; Max tells the mini-app who you are, and the mini-app has no independent way to check.

## **Claim 6: deciding where the page may go**

Android gives the host a callback, `shouldOverrideUrlLoading`, whose entire purpose is to let it decide whether a navigation proceeds. Max's WebView client class overrides it.

I could not catch it in use. Across four attempts, including Digital ID, its account-creation flow, a bot conversation and a bot mini-app launch, the callback fired zero times. The reason appears to be that Max's first-party mini-apps are single-page applications that route entirely client-side; the console shows `[app] route changed` where a real navigation would have been.

So: the decision point exists, it is overridden by Max rather than inherited, and I did not observe it being exercised. Completing this would need a mini-app that genuinely navigates outward, the paper's own example is a government-services login flow. I would rather leave this as an open item than round it up.

## **Claim 7: who actually authorises anything**

This is the part that I think matters most to an ordinary reader, and it is the paper's strongest argument.

Android's permission model is built on the idea that three parties must agree before an app gets access to something sensitive: the developer declares it, the platform mediates it, and you approve it. You have probably seen this work when the dialog asking whether an app may use your camera.

Inside a mini-app, that model does not apply. The mini-app is not an app as far as Android is concerned; it is web content in a WebView belonging to Max. So when a mini-app wants something, it asks Max, and Max decides.

I instrumented this to see whether any part of the system objects. Opening Digital ID, three capability requests were made and answered, NFC status, biometric information, and a secure-storage key. In the same session, Android's permission-request method was called zero times, and Max did not even consult Android about its own permissions: the permission-check method was also called zero times. Max answered all three from its own state.

No dialog. No indicator. Nothing in your Android settings will ever list that mini-app, because as far as your phone is concerned it was never there.

A caveat worth stating: these three are capability queries, not high-risk grants. A mini-app asking to use your camera or read your contacts would be the stronger test. But the mechanism does not change with the sensitivity of the request.

## **Claim 8: code that nobody checks**

Android's model assumes third-party code is packaged, signed, and reviewed before it runs. A mini-app is a remote web bundle fetched at launch. There is no package, no signature the platform verifies, and no store review of the version you actually receive, which can differ between users and between loads. The paper notes that this is also precisely why the model is commercially attractive: instant distribution, no per-release review.

I have marked this one confirmed by inspection rather than by measurement, because that is what it is: it follows from how the platform is built, and there is nothing to instrument. Worth separating from the claims above, which I actually watched happen.

## **Why it probably exists**

Almost nothing above is a mistake, and it is worth being clear about that.

If you want to build a platform where web content can use native capabilities, the host must sit in the middle: something has to translate a JavaScript call into a biometric prompt. If you want the host to answer bridge calls, it needs to be able to run code in the page. If you want it to show a loading state or handle an error, it needs the WebView's lifecycle callbacks. Each capability is the direct consequence of a reasonable decision.

What the paper argues is that the sum of those reasonable decisions is a position of total control that Android has no mechanism to constrain, audit or even reveal. That is a design gap, not a bug, and it exists in every super-app, not just Max. Telegram's mini-app bridge is near-identical, WeChat's has been studied for years.

## **Why it warrants attention**

Max is not a general-purpose messenger competing for your attention. It is being positioned as national infrastructure, with state services arriving inside it, and Digital ID already shipping as a first-party mini-app.

The protection a mini-app can request is real against the operating system and absent against the host. The mini-app developer cannot verify it. The user cannot observe it. And the thing being protected, in the case I tested, was an identity screen carrying a promise that the data is securely protected.

None of that requires anyone to be acting in bad faith today. It requires only that the capability exists, is invisible, and is unauditable which is exactly what I found.
