SKILL.md
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.cleartextandwebContentsDebuggingEnabledfalse 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: trueincapacitor.config— dev/live-reload only; never production.- Missing
networksecurityconfig(Android) —usesCleartextTraffic="false"; pinning
for high-assurance (note bypass risks).
4 · WebView security
- No / weak CSP —
Content-Security-Policymeta 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: truein production — remote inspection; gate to dev.addJavascriptInterface/ bridge exposure — untrusted content + JS interface =
Critical RCE vector.
- **
setAllowFileAccess*/file://**,evalon user input, insecurepostMessage.
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/
addJavascriptInterfaceto 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-readinessbefore 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.