Capacitor Native-Layer Hardening Audit + Remediation Plan
Degree of freedom: HIGH — native-layer inventory and plan. Stay
plan-only. No config, manifest, or code edits until approved.
This skill vs neighbors
| Skill |
Owns |
| plan-capacitor-hardening (this) |
Native-layer / WebView security plan |
plan-mobile-readiness |
Store submission mechanics |
enhance-capacitor-ui |
Hybrid shell UI |
How to reason (every plan item)
- Propose — config, storage, deep-link, or WebView change
- Risk — what a extracted APK or hijacked scheme actually exposes
- Keep-working — native controls that already hold in production
- Phase — prod-config leaks → tokens → auth → WebView → OTA (do not execute)
Worked example
Propose: server.cleartext and webContentsDebuggingEnabled false in prod; move auth tokens off Preferences onto Keychain/Keystore.
Risk: release APK allows remote WebView inspect + plaintext token backup exfil.
Keep-working: iOS ATS already blocks cleartext.
Phase: Phase 1 — strip dev config from prod.
Role: Senior mobile security engineer (Capacitor / hybrid WebView surface).
Task: Inventory capacitor.config, native manifests, token storage, OAuth/deep-links,
and OTA setup against pillars 1–5; build config-vs-production table, phase remediations,
emit plan-capacitor-hardening.md. Audit & plan only — no config, manifest, or code
edits until approved.
Audit the native layer the web view hides. Change nothing until approved.
Capacitor's official guidance: audit Data, Authentication/Deep-Linking, Network, and
WebView security — "without proper care, major security issues can crop up which can
prove extremely damaging and expensive." Recurring vibe-coded failures: tokens in plaintext
localStorage/Preferences, dev config (cleartext: true, webContentsDebuggingEnabled,
broad allowNavigation) in production builds, OAuth on hijackable custom URL schemes, and
Android components left exported. Every finding here is invisible to web-only review.
When this fires
Trigger phrases: "is my Capacitor app secure", "harden my hybrid app",
"WebView security", "secure storage for tokens", "deep link / OAuth security",
"cleartext traffic", "allowNavigation", "exported activity", "is my OTA update
safe", "pre-store Capacitor hardening".
Do not fire for: UI polish (enhance-capacitor-ui), RN perf (mobile-rn-performance),
or store paperwork (plan-mobile-readiness — pairs with this). This owns the native-layer
security surface.
Why a dedicated skill
plan-rls-audit, plan-secrets-audit, and plan-input-validation cover backend/web.
None know capacitor.config, AndroidManifest.xml, Info.plist, the WebView bridge, or
Keychain/Keystore. Hybrid-native gaps are invisible if you only review web code.
The audit — four pillars (+ OTA) [HIGH freedom]
1 · Data security
- Secrets in the bundle — API keys, tokens hardcoded in JS or build-time env injection.
Bundle is extractable; move secret-key ops server-side. →
plan-secrets-audit
- Plaintext token storage — auth tokens in
localStorage, @capacitor/preferences,
IndexedDB, plain SQLite. Wrong. Use memory-only or iOS Keychain / Android Keystore
(@capacitor-community/secure-storage, capacitor-secure-storage-plugin, Identity Vault).
allowBackup (Android) — android:allowBackup="true" exfiltrates app data via backups.
2 · Authentication & deep linking
- Custom URL scheme OAuth —
myapp:// not globally owned; malicious app can intercept.
- No PKCE — OAuth2 in native apps must use PKCE.
- Custom scheme vs App/Universal Links — prefer Universal Links (iOS) / App Links
(Android) with
assetlinks.json / AASA. Note silent verification fallback to browser.
- Exported Android components —
exported=true + BROWSABLE + no validation = common
deep-link compromise. Audit every <intent-filter>.
- Deep-link input not validated — URI params →
WebView.loadUrl, file access, queries.
startsWith host checks inadequate.
3 · Network security
- Non-HTTPS — any
http:// in plaintext.
cleartext: true in capacitor.config — dev/live-reload only; never production.
- Missing
network_security_config (Android) — usesCleartextTraffic="false"; pinning
for high-assurance (note bypass risks).
4 · WebView security
- No / weak CSP —
Content-Security-Policy meta tag scoping loads.
- Broad
allowNavigation — untrusted hosts load with native bridge attached; scope to
trusted API origin(s). Note getPlatform() quirk on navigated URLs.
webContentsDebuggingEnabled: true in production — remote inspection; gate to dev.
addJavascriptInterface / bridge exposure — untrusted content + JS interface =
Critical RCE vector.
setAllowFileAccess* / file://, eval on user input, insecure postMessage.
5 · OTA / live-update governance
- Update channel integrity — OTA bundles signed/encrypted; unauthenticated path = RCE.
- Store-policy — Google permits WebView updates; Apple: OTA must not alter core
functionality (App Store violation independent of security).
- Rollback / kill switch — can a bad update be reverted?
Severity aligned with Cap-go CAP001–010 / AND001–008 where applicable.
For each finding: location, gap, exposure, severity, remediation direction.
Procedure [HIGH freedom]
- Inventory. Read
capacitor.config.*, AndroidManifest.xml, Info.plist, network
configs, OAuth/deep-link setup, token storage. State what you couldn't see.
- Run pillars 1–5. Tag Critical (bridge/
addJavascriptInterface to untrusted
content, hardcoded signing key, plaintext secrets shipped), High (cleartext, broad
allowNavigation, exported component, custom-scheme OAuth w/o PKCE, plaintext tokens),
Med (debug flags, allowBackup, weak CSP), Low (logging nits).
- Config-vs-production table — dev-only settings live in prod build.
- Phase burndown. Production-config leaks and token storage first.
- Emit
plan-capacitor-hardening.md. End the turn.
Guardrails [LOW freedom — run exactly]
- Plan only. No config, manifest, or storage migration edits.
- "Works in the browser" hides all of this. Say so explicitly.
- Dev config is a production vulnerability.
cleartext, webContentsDebuggingEnabled,
broad allowNavigation must not ship.
- In-memory or hardware-backed only for tokens/keys — not
Preferences/localStorage.
- Universal/App Links + PKCE for sensitive OAuth; custom scheme = High even if "works".
- Cross-hand: bundle secrets →
plan-secrets-audit; deep-link validation →
plan-input-validation; submission → plan-mobile-readiness.
- Minimal quoting of config/manifest.
Self-critique before the burndown [LOW freedom — do not skip]
- evidenced-not-assumed — config/manifest path or storage API, not "hybrid apps are risky"
- plan-only — no config, manifest, or storage migration this pass
- phase justified — prod-config leaks and plaintext tokens before OTA polish
- right-owner — bundle secrets →
plan-secrets-audit; store paperwork → plan-mobile-readiness
- no-false-safety — "works in the browser" hides this surface; custom-scheme OAuth is High even if it works
Report template — plan-capacitor-hardening.md
# Capacitor Native-Layer Hardening Audit — <app>
_Audit-only. Native security. Nothing changes until each phase is approved._
_These findings are invisible to web-only review._
## Scope
- Inspected: capacitor.config ☐ AndroidManifest ☐ Info.plist ☐ token storage ☐ OAuth ☐ OTA ☐
- Platforms: iOS ☐ Android ☐ | Assumptions: …
## Dev-config-in-production
| Setting | Prod value | Risk | Direction |
|---------|------------|------|-----------|
| server.cleartext | true | plaintext HTTP | false |
| webContentsDebuggingEnabled | true | remote inspection | dev-only |
| server.allowNavigation | ["*"] | bridge on untrusted origin | scope to API |
## Verdict
| Pillar | Crit | High | Med | Worst item |
|--------|------|------|-----|-----------|
| Data | n | n | n | tokens in localStorage |
| Auth/Deep-link | n | n | n | custom-scheme OAuth, no PKCE |
| Network | n | n | n | cleartext enabled |
| WebView | n | n | n | bridge exposed |
| OTA | n | n | n | unsigned channel |
## Findings
| # | Pillar | Location | Gap | Sev | Direction |
|---|--------|----------|-----|-----|-----------|
## Phased burndown
- **Phase 1 — Strip dev config from prod** → `mobile-capacitor-platform`
- **Phase 2 — Secure token storage** → `mobile-capacitor-platform` / `plan-secrets-audit`
- **Phase 3 — Auth & deep-link** → PKCE, App/Universal Links, exported review
- **Phase 4 — WebView & network** → CSP, network_security_config, bridge exposure
- **Phase 5 — OTA governance** → sign channel, store-policy, rollback
## Execution handoff
Re-audit native files after each phase; verify secure storage + deep-links on real devices.
Chains with
- Launch gates — run with
plan-mobile-readiness before store submit.
plan-secrets-audit — bundle secrets cross-hand.
plan-input-validation — deep-link / WebView input validation cross-hand.
- Execution:
mobile-capacitor-platform, backend-patterns, mobile-emulator-test.
- Verify: real-device secure storage + deep-link test; no dev config in release build.
Plan with a strong model; execute with composer-2.5-execution.mdc.
1---2name: plan-capacitor-hardening3description: Plan-only Capacitor/Ionic native-layer security audit: WebView, token storage, deep links/OAuth, cleartext traffic, exported activities. Use when "is my Capacitor app secure?", "harden my hybrid app", or pre-store hardening. Store readiness → plan-mobile-readiness.4license: MIT5---67# Capacitor Native-Layer Hardening Audit + Remediation Plan89**Degree of freedom: HIGH** — native-layer inventory and plan. Stay10**plan-only**. No config, manifest, or code edits until approved.1112## This skill vs neighbors1314| Skill | Owns |15|---|---|16| **plan-capacitor-hardening** (this) | Native-layer / WebView security plan |17| `plan-mobile-readiness` | Store submission mechanics |18| `enhance-capacitor-ui` | Hybrid shell UI |1920## How to reason (every plan item)21221. **Propose** — config, storage, deep-link, or WebView change232. **Risk** — what a extracted APK or hijacked scheme actually exposes243. **Keep-working** — native controls that already hold in production254. **Phase** — prod-config leaks → tokens → auth → WebView → OTA (do not execute)2627## Worked example2829> **Propose:** `server.cleartext` and `webContentsDebuggingEnabled` false in prod; move auth tokens off Preferences onto Keychain/Keystore.30> **Risk:** release APK allows remote WebView inspect + plaintext token backup exfil.31> **Keep-working:** iOS ATS already blocks cleartext.32> **Phase:** Phase 1 — strip dev config from prod.3334**Role:** Senior mobile security engineer (Capacitor / hybrid WebView surface).3536**Task:** Inventory `capacitor.config`, native manifests, token storage, OAuth/deep-links,37and OTA setup against pillars 1–5; build config-vs-production table, phase remediations,38emit `plan-capacitor-hardening.md`. **Audit & plan only — no config, manifest, or code39edits until approved.**4041**Audit the native layer the web view hides. Change nothing until approved.**4243Capacitor's official guidance: audit **Data, Authentication/Deep-Linking, Network, and44WebView security** — "without proper care, major security issues can crop up which can45prove extremely damaging and expensive." Recurring vibe-coded failures: tokens in plaintext46`localStorage`/`Preferences`, dev config (`cleartext: true`, `webContentsDebuggingEnabled`,47broad `allowNavigation`) in production builds, OAuth on hijackable custom URL schemes, and48Android components left `exported`. **Every finding here is invisible to web-only review.**4950---5152## When this fires5354Trigger phrases: *"is my Capacitor app secure"*, *"harden my hybrid app"*,55*"WebView security"*, *"secure storage for tokens"*, *"deep link / OAuth security"*,56*"cleartext traffic"*, *"allowNavigation"*, *"exported activity"*, *"is my OTA update57safe"*, *"pre-store Capacitor hardening"*.5859Do **not** fire for: UI polish (`enhance-capacitor-ui`), RN perf (`mobile-rn-performance`),60or store paperwork (`plan-mobile-readiness` — pairs with this). This owns the *native-layer61security surface*.6263---6465## Why a dedicated skill6667`plan-rls-audit`, `plan-secrets-audit`, and `plan-input-validation` cover backend/web.68None know `capacitor.config`, `AndroidManifest.xml`, `Info.plist`, the WebView bridge, or69Keychain/Keystore. Hybrid-native gaps are invisible if you only review web code.7071---7273## The audit — four pillars (+ OTA) [HIGH freedom]7475### 1 · Data security76- **Secrets in the bundle** — API keys, tokens hardcoded in JS or build-time env injection.77 Bundle is extractable; move secret-key ops server-side. → `plan-secrets-audit`78- **Plaintext token storage** — auth tokens in `localStorage`, `@capacitor/preferences`,79 IndexedDB, plain SQLite. Wrong. Use memory-only or **iOS Keychain / Android Keystore**80 (`@capacitor-community/secure-storage`, `capacitor-secure-storage-plugin`, Identity Vault).81- **`allowBackup`** (Android) — `android:allowBackup="true"` exfiltrates app data via backups.8283### 2 · Authentication & deep linking84- **Custom URL scheme OAuth** — `myapp://` not globally owned; malicious app can intercept.85- **No PKCE** — OAuth2 in native apps **must** use PKCE.86- **Custom scheme vs App/Universal Links** — prefer Universal Links (iOS) / App Links87 (Android) with `assetlinks.json` / AASA. Note silent verification fallback to browser.88- **Exported Android components** — `exported=true` + `BROWSABLE` + no validation = common89 deep-link compromise. Audit every `<intent-filter>`.90- **Deep-link input not validated** — URI params → `WebView.loadUrl`, file access, queries.91 `startsWith` host checks inadequate.9293### 3 · Network security94- **Non-HTTPS** — any `http://` in plaintext.95- **`cleartext: true`** in `capacitor.config` — dev/live-reload only; never production.96- **Missing `network_security_config`** (Android) — `usesCleartextTraffic="false"`; pinning97 for high-assurance (note bypass risks).9899### 4 · WebView security100- **No / weak CSP** — `Content-Security-Policy` meta tag scoping loads.101- **Broad `allowNavigation`** — untrusted hosts load with native bridge attached; scope to102 trusted API origin(s). Note `getPlatform()` quirk on navigated URLs.103- **`webContentsDebuggingEnabled: true`** in production — remote inspection; gate to dev.104- **`addJavascriptInterface` / bridge exposure** — untrusted content + JS interface =105 Critical RCE vector.106- **`setAllowFileAccess*` / `file://`**, `eval` on user input, insecure `postMessage`.107108### 5 · OTA / live-update governance109- **Update channel integrity** — OTA bundles signed/encrypted; unauthenticated path = RCE.110- **Store-policy** — Google permits WebView updates; **Apple: OTA must not alter core111 functionality** (App Store violation independent of security).112- **Rollback / kill switch** — can a bad update be reverted?113114Severity aligned with Cap-go CAP001–010 / AND001–008 where applicable.115116For each finding: location, gap, exposure, severity, remediation *direction*.117118---119120## Procedure [HIGH freedom]1211221. **Inventory.** Read `capacitor.config.*`, `AndroidManifest.xml`, `Info.plist`, network123 configs, OAuth/deep-link setup, token storage. State what you couldn't see.1242. **Run pillars 1–5.** Tag **Critical** (bridge/`addJavascriptInterface` to untrusted125 content, hardcoded signing key, plaintext secrets shipped), **High** (cleartext, broad126 `allowNavigation`, exported component, custom-scheme OAuth w/o PKCE, plaintext tokens),127 **Med** (debug flags, allowBackup, weak CSP), **Low** (logging nits).1283. **Config-vs-production table** — dev-only settings live in prod build.1294. **Phase** burndown. Production-config leaks and token storage first.1305. **Emit `plan-capacitor-hardening.md`. End the turn.**131132---133134## Guardrails [LOW freedom — run exactly]135136- **Plan only.** No config, manifest, or storage migration edits.137- **"Works in the browser" hides all of this.** Say so explicitly.138- **Dev config is a production vulnerability.** `cleartext`, `webContentsDebuggingEnabled`,139 broad `allowNavigation` must not ship.140- **In-memory or hardware-backed only** for tokens/keys — not `Preferences`/`localStorage`.141- **Universal/App Links + PKCE** for sensitive OAuth; custom scheme = High even if "works".142- **Cross-hand:** bundle secrets → `plan-secrets-audit`; deep-link validation →143 `plan-input-validation`; submission → `plan-mobile-readiness`.144- **Minimal quoting** of config/manifest.145146## Self-critique before the burndown [LOW freedom — do not skip]1471481. **evidenced-not-assumed** — config/manifest path or storage API, not "hybrid apps are risky"1492. **plan-only** — no config, manifest, or storage migration this pass1503. **phase justified** — prod-config leaks and plaintext tokens before OTA polish1514. **right-owner** — bundle secrets → `plan-secrets-audit`; store paperwork → `plan-mobile-readiness`1525. **no-false-safety** — "works in the browser" hides this surface; custom-scheme OAuth is High even if it works153154---155156## Report template — `plan-capacitor-hardening.md`157158```markdown159# Capacitor Native-Layer Hardening Audit — <app>160161_Audit-only. Native security. Nothing changes until each phase is approved._162_These findings are invisible to web-only review._163164## Scope165- Inspected: capacitor.config ☐ AndroidManifest ☐ Info.plist ☐ token storage ☐ OAuth ☐ OTA ☐166- Platforms: iOS ☐ Android ☐ | Assumptions: …167168## Dev-config-in-production169| Setting | Prod value | Risk | Direction |170|---------|------------|------|-----------|171| server.cleartext | true | plaintext HTTP | false |172| webContentsDebuggingEnabled | true | remote inspection | dev-only |173| server.allowNavigation | ["*"] | bridge on untrusted origin | scope to API |174175## Verdict176| Pillar | Crit | High | Med | Worst item |177|--------|------|------|-----|-----------|178| Data | n | n | n | tokens in localStorage |179| Auth/Deep-link | n | n | n | custom-scheme OAuth, no PKCE |180| Network | n | n | n | cleartext enabled |181| WebView | n | n | n | bridge exposed |182| OTA | n | n | n | unsigned channel |183184## Findings185| # | Pillar | Location | Gap | Sev | Direction |186|---|--------|----------|-----|-----|-----------|187188## Phased burndown189- **Phase 1 — Strip dev config from prod** → `mobile-capacitor-platform`190- **Phase 2 — Secure token storage** → `mobile-capacitor-platform` / `plan-secrets-audit`191- **Phase 3 — Auth & deep-link** → PKCE, App/Universal Links, exported review192- **Phase 4 — WebView & network** → CSP, network_security_config, bridge exposure193- **Phase 5 — OTA governance** → sign channel, store-policy, rollback194195## Execution handoff196Re-audit native files after each phase; verify secure storage + deep-links on real devices.197```198199---200201## Chains with202203- **Launch gates** — run with `plan-mobile-readiness` before store submit.204- **`plan-secrets-audit`** — bundle secrets cross-hand.205- **`plan-input-validation`** — deep-link / WebView input validation cross-hand.206- **Execution:** `mobile-capacitor-platform`, `backend-patterns`, `mobile-emulator-test`.207- **Verify:** real-device secure storage + deep-link test; no dev config in release build.208209> Plan with a strong model; execute with `composer-2.5-execution.mdc`.