@capgo/capgo-sec 1.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/bump_version.yml +83 -0
- package/.github/workflows/ci.yml +44 -0
- package/AGENTS.md +125 -0
- package/LICENSE +21 -0
- package/README.md +291 -0
- package/bun.lock +146 -0
- package/dist/cli/index.js +13248 -0
- package/dist/index.js +8273 -0
- package/package.json +53 -0
- package/renovate.json +39 -0
- package/src/cli/index.ts +183 -0
- package/src/index.ts +31 -0
- package/src/rules/android.ts +392 -0
- package/src/rules/authentication.ts +261 -0
- package/src/rules/capacitor.ts +435 -0
- package/src/rules/cryptography.ts +190 -0
- package/src/rules/index.ts +56 -0
- package/src/rules/ios.ts +326 -0
- package/src/rules/logging.ts +218 -0
- package/src/rules/network.ts +310 -0
- package/src/rules/secrets.ts +163 -0
- package/src/rules/storage.ts +241 -0
- package/src/rules/webview.ts +232 -0
- package/src/scanners/engine.ts +233 -0
- package/src/types.ts +96 -0
- package/src/utils/reporter.ts +209 -0
- package/test/rules.test.ts +235 -0
- package/test/scanner.test.ts +292 -0
- package/tsconfig.json +19 -0
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@capgo/capgo-sec",
|
|
3
|
+
"version": "1.0.4",
|
|
4
|
+
"description": "Security scanner for Capacitor apps - detect vulnerabilities, hardcoded secrets, and security misconfigurations",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"capsec": "./dist/cli/index.js"
|
|
9
|
+
},
|
|
10
|
+
"type": "module",
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "bun build src/cli/index.ts --outdir dist/cli --target node && bun build src/index.ts --outdir dist --target node",
|
|
13
|
+
"dev": "bun run src/cli/index.ts",
|
|
14
|
+
"test": "bun test",
|
|
15
|
+
"lint": "eslint src --ext .ts",
|
|
16
|
+
"prepublishOnly": "bun run build"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"capacitor",
|
|
20
|
+
"security",
|
|
21
|
+
"scanner",
|
|
22
|
+
"vulnerability",
|
|
23
|
+
"static-analysis",
|
|
24
|
+
"ionic",
|
|
25
|
+
"mobile",
|
|
26
|
+
"android",
|
|
27
|
+
"ios"
|
|
28
|
+
],
|
|
29
|
+
"author": "Capgo Team",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"repository": {
|
|
32
|
+
"type": "git",
|
|
33
|
+
"url": "https://github.com/Cap-go/capgo-sec"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://capgo.app/security-scanner/",
|
|
36
|
+
"engines": {
|
|
37
|
+
"node": ">=18.0.0"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@babel/parser": "^7.24.0",
|
|
41
|
+
"@babel/traverse": "^7.24.0",
|
|
42
|
+
"@babel/types": "^7.24.0",
|
|
43
|
+
"chalk": "^5.3.0",
|
|
44
|
+
"commander": "^12.0.0",
|
|
45
|
+
"fast-glob": "^3.3.2",
|
|
46
|
+
"ora": "^8.0.1"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/babel__traverse": "^7.20.5",
|
|
50
|
+
"@types/bun": "latest",
|
|
51
|
+
"typescript": "^5.4.0"
|
|
52
|
+
}
|
|
53
|
+
}
|
package/renovate.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
|
3
|
+
"extends": [
|
|
4
|
+
"config:base"
|
|
5
|
+
],
|
|
6
|
+
"dependencyDashboard": false,
|
|
7
|
+
"automerge": true,
|
|
8
|
+
"automergeType": "pr",
|
|
9
|
+
"packageRules": [
|
|
10
|
+
{
|
|
11
|
+
"matchUpdateTypes": [
|
|
12
|
+
"minor",
|
|
13
|
+
"patch"
|
|
14
|
+
],
|
|
15
|
+
"automerge": true
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"matchDepTypes": [
|
|
19
|
+
"devDependencies"
|
|
20
|
+
],
|
|
21
|
+
"automerge": true
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"matchManagers": [
|
|
25
|
+
"swift-pm"
|
|
26
|
+
],
|
|
27
|
+
"matchPackagePatterns": [
|
|
28
|
+
"^https://github.com/ionic-team/capacitor-swift-pm\\.git$",
|
|
29
|
+
"^ionic-team/capacitor-swift-pm$",
|
|
30
|
+
"^capacitor-swift-pm$"
|
|
31
|
+
],
|
|
32
|
+
"allowedVersions": "8.0.0",
|
|
33
|
+
"enabled": false
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"schedule": [
|
|
37
|
+
"before 5am on monday"
|
|
38
|
+
]
|
|
39
|
+
}
|
package/src/cli/index.ts
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import ora from 'ora';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { SecurityScanner } from '../scanners/engine.js';
|
|
7
|
+
import { formatCliReport, formatJsonReport, formatHtmlReport } from '../utils/reporter.js';
|
|
8
|
+
import type { ScanOptions, RuleCategory, Severity } from '../types.js';
|
|
9
|
+
import { ruleCount } from '../rules/index.js';
|
|
10
|
+
|
|
11
|
+
const program = new Command();
|
|
12
|
+
|
|
13
|
+
const VERSION = '1.0.0';
|
|
14
|
+
|
|
15
|
+
const BANNER = `
|
|
16
|
+
${chalk.hex('#7c3aed').bold('╔═══════════════════════════════════════════════════════════╗')}
|
|
17
|
+
${chalk.hex('#7c3aed').bold('║')} ${chalk.white.bold('🔒 CAPSEC')} - Capacitor Security Scanner ${chalk.hex('#7c3aed').bold('║')}
|
|
18
|
+
${chalk.hex('#7c3aed').bold('║')} ${chalk.gray(`v${VERSION} • ${ruleCount} security rules • capacitor-sec.dev`)} ${chalk.hex('#7c3aed').bold('║')}
|
|
19
|
+
${chalk.hex('#7c3aed').bold('╚═══════════════════════════════════════════════════════════╝')}
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
program
|
|
23
|
+
.name('capsec')
|
|
24
|
+
.version(VERSION)
|
|
25
|
+
.description('Security scanner for Capacitor apps - detect vulnerabilities, hardcoded secrets, and security misconfigurations')
|
|
26
|
+
.addHelpText('before', BANNER);
|
|
27
|
+
|
|
28
|
+
program
|
|
29
|
+
.command('scan')
|
|
30
|
+
.description('Scan a Capacitor project for security issues')
|
|
31
|
+
.argument('[path]', 'Path to the Capacitor project', '.')
|
|
32
|
+
.option('-o, --output <format>', 'Output format: cli, json, html', 'cli')
|
|
33
|
+
.option('-f, --output-file <file>', 'Write output to a file')
|
|
34
|
+
.option('-s, --severity <level>', 'Minimum severity to report: critical, high, medium, low, info', 'low')
|
|
35
|
+
.option('-c, --categories <categories>', 'Categories to scan (comma-separated)', '')
|
|
36
|
+
.option('-e, --exclude <patterns>', 'Additional patterns to exclude (comma-separated)', '')
|
|
37
|
+
.option('--ci', 'CI mode: exit with code 1 if high or critical issues found', false)
|
|
38
|
+
.option('-v, --verbose', 'Verbose output', false)
|
|
39
|
+
.action(async (path: string, options: any) => {
|
|
40
|
+
console.log(BANNER);
|
|
41
|
+
|
|
42
|
+
const spinner = ora('Initializing security scan...').start();
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const scanOptions: ScanOptions = {
|
|
46
|
+
path: path.startsWith('/') ? path : `${process.cwd()}/${path}`,
|
|
47
|
+
output: options.output,
|
|
48
|
+
outputFile: options.outputFile,
|
|
49
|
+
severity: options.severity as Severity,
|
|
50
|
+
categories: options.categories ? options.categories.split(',').map((c: string) => c.trim()) as RuleCategory[] : undefined,
|
|
51
|
+
exclude: options.exclude ? options.exclude.split(',').map((e: string) => e.trim()) : undefined,
|
|
52
|
+
ci: options.ci,
|
|
53
|
+
verbose: options.verbose
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
spinner.text = `Scanning ${scanOptions.path}...`;
|
|
57
|
+
|
|
58
|
+
const scanner = new SecurityScanner(scanOptions);
|
|
59
|
+
const result = await scanner.scan();
|
|
60
|
+
|
|
61
|
+
spinner.succeed(`Scanned ${result.filesScanned} files in ${result.duration}ms`);
|
|
62
|
+
|
|
63
|
+
// Format output
|
|
64
|
+
let output: string;
|
|
65
|
+
switch (options.output) {
|
|
66
|
+
case 'json':
|
|
67
|
+
output = formatJsonReport(result);
|
|
68
|
+
break;
|
|
69
|
+
case 'html':
|
|
70
|
+
output = formatHtmlReport(result);
|
|
71
|
+
break;
|
|
72
|
+
default:
|
|
73
|
+
output = formatCliReport(result);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Write to file or stdout
|
|
77
|
+
if (options.outputFile) {
|
|
78
|
+
await Bun.write(options.outputFile, output);
|
|
79
|
+
console.log(chalk.green(`\n✓ Report saved to ${options.outputFile}`));
|
|
80
|
+
} else {
|
|
81
|
+
console.log(output);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// CI mode exit codes
|
|
85
|
+
if (options.ci) {
|
|
86
|
+
if (result.summary.critical > 0 || result.summary.high > 0) {
|
|
87
|
+
console.log(chalk.red('\n✗ CI check failed: High or critical severity issues found'));
|
|
88
|
+
process.exit(1);
|
|
89
|
+
} else {
|
|
90
|
+
console.log(chalk.green('\n✓ CI check passed'));
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
} catch (error) {
|
|
96
|
+
spinner.fail('Scan failed');
|
|
97
|
+
console.error(chalk.red(`Error: ${(error as Error).message}`));
|
|
98
|
+
if (options.verbose) {
|
|
99
|
+
console.error(error);
|
|
100
|
+
}
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
program
|
|
106
|
+
.command('rules')
|
|
107
|
+
.description('List all security rules')
|
|
108
|
+
.option('-c, --category <category>', 'Filter by category')
|
|
109
|
+
.option('-s, --severity <severity>', 'Filter by severity')
|
|
110
|
+
.action((options: any) => {
|
|
111
|
+
console.log(BANNER);
|
|
112
|
+
|
|
113
|
+
// Import rules dynamically
|
|
114
|
+
import('../rules/index.js').then(({ allRules }) => {
|
|
115
|
+
let rules = allRules;
|
|
116
|
+
|
|
117
|
+
if (options.category) {
|
|
118
|
+
rules = rules.filter(r => r.category === options.category);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (options.severity) {
|
|
122
|
+
rules = rules.filter(r => r.severity === options.severity);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
console.log(chalk.bold(`\n${rules.length} Security Rules\n`));
|
|
126
|
+
console.log('─'.repeat(60));
|
|
127
|
+
|
|
128
|
+
const byCategory = new Map<string, typeof rules>();
|
|
129
|
+
for (const rule of rules) {
|
|
130
|
+
if (!byCategory.has(rule.category)) {
|
|
131
|
+
byCategory.set(rule.category, []);
|
|
132
|
+
}
|
|
133
|
+
byCategory.get(rule.category)!.push(rule);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
for (const [category, categoryRules] of byCategory) {
|
|
137
|
+
console.log(chalk.bold.cyan(`\n${category.toUpperCase()} (${categoryRules.length})`));
|
|
138
|
+
for (const rule of categoryRules) {
|
|
139
|
+
const severityColor =
|
|
140
|
+
rule.severity === 'critical' ? chalk.bgRed.white :
|
|
141
|
+
rule.severity === 'high' ? chalk.red :
|
|
142
|
+
rule.severity === 'medium' ? chalk.yellow :
|
|
143
|
+
rule.severity === 'low' ? chalk.cyan :
|
|
144
|
+
chalk.gray;
|
|
145
|
+
|
|
146
|
+
console.log(` ${chalk.gray(rule.id)} ${severityColor(`[${rule.severity}]`)} ${rule.name}`);
|
|
147
|
+
console.log(chalk.gray(` ${rule.description}`));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
console.log('\n');
|
|
152
|
+
});
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
program
|
|
156
|
+
.command('init')
|
|
157
|
+
.description('Initialize capsec configuration file')
|
|
158
|
+
.action(async () => {
|
|
159
|
+
console.log(BANNER);
|
|
160
|
+
|
|
161
|
+
const configContent = `{
|
|
162
|
+
"$schema": "https://capacitor-sec.dev/schema/capsec.json",
|
|
163
|
+
"exclude": [
|
|
164
|
+
"**/node_modules/**",
|
|
165
|
+
"**/dist/**",
|
|
166
|
+
"**/build/**"
|
|
167
|
+
],
|
|
168
|
+
"severity": "low",
|
|
169
|
+
"categories": [],
|
|
170
|
+
"rules": {}
|
|
171
|
+
}`;
|
|
172
|
+
|
|
173
|
+
const configPath = `${process.cwd()}/capsec.config.json`;
|
|
174
|
+
await Bun.write(configPath, configContent);
|
|
175
|
+
console.log(chalk.green(`✓ Created ${configPath}`));
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Default command is scan
|
|
179
|
+
if (process.argv.length === 2) {
|
|
180
|
+
process.argv.push('scan');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
program.parse();
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Capsec - Capacitor Security Scanner
|
|
2
|
+
// https://capacitor-sec.dev
|
|
3
|
+
|
|
4
|
+
export { SecurityScanner } from './scanners/engine.js';
|
|
5
|
+
export { formatCliReport, formatJsonReport, formatHtmlReport } from './utils/reporter.js';
|
|
6
|
+
export { allRules, rulesByCategory, ruleCount } from './rules/index.js';
|
|
7
|
+
|
|
8
|
+
export type {
|
|
9
|
+
Rule,
|
|
10
|
+
Finding,
|
|
11
|
+
ScanResult,
|
|
12
|
+
ScanOptions,
|
|
13
|
+
Severity,
|
|
14
|
+
RuleCategory,
|
|
15
|
+
CapacitorConfig
|
|
16
|
+
} from './types.js';
|
|
17
|
+
|
|
18
|
+
// Re-export individual rule sets for custom configurations
|
|
19
|
+
export {
|
|
20
|
+
secretsRules,
|
|
21
|
+
storageRules,
|
|
22
|
+
networkRules,
|
|
23
|
+
capacitorRules,
|
|
24
|
+
androidRules,
|
|
25
|
+
iosRules,
|
|
26
|
+
authenticationRules,
|
|
27
|
+
webviewRules,
|
|
28
|
+
loggingRules,
|
|
29
|
+
debugRules,
|
|
30
|
+
cryptographyRules
|
|
31
|
+
} from './rules/index.js';
|