monomind 1.16.11 → 1.17.1
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/.claude/agents/engineering/engineering-security-engineer.md +1 -1
- package/.claude/agents/github/code-review-swarm.md +19 -19
- package/.claude/agents/github/github-modes.md +4 -4
- package/.claude/agents/github/multi-repo-swarm.md +24 -24
- package/.claude/agents/github/project-board-sync.md +28 -28
- package/.claude/agents/github/swarm-issue.md +26 -26
- package/.claude/agents/github/swarm-pr.md +18 -18
- package/.claude/agents/github/workflow-automation.md +27 -27
- package/.claude/agents/reengineer-squad/git-manager.md +2 -2
- package/.claude/commands/mastermind/_repeat.md +4 -0
- package/.claude/commands/mastermind/master.md +61 -4
- package/.claude/commands/mastermind/references/antigravity-tools.md +60 -0
- package/.claude/commands/mastermind/references/claude-code-tools.md +50 -0
- package/.claude/commands/mastermind/references/codex-tools.md +64 -0
- package/.claude/commands/mastermind/references/copilot-tools.md +49 -0
- package/.claude/commands/mastermind/references/gemini-tools.md +63 -0
- package/.claude/commands/mastermind/references/pi-tools.md +28 -0
- package/.claude/helpers/mastermind-activate.cjs +53 -0
- package/.claude/scheduled_tasks.lock +1 -1
- package/.claude/settings.json +4 -0
- package/.claude/skills/mastermind/_repeat.md +2 -0
- package/.claude/skills/mastermind/runorg.md +14 -0
- package/.claude/skills/mastermind/techport.md +5 -5
- package/README.md +1 -1
- package/package.json +6 -5
- package/packages/@monomind/cli/.claude/agents/engineering/engineering-security-engineer.md +1 -1
- package/packages/@monomind/cli/.claude/agents/github/code-review-swarm.md +19 -19
- package/packages/@monomind/cli/.claude/agents/github/github-modes.md +4 -4
- package/packages/@monomind/cli/.claude/agents/github/multi-repo-swarm.md +24 -24
- package/packages/@monomind/cli/.claude/agents/github/project-board-sync.md +28 -28
- package/packages/@monomind/cli/.claude/agents/github/swarm-issue.md +26 -26
- package/packages/@monomind/cli/.claude/agents/github/swarm-pr.md +18 -18
- package/packages/@monomind/cli/.claude/agents/github/workflow-automation.md +27 -27
- package/packages/@monomind/cli/.claude/agents/reengineer-squad/git-manager.md +2 -2
- package/packages/@monomind/cli/.claude/commands/mastermind/_repeat.md +4 -0
- package/packages/@monomind/cli/.claude/commands/mastermind/master.md +61 -4
- package/packages/@monomind/cli/.claude/commands/mastermind/references/antigravity-tools.md +60 -0
- package/packages/@monomind/cli/.claude/commands/mastermind/references/claude-code-tools.md +50 -0
- package/packages/@monomind/cli/.claude/commands/mastermind/references/codex-tools.md +64 -0
- package/packages/@monomind/cli/.claude/commands/mastermind/references/copilot-tools.md +49 -0
- package/packages/@monomind/cli/.claude/commands/mastermind/references/gemini-tools.md +63 -0
- package/packages/@monomind/cli/.claude/commands/mastermind/references/pi-tools.md +28 -0
- package/packages/@monomind/cli/.claude/helpers/mastermind-activate.cjs +53 -0
- package/packages/@monomind/cli/.claude/skills/mastermind/_repeat.md +2 -0
- package/packages/@monomind/cli/.claude/skills/mastermind/runorg.md +14 -0
- package/packages/@monomind/cli/.claude/skills/mastermind/techport.md +5 -5
- package/packages/@monomind/cli/README.md +1 -1
- package/packages/@monomind/cli/dist/src/__tests__/browse-analyzer.test.js +42 -59
- package/packages/@monomind/cli/dist/src/browser/dashboard/server.js +18 -0
- package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.d.ts +17 -0
- package/packages/@monomind/cli/dist/src/commands/agent-lifecycle.js +320 -0
- package/packages/@monomind/cli/dist/src/commands/agent-ops.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/commands/agent-ops.js +329 -0
- package/packages/@monomind/cli/dist/src/commands/agent.js +5 -907
- package/packages/@monomind/cli/dist/src/commands/analyze-ast.d.ts +26 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-ast.js +284 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-boundaries.d.ts +14 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-boundaries.js +295 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-diff.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-diff.js +395 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-graph.d.ts +14 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-graph.js +304 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-imports.d.ts +11 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-imports.js +287 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-symbols.d.ts +14 -0
- package/packages/@monomind/cli/dist/src/commands/analyze-symbols.js +302 -0
- package/packages/@monomind/cli/dist/src/commands/analyze.d.ts +38 -0
- package/packages/@monomind/cli/dist/src/commands/analyze.js +12 -1827
- package/packages/@monomind/cli/dist/src/commands/doctor-env-checks.d.ts +26 -0
- package/packages/@monomind/cli/dist/src/commands/doctor-env-checks.js +189 -0
- package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.d.ts +19 -0
- package/packages/@monomind/cli/dist/src/commands/doctor-project-checks.js +388 -0
- package/packages/@monomind/cli/dist/src/commands/doctor.js +51 -942
- package/packages/@monomind/cli/dist/src/commands/hive-mind-comms.d.ts +11 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-comms.js +242 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-helpers.d.ts +35 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-helpers.js +203 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-ops.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-ops.js +233 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-spawn.d.ts +12 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind-spawn.js +274 -0
- package/packages/@monomind/cli/dist/src/commands/hive-mind.js +10 -1129
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.d.ts +4 -4
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.js +19 -819
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-gaps.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-gaps.js +334 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-routing.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/commands/hooks-coverage-routing.js +399 -0
- package/packages/@monomind/cli/dist/src/commands/index.js +0 -2
- package/packages/@monomind/cli/dist/src/commands/init-subcommands.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/commands/init-subcommands.js +156 -0
- package/packages/@monomind/cli/dist/src/commands/init-upgrade.d.ts +6 -0
- package/packages/@monomind/cli/dist/src/commands/init-upgrade.js +203 -0
- package/packages/@monomind/cli/dist/src/commands/init-wizard.d.ts +6 -0
- package/packages/@monomind/cli/dist/src/commands/init-wizard.js +246 -0
- package/packages/@monomind/cli/dist/src/commands/init.js +6 -623
- package/packages/@monomind/cli/dist/src/commands/memory-admin.d.ts +10 -0
- package/packages/@monomind/cli/dist/src/commands/memory-admin.js +433 -0
- package/packages/@monomind/cli/dist/src/commands/memory-crud.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/commands/memory-crud.js +342 -0
- package/packages/@monomind/cli/dist/src/commands/memory-list.d.ts +10 -0
- package/packages/@monomind/cli/dist/src/commands/memory-list.js +321 -0
- package/packages/@monomind/cli/dist/src/commands/memory-transfer.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/commands/memory-transfer.js +372 -0
- package/packages/@monomind/cli/dist/src/commands/memory.d.ts +6 -0
- package/packages/@monomind/cli/dist/src/commands/memory.js +10 -1441
- package/packages/@monomind/cli/dist/src/commands/neural-core.d.ts +8 -0
- package/packages/@monomind/cli/dist/src/commands/neural-core.js +274 -0
- package/packages/@monomind/cli/dist/src/commands/neural-optimize.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/commands/neural-optimize.js +332 -0
- package/packages/@monomind/cli/dist/src/commands/neural-registry.d.ts +7 -0
- package/packages/@monomind/cli/dist/src/commands/neural-registry.js +290 -0
- package/packages/@monomind/cli/dist/src/commands/neural.js +3 -974
- package/packages/@monomind/cli/dist/src/commands/platforms.js +327 -7
- package/packages/@monomind/cli/dist/src/commands/security-cve.d.ts +6 -0
- package/packages/@monomind/cli/dist/src/commands/security-cve.js +310 -0
- package/packages/@monomind/cli/dist/src/commands/security-misc.d.ts +9 -0
- package/packages/@monomind/cli/dist/src/commands/security-misc.js +293 -0
- package/packages/@monomind/cli/dist/src/commands/security-scan.d.ts +18 -0
- package/packages/@monomind/cli/dist/src/commands/security-scan.js +328 -0
- package/packages/@monomind/cli/dist/src/commands/security.js +3 -958
- package/packages/@monomind/cli/dist/src/commands/session.js +1 -1
- package/packages/@monomind/cli/dist/src/commands/swarm.js +23 -17
- package/packages/@monomind/cli/dist/src/init/executor.js +0 -24
- package/packages/@monomind/cli/dist/src/init/statusline-generator.js +0 -45
- package/packages/@monomind/cli/dist/src/init/types.d.ts +0 -2
- package/packages/@monomind/cli/dist/src/init/types.js +0 -2
- package/packages/@monomind/cli/dist/src/mcp-tools/swarm-tools.js +77 -0
- package/packages/@monomind/cli/dist/src/parser.js +11 -6
- package/packages/@monomind/cli/dist/src/routing/llm-caller.js +1 -2
- package/packages/@monomind/cli/dist/src/ui/dashboard.html +82 -75
- package/packages/@monomind/cli/dist/src/ui/server.mjs +41 -4
- package/packages/@monomind/cli/package.json +3 -4
- package/packages/@monomind/cli/scripts/understand-analyze.mjs +1 -1
- package/packages/@monomind/guidance/README.md +0 -1
- package/packages/@monomind/guidance/package.json +2 -14
- package/scripts/verify-appliance.sh +16 -20
- package/.claude-plugin/README.md +0 -704
- package/.claude-plugin/docs/INSTALLATION.md +0 -258
- package/.claude-plugin/docs/PLUGIN_SUMMARY.md +0 -358
- package/.claude-plugin/docs/QUICKSTART.md +0 -357
- package/.claude-plugin/docs/STRUCTURE.md +0 -122
- package/.claude-plugin/hooks/hooks.json +0 -74
- package/.claude-plugin/marketplace.json +0 -98
- package/.claude-plugin/plugin.json +0 -70
- package/.claude-plugin/scripts/install.sh +0 -234
- package/.claude-plugin/scripts/uninstall.sh +0 -36
- package/.claude-plugin/scripts/verify.sh +0 -102
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security scan commands — code/dep/container scanning and secret detection
|
|
3
|
+
*/
|
|
4
|
+
import { output } from '../output.js';
|
|
5
|
+
import { statSync, readFileSync, readdirSync, realpathSync } from 'fs';
|
|
6
|
+
import { join, resolve, sep, relative } from 'path';
|
|
7
|
+
// ─── Shared secret scanning ─────────────────────────────────────────────────
|
|
8
|
+
export const SECRET_PATTERNS = [
|
|
9
|
+
{ pattern: /['"](?:sk-|sk_live_|sk_test_)[a-zA-Z0-9]{20,}['"]/g, type: 'API Key (Stripe/OpenAI)' },
|
|
10
|
+
{ pattern: /['"]AKIA[A-Z0-9]{16}['"]/g, type: 'AWS Access Key' },
|
|
11
|
+
{ pattern: /['"]ghp_[a-zA-Z0-9]{36}['"]/g, type: 'GitHub Token' },
|
|
12
|
+
{ pattern: /['"]xox[baprs]-[a-zA-Z0-9-]+['"]/g, type: 'Slack Token' },
|
|
13
|
+
{ pattern: /password\s*[:=]\s*['"][^'"]{8,}['"]/gi, type: 'Hardcoded Password' },
|
|
14
|
+
];
|
|
15
|
+
export function findSecretsInDir(dir, depthLimit, baseDir, findings) {
|
|
16
|
+
if (depthLimit <= 0)
|
|
17
|
+
return;
|
|
18
|
+
try {
|
|
19
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
20
|
+
for (const entry of entries) {
|
|
21
|
+
const isDotEnv = /^\.env(\..+)?$/.test(entry.name);
|
|
22
|
+
if ((entry.name.startsWith('.') && !isDotEnv) || entry.name === 'node_modules' || entry.name === 'dist')
|
|
23
|
+
continue;
|
|
24
|
+
const fullPath = join(dir, entry.name);
|
|
25
|
+
if (entry.isDirectory()) {
|
|
26
|
+
findSecretsInDir(fullPath, depthLimit - 1, baseDir, findings);
|
|
27
|
+
}
|
|
28
|
+
else if (entry.isFile() && (/\.(ts|js|json|yml|yaml)$/.test(entry.name) || isDotEnv) && !entry.name.endsWith('.d.ts')) {
|
|
29
|
+
try {
|
|
30
|
+
if (statSync(fullPath).size > 1024 * 1024)
|
|
31
|
+
continue;
|
|
32
|
+
const content = readFileSync(fullPath, 'utf-8');
|
|
33
|
+
const lines = content.split('\n');
|
|
34
|
+
for (let i = 0; i < lines.length; i++) {
|
|
35
|
+
for (const { pattern, type } of SECRET_PATTERNS) {
|
|
36
|
+
pattern.lastIndex = 0;
|
|
37
|
+
let m;
|
|
38
|
+
while ((m = pattern.exec(lines[i])) !== null) {
|
|
39
|
+
findings.push({
|
|
40
|
+
severity: output.warning('HIGH'),
|
|
41
|
+
type: 'Hardcoded Secret',
|
|
42
|
+
location: `${relative(baseDir, fullPath)}:${i + 1}`,
|
|
43
|
+
description: type,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch { /* file read error */ }
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
catch { /* dir read error */ }
|
|
54
|
+
}
|
|
55
|
+
// ─── scan subcommand ─────────────────────────────────────────────────────────
|
|
56
|
+
export const scanCommand = {
|
|
57
|
+
name: 'scan',
|
|
58
|
+
description: 'Run security scan on target (code, dependencies, containers)',
|
|
59
|
+
options: [
|
|
60
|
+
{ name: 'target', short: 't', type: 'string', description: 'Target path or URL to scan', default: '.' },
|
|
61
|
+
{ name: 'depth', short: 'd', type: 'string', description: 'Scan depth: quick, standard, deep', default: 'standard' },
|
|
62
|
+
{ name: 'type', type: 'string', description: 'Scan type: code, deps, container, all', default: 'all' },
|
|
63
|
+
{ name: 'output', short: 'o', type: 'string', description: 'Output format: text, json, sarif', default: 'text' },
|
|
64
|
+
{ name: 'fix', short: 'f', type: 'boolean', description: 'Auto-fix vulnerabilities where possible' },
|
|
65
|
+
],
|
|
66
|
+
examples: [
|
|
67
|
+
{ command: 'monomind security scan -t ./src', description: 'Scan source directory' },
|
|
68
|
+
{ command: 'monomind security scan --depth deep --fix', description: 'Deep scan with auto-fix' },
|
|
69
|
+
],
|
|
70
|
+
action: async (ctx) => {
|
|
71
|
+
const target = ctx.flags.target || '.';
|
|
72
|
+
const depth = ctx.flags.depth || 'standard';
|
|
73
|
+
const scanType = ctx.flags.type || 'all';
|
|
74
|
+
const fix = ctx.flags.fix;
|
|
75
|
+
if (target !== '.') {
|
|
76
|
+
try {
|
|
77
|
+
const resolvedTgt = realpathSync(resolve(target));
|
|
78
|
+
const cwd = realpathSync(process.cwd());
|
|
79
|
+
if (!resolvedTgt.startsWith(cwd + sep) && resolvedTgt !== cwd) {
|
|
80
|
+
output.printError('--target must be within the current working directory');
|
|
81
|
+
return { success: false };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
output.printError(`--target path does not exist or is not accessible: ${target}`);
|
|
86
|
+
return { success: false };
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
output.writeln();
|
|
90
|
+
output.writeln(output.bold('Security Scan'));
|
|
91
|
+
output.writeln(output.dim('─'.repeat(50)));
|
|
92
|
+
const spinner = output.createSpinner({ text: `Scanning ${target}...`, spinner: 'dots' });
|
|
93
|
+
spinner.start();
|
|
94
|
+
const findings = [];
|
|
95
|
+
let criticalCount = 0, highCount = 0, mediumCount = 0, lowCount = 0;
|
|
96
|
+
try {
|
|
97
|
+
const fs = await import('fs');
|
|
98
|
+
const path = await import('path');
|
|
99
|
+
const { execSync } = await import('child_process');
|
|
100
|
+
if (scanType === 'all' || scanType === 'deps') {
|
|
101
|
+
spinner.setText('Checking dependencies with npm audit...');
|
|
102
|
+
try {
|
|
103
|
+
const packageJsonPath = path.resolve(target, 'package.json');
|
|
104
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
105
|
+
let auditResult;
|
|
106
|
+
try {
|
|
107
|
+
auditResult = execSync('npm audit --json', {
|
|
108
|
+
cwd: path.resolve(target),
|
|
109
|
+
encoding: 'utf-8',
|
|
110
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
111
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
112
|
+
timeout: 30_000,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
catch (auditErr) {
|
|
116
|
+
auditResult = auditErr.stdout || '{}';
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
const audit = JSON.parse(auditResult);
|
|
120
|
+
if (audit.vulnerabilities) {
|
|
121
|
+
for (const [pkg, vuln] of Object.entries(audit.vulnerabilities)) {
|
|
122
|
+
const sev = vuln.severity || 'low';
|
|
123
|
+
const firstVia = Array.isArray(vuln.via) ? vuln.via[0] : undefined;
|
|
124
|
+
const title = firstVia && typeof firstVia === 'object' && firstVia.title ? firstVia.title : 'Vulnerability';
|
|
125
|
+
if (sev === 'critical')
|
|
126
|
+
criticalCount++;
|
|
127
|
+
else if (sev === 'high')
|
|
128
|
+
highCount++;
|
|
129
|
+
else if (sev === 'moderate' || sev === 'medium')
|
|
130
|
+
mediumCount++;
|
|
131
|
+
else
|
|
132
|
+
lowCount++;
|
|
133
|
+
findings.push({
|
|
134
|
+
severity: sev === 'critical' ? output.error('CRITICAL') :
|
|
135
|
+
sev === 'high' ? output.warning('HIGH') :
|
|
136
|
+
sev === 'moderate' || sev === 'medium' ? output.warning('MEDIUM') : output.info('LOW'),
|
|
137
|
+
type: 'Dependency CVE',
|
|
138
|
+
location: `package.json:${pkg}`,
|
|
139
|
+
description: title.substring(0, 35),
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
catch { /* JSON parse failed */ }
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch { /* npm audit failed */ }
|
|
148
|
+
}
|
|
149
|
+
if (scanType === 'all' || scanType === 'code') {
|
|
150
|
+
spinner.setText('Scanning for hardcoded secrets...');
|
|
151
|
+
const scanDepth = depth === 'deep' ? 10 : depth === 'standard' ? 5 : 3;
|
|
152
|
+
const prevCount = findings.length;
|
|
153
|
+
findSecretsInDir(path.resolve(target), scanDepth, path.resolve(target), findings);
|
|
154
|
+
highCount += findings.length - prevCount;
|
|
155
|
+
}
|
|
156
|
+
if ((scanType === 'all' || scanType === 'code') && depth !== 'quick') {
|
|
157
|
+
spinner.setText('Analyzing code patterns...');
|
|
158
|
+
const codePatterns = [
|
|
159
|
+
{ pattern: /eval\s*\(/g, type: 'Eval Usage', severity: 'medium', desc: 'eval() can execute arbitrary code' },
|
|
160
|
+
{ pattern: /innerHTML\s*=/g, type: 'innerHTML', severity: 'medium', desc: 'XSS risk with innerHTML' },
|
|
161
|
+
{ pattern: /dangerouslySetInnerHTML/g, type: 'React XSS', severity: 'medium', desc: 'React XSS risk' },
|
|
162
|
+
{ pattern: /child_process.*exec[^S]/g, type: 'Command Injection', severity: 'high', desc: 'Possible command injection' },
|
|
163
|
+
{ pattern: /\$\{.*\}.*sql|sql.*\$\{/gi, type: 'SQL Injection', severity: 'high', desc: 'Possible SQL injection' },
|
|
164
|
+
];
|
|
165
|
+
const scanCodeDir = (dir, depthLimit) => {
|
|
166
|
+
if (depthLimit <= 0)
|
|
167
|
+
return;
|
|
168
|
+
try {
|
|
169
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
170
|
+
for (const entry of entries) {
|
|
171
|
+
if (entry.name.startsWith('.') || entry.name === 'node_modules' || entry.name === 'dist')
|
|
172
|
+
continue;
|
|
173
|
+
const fullPath = path.join(dir, entry.name);
|
|
174
|
+
if (entry.isDirectory()) {
|
|
175
|
+
scanCodeDir(fullPath, depthLimit - 1);
|
|
176
|
+
}
|
|
177
|
+
else if (entry.isFile() && /\.(ts|js|tsx|jsx)$/.test(entry.name) && !entry.name.endsWith('.d.ts')) {
|
|
178
|
+
try {
|
|
179
|
+
if (fs.statSync(fullPath).size > 1024 * 1024)
|
|
180
|
+
continue;
|
|
181
|
+
const content = fs.readFileSync(fullPath, 'utf-8');
|
|
182
|
+
const lines = content.split('\n');
|
|
183
|
+
for (let i = 0; i < lines.length; i++) {
|
|
184
|
+
for (const { pattern, type, severity, desc } of codePatterns) {
|
|
185
|
+
pattern.lastIndex = 0;
|
|
186
|
+
let m;
|
|
187
|
+
while ((m = pattern.exec(lines[i])) !== null) {
|
|
188
|
+
if (severity === 'high')
|
|
189
|
+
highCount++;
|
|
190
|
+
else
|
|
191
|
+
mediumCount++;
|
|
192
|
+
findings.push({
|
|
193
|
+
severity: severity === 'high' ? output.warning('HIGH') : output.warning('MEDIUM'),
|
|
194
|
+
type,
|
|
195
|
+
location: `${path.relative(target, fullPath)}:${i + 1}`,
|
|
196
|
+
description: desc,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
catch { /* file read error */ }
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
catch { /* dir read error */ }
|
|
207
|
+
};
|
|
208
|
+
const scanDepth = depth === 'deep' ? 10 : 5;
|
|
209
|
+
scanCodeDir(path.resolve(target), scanDepth);
|
|
210
|
+
}
|
|
211
|
+
spinner.succeed('Scan complete');
|
|
212
|
+
output.writeln();
|
|
213
|
+
if (findings.length > 0) {
|
|
214
|
+
output.printTable({
|
|
215
|
+
columns: [
|
|
216
|
+
{ key: 'severity', header: 'Severity', width: 12 },
|
|
217
|
+
{ key: 'type', header: 'Type', width: 18 },
|
|
218
|
+
{ key: 'location', header: 'Location', width: 25 },
|
|
219
|
+
{ key: 'description', header: 'Description', width: 35 },
|
|
220
|
+
],
|
|
221
|
+
data: findings.slice(0, 20),
|
|
222
|
+
});
|
|
223
|
+
if (findings.length > 20)
|
|
224
|
+
output.writeln(output.dim(`... and ${findings.length - 20} more issues`));
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
output.writeln(output.success('No security issues found!'));
|
|
228
|
+
}
|
|
229
|
+
output.writeln();
|
|
230
|
+
output.printBox([
|
|
231
|
+
`Target: ${target}`,
|
|
232
|
+
`Depth: ${depth}`,
|
|
233
|
+
`Type: ${scanType}`,
|
|
234
|
+
``,
|
|
235
|
+
`Critical: ${criticalCount} High: ${highCount} Medium: ${mediumCount} Low: ${lowCount}`,
|
|
236
|
+
`Total Issues: ${findings.length}`,
|
|
237
|
+
].join('\n'), 'Scan Summary');
|
|
238
|
+
if (fix && criticalCount + highCount > 0) {
|
|
239
|
+
const resolvedTarget = realpathSync(path.resolve(target));
|
|
240
|
+
const cwd = realpathSync(process.cwd());
|
|
241
|
+
if (!resolvedTarget.startsWith(cwd + path.sep) && resolvedTarget !== cwd) {
|
|
242
|
+
output.writeln();
|
|
243
|
+
output.printError('--fix is only allowed when --target is within the current working directory');
|
|
244
|
+
return { success: false };
|
|
245
|
+
}
|
|
246
|
+
output.writeln();
|
|
247
|
+
const fixSpinner = output.createSpinner({ text: 'Attempting to fix vulnerabilities...', spinner: 'dots' });
|
|
248
|
+
fixSpinner.start();
|
|
249
|
+
try {
|
|
250
|
+
try {
|
|
251
|
+
execSync('npm audit fix', { cwd: resolvedTarget, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
252
|
+
}
|
|
253
|
+
catch { /* npm audit fix may exit non-zero */ }
|
|
254
|
+
fixSpinner.succeed('Applied available fixes (run scan again to verify)');
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
fixSpinner.fail('Some fixes could not be applied automatically');
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return { success: findings.length === 0 || (criticalCount === 0 && highCount === 0) };
|
|
261
|
+
}
|
|
262
|
+
catch (error) {
|
|
263
|
+
spinner.fail('Scan failed');
|
|
264
|
+
output.printError(`Error: ${error}`);
|
|
265
|
+
return { success: false };
|
|
266
|
+
}
|
|
267
|
+
},
|
|
268
|
+
};
|
|
269
|
+
// ─── secrets subcommand ──────────────────────────────────────────────────────
|
|
270
|
+
export const secretsCommand = {
|
|
271
|
+
name: 'secrets',
|
|
272
|
+
description: 'Detect hardcoded secrets in codebase',
|
|
273
|
+
options: [
|
|
274
|
+
{ name: 'path', short: 'p', type: 'string', description: 'Path to scan', default: '.' },
|
|
275
|
+
{ name: 'depth', short: 'd', type: 'string', description: 'Scan depth: quick, standard, deep', default: 'standard' },
|
|
276
|
+
],
|
|
277
|
+
examples: [
|
|
278
|
+
{ command: 'monomind security secrets', description: 'Scan current directory for secrets' },
|
|
279
|
+
{ command: 'monomind security secrets -p ./src --depth deep', description: 'Deep scan of src directory' },
|
|
280
|
+
],
|
|
281
|
+
action: async (ctx) => {
|
|
282
|
+
const targetPath = ctx.flags.path || '.';
|
|
283
|
+
const depth = ctx.flags.depth || 'standard';
|
|
284
|
+
if (targetPath !== '.') {
|
|
285
|
+
try {
|
|
286
|
+
const resolvedTgt = realpathSync(resolve(targetPath));
|
|
287
|
+
const cwd = realpathSync(process.cwd());
|
|
288
|
+
if (!resolvedTgt.startsWith(cwd + sep) && resolvedTgt !== cwd) {
|
|
289
|
+
output.printError('--path must be within the current working directory');
|
|
290
|
+
return { success: false };
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
catch {
|
|
294
|
+
output.printError(`--path does not exist or is not accessible: ${targetPath}`);
|
|
295
|
+
return { success: false };
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
output.writeln();
|
|
299
|
+
output.writeln(output.bold('Secret Detection'));
|
|
300
|
+
output.writeln(output.dim('─'.repeat(50)));
|
|
301
|
+
const spinner = output.createSpinner({ text: `Scanning ${targetPath}...`, spinner: 'dots' });
|
|
302
|
+
spinner.start();
|
|
303
|
+
const findings = [];
|
|
304
|
+
const scanDepth = depth === 'deep' ? 10 : depth === 'standard' ? 5 : 3;
|
|
305
|
+
findSecretsInDir(resolve(targetPath), scanDepth, resolve(targetPath), findings);
|
|
306
|
+
spinner.succeed('Scan complete');
|
|
307
|
+
output.writeln();
|
|
308
|
+
if (findings.length === 0) {
|
|
309
|
+
output.writeln(output.success('No secrets found.'));
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
output.printTable({
|
|
313
|
+
columns: [
|
|
314
|
+
{ key: 'severity', header: 'Severity', width: 12 },
|
|
315
|
+
{ key: 'description', header: 'Description', width: 25 },
|
|
316
|
+
{ key: 'location', header: 'Location', width: 40 },
|
|
317
|
+
],
|
|
318
|
+
data: findings.slice(0, 20),
|
|
319
|
+
});
|
|
320
|
+
if (findings.length > 20)
|
|
321
|
+
output.writeln(output.dim(`... and ${findings.length - 20} more`));
|
|
322
|
+
}
|
|
323
|
+
output.writeln();
|
|
324
|
+
output.writeln(output.bold('Summary: ') + `${findings.length} secret(s) found in ${targetPath}`);
|
|
325
|
+
return { success: findings.length === 0 };
|
|
326
|
+
},
|
|
327
|
+
};
|
|
328
|
+
//# sourceMappingURL=security-scan.js.map
|