Security model
Start from what has already gone wrong
Section titled “Start from what has already gone wrong”The reference study is A Small Leak Will Sink Many Ships: Vulnerabilities in Mini-Programs (arXiv 2205.15202). It tested more than 2,580 APIs across 9 mini-program ecosystems, and its result is the honest starting point for anything new in this category:
Every one of the nine ecosystems examined had at least one vulnerability.
There is no secure implementation here to copy. There is a catalogue of how everyone got it wrong.
Section 3.2 of the paper names six categories. They are Sollar’s review checklist and the agenda for its internal penetration testing.
1. Cache file reuse
Section titled “1. Cache file reuse”One mini app reads a file cached by another, because the host stores everything in a common directory.
Sollar: a synthetic origin per mini app. Cache, localStorage, IndexedDB and cookies are
partitioned by the browser engine, not by a directory convention. It is the difference between a
rule the runtime remembers to apply and one it cannot violate.
2. PEL-API — permission encapsulated in a leaked API
Section titled “2. PEL-API — permission encapsulated in a leaked API”A high-level API internally invokes a privileged capability and, once exposed, hands the privilege over for free. The paper’s example: obtaining the latitude and longitude of a map’s centre “without the user’s authorisation”, through a map context function.
Sollar: the bridge is minimal by construction, and every method is audited against one question
— what capability does this grant indirectly? A method that accepts a broad configuration object
is suspect by default, because that is where the extra capability hides. Where a web equivalent
exists it is used instead: navigator.geolocation already carries the engine’s own prompt and has
no side path through another API.
3. Silent transmission of sensitive data
Section titled “3. Silent transmission of sensitive data”Data leaves for a remote server without the user noticing. The paper cites the clipboard: “the copied text could be sent to a remote server without user’s awareness.”
Sollar: a network allowlist in the signed manifest, enforced in the native request interceptor. Changing a destination requires a new version and a new gate. CSP is the second layer — a mini app can rewrite its own CSP at runtime, but it cannot reach the native interceptor.
4. Permission management failure
Section titled “4. Permission management failure”A permission granted once lasts forever, leaks between contexts, or cannot be revoked.
Sollar: permissions are keyed on (user, mini app, scope) and revocable at any time. Host app
permissions do not flow to a mini app — required by Apple’s Guideline 4.7.3 and enforced on both
platforms. And the mandatory purpose string turns the grant into a contestable claim at review
rather than a technical checkbox.
5. WebView bypassing permission control
Section titled “5. WebView bypassing permission control”An embedded webview component loads external content that escapes the runtime’s permission model.
Sollar: there is no <web-view> component. External content opens in the system browser via
sollar.ui.openExternal(), with a visible address bar and lock icon.
This is a deliberate amputation. WeChat’s web-view requires a business-domain allowlist covering
nested iframes and still is the vector for this entire category. The feature is not worth its
attack surface.
6. Permission divergence across environments
Section titled “6. Permission divergence across environments”The permission model behaves differently in development, trial and production — and the hole exists in only one of them.
Sollar: the same permission-checking code in all three environments. What changes between environments is data and endpoints, never the authorisation path. The conformance suite runs the same permission tests against all three.
Platform controls
Section titled “Platform controls”Android
Section titled “Android”| Control | Rule |
|---|---|
| Bridge | WebViewCompat.addWebMessageListener with allowedOriginRules. Never addJavascriptInterface. |
| Serving the package | WebViewAssetLoader + shouldInterceptRequest → https://<appid>.miniapp.localhost/ |
| File access | setAllowFileAccessFromFileURLs(false), setAllowUniversalAccessFromFileURLs(false), setAllowFileAccess(false) |
| Safe Browsing | Enabled |
| Renderer | Separate process, with WebViewRenderProcessClient to detect a hang |
| Priority | setRendererPriorityPolicy — the mini app’s renderer is sacrificed before the host |
allowedOriginRules matches scheme, host and port; the path is ignored.
| Control | Rule |
|---|---|
| Bridge | WKScriptMessageHandlerWithReply in a dedicated WKContentWorld |
| Serving the package | WKURLSchemeHandler — see the secure-context risk in Architecture |
| Storage | WKWebsiteDataStore(forIdentifier:) per mini app; nonPersistent() for ephemeral mode |
| Domains | WKAppBoundDomains where applicable — capped at 10 domains, which makes it unusable as a per-mini-app control |
| JIT | Present in the WebContent process — an advantage, not a risk |
The CSP floor
Section titled “The CSP floor”The submission validator refuses a package that does not meet:
default-src 'self';script-src 'self'; ← no 'unsafe-eval', no remote hostobject-src 'none';base-uri 'none';frame-ancestors 'none';connect-src 'self' <manifest hosts>script-src 'self' with no remote host is also what keeps Sollar inside Google Play’s interpreter
exemption: every executable byte arrives in the verified package. A mini app pulling a script from a
CDN is, technically, downloading executable code from outside it.
A third-party resource that genuinely must be remote requires Subresource Integrity with a pinned hash.
Denial of service
Section titled “Denial of service”Per instance: a ceiling on concurrent connections, on storage, and on memory — with the mini app destroyed rather than the host running out of memory — plus a rate limit per bridge method. A mini app cannot degrade Sollar for the user.
What review checks
Section titled “What review checks”Beyond the six categories:
- The manifest validates, and every permission has a
purposeplausible for the declared function. - The network allowlist is justifiable — an unexplained host is a question to the developer.
- The CSP meets the floor.
- An SBOM is present, with no high-severity CVE in a shipped dependency.
- No remote scripts, no
eval, no obfuscation that defeats review. actions[]describe honestly what they do;destructiveis set where it is destructive.- Reporting and blocking are reachable (Guideline 4.7.1); a content rating is declared (4.7.5).
Automated checks
Section titled “Automated checks”The submission pipeline: validate the manifest → scan the SBOM against vulnerability data → check
the CSP → static analysis for prohibited patterns (eval, Function(), remote scripts) → dynamic
tests for all six categories, with a deliberately malicious mini app kept as a permanent test case.
TaintMini (ICSE 2023), a static taint-analysis tool for mini programs, is a candidate for the pipeline.
What remains open
Section titled “What remains open”Stated plainly, because a security page that claims completeness is not a security page.
- Secure context on iOS. If a custom scheme is not a secure context,
crypto.subtleand service workers are unavailable inside mini apps and the storage design changes. Verified per release. - Obfuscation versus review. WeChat ships code obfuscation as a first-party plugin with a reversible sourcemap. Obfuscation that review cannot undo is incompatible with real review; the policy must be settled before the store opens.
- No independent audit yet. Nothing described here has been tested by an outside team. Before opening the store to unvetted third parties, penetration testing with a written scope, an isolated environment and synthetic data is a precondition, not a nice-to-have.