Capacitor Security with Capsec
Zero-config security scanning for Capacitor and Ionic apps.
When to Use This Skill
- User wants to secure their app
- User asks about security vulnerabilities
- User needs to run security audit
- User has hardcoded secrets
- User needs CI/CD security scanning
- User asks about OWASP mobile security
Quick Start with Capsec
Run Security Scan
# Scan current directory (no installation needed)
bunx capsec scan
# Scan specific path
bunx capsec scan ./my-app
# CI mode (exit code 1 on high/critical issues)
bunx capsec scan --ci
Output Formats
# CLI output (default)
bunx capsec scan
# JSON report
bunx capsec scan --output json --output-file report.json
# HTML report
bunx capsec scan --output html --output-file security-report.html
Filtering
# Only critical and high severity
bunx capsec scan --severity high
# Specific categories
bunx capsec scan --categories secrets,network,storage
# Exclude test files
bunx capsec scan --exclude "**/test/**,**/*.spec.ts"
Security Rules Reference
Secrets Detection (SEC)
| Rule | Severity | Description | |------|----------|-------------| | SEC001 | Critical | Hardcoded API Keys & Secrets | | SEC002 | High | Exposed .env File |
What Capsec Detects:
- AWS Access Keys
- Google API Keys
- Firebase Keys
- Stripe Keys
- GitHub Tokens
- JWT Secrets
- Database Credentials
- 30+ secret patterns
Fix Example:
// BAD - Hardcoded API key
const API_KEY = 'sk_live_abc123xyz';
// GOOD - Use environment variables
import { Env } from '@capgo/capacitor-env';
const API_KEY = await Env.get({ key: 'API_KEY' });
Storage Security (STO)
| Rule | Severity | Description | |------|----------|-------------| | STO001 | High | Unencrypted Sensitive Data in Preferences | | STO002 | High | localStorage Usage for Sensitive Data | | STO003 | Medium | SQLite Database Without Encryption | | STO004 | Medium | Filesystem Storage of Sensitive Data | | STO005 | Low | Insecure Data Caching | | STO006 | High | Keychain/Keystore Not Used for Credentials |
Fix Example:
// BAD - Plain preferences for tokens
import { Preferences } from '@capacitor/preferences';
await Preferences.set({ key: 'auth_token', value: token });
// GOOD - Use secure storage
import { NativeBiometric } from '@capgo/capacitor-native-biometric';
await NativeBiometric.setCredentials({
username: email,
password: token,
server: 'api.myapp.com',
});
Network Security (NET)
| Rule | Severity | Description | |------|----------|-------------| | NET001 | Critical | HTTP Cleartext Traffic | | NET002 | High | SSL/TLS Certificate Pinning Missing | | NET003 | High | Capacitor Server Cleartext Enabled | | NET004 | Medium | Insecure WebSocket Connection | | NET005 | Medium | CORS Wildcard Configuration | | NET006 | Medium | Insecure Deep Link Validation | | NET007 | Low | Capacitor HTTP Plugin Misuse | | NET008 | High | Sensitive Data in URL Parameters |
Fix Example:
// BAD - HTTP in production
const config: CapacitorConfig = {
server: {
cleartext: true, // Never in production!
},
};
// GOOD - HTTPS only
const config: CapacitorConfig = {
server: {
cleartext: false,
// Only allow specific domains
allowNavigation: ['https://api.myapp.com'],
},
};
Capacitor-Specific (CAP)
| Rule | Severity | Description | |------|----------|-------------| | CAP001 | High | WebView Debug Mode Enabled | | CAP002 | Medium | Insecure Plugin Configuration | | CAP003 | Low | Verbose Logging in Production | | CAP004 | High | Insecure allowNavigation | | CAP005 | Critical | Native Bridge Exposure | | CAP006 | Critical | Eval Usage with User Input | | CAP007 | Medium | Missing Root/Jailbreak Detection | | CAP008 | Low | Insecure Plugin Import | | CAP009 | Medium | Live Update Security | | CAP010 | High | Insecure postMessage Handler |
Fix Example:
// BAD - Debug mode in production
const config: CapacitorConfig = {
ios: {
webContentsDebuggingEnabled: true, // Remove in production!
},
android: {
webContentsDebuggingEnabled: true, // Remove in production!
},
};
// GOOD - Only in development
const config: CapacitorConfig = {
ios: {
webContentsDebuggingEnabled: process.env.NODE_ENV === 'development',
},
};
Android Security (AND)
| Rule | Severity | Description | |------|----------|-------------| | AND001 | High | Android Cleartext Traffic Allowed | | AND002 | Medium | Android Debug Mode Enabled | | AND003 | Medium | Insecure Android Permissions | | AND004 | Low | Android Backup Allowed | | AND005 | High | Exported Components Without Permission | | AND006 | Medium | WebView JavaScript Enabled Without Safeguards | | AND007 | Critical | Insecure WebView addJavascriptInterface | | AND008 | Critical | Hardcoded Signing Key |
Fix AndroidManifest.xml:
<!-- BAD -->
<application android:usesCleartextTraffic="true">
<!-- GOOD -->
<application
android:usesCleartextTraffic="false"
android:allowBackup="false"
android:networkSecurityConfig="@xml/network_security_config">
network_security_config.xml:
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">api.myapp.com</domain>
<pin-set>
<pin digest="SHA-256">your-pin-hash</pin>
</pin-set>
</domain-config>
</network-security-config>
iOS Security (IOS)
| Rule | Severity | Description | |------|----------|-------------| | IOS001 | High | App Transport Security Disabled | | IOS002 | Medium | Insecure Keychain Access | | IOS003 | Medium | URL Scheme Without Validation | | IOS004 | Low | iOS Pasteboard Sensitive Data | | IOS005 | Medium | Insecure iOS Entitlements | | IOS006 | Low | Background App Refresh Data Exposure | | IOS007 | Medium | Missing iOS Jailbreak Detection | | IOS008 | Low | Screenshots Not Disabled for Sensitive Screens |
Fix Info.plist:
<!-- BAD - Disables ATS -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<!-- GOOD - Specific exceptions only -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>legacy-api.example.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSExceptionMinimumTLSVersion</key>
<string>TLSv1.2</string>
</dict>
</dict>
</dict>
Authentication (AUTH)
| Rule | Severity | Description | |------|----------|-------------| | AUTH001 | Critical | Weak JWT Validation | | AUTH002 | High | Insecure Biometric Implementation | | AUTH003 | High | Weak Random Number Generation | | AUTH004 | Medium | Missing Session Timeout | | AUTH005 | High | OAuth State Parameter Missing | | AUTH006 | Critical | Hardcoded Credentials in Auth |
Fix Example:
// BAD - No JWT validation
const decoded = jwt.decode(token);
// GOOD - Verify JWT signature
const decoded = jwt.verify(token, publicKey, {
algorithms: ['RS256'],
issuer: 'https://auth.myapp.com',
audience: 'myapp',
});
WebView Security (WEB)
| Rule | Severity | Description | |------|----------|-------------| | WEB001 | Critical | WebView JavaScript Injection | | WEB002 | Medium | Unsafe iframe Configuration | | WEB003 | Medium | External Script Loading | | WEB004 | Medium | Content Security Policy Missing | | WEB005 | Low | Target _blank Without noopener |
Fix - Add CSP:
<!-- index.html -->
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://api.myapp.com;
font-src 'self';
frame-ancestors 'none';
">
Cryptography (CRY)
| Rule | Severity | Description | |------|----------|-------------| | CRY001 | Critical | Weak Cryptographic Algorithm | | CRY002 | Critical | Hardcoded Encryption Key | | CRY003 | High | Insecure Random IV Generation | | CRY004 | High | Weak Password Hashing |
Fix Example:
// BAD - Weak algorithm
const encrypted = CryptoJS.DES.encrypt(data, key);
// GOOD - Strong algorithm
const encrypted = CryptoJS.AES.encrypt(data, key, {
mode: CryptoJS.mode.GCM,
padding: CryptoJS.pad.Pkcs7,
});
// BAD - Hardcoded key
const key = 'my-secret-key-123';
// GOOD - Derived key
const key = await crypto.subtle.deriveKey(
{ name: 'PBKDF2', salt, iterations: 100000, hash: 'SHA-256' },
baseKey,
{ name: 'AES-GCM', length: 256 },
false,
['encrypt', 'decrypt']
);
Logging (LOG)
| Rule | Severity | Description | |------|----------|-------------| | LOG001 | High | Sensitive Data in Console Logs | | LOG002 | Low | Console Logs in Production |
Fix Example:
// BAD - Logging sensitive data
console.log('User password:', password);
console.log('Token:', authToken);
// GOOD - Redact sensitive data
console.log('User authenticated:', userId);
// Use conditional logging
if (process.env.NODE_ENV === 'development') {
console.debug('Debug info:', data);
}
CI/CD Integration
GitHub Actions
name: Security Scan
on: [push, pull_request]
jobs:
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: oven-sh/setup-bun@v1
- name: Run Capsec Security Scan
run: bunx capsec scan --ci --output json --output-file security-report.json
- name: Upload Security Report
uses: actions/upload-artifact@v4
if: always()
with:
name: security-report
path: security-report.json
GitLab CI
security-scan:
image: oven/bun:latest
script:
- bunx capsec scan --ci
artifacts:
reports:
security: security-report.json
only:
- merge_requests
- main
Configuration
capsec.config.json
{
"exclude": [
"**/node_modules/**",
"**/dist/**",
"**/*.test.ts",
"**/*.spec.ts"
],
"severity": "low",
"categories": [],
"rules": {
"LOG002": {
"enabled": false
},
"SEC001": {
"severity": "critical"
}
}
}
Initialize Config
bunx capsec init
Root/Jailbreak Detection
import { IsRoot } from '@capgo/capacitor-is-root';
async function checkDeviceSecurity() {
const { isRooted } = await IsRoot.isRooted();
if (isRooted) {
// Option 1: Warn user
showWarning('Device security compromised');
// Option 2: Restrict features
disableSensitiveFeatures();
// Option 3: Block app (for high-security apps)
blockApp();
}
}
Security Checklist
Before Release
- [ ] Run
bunx capsec scan --severity high - [ ] Remove all console.log statements
- [ ] Disable WebView debugging
- [ ] Remove development URLs
- [ ] Verify no hardcoded secrets
- [ ] Enable certificate pinning
- [ ] Implement root/jailbreak detection
- [ ] Add Content Security Policy
- [ ] Use secure storage for credentials
- [ ] Enable ProGuard (Android)
- [ ] Verify ATS settings (iOS)
Ongoing
- [ ] Run security scans in CI/CD
- [ ] Monitor for new vulnerabilities
- [ ] Update dependencies regularly
- [ ] Review third-party plugins
- [ ] Audit authentication flows
Resources
- Capsec Documentation: https://capacitor-sec.dev
- OWASP Mobile Top 10: https://owasp.org/www-project-mobile-top-10
- OWASP MASTG: https://mas.owasp.org/MASTG
- Capgo Security Plugins: https://capgo.app