cyberaudit-skill 3.1.5 ā 3.2.0
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/README.md +84 -25
- package/dist/cli.d.ts +10 -0
- package/dist/cli.js +552 -172
- package/dist/cli.test.d.ts +1 -0
- package/dist/cli.test.js +100 -0
- package/dist/mcp-server.d.ts +135 -1
- package/dist/mcp-server.js +185 -96
- package/dist/mcp-server.test.d.ts +1 -0
- package/dist/mcp-server.test.js +77 -0
- package/dist/report/generator.d.ts +2 -0
- package/dist/report/generator.js +260 -0
- package/dist/report/generator.test.d.ts +1 -0
- package/dist/report/generator.test.js +73 -0
- package/dist/report/types.d.ts +31 -0
- package/dist/report/types.js +1 -0
- package/dist/scanners/secrets.d.ts +32 -0
- package/dist/scanners/secrets.js +437 -0
- package/dist/scanners/secrets.test.d.ts +1 -0
- package/dist/scanners/secrets.test.js +96 -0
- package/dist/scanners/types.d.ts +22 -0
- package/dist/scanners/types.js +1 -0
- package/dist/scanners/web.d.ts +37 -0
- package/dist/scanners/web.js +327 -0
- package/dist/scanners/web.test.d.ts +1 -0
- package/dist/scanners/web.test.js +67 -0
- package/package.json +15 -3
- package/skills/cyberaudit/AGENT-BOOT.md +1 -1
- package/skills/cyberaudit/COMMANDS.md +11 -1
- package/skills/cyberaudit/INSTALL.md +7 -6
- package/skills/cyberaudit/README.md +20 -5
- package/skills/cyberaudit/SKILL.md +20 -5
- package/skills/cyberaudit/cloud/CLOUD-CHECKLIST.md +85 -0
- package/skills/cyberaudit/cloud/CLOUD-PHILOSOPHY.md +91 -0
- package/skills/cyberaudit/cloud/CLOUD-REMEDIATION-LIBRARY.md +145 -0
- package/skills/cyberaudit/commands/audit-angular.md +11 -0
- package/skills/cyberaudit/commands/audit-api-bola.md +11 -0
- package/skills/cyberaudit/commands/audit-api-bopla.md +11 -0
- package/skills/cyberaudit/commands/audit-api-graphql.md +11 -0
- package/skills/cyberaudit/commands/audit-api-inventory.md +11 -0
- package/skills/cyberaudit/commands/audit-api-rate-limit.md +11 -0
- package/skills/cyberaudit/commands/audit-api-rest.md +11 -0
- package/skills/cyberaudit/commands/audit-api-third-party.md +11 -0
- package/skills/cyberaudit/commands/audit-api-ws.md +11 -0
- package/skills/cyberaudit/commands/audit-auth-api.md +11 -0
- package/skills/cyberaudit/commands/audit-auth-mobile.md +11 -0
- package/skills/cyberaudit/commands/audit-auth.md +12 -0
- package/skills/cyberaudit/commands/audit-binary.md +11 -0
- package/skills/cyberaudit/commands/audit-cis.md +11 -0
- package/skills/cyberaudit/commands/audit-cloud-iam.md +11 -0
- package/skills/cyberaudit/commands/audit-cloud-network.md +11 -0
- package/skills/cyberaudit/commands/audit-cloud-s3.md +11 -0
- package/skills/cyberaudit/commands/audit-cloud.md +10 -0
- package/skills/cyberaudit/commands/audit-cors.md +11 -0
- package/skills/cyberaudit/commands/audit-crypto-mobile.md +11 -0
- package/skills/cyberaudit/commands/audit-crypto.md +10 -0
- package/skills/cyberaudit/commands/audit-csrf.md +11 -0
- package/skills/cyberaudit/commands/audit-deeplinks.md +11 -0
- package/skills/cyberaudit/commands/audit-deps.md +11 -0
- package/skills/cyberaudit/commands/audit-deserial.md +11 -0
- package/skills/cyberaudit/commands/audit-exec.md +11 -0
- package/skills/cyberaudit/commands/audit-expo.md +11 -0
- package/skills/cyberaudit/commands/audit-express.md +11 -0
- package/skills/cyberaudit/commands/audit-flutter.md +11 -0
- package/skills/cyberaudit/commands/audit-headers.md +11 -0
- package/skills/cyberaudit/commands/audit-hipaa.md +11 -0
- package/skills/cyberaudit/commands/audit-idor.md +11 -0
- package/skills/cyberaudit/commands/audit-injection.md +12 -0
- package/skills/cyberaudit/commands/audit-ionic.md +11 -0
- package/skills/cyberaudit/commands/audit-laravel.md +11 -0
- package/skills/cyberaudit/commands/audit-logic.md +11 -0
- package/skills/cyberaudit/commands/audit-masvs.md +11 -0
- package/skills/cyberaudit/commands/audit-nestjs.md +11 -0
- package/skills/cyberaudit/commands/audit-network.md +11 -0
- package/skills/cyberaudit/commands/audit-nextjs.md +11 -0
- package/skills/cyberaudit/commands/audit-owasp.md +11 -0
- package/skills/cyberaudit/commands/audit-pci.md +11 -0
- package/skills/cyberaudit/commands/audit-permissions.md +11 -0
- package/skills/cyberaudit/commands/audit-react-native.md +11 -0
- package/skills/cyberaudit/commands/audit-react.md +11 -0
- package/skills/cyberaudit/commands/audit-rgpd.md +11 -0
- package/skills/cyberaudit/commands/audit-runtime.md +11 -0
- package/skills/cyberaudit/commands/audit-secrets.md +11 -0
- package/skills/cyberaudit/commands/audit-ssrf.md +11 -0
- package/skills/cyberaudit/commands/audit-storage.md +11 -0
- package/skills/cyberaudit/commands/audit-vue.md +11 -0
- package/skills/cyberaudit/commands/audit-xss.md +11 -0
- package/skills/cyberaudit/commands/audit-xxe.md +11 -0
- package/skills/cyberaudit/reports/EXECUTIVE-SUMMARY-TEMPLATE.md +1 -1
- package/skills/cyberaudit/reports/REPORT-TEMPLATE-CLOUD.md +103 -0
- package/skills/cyberaudit/reports/REPORT-TEMPLATE-MOBILE.md +2 -2
- package/skills/cyberaudit/reports/REPORT-TEMPLATE-WEB.md +2 -2
package/dist/cli.js
CHANGED
|
@@ -1,273 +1,653 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from "commander";
|
|
3
|
-
import { existsSync, mkdirSync, cpSync, readdirSync, writeFileSync, readFileSync, rmSync, unlinkSync } from "fs";
|
|
4
|
-
import { createInterface } from "readline";
|
|
3
|
+
import { existsSync, mkdirSync, cpSync, readdirSync, writeFileSync, readFileSync, rmSync, unlinkSync, copyFileSync } from "fs";
|
|
5
4
|
import { homedir } from "os";
|
|
6
|
-
import { join, dirname } from "path";
|
|
5
|
+
import { join, dirname, resolve, relative } from "path";
|
|
7
6
|
import { fileURLToPath } from "url";
|
|
7
|
+
import { startMcpServer as startMcpServerImpl } from "./mcp-server.js";
|
|
8
|
+
import { scanSecrets, formatFindingsText } from "./scanners/secrets.js";
|
|
9
|
+
import { scanWeb, formatWebFindingsText } from "./scanners/web.js";
|
|
8
10
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
9
11
|
const PKG_ROOT = join(__dirname, "..");
|
|
10
12
|
const PKG = JSON.parse(readFileSync(join(PKG_ROOT, "package.json"), "utf-8"));
|
|
11
13
|
const SKILL_SRC = join(PKG_ROOT, "skills", "cyberaudit");
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
const COMMANDS_SRC = join(SKILL_SRC, "commands");
|
|
15
|
+
const VERSION = PKG.version || "3.1.5";
|
|
16
|
+
const MAIN_COMMANDS = [
|
|
17
|
+
'audit.md',
|
|
18
|
+
'audit-web.md',
|
|
19
|
+
'audit-mobile.md',
|
|
20
|
+
'audit-api.md',
|
|
21
|
+
'audit-cloud.md',
|
|
22
|
+
'audit-quick.md',
|
|
23
|
+
'audit-report.md',
|
|
24
|
+
'audit-help.md',
|
|
25
|
+
];
|
|
26
|
+
const H = homedir();
|
|
27
|
+
function j(...parts) {
|
|
28
|
+
return join(H, ...parts);
|
|
29
|
+
}
|
|
30
|
+
export const AGENT_CONFIG = {
|
|
31
|
+
"opencode": {
|
|
32
|
+
displayName: "OpenCode",
|
|
33
|
+
skillPaths: [j(".agents", "skills", "cyberaudit"), j(".config", "opencode", "skills", "cyberaudit")],
|
|
34
|
+
commandPaths: [j(".config", "opencode", "commands")],
|
|
35
|
+
},
|
|
36
|
+
"claude-code": {
|
|
37
|
+
displayName: "Claude Code",
|
|
38
|
+
skillPaths: [j(".claude", "skills", "cyberaudit")],
|
|
39
|
+
commandPaths: [j(".claude", "commands")],
|
|
40
|
+
},
|
|
41
|
+
"cursor": {
|
|
42
|
+
displayName: "Cursor",
|
|
43
|
+
skillPaths: [j(".cursor", "skills", "cyberaudit")],
|
|
44
|
+
commandPaths: [j(".cursor", "commands")],
|
|
45
|
+
mcpPath: j(".cursor", "mcp.json"),
|
|
46
|
+
},
|
|
47
|
+
"windsurf": {
|
|
48
|
+
displayName: "Windsurf",
|
|
49
|
+
skillPaths: [j(".windsurf", "skills", "cyberaudit"), j(".codeium", "windsurf", "skills", "cyberaudit")],
|
|
50
|
+
commandPaths: [j(".windsurf", "commands")],
|
|
51
|
+
workflowPaths: [j(".windsurf", "workflows")],
|
|
52
|
+
},
|
|
53
|
+
"antigravity": {
|
|
54
|
+
displayName: "Antigravity",
|
|
55
|
+
skillPaths: [j(".gemini", "antigravity", "skills", "cyberaudit"), j(".agent", "skills", "cyberaudit")],
|
|
56
|
+
commandPaths: [j(".gemini", "antigravity", "commands"), j(".agent", "commands")],
|
|
57
|
+
workflowPaths: [j(".agent", "workflows")],
|
|
58
|
+
},
|
|
59
|
+
"antigravity-cli": {
|
|
60
|
+
displayName: "Antigravity CLI",
|
|
61
|
+
skillPaths: [j(".gemini", "antigravity-cli", "skills", "cyberaudit")],
|
|
62
|
+
commandPaths: [j(".gemini", "antigravity-cli", "commands")],
|
|
63
|
+
},
|
|
64
|
+
"copilot": {
|
|
65
|
+
displayName: "GitHub Copilot",
|
|
66
|
+
skillPaths: [j(".copilot", "skills", "cyberaudit"), j(".github", "copilot", "skills", "cyberaudit")],
|
|
67
|
+
commandPaths: [j(".copilot", "commands"), j(".github", "copilot", "commands")],
|
|
68
|
+
},
|
|
69
|
+
"kiro": {
|
|
70
|
+
displayName: "Kiro",
|
|
71
|
+
skillPaths: [j(".kiro", "skills", "cyberaudit")],
|
|
72
|
+
commandPaths: [j(".kiro", "commands")],
|
|
73
|
+
},
|
|
74
|
+
"codex": {
|
|
75
|
+
displayName: "Codex CLI",
|
|
76
|
+
skillPaths: [j(".codex", "skills", "cyberaudit")],
|
|
77
|
+
commandPaths: [j(".codex", "commands")],
|
|
78
|
+
},
|
|
79
|
+
"qoder": {
|
|
80
|
+
displayName: "Qoder",
|
|
81
|
+
skillPaths: [j(".qoder", "skills", "cyberaudit")],
|
|
82
|
+
commandPaths: [j(".qoder", "commands")],
|
|
83
|
+
},
|
|
84
|
+
"roocode": {
|
|
85
|
+
displayName: "Roo Code",
|
|
86
|
+
skillPaths: [j(".roo", "skills", "cyberaudit"), j(".roocode", "skills", "cyberaudit")],
|
|
87
|
+
commandPaths: [j(".roo", "commands"), j(".roocode", "commands")],
|
|
88
|
+
},
|
|
89
|
+
"gemini": {
|
|
90
|
+
displayName: "Gemini CLI",
|
|
91
|
+
skillPaths: [j(".gemini", "skills", "cyberaudit")],
|
|
92
|
+
commandPaths: [j(".gemini", "commands")],
|
|
93
|
+
},
|
|
94
|
+
"trae": {
|
|
95
|
+
displayName: "Trae",
|
|
96
|
+
skillPaths: [j(".trae", "skills", "cyberaudit")],
|
|
97
|
+
commandPaths: [j(".trae", "commands")],
|
|
98
|
+
},
|
|
99
|
+
"continue": {
|
|
100
|
+
displayName: "Continue",
|
|
101
|
+
skillPaths: [j(".continue", "skills", "cyberaudit")],
|
|
102
|
+
commandPaths: [j(".continue", "commands")],
|
|
103
|
+
},
|
|
104
|
+
"codebuddy": {
|
|
105
|
+
displayName: "CodeBuddy",
|
|
106
|
+
skillPaths: [j(".codebuddy", "skills", "cyberaudit")],
|
|
107
|
+
commandPaths: [j(".codebuddy", "commands")],
|
|
108
|
+
},
|
|
109
|
+
"droid": {
|
|
110
|
+
displayName: "Droid (Factory)",
|
|
111
|
+
skillPaths: [j(".factory", "skills", "cyberaudit"), j(".factory", "droid", "skills", "cyberaudit")],
|
|
112
|
+
commandPaths: [j(".factory", "commands"), j(".factory", "droid", "commands")],
|
|
113
|
+
},
|
|
114
|
+
"kilocode": {
|
|
115
|
+
displayName: "KiloCode",
|
|
116
|
+
skillPaths: [j(".kilocode", "skills", "cyberaudit"), j(".kilo", "skills", "cyberaudit")],
|
|
117
|
+
commandPaths: [j(".kilocode", "commands"), j(".kilo", "commands")],
|
|
118
|
+
},
|
|
119
|
+
"warp": {
|
|
120
|
+
displayName: "Warp",
|
|
121
|
+
skillPaths: [j(".warp", "skills", "cyberaudit")],
|
|
122
|
+
commandPaths: [j(".warp", "commands")],
|
|
123
|
+
},
|
|
124
|
+
"augment": {
|
|
125
|
+
displayName: "Augment",
|
|
126
|
+
skillPaths: [j(".augment", "skills", "cyberaudit")],
|
|
127
|
+
commandPaths: [j(".augment", "commands")],
|
|
128
|
+
},
|
|
129
|
+
"codewhale": {
|
|
130
|
+
displayName: "CodeWhale",
|
|
131
|
+
skillPaths: [j(".codewhale", "skills", "cyberaudit")],
|
|
132
|
+
commandPaths: [j(".codewhale", "commands")],
|
|
133
|
+
},
|
|
134
|
+
"cline": {
|
|
135
|
+
displayName: "Cline",
|
|
136
|
+
skillPaths: [j(".cline", "skills", "cyberaudit")],
|
|
137
|
+
commandPaths: [j(".cline", "commands")],
|
|
138
|
+
},
|
|
139
|
+
"aider": {
|
|
140
|
+
displayName: "Aider",
|
|
141
|
+
skillPaths: [j(".aider", "skills", "cyberaudit")],
|
|
142
|
+
commandPaths: [j(".aider", "commands")],
|
|
143
|
+
},
|
|
20
144
|
};
|
|
145
|
+
export const AGENT_TARGETS = Object.fromEntries(Object.entries(AGENT_CONFIG).map(([k, v]) => [k, v.skillPaths]));
|
|
146
|
+
function isSafeInsideHome(p) {
|
|
147
|
+
const home = resolve(H);
|
|
148
|
+
const resolved = resolve(p);
|
|
149
|
+
return resolved.startsWith(home);
|
|
150
|
+
}
|
|
151
|
+
function isSafeInsideCwd(p, cwd) {
|
|
152
|
+
return resolve(p).startsWith(resolve(cwd));
|
|
153
|
+
}
|
|
154
|
+
function isSafeSkillPath(p, mode = 'global', cwd = process.cwd()) {
|
|
155
|
+
if (mode === 'local') {
|
|
156
|
+
return isSafeInsideCwd(p, cwd) && p.includes("cyberaudit");
|
|
157
|
+
}
|
|
158
|
+
return isSafeInsideHome(p) && p.includes("cyberaudit");
|
|
159
|
+
}
|
|
160
|
+
function isSafeCommandPath(p, mode = 'global', cwd = process.cwd()) {
|
|
161
|
+
const inside = mode === 'local' ? isSafeInsideCwd(p, cwd) : isSafeInsideHome(p);
|
|
162
|
+
if (!inside)
|
|
163
|
+
return false;
|
|
164
|
+
return p.includes("commands") || p.includes("workflows") || p.includes("skills");
|
|
165
|
+
}
|
|
166
|
+
function toLocalPath(globalPath, cwd) {
|
|
167
|
+
// ~/.claude/skills/cyberaudit -> ./.claude/skills/cyberaudit
|
|
168
|
+
try {
|
|
169
|
+
const rel = relative(H, globalPath);
|
|
170
|
+
if (rel.startsWith('..'))
|
|
171
|
+
return join(cwd, globalPath.split('/').pop() || '');
|
|
172
|
+
return join(cwd, rel);
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return join(cwd, '.' + globalPath.split('/').pop());
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
function getPathsForMode(agent, mode, cwd = process.cwd()) {
|
|
179
|
+
const cfg = AGENT_CONFIG[agent];
|
|
180
|
+
if (mode === 'global')
|
|
181
|
+
return cfg;
|
|
182
|
+
// local: transform each path to be relative to cwd
|
|
183
|
+
return {
|
|
184
|
+
displayName: cfg.displayName,
|
|
185
|
+
skillPaths: cfg.skillPaths.map(p => toLocalPath(p, cwd)),
|
|
186
|
+
commandPaths: cfg.commandPaths.map(p => toLocalPath(p, cwd)),
|
|
187
|
+
workflowPaths: cfg.workflowPaths?.map(p => toLocalPath(p, cwd)),
|
|
188
|
+
mcpPath: cfg.mcpPath ? toLocalPath(cfg.mcpPath, cwd) : undefined,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
21
191
|
function detectInstalledAgents() {
|
|
22
192
|
const found = [];
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
193
|
+
for (const [agent, cfg] of Object.entries(AGENT_CONFIG)) {
|
|
194
|
+
const checkPaths = [
|
|
195
|
+
...cfg.skillPaths.map(p => dirname(p)),
|
|
196
|
+
...cfg.commandPaths.map(p => p),
|
|
197
|
+
...(cfg.workflowPaths || []).map(p => p),
|
|
198
|
+
...(cfg.mcpPath ? [dirname(cfg.mcpPath)] : []),
|
|
199
|
+
];
|
|
200
|
+
if (checkPaths.some(parent => existsSync(parent))) {
|
|
28
201
|
found.push(agent);
|
|
29
202
|
}
|
|
30
203
|
}
|
|
31
|
-
if (existsSync(
|
|
204
|
+
if (existsSync(j(".agents")) && !found.includes("opencode"))
|
|
205
|
+
found.push("opencode");
|
|
206
|
+
if (existsSync(j(".claude")) && !found.includes("claude-code"))
|
|
207
|
+
found.push("claude-code");
|
|
208
|
+
if (existsSync(j(".cursor")) && !found.includes("cursor"))
|
|
32
209
|
found.push("cursor");
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
210
|
+
return [...new Set(found)];
|
|
211
|
+
}
|
|
212
|
+
function detectByHomeFolders() {
|
|
213
|
+
const mapping = {
|
|
214
|
+
".agents": "opencode",
|
|
215
|
+
".claude": "claude-code",
|
|
216
|
+
".cursor": "cursor",
|
|
217
|
+
".windsurf": "windsurf",
|
|
218
|
+
".codeium": "windsurf",
|
|
219
|
+
".agent": "antigravity",
|
|
220
|
+
".copilot": "copilot",
|
|
221
|
+
".kiro": "kiro",
|
|
222
|
+
".codex": "codex",
|
|
223
|
+
".qoder": "qoder",
|
|
224
|
+
".roo": "roocode",
|
|
225
|
+
".roocode": "roocode",
|
|
226
|
+
".gemini": "gemini",
|
|
227
|
+
".trae": "trae",
|
|
228
|
+
".continue": "continue",
|
|
229
|
+
".codebuddy": "codebuddy",
|
|
230
|
+
".factory": "droid",
|
|
231
|
+
".kilocode": "kilocode",
|
|
232
|
+
".kilo": "kilocode",
|
|
233
|
+
".warp": "warp",
|
|
234
|
+
".augment": "augment",
|
|
235
|
+
".codewhale": "codewhale",
|
|
236
|
+
".cline": "cline",
|
|
237
|
+
".aider": "aider",
|
|
238
|
+
};
|
|
239
|
+
const found = [];
|
|
240
|
+
for (const [folder, agent] of Object.entries(mapping)) {
|
|
241
|
+
if (existsSync(j(folder))) {
|
|
242
|
+
if (!found.includes(agent))
|
|
243
|
+
found.push(agent);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
if (existsSync(j(".config", "opencode")) && !found.includes("opencode"))
|
|
247
|
+
found.push("opencode");
|
|
39
248
|
return found;
|
|
40
249
|
}
|
|
41
|
-
function installDir(src, dst) {
|
|
42
|
-
if (existsSync(
|
|
250
|
+
function installDir(src, dst, mode = 'global', cwd = process.cwd(), allowCommandPath = false) {
|
|
251
|
+
if (!existsSync(src))
|
|
252
|
+
throw new Error(`Source not found: ${src}`);
|
|
253
|
+
const safeCheck = allowCommandPath
|
|
254
|
+
? (p) => isSafeCommandPath(p, mode, cwd)
|
|
255
|
+
: (p) => isSafeSkillPath(p, mode, cwd);
|
|
256
|
+
if (!safeCheck(dst)) {
|
|
257
|
+
console.error(`ā Unsafe destination blocked: ${dst} (mode=${mode})`);
|
|
258
|
+
throw new Error(`Unsafe path: ${dst}`);
|
|
259
|
+
}
|
|
260
|
+
if (existsSync(dst)) {
|
|
261
|
+
console.log(` ā»ļø Cleaning previous install at ${dst}`);
|
|
43
262
|
rmSync(dst, { recursive: true, force: true });
|
|
263
|
+
}
|
|
44
264
|
mkdirSync(dirname(dst), { recursive: true });
|
|
45
265
|
cpSync(src, dst, { recursive: true });
|
|
46
266
|
}
|
|
47
|
-
function
|
|
267
|
+
function installCommands(commandFiles, targetDir, dryRun, agentName, mode = 'global', cwd = process.cwd()) {
|
|
268
|
+
if (!isSafeCommandPath(targetDir, mode, cwd)) {
|
|
269
|
+
console.error(` ā Unsafe command destination blocked: ${targetDir}`);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
if (dryRun) {
|
|
273
|
+
console.log(` ā Would install ${commandFiles.length} commands to ${targetDir} (--dry-run) [${mode}]`);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
mkdirSync(targetDir, { recursive: true });
|
|
277
|
+
try {
|
|
278
|
+
if (existsSync(targetDir)) {
|
|
279
|
+
const stale = readdirSync(targetDir).filter(f => typeof f === "string" && f.startsWith("audit") && f.endsWith(".md"));
|
|
280
|
+
if (stale.length > 0) {
|
|
281
|
+
console.log(` ā»ļø Removing ${stale.length} stale audit commands from ${agentName}: ${stale.join(", ")}`);
|
|
282
|
+
for (const f of stale) {
|
|
283
|
+
try {
|
|
284
|
+
unlinkSync(join(targetDir, f));
|
|
285
|
+
}
|
|
286
|
+
catch { }
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
catch { }
|
|
292
|
+
for (const fileName of commandFiles) {
|
|
293
|
+
const src = join(COMMANDS_SRC, fileName);
|
|
294
|
+
if (!existsSync(src))
|
|
295
|
+
continue;
|
|
296
|
+
const dst = join(targetDir, fileName);
|
|
297
|
+
try {
|
|
298
|
+
mkdirSync(dirname(dst), { recursive: true });
|
|
299
|
+
copyFileSync(src, dst);
|
|
300
|
+
}
|
|
301
|
+
catch (e) {
|
|
302
|
+
console.error(` ā Failed to copy command ${fileName} to ${targetDir}: ${e.message}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
console.log(` ā Commands (${commandFiles.length}) installed to ${agentName} (${targetDir}) [${mode}] ā "/" will show ${commandFiles.length} main commands`);
|
|
306
|
+
}
|
|
307
|
+
function installSkillForAgent(agent, dryRun, mode = 'global', cwd = process.cwd()) {
|
|
308
|
+
const cfg = getPathsForMode(agent, mode, cwd);
|
|
309
|
+
if (!cfg)
|
|
310
|
+
return false;
|
|
48
311
|
if (!existsSync(SKILL_SRC)) {
|
|
49
312
|
console.error(`ā Skill source not found at ${SKILL_SRC}.`);
|
|
50
313
|
return false;
|
|
51
314
|
}
|
|
52
315
|
if (dryRun) {
|
|
53
|
-
|
|
316
|
+
for (const sp of cfg.skillPaths)
|
|
317
|
+
console.log(` ā Would install skill to ${sp} (--dry-run) [${mode}]`);
|
|
318
|
+
for (const cp of cfg.commandPaths)
|
|
319
|
+
console.log(` ā Would install ${MAIN_COMMANDS.length} main commands to ${cp} (--dry-run) [${mode}]`);
|
|
320
|
+
if (cfg.workflowPaths)
|
|
321
|
+
for (const wp of cfg.workflowPaths)
|
|
322
|
+
console.log(` ā Would install ${MAIN_COMMANDS.length} workflows to ${wp} (--dry-run) [${mode}]`);
|
|
323
|
+
if (cfg.mcpPath)
|
|
324
|
+
console.log(` ā Would configure MCP at ${cfg.mcpPath} (--dry-run) [${mode}]`);
|
|
54
325
|
return true;
|
|
55
326
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
327
|
+
let ok = false;
|
|
328
|
+
for (const skillPath of cfg.skillPaths) {
|
|
329
|
+
try {
|
|
330
|
+
installDir(SKILL_SRC, skillPath, mode, cwd, false);
|
|
331
|
+
console.log(` ā Skill installed at ${skillPath} [${mode}]`);
|
|
332
|
+
ok = true;
|
|
333
|
+
}
|
|
334
|
+
catch (e) {
|
|
335
|
+
console.error(` ā Failed skill install at ${skillPath}: ${e.message}`);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
const isOpenCode = agent === "opencode";
|
|
339
|
+
const commandsToInstall = isOpenCode ? readdirSync(COMMANDS_SRC).filter(f => f.endsWith('.md')) : MAIN_COMMANDS;
|
|
340
|
+
for (const cmdPath of cfg.commandPaths) {
|
|
341
|
+
try {
|
|
342
|
+
installCommands(commandsToInstall, cmdPath, dryRun, agent, mode, cwd);
|
|
343
|
+
ok = true;
|
|
344
|
+
}
|
|
345
|
+
catch (e) {
|
|
346
|
+
console.error(` ā Failed commands install at ${cmdPath}: ${e.message}`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (cfg.workflowPaths) {
|
|
350
|
+
for (const wfPath of cfg.workflowPaths) {
|
|
351
|
+
try {
|
|
352
|
+
installCommands(commandsToInstall, wfPath, dryRun, agent, mode, cwd);
|
|
353
|
+
ok = true;
|
|
74
354
|
}
|
|
75
|
-
|
|
76
|
-
cpSync(cmdSrc, join(opencodeConf, "commands"), { recursive: true });
|
|
77
|
-
console.log(` ā Commands installed to opencode`);
|
|
355
|
+
catch { }
|
|
78
356
|
}
|
|
79
|
-
// Wipe + re-copy opencode skill dir
|
|
80
|
-
installDir(SKILL_SRC, join(opencodeConf, "skills", "cyberaudit"));
|
|
81
|
-
console.log(` ā Skill installed to opencode`);
|
|
82
357
|
}
|
|
83
|
-
|
|
358
|
+
if (agent === "cursor") {
|
|
359
|
+
const cursorOk = installForCursor(dryRun, mode, cwd);
|
|
360
|
+
ok = ok || cursorOk;
|
|
361
|
+
}
|
|
362
|
+
return ok;
|
|
84
363
|
}
|
|
85
|
-
function installForCursor(dryRun) {
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
364
|
+
function installForCursor(dryRun, mode = 'global', cwd = process.cwd()) {
|
|
365
|
+
const baseDir = mode === 'local' ? cwd : H;
|
|
366
|
+
const cursorDir = mode === 'local' ? join(cwd, ".cursor") : join(H, ".cursor");
|
|
367
|
+
// For local, .cursor folder may not exist, but we should still create mcp.json
|
|
368
|
+
if (mode === 'global' && !existsSync(cursorDir)) {
|
|
369
|
+
console.log(" ~ Cursor not found (no ~/.cursor/) ā skipping MCP, but commands may still install");
|
|
89
370
|
return false;
|
|
90
371
|
}
|
|
91
372
|
const mcpPath = join(cursorDir, "mcp.json");
|
|
92
373
|
let mcpConfig = { mcpServers: {} };
|
|
93
374
|
if (existsSync(mcpPath)) {
|
|
94
|
-
|
|
375
|
+
try {
|
|
376
|
+
const raw = readFileSync(mcpPath, "utf-8");
|
|
377
|
+
if (raw.trim().length > 0)
|
|
378
|
+
mcpConfig = JSON.parse(raw);
|
|
379
|
+
}
|
|
380
|
+
catch (e) {
|
|
381
|
+
console.error(` ā Cursor mcp.json is corrupted (${e.message}) ā backing up`);
|
|
382
|
+
if (!dryRun) {
|
|
383
|
+
try {
|
|
384
|
+
const bak = `${mcpPath}.bak.${Date.now()}`;
|
|
385
|
+
copyFileSync(mcpPath, bak);
|
|
386
|
+
console.log(` ā³ Backup saved to ${bak}`);
|
|
387
|
+
}
|
|
388
|
+
catch { }
|
|
389
|
+
}
|
|
390
|
+
mcpConfig = { mcpServers: {} };
|
|
391
|
+
}
|
|
95
392
|
}
|
|
96
393
|
if (mcpConfig.mcpServers?.["cyberaudit-skill"]) {
|
|
97
|
-
console.log(` ā Cursor MCP already configured`);
|
|
394
|
+
console.log(` ā Cursor MCP already configured (${mcpPath}) [${mode}]`);
|
|
98
395
|
return true;
|
|
99
396
|
}
|
|
100
397
|
if (dryRun) {
|
|
101
|
-
console.log(` ā Would add Cursor MCP entry to ${mcpPath}`);
|
|
398
|
+
console.log(` ā Would add Cursor MCP entry to ${mcpPath} [${mode}]`);
|
|
102
399
|
return true;
|
|
103
400
|
}
|
|
401
|
+
if (existsSync(mcpPath)) {
|
|
402
|
+
try {
|
|
403
|
+
copyFileSync(mcpPath, `${mcpPath}.bak`);
|
|
404
|
+
}
|
|
405
|
+
catch { }
|
|
406
|
+
}
|
|
104
407
|
mcpConfig.mcpServers = mcpConfig.mcpServers || {};
|
|
105
408
|
mcpConfig.mcpServers["cyberaudit-skill"] = {
|
|
106
409
|
command: "npx",
|
|
107
410
|
args: ["-y", "cyberaudit-skill", "serve"],
|
|
108
411
|
};
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const paths = AGENT_TARGETS[agent];
|
|
119
|
-
if (!paths?.length)
|
|
120
|
-
return false;
|
|
121
|
-
return installSkill(paths[0], agent, dryRun);
|
|
122
|
-
}
|
|
412
|
+
try {
|
|
413
|
+
mkdirSync(dirname(mcpPath), { recursive: true });
|
|
414
|
+
writeFileSync(mcpPath, JSON.stringify(mcpConfig, null, 2));
|
|
415
|
+
console.log(` ā Added Cursor MCP entry to ${mcpPath} [${mode}]`);
|
|
416
|
+
return true;
|
|
417
|
+
}
|
|
418
|
+
catch (e) {
|
|
419
|
+
console.error(` ā Failed to write Cursor MCP config: ${e.message}`);
|
|
420
|
+
return false;
|
|
123
421
|
}
|
|
124
422
|
}
|
|
125
423
|
async function main() {
|
|
126
424
|
const program = new Command();
|
|
127
425
|
program
|
|
128
426
|
.name("cyberaudit-skill")
|
|
129
|
-
.description("CyberAudit Skill ā
|
|
130
|
-
.version(
|
|
427
|
+
.description("CyberAudit Skill ā 22 agents like ui-ux-pro-max-skill, 8 main commands on '/'")
|
|
428
|
+
.version(VERSION);
|
|
131
429
|
program
|
|
132
430
|
.command("install")
|
|
133
|
-
.description("Install CyberAudit Skill for AI agent(s)")
|
|
134
|
-
.option("-a, --agent <agent>", "Target
|
|
431
|
+
.description("Install CyberAudit Skill for AI agent(s) ā supports 22 agents like ui-ux-pro-max-skill, with --global and --local modes")
|
|
432
|
+
.option("-a, --agent <agent>", "Target agent or all", "all")
|
|
135
433
|
.option("--dry-run", "Show changes without applying", false)
|
|
434
|
+
.option("--global", "Install globally to ~/ (default)", false)
|
|
435
|
+
.option("--local", "Install locally to ./ (project) like ui-ux-pro ā ensures '/' shows commands", false)
|
|
136
436
|
.action((options) => {
|
|
137
437
|
const dryRun = !!options.dryRun;
|
|
138
438
|
const agentOpt = (options.agent || "all").toLowerCase();
|
|
439
|
+
// Determine mode: if --local flag, local; else if --global or no flag, global (backward compat)
|
|
440
|
+
let mode = 'global';
|
|
441
|
+
if (options.local)
|
|
442
|
+
mode = 'local';
|
|
443
|
+
else if (options.global)
|
|
444
|
+
mode = 'global';
|
|
445
|
+
// If neither, default global for backward compat, but hint about local
|
|
446
|
+
const cwd = process.cwd();
|
|
139
447
|
let agents;
|
|
140
448
|
if (agentOpt === "all") {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
449
|
+
if (mode === 'local') {
|
|
450
|
+
// For local, install to all agents in project (like ui-ux-pro init --ai all)
|
|
451
|
+
agents = Object.keys(AGENT_CONFIG);
|
|
452
|
+
console.log(`Installing locally to project ${cwd} for all ${agents.length} agents (like ui-ux-pro)`);
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
const byFolder = detectByHomeFolders();
|
|
456
|
+
const byLegacy = detectInstalledAgents();
|
|
457
|
+
agents = [...new Set([...byFolder, ...byLegacy])];
|
|
458
|
+
if (agents.length === 0) {
|
|
459
|
+
console.log("No supported AI agents detected globally. Checked:");
|
|
460
|
+
console.log(" ~/.claude, ~/.cursor, ~/.windsurf, ~/.agent, ~/.copilot, ~/.kiro, ~/.codex, ~/.qoder, ~/.roo, ~/.gemini, ~/.trae, ~/.agents, ~/.continue, ~/.codebuddy, ~/.factory, ~/.kilocode, ~/.warp, ~/.augment, ~/.codewhale, ~/.cline, ~/.aider");
|
|
461
|
+
console.log("\nTry: --agent claude-code --local (project-local, ensures '/' shows)");
|
|
462
|
+
console.log("Or: --agent all --local (like ui-ux-pro init --ai all)");
|
|
463
|
+
process.exit(1);
|
|
464
|
+
}
|
|
465
|
+
console.log(`Detected agents (${agents.length}): ${agents.join(", ")} [global mode]`);
|
|
145
466
|
}
|
|
146
467
|
}
|
|
147
468
|
else {
|
|
148
|
-
const valid =
|
|
469
|
+
const valid = Object.keys(AGENT_CONFIG);
|
|
149
470
|
if (!valid.includes(agentOpt)) {
|
|
150
|
-
console.error(`Unknown agent: "${agentOpt}". Valid: ${valid.join(", ")}`);
|
|
471
|
+
console.error(`Unknown agent: "${agentOpt}". Valid: ${valid.join(", ")}, all`);
|
|
151
472
|
process.exit(1);
|
|
152
473
|
}
|
|
153
474
|
agents = [agentOpt];
|
|
154
475
|
}
|
|
155
476
|
const label = dryRun ? " (dry run)" : "";
|
|
156
|
-
console.log(`\nāāā CyberAudit Installation${label} āāā\n`);
|
|
477
|
+
console.log(`\nāāā CyberAudit v${VERSION} Installation${label} ā ${agents.length} agents [${mode}] ā like ui-ux-pro āāā\n`);
|
|
478
|
+
console.log(`Main commands for "/" menu: ${MAIN_COMMANDS.map(f => f.replace('.md', '')).join(', ')}\n`);
|
|
479
|
+
if (mode === 'local')
|
|
480
|
+
console.log(`Project: ${cwd}\n`);
|
|
157
481
|
let ok = 0, fail = 0;
|
|
158
482
|
for (const agent of agents) {
|
|
159
|
-
const
|
|
160
|
-
|
|
483
|
+
const cfg = getPathsForMode(agent, mode, cwd);
|
|
484
|
+
console.log(`\nā ${cfg.displayName} (${agent}) [${mode}]:`);
|
|
485
|
+
if (installSkillForAgent(agent, dryRun, mode, cwd))
|
|
161
486
|
ok++;
|
|
162
487
|
else
|
|
163
488
|
fail++;
|
|
164
489
|
}
|
|
165
|
-
console.log(`\nDone. ${ok} configured, ${fail} skipped.\n`);
|
|
166
|
-
if (!dryRun)
|
|
167
|
-
console.log(
|
|
490
|
+
console.log(`\nDone. ${ok} configured, ${fail} skipped/failed [${mode}].\n`);
|
|
491
|
+
if (!dryRun && ok > 0) {
|
|
492
|
+
console.log(`Verify: npx -y cyberaudit-skill list${mode === 'local' ? ' --local' : ''}`);
|
|
493
|
+
console.log(`Tip: Type "/" in your agent ā you should see: ${MAIN_COMMANDS.slice(0, 4).map(f => '/' + f.replace('.md', '').replace('audit-', 'audit:')).join(', ')}... (${MAIN_COMMANDS.length} commands) [${mode}]\n`);
|
|
494
|
+
if (mode === 'global') {
|
|
495
|
+
console.log(`For 100% guarantee like ui-ux-pro, also run: npx -y cyberaudit-skill install --agent all --local\n`);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
168
498
|
});
|
|
169
499
|
program
|
|
170
500
|
.command("list")
|
|
171
|
-
.description("List audits and installed agents")
|
|
172
|
-
.
|
|
173
|
-
|
|
501
|
+
.description("List audits and installed agents (22 agents)")
|
|
502
|
+
.option("--local", "List project-local installation (./) instead of global (~/)", false)
|
|
503
|
+
.action((opts) => {
|
|
504
|
+
const mode = opts.local ? 'local' : 'global';
|
|
505
|
+
const cwd = process.cwd();
|
|
506
|
+
console.log(`\nāāā CyberAudit v${VERSION} ā Available Audits [${mode}] āāā\n`);
|
|
174
507
|
console.log(" cyberaudit-web OWASP web app security audit");
|
|
175
508
|
console.log(" cyberaudit-mobile OWASP mobile app security audit");
|
|
176
509
|
console.log(" cyberaudit-api API security audit");
|
|
177
510
|
console.log(" cyberaudit-cloud Cloud config audit");
|
|
178
|
-
console.log(" cyberaudit-full Full stack
|
|
179
|
-
console.log(" cyberaudit-quick Quick
|
|
180
|
-
console.log(
|
|
181
|
-
for (const [
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
511
|
+
console.log(" cyberaudit-full Full stack");
|
|
512
|
+
console.log(" cyberaudit-quick Quick scan (deterministic)\n");
|
|
513
|
+
console.log(`āāā Supported Agents (${Object.keys(AGENT_CONFIG).length}) [${mode}] āāā\n`);
|
|
514
|
+
for (const [agentKey, cfgGlobal] of Object.entries(AGENT_CONFIG)) {
|
|
515
|
+
const cfg = getPathsForMode(agentKey, mode, cwd);
|
|
516
|
+
let installed = false;
|
|
517
|
+
let loc = "";
|
|
518
|
+
for (const sp of cfg.skillPaths) {
|
|
519
|
+
if (existsSync(sp)) {
|
|
520
|
+
installed = true;
|
|
521
|
+
loc = sp;
|
|
522
|
+
break;
|
|
523
|
+
}
|
|
186
524
|
}
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
525
|
+
if (!installed) {
|
|
526
|
+
for (const cp of cfg.commandPaths) {
|
|
527
|
+
if (existsSync(cp)) {
|
|
528
|
+
try {
|
|
529
|
+
const files = readdirSync(cp).filter(f => f.startsWith('audit'));
|
|
530
|
+
if (files.length > 0) {
|
|
531
|
+
installed = true;
|
|
532
|
+
loc = `${cp} (${files.length} cmds)`;
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
catch { }
|
|
537
|
+
}
|
|
538
|
+
}
|
|
191
539
|
}
|
|
540
|
+
if (!installed && cfg.mcpPath && existsSync(cfg.mcpPath)) {
|
|
541
|
+
try {
|
|
542
|
+
if (readFileSync(cfg.mcpPath, 'utf-8').includes('cyberaudit-skill')) {
|
|
543
|
+
installed = true;
|
|
544
|
+
loc = cfg.mcpPath;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
catch { }
|
|
548
|
+
}
|
|
549
|
+
console.log(` ${installed ? "ā" : "ā"} ${cfg.displayName.padEnd(20)} (${agentKey})${installed ? ` ā ${loc}` : ""}`);
|
|
192
550
|
}
|
|
193
|
-
console.log();
|
|
551
|
+
console.log(`\nMain "/" commands: ${MAIN_COMMANDS.map(f => f.replace('.md', '')).join(', ')} [${mode}]`);
|
|
552
|
+
console.log(`Total commands available: ${readdirSync(COMMANDS_SRC).length} (8 main shown on "/")\n`);
|
|
194
553
|
});
|
|
195
554
|
program
|
|
196
|
-
.command("
|
|
197
|
-
.description("
|
|
198
|
-
.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
tools: [
|
|
208
|
-
{
|
|
209
|
-
name: "cyberaudit-web",
|
|
210
|
-
description: "Web app security audit (OWASP Top 10)",
|
|
211
|
-
inputSchema: {
|
|
212
|
-
type: "object",
|
|
213
|
-
properties: {
|
|
214
|
-
target: { type: "string", description: "URL or project path" },
|
|
215
|
-
depth: { type: "string", enum: ["quick", "standard", "deep"], default: "standard" },
|
|
216
|
-
},
|
|
217
|
-
},
|
|
218
|
-
},
|
|
219
|
-
{
|
|
220
|
-
name: "cyberaudit-mobile",
|
|
221
|
-
description: "Mobile app security audit (OWASP MASVS)",
|
|
222
|
-
inputSchema: {
|
|
223
|
-
type: "object",
|
|
224
|
-
properties: {
|
|
225
|
-
target: { type: "string", description: "App package or project path" },
|
|
226
|
-
platform: { type: "string", enum: ["android", "ios", "both"], default: "both" },
|
|
227
|
-
},
|
|
228
|
-
},
|
|
229
|
-
},
|
|
230
|
-
{
|
|
231
|
-
name: "cyberaudit-api",
|
|
232
|
-
description: "API security audit",
|
|
233
|
-
inputSchema: {
|
|
234
|
-
type: "object",
|
|
235
|
-
properties: {
|
|
236
|
-
target: { type: "string", description: "API endpoint or spec file" },
|
|
237
|
-
},
|
|
238
|
-
},
|
|
239
|
-
},
|
|
240
|
-
{
|
|
241
|
-
name: "cyberaudit-list",
|
|
242
|
-
description: "List all available audit types",
|
|
243
|
-
inputSchema: { type: "object", properties: {} },
|
|
244
|
-
},
|
|
245
|
-
],
|
|
246
|
-
};
|
|
247
|
-
function write(id, result) {
|
|
248
|
-
process.stdout.write(JSON.stringify({ jsonrpc: "2.0", id, ...result }) + "\n");
|
|
249
|
-
}
|
|
250
|
-
rl.on("line", (line) => {
|
|
555
|
+
.command("scan")
|
|
556
|
+
.description("Deterministic security scan (secrets, web vulns)")
|
|
557
|
+
.argument("[target]", "Path to scan", ".")
|
|
558
|
+
.option("--json", "Output JSON", false)
|
|
559
|
+
.option("--type <type>", "Scan type: secrets, web, all", "all")
|
|
560
|
+
.option("--ignore <patterns>", "Comma-separated ignore", "")
|
|
561
|
+
.action((target, options) => {
|
|
562
|
+
const scanTarget = resolve(target || ".");
|
|
563
|
+
const ignore = options.ignore ? options.ignore.split(',').map((s) => s.trim()).filter(Boolean) : [];
|
|
564
|
+
const type = (options.type || 'all').toLowerCase();
|
|
565
|
+
console.error(`[CyberAudit] Scanning ${scanTarget} [type=${type}]... (${VERSION})`);
|
|
251
566
|
try {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
567
|
+
let allFindings = [];
|
|
568
|
+
let secretFindings = [];
|
|
569
|
+
let webFindings = [];
|
|
570
|
+
if (type === 'secrets' || type === 'all') {
|
|
571
|
+
secretFindings = scanSecrets(scanTarget, { ignore });
|
|
572
|
+
allFindings.push(...secretFindings.map(f => ({ ...f, scanner: 'secrets' })));
|
|
256
573
|
}
|
|
257
|
-
|
|
258
|
-
|
|
574
|
+
if (type === 'web' || type === 'all') {
|
|
575
|
+
webFindings = scanWeb(scanTarget);
|
|
576
|
+
allFindings.push(...webFindings.map(f => ({ ...f, scanner: 'web' })));
|
|
259
577
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
content: [{ type: "text", text: `[CyberAudit] "${msg.params.name}" called with ${JSON.stringify(msg.params.arguments)}\n\nRun the audit by loading the CyberAudit Skill.` }],
|
|
263
|
-
});
|
|
578
|
+
if (options.json) {
|
|
579
|
+
console.log(JSON.stringify({ target: scanTarget, version: VERSION, type, findings: allFindings }, null, 2));
|
|
264
580
|
}
|
|
265
581
|
else {
|
|
266
|
-
|
|
582
|
+
if (type === 'secrets' || type === 'all')
|
|
583
|
+
console.log(formatFindingsText(secretFindings, scanTarget));
|
|
584
|
+
if (type === 'web' || type === 'all')
|
|
585
|
+
console.log(formatWebFindingsText(webFindings, scanTarget));
|
|
586
|
+
if (allFindings.length > 0)
|
|
587
|
+
console.log(`\nš” Next: /audit:secrets, /audit:web or /audit`);
|
|
588
|
+
else
|
|
589
|
+
console.log(`\nā
No findings for type=${type} in ${scanTarget}`);
|
|
267
590
|
}
|
|
591
|
+
if (allFindings.some(f => f.severity === 'CRITICAL'))
|
|
592
|
+
process.exitCode = 1;
|
|
593
|
+
}
|
|
594
|
+
catch (e) {
|
|
595
|
+
console.error(`[CyberAudit] Scan failed: ${e.message}`);
|
|
596
|
+
process.exit(1);
|
|
268
597
|
}
|
|
269
|
-
catch { /* skip malformed */ }
|
|
270
598
|
});
|
|
599
|
+
program
|
|
600
|
+
.command("report")
|
|
601
|
+
.description("Generate deterministic security report")
|
|
602
|
+
.argument("[target]", "Target path or app name", ".")
|
|
603
|
+
.option("--input <file>", "JSON file from scan --json")
|
|
604
|
+
.option("--type <type>", "Report type: web|api|mobile|cloud|full", "web")
|
|
605
|
+
.option("--framework <fw>", "Framework name")
|
|
606
|
+
.option("--output <file>", "Output markdown file")
|
|
607
|
+
.option("--json", " Also emit JSON summary", false)
|
|
608
|
+
.action(async (target, options) => {
|
|
609
|
+
const { generateReport } = await import("./report/generator.js");
|
|
610
|
+
let findings = [];
|
|
611
|
+
let scanTarget = target || ".";
|
|
612
|
+
const reportType = (options.type || 'web').toLowerCase();
|
|
613
|
+
if (options.input) {
|
|
614
|
+
const inputPath = resolve(options.input);
|
|
615
|
+
if (!existsSync(inputPath)) {
|
|
616
|
+
console.error(`Input file not found: ${inputPath}`);
|
|
617
|
+
process.exit(1);
|
|
618
|
+
}
|
|
619
|
+
const raw = readFileSync(inputPath, 'utf-8');
|
|
620
|
+
const parsed = JSON.parse(raw);
|
|
621
|
+
findings = parsed.findings || parsed;
|
|
622
|
+
scanTarget = parsed.target || scanTarget;
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
console.error(`[CyberAudit] No input file, running scan on ${scanTarget}...`);
|
|
626
|
+
const secrets = scanSecrets(resolve(scanTarget));
|
|
627
|
+
const web = scanWeb(resolve(scanTarget));
|
|
628
|
+
findings = [...secrets.map(f => ({ ...f, scanner: 'secrets' })), ...web.map(f => ({ ...f, scanner: 'web' }))];
|
|
629
|
+
}
|
|
630
|
+
const report = generateReport({ target: scanTarget, version: VERSION, type: reportType, findings, framework: options.framework });
|
|
631
|
+
if (options.output) {
|
|
632
|
+
const outPath = resolve(options.output);
|
|
633
|
+
writeFileSync(outPath, report.markdown, 'utf-8');
|
|
634
|
+
console.error(`[CyberAudit] Report written to ${outPath} | Score ${report.score}/100 ${report.verdict}`);
|
|
635
|
+
}
|
|
636
|
+
else
|
|
637
|
+
console.log(report.markdown);
|
|
638
|
+
if (options.json)
|
|
639
|
+
console.error(JSON.stringify({ score: report.score, verdict: report.verdict, dashboard: report.dashboard, owasp: report.owaspCompliance }, null, 2));
|
|
640
|
+
if (report.verdict === 'CRITICAL' || report.verdict === 'HIGH')
|
|
641
|
+
process.exitCode = 1;
|
|
642
|
+
});
|
|
643
|
+
program
|
|
644
|
+
.command("serve")
|
|
645
|
+
.description("Start CyberAudit MCP server (stdio)")
|
|
646
|
+
.action(() => {
|
|
647
|
+
console.error(`[CyberAudit] MCP server starting v${VERSION}...`);
|
|
648
|
+
startMcpServerImpl();
|
|
649
|
+
});
|
|
650
|
+
program.parse(process.argv);
|
|
271
651
|
}
|
|
272
652
|
main().catch((err) => {
|
|
273
653
|
console.error("[CyberAudit] Fatal error:", err);
|