@panguard-ai/panguard 0.3.1 → 0.3.3
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/dist/cli/auth-guard.d.ts +9 -7
- package/dist/cli/auth-guard.d.ts.map +1 -1
- package/dist/cli/auth-guard.js +33 -199
- package/dist/cli/auth-guard.js.map +1 -1
- package/dist/cli/commands/doctor.d.ts.map +1 -1
- package/dist/cli/commands/doctor.js +146 -26
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/guard.d.ts.map +1 -1
- package/dist/cli/commands/guard.js +264 -7
- package/dist/cli/commands/guard.js.map +1 -1
- package/dist/cli/commands/hacktivity.d.ts +10 -0
- package/dist/cli/commands/hacktivity.d.ts.map +1 -0
- package/dist/cli/commands/hacktivity.js +308 -0
- package/dist/cli/commands/hacktivity.js.map +1 -0
- package/dist/cli/commands/login.d.ts +3 -4
- package/dist/cli/commands/login.d.ts.map +1 -1
- package/dist/cli/commands/login.js +8 -258
- package/dist/cli/commands/login.js.map +1 -1
- package/dist/cli/commands/logout.d.ts +3 -3
- package/dist/cli/commands/logout.d.ts.map +1 -1
- package/dist/cli/commands/logout.js +7 -35
- package/dist/cli/commands/logout.js.map +1 -1
- package/dist/cli/commands/report.d.ts +2 -2
- package/dist/cli/commands/report.d.ts.map +1 -1
- package/dist/cli/commands/report.js +30 -40
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/scan.d.ts.map +1 -1
- package/dist/cli/commands/scan.js +16 -19
- package/dist/cli/commands/scan.js.map +1 -1
- package/dist/cli/commands/setup-skill-scan.d.ts +39 -0
- package/dist/cli/commands/setup-skill-scan.d.ts.map +1 -0
- package/dist/cli/commands/setup-skill-scan.js +186 -0
- package/dist/cli/commands/setup-skill-scan.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +11 -0
- package/dist/cli/commands/setup.d.ts.map +1 -0
- package/dist/cli/commands/setup.js +219 -0
- package/dist/cli/commands/setup.js.map +1 -0
- package/dist/cli/commands/status.js +43 -0
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/threat.d.ts +3 -0
- package/dist/cli/commands/threat.d.ts.map +1 -1
- package/dist/cli/commands/threat.js +31 -11
- package/dist/cli/commands/threat.js.map +1 -1
- package/dist/cli/commands/trap.d.ts +2 -2
- package/dist/cli/commands/trap.d.ts.map +1 -1
- package/dist/cli/commands/trap.js +32 -29
- package/dist/cli/commands/trap.js.map +1 -1
- package/dist/cli/commands/upgrade.d.ts +3 -4
- package/dist/cli/commands/upgrade.d.ts.map +1 -1
- package/dist/cli/commands/upgrade.js +10 -234
- package/dist/cli/commands/upgrade.js.map +1 -1
- package/dist/cli/commands/whoami.d.ts +2 -2
- package/dist/cli/commands/whoami.d.ts.map +1 -1
- package/dist/cli/commands/whoami.js +9 -60
- package/dist/cli/commands/whoami.js.map +1 -1
- package/dist/cli/index.js +4 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/interactive.d.ts +1 -1
- package/dist/cli/interactive.d.ts.map +1 -1
- package/dist/cli/interactive.js +477 -236
- package/dist/cli/interactive.js.map +1 -1
- package/dist/cli/menu.d.ts.map +1 -1
- package/dist/cli/menu.js +2 -5
- package/dist/cli/menu.js.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/init/wizard-runner.d.ts.map +1 -1
- package/dist/init/wizard-runner.js +136 -6
- package/dist/init/wizard-runner.js.map +1 -1
- package/package.json +25 -13
package/dist/cli/interactive.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Panguard AI - Interactive CLI Mode
|
|
3
3
|
*
|
|
4
|
-
* Number-key menu [0]-[
|
|
4
|
+
* Number-key menu [0]-[8] with panguard > prompt for text commands.
|
|
5
5
|
* Box-bordered status panel, breadcrumb navigation, no "press any key" interrupts.
|
|
6
6
|
*
|
|
7
7
|
* @module @panguard-ai/panguard/cli/interactive
|
|
@@ -79,11 +79,11 @@ const MENU_DEFS = [
|
|
|
79
79
|
key: 'report',
|
|
80
80
|
icon: '\u25A0',
|
|
81
81
|
number: 2,
|
|
82
|
-
en: 'Compliance Report',
|
|
83
|
-
zh: '\u5408\u898F\u5831\u544A',
|
|
84
|
-
enDesc: '
|
|
85
|
-
zhDesc: '
|
|
86
|
-
tierBadge: '
|
|
82
|
+
en: 'Compliance Report [Coming Soon]',
|
|
83
|
+
zh: '\u5408\u898F\u5831\u544A [\u5373\u5C07\u63A8\u51FA]',
|
|
84
|
+
enDesc: 'ISO 27001, SOC 2, TW Cyber Security Act — under development',
|
|
85
|
+
zhDesc: 'ISO 27001\u3001SOC 2\u3001\u8CC7\u5B89\u7BA1\u7406\u6CD5 \u2014 \u958B\u767C\u4E2D',
|
|
86
|
+
tierBadge: '',
|
|
87
87
|
featureKey: 'report',
|
|
88
88
|
},
|
|
89
89
|
{
|
|
@@ -101,11 +101,11 @@ const MENU_DEFS = [
|
|
|
101
101
|
key: 'trap',
|
|
102
102
|
icon: '\u00B7',
|
|
103
103
|
number: 4,
|
|
104
|
-
en: 'Honeypot System',
|
|
105
|
-
zh: '\u871C\u7F50\u7CFB\u7D71',
|
|
106
|
-
enDesc: '
|
|
107
|
-
zhDesc: '\
|
|
108
|
-
tierBadge: '
|
|
104
|
+
en: 'Honeypot System [Coming Soon]',
|
|
105
|
+
zh: '\u871C\u7F50\u7CFB\u7D71 [\u5373\u5C07\u63A8\u51FA]',
|
|
106
|
+
enDesc: 'Decoy services to detect attackers — under development',
|
|
107
|
+
zhDesc: '\u8A98\u990C\u670D\u52D9\u5075\u6E2C\u653B\u64CA\u8005 \u2014 \u958B\u767C\u4E2D',
|
|
108
|
+
tierBadge: '',
|
|
109
109
|
featureKey: 'trap',
|
|
110
110
|
},
|
|
111
111
|
{
|
|
@@ -116,7 +116,7 @@ const MENU_DEFS = [
|
|
|
116
116
|
zh: '\u901A\u77E5\u7CFB\u7D71',
|
|
117
117
|
enDesc: 'LINE, Telegram, Slack, Email, Webhook channels',
|
|
118
118
|
zhDesc: 'LINE\u3001Telegram\u3001Slack\u3001Email\u3001Webhook \u901A\u77E5\u7BA1\u9053',
|
|
119
|
-
tierBadge: '
|
|
119
|
+
tierBadge: '',
|
|
120
120
|
featureKey: 'notify',
|
|
121
121
|
},
|
|
122
122
|
{
|
|
@@ -127,7 +127,7 @@ const MENU_DEFS = [
|
|
|
127
127
|
zh: '\u5A01\u8105\u60C5\u5831',
|
|
128
128
|
enDesc: 'Threat intelligence REST API server',
|
|
129
129
|
zhDesc: '\u5A01\u8105\u60C5\u5831 REST API \u4F3A\u670D\u52D9',
|
|
130
|
-
tierBadge: '
|
|
130
|
+
tierBadge: '',
|
|
131
131
|
featureKey: 'threat-cloud',
|
|
132
132
|
},
|
|
133
133
|
{
|
|
@@ -141,8 +141,32 @@ const MENU_DEFS = [
|
|
|
141
141
|
tierBadge: '',
|
|
142
142
|
featureKey: 'demo',
|
|
143
143
|
},
|
|
144
|
+
{
|
|
145
|
+
key: 'audit',
|
|
146
|
+
icon: '\u25A0',
|
|
147
|
+
number: 8,
|
|
148
|
+
en: 'Skill Auditor',
|
|
149
|
+
zh: '\u6280\u80FD\u5BE9\u8A08',
|
|
150
|
+
enDesc: 'Audit AI agent skills for security issues',
|
|
151
|
+
zhDesc: '\u5BE9\u8A08 AI \u4EE3\u7406\u6280\u80FD\u7684\u5B89\u5168\u554F\u984C',
|
|
152
|
+
tierBadge: '',
|
|
153
|
+
featureKey: 'audit',
|
|
154
|
+
},
|
|
144
155
|
];
|
|
145
156
|
// ---------------------------------------------------------------------------
|
|
157
|
+
// MCP detection helper
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
async function isMCPConfigured() {
|
|
160
|
+
try {
|
|
161
|
+
const mcpConfig = await import('@panguard-ai/panguard-mcp/config');
|
|
162
|
+
const platforms = await mcpConfig.detectPlatforms();
|
|
163
|
+
return platforms.some((p) => p.detected && p.alreadyConfigured);
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// ---------------------------------------------------------------------------
|
|
146
170
|
// Guard process helpers
|
|
147
171
|
// ---------------------------------------------------------------------------
|
|
148
172
|
function isGuardRunning() {
|
|
@@ -223,20 +247,18 @@ function showHelp() {
|
|
|
223
247
|
const menuTitle = currentLang === 'zh-TW' ? '\u4E3B\u9078\u55AE' : 'Main Menu';
|
|
224
248
|
console.log(` ${c.sage(menuTitle)}`);
|
|
225
249
|
console.log(c.dim(currentLang === 'zh-TW'
|
|
226
|
-
? ' [0]-[
|
|
227
|
-
: ' [0]-[
|
|
250
|
+
? ' [0]-[8] \u6309\u6578\u5B57\u9375\u5373\u523B\u9078\u64C7\uFF08\u4E0D\u9700\u6309 Enter\uFF09'
|
|
251
|
+
: ' [0]-[8] Press number key to select instantly (no Enter needed)'));
|
|
228
252
|
console.log('');
|
|
229
253
|
const promptTitle = currentLang === 'zh-TW' ? '\u6587\u5B57\u6307\u4EE4' : 'Text Commands';
|
|
230
254
|
console.log(` ${c.sage(promptTitle)}`);
|
|
231
255
|
const cmds = [
|
|
256
|
+
{ cmd: 'setup', en: 'Connect AI agents via MCP', zh: '\u900F\u904E MCP \u9023\u63A5 AI \u4EE3\u7406' },
|
|
257
|
+
{ cmd: 'audit', en: 'Audit AI agent skills', zh: '\u5BE9\u8A08 AI \u4EE3\u7406\u6280\u80FD' },
|
|
232
258
|
{ cmd: 'status', en: 'System status overview', zh: '\u7CFB\u7D71\u72C0\u614B\u7E3D\u89BD' },
|
|
233
|
-
{ cmd: 'login', en: 'Account login', zh: '\u5E33\u865F\u767B\u5165' },
|
|
234
|
-
{ cmd: 'logout', en: 'Account logout', zh: '\u767B\u51FA' },
|
|
235
259
|
{ cmd: 'config', en: 'Settings management', zh: '\u8A2D\u5B9A\u7BA1\u7406' },
|
|
236
|
-
{ cmd: 'upgrade', en: 'Upgrade plan', zh: '\u5347\u7D1A\u65B9\u6848' },
|
|
237
260
|
{ cmd: 'hardening', en: 'Security hardening', zh: '\u5B89\u5168\u52A0\u56FA' },
|
|
238
261
|
{ cmd: 'doctor', en: 'Health diagnostics', zh: '\u5065\u5EB7\u8A3A\u65B7' },
|
|
239
|
-
{ cmd: 'whoami', en: 'Current account info', zh: '\u986F\u793A\u7576\u524D\u5E33\u865F' },
|
|
240
262
|
{ cmd: 'help', en: 'Show this help', zh: '\u986F\u793A\u6B64\u8AAA\u660E' },
|
|
241
263
|
];
|
|
242
264
|
for (const { cmd, en, zh } of cmds) {
|
|
@@ -283,12 +305,24 @@ export async function startInteractive(lang) {
|
|
|
283
305
|
process.on('SIGINT', exit);
|
|
284
306
|
process.on('SIGTERM', exit);
|
|
285
307
|
renderStartup();
|
|
286
|
-
// First-time user hint
|
|
308
|
+
// First-time user hint — detect MCP status and guide user
|
|
309
|
+
const mcpConfigured = await isMCPConfigured();
|
|
287
310
|
if (!existsSync(CONFIG_PATH)) {
|
|
288
|
-
|
|
289
|
-
? ` ${c.sage('\u25C6')} \u9996\u6B21\u4F7F\u7528\uFF1F\u5EFA\u8B70\
|
|
290
|
-
: ` ${c.sage('\u25C6')} First time?
|
|
291
|
-
console.log(
|
|
311
|
+
console.log(currentLang === 'zh-TW'
|
|
312
|
+
? ` ${c.sage('\u25C6')} \u9996\u6B21\u4F7F\u7528\uFF1F\u5EFA\u8B70\u6D41\u7A0B\uFF1A`
|
|
313
|
+
: ` ${c.sage('\u25C6')} First time? Recommended flow:`);
|
|
314
|
+
console.log(currentLang === 'zh-TW'
|
|
315
|
+
? ` ${c.sage('[0]')} \u521D\u59CB\u8A2D\u5B9A \u2192 \u81EA\u52D5\u9023\u63A5 AI \u4EE3\u7406 + \u6280\u80FD\u5BE9\u8A08 + \u6383\u63CF`
|
|
316
|
+
: ` ${c.sage('[0]')} Setup Wizard \u2192 auto-connect AI agents + skill audit + scan`);
|
|
317
|
+
console.log(currentLang === 'zh-TW'
|
|
318
|
+
? ` ${c.sage('[8]')} \u6280\u80FD\u5BE9\u8A08 \u2192 \u5BE9\u8A08\u5DF2\u5B89\u88DD AI \u6280\u80FD\u7684\u5B89\u5168\u554F\u984C`
|
|
319
|
+
: ` ${c.sage('[8]')} Skill Auditor \u2192 check installed AI skills for security issues`);
|
|
320
|
+
console.log('');
|
|
321
|
+
}
|
|
322
|
+
else if (!mcpConfigured) {
|
|
323
|
+
console.log(currentLang === 'zh-TW'
|
|
324
|
+
? ` ${c.sage('\u25C6')} AI \u4EE3\u7406\u5C1A\u672A\u9023\u63A5\u3002\u57F7\u884C ${c.sage('panguard setup')} \u6216\u6309 ${c.sage('[0]')} \u9023\u63A5 Claude Code\u3001Cursor \u7B49\u5E73\u53F0\u3002`
|
|
325
|
+
: ` ${c.sage('\u25C6')} AI agents not connected. Run ${c.sage('panguard setup')} or press ${c.sage('[0]')} to connect Claude Code, Cursor, etc.`);
|
|
292
326
|
console.log('');
|
|
293
327
|
}
|
|
294
328
|
// Main loop
|
|
@@ -362,16 +396,11 @@ async function dispatchCommand(text) {
|
|
|
362
396
|
renderStartup();
|
|
363
397
|
return true;
|
|
364
398
|
case 'login':
|
|
365
|
-
console.clear();
|
|
366
|
-
await actionLogin();
|
|
367
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
368
|
-
renderStartup();
|
|
369
|
-
return true;
|
|
370
399
|
case 'logout': {
|
|
371
400
|
console.clear();
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
401
|
+
console.log('');
|
|
402
|
+
console.log(' Authentication removed. All features are free and open source.');
|
|
403
|
+
console.log('');
|
|
375
404
|
await new Promise((r) => setTimeout(r, 500));
|
|
376
405
|
renderStartup();
|
|
377
406
|
return true;
|
|
@@ -382,12 +411,15 @@ async function dispatchCommand(text) {
|
|
|
382
411
|
await new Promise((r) => setTimeout(r, 500));
|
|
383
412
|
renderStartup();
|
|
384
413
|
return true;
|
|
385
|
-
case 'upgrade':
|
|
414
|
+
case 'upgrade': {
|
|
386
415
|
console.clear();
|
|
387
|
-
|
|
416
|
+
console.log('');
|
|
417
|
+
console.log(' All features are free and open source.');
|
|
418
|
+
console.log('');
|
|
388
419
|
await new Promise((r) => setTimeout(r, 500));
|
|
389
420
|
renderStartup();
|
|
390
421
|
return true;
|
|
422
|
+
}
|
|
391
423
|
case 'hardening':
|
|
392
424
|
console.clear();
|
|
393
425
|
await actionHardening();
|
|
@@ -408,9 +440,22 @@ async function dispatchCommand(text) {
|
|
|
408
440
|
return true;
|
|
409
441
|
case 'whoami': {
|
|
410
442
|
console.clear();
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
443
|
+
console.log('');
|
|
444
|
+
console.log(' All features available (no login required).');
|
|
445
|
+
console.log('');
|
|
446
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
447
|
+
renderStartup();
|
|
448
|
+
return true;
|
|
449
|
+
}
|
|
450
|
+
case 'audit':
|
|
451
|
+
console.clear();
|
|
452
|
+
await actionAudit();
|
|
453
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
454
|
+
renderStartup();
|
|
455
|
+
return true;
|
|
456
|
+
case 'setup': {
|
|
457
|
+
console.clear();
|
|
458
|
+
await actionMCPSetup();
|
|
414
459
|
await new Promise((r) => setTimeout(r, 500));
|
|
415
460
|
renderStartup();
|
|
416
461
|
return true;
|
|
@@ -451,6 +496,9 @@ async function dispatch(key) {
|
|
|
451
496
|
case 'demo':
|
|
452
497
|
await actionDemo();
|
|
453
498
|
break;
|
|
499
|
+
case 'audit':
|
|
500
|
+
await actionAudit();
|
|
501
|
+
break;
|
|
454
502
|
}
|
|
455
503
|
}
|
|
456
504
|
// ---------------------------------------------------------------------------
|
|
@@ -462,6 +510,110 @@ async function actionInit() {
|
|
|
462
510
|
await runInitWizard(currentLang);
|
|
463
511
|
}
|
|
464
512
|
// ---------------------------------------------------------------------------
|
|
513
|
+
// Text command: setup (MCP-only, for users who already have config)
|
|
514
|
+
// ---------------------------------------------------------------------------
|
|
515
|
+
async function actionMCPSetup() {
|
|
516
|
+
breadcrumb(['Panguard', currentLang === 'zh-TW' ? 'MCP \u8A2D\u5B9A' : 'MCP Setup']);
|
|
517
|
+
const title = currentLang === 'zh-TW' ? 'AI \u4EE3\u7406\u9023\u63A5 (MCP)' : 'AI Agent Connection (MCP)';
|
|
518
|
+
console.log(` ${theme.brandBold(title)}`);
|
|
519
|
+
console.log('');
|
|
520
|
+
const { spinner: sp } = await import('@panguard-ai/core');
|
|
521
|
+
const detectSp = sp(currentLang === 'zh-TW'
|
|
522
|
+
? '\u6B63\u5728\u5075\u6E2C AI \u4EE3\u7406\u5E73\u53F0...'
|
|
523
|
+
: 'Detecting AI agent platforms...');
|
|
524
|
+
try {
|
|
525
|
+
const mcpConfig = await import('@panguard-ai/panguard-mcp/config');
|
|
526
|
+
const platforms = await mcpConfig.detectPlatforms();
|
|
527
|
+
const detected = platforms.filter((p) => p.detected);
|
|
528
|
+
const unconfigured = detected.filter((p) => !p.alreadyConfigured);
|
|
529
|
+
if (detected.length === 0) {
|
|
530
|
+
detectSp.warn(currentLang === 'zh-TW'
|
|
531
|
+
? '\u672A\u5075\u6E2C\u5230\u4EFB\u4F55 AI \u4EE3\u7406\u5E73\u53F0'
|
|
532
|
+
: 'No AI agent platforms detected');
|
|
533
|
+
console.log('');
|
|
534
|
+
console.log(c.dim(currentLang === 'zh-TW'
|
|
535
|
+
? ' \u652F\u63F4\u5E73\u53F0: Claude Code, Cursor, OpenClaw, Codex, WorkBuddy, NemoClaw, Claude Desktop'
|
|
536
|
+
: ' Supported: Claude Code, Cursor, OpenClaw, Codex, WorkBuddy, NemoClaw, Claude Desktop'));
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
detectSp.succeed(currentLang === 'zh-TW'
|
|
540
|
+
? `\u5075\u6E2C\u5230 ${detected.length} \u500B\u5E73\u53F0`
|
|
541
|
+
: `Found ${detected.length} platform(s)`);
|
|
542
|
+
console.log('');
|
|
543
|
+
// Show all platforms
|
|
544
|
+
for (const p of platforms) {
|
|
545
|
+
const status = p.detected
|
|
546
|
+
? p.alreadyConfigured
|
|
547
|
+
? c.safe('\u2713 configured')
|
|
548
|
+
: c.caution('~ not configured')
|
|
549
|
+
: c.dim('- not found');
|
|
550
|
+
console.log(` ${p.detected ? c.bold(p.name) : c.dim(p.name)} ${status}`);
|
|
551
|
+
}
|
|
552
|
+
console.log('');
|
|
553
|
+
if (unconfigured.length === 0) {
|
|
554
|
+
console.log(c.safe(currentLang === 'zh-TW'
|
|
555
|
+
? ` \u2713 \u6240\u6709\u5E73\u53F0\u5DF2\u8A2D\u5B9A\u5B8C\u6210\uFF01`
|
|
556
|
+
: ' \u2713 All platforms already configured!'));
|
|
557
|
+
console.log('');
|
|
558
|
+
console.log(c.dim(currentLang === 'zh-TW'
|
|
559
|
+
? ' \u91CD\u555F AI \u4EE3\u7406\u5F8C\uFF0C\u8ACB\u6C42\u300Cpanguard_status\u300D\u5373\u53EF\u9A57\u8B49\u3002'
|
|
560
|
+
: ' Restart your AI agent, then ask "panguard_status" to verify.'));
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
// Configure unconfigured platforms
|
|
564
|
+
const configSp = sp(currentLang === 'zh-TW'
|
|
565
|
+
? `\u6B63\u5728\u8A2D\u5B9A ${unconfigured.length} \u500B\u5E73\u53F0...`
|
|
566
|
+
: `Configuring ${unconfigured.length} platform(s)...`);
|
|
567
|
+
let successCount = 0;
|
|
568
|
+
for (const p of unconfigured) {
|
|
569
|
+
const result = mcpConfig.injectMCPConfig(p.id);
|
|
570
|
+
if (result.success)
|
|
571
|
+
successCount++;
|
|
572
|
+
}
|
|
573
|
+
if (successCount === unconfigured.length) {
|
|
574
|
+
configSp.succeed(currentLang === 'zh-TW'
|
|
575
|
+
? `${successCount} \u500B\u5E73\u53F0\u8A2D\u5B9A\u5B8C\u6210`
|
|
576
|
+
: `${successCount} platform(s) configured`);
|
|
577
|
+
}
|
|
578
|
+
else {
|
|
579
|
+
configSp.warn(currentLang === 'zh-TW'
|
|
580
|
+
? `${successCount}/${unconfigured.length} \u5E73\u53F0\u8A2D\u5B9A\u6210\u529F`
|
|
581
|
+
: `${successCount}/${unconfigured.length} platform(s) configured`);
|
|
582
|
+
}
|
|
583
|
+
console.log('');
|
|
584
|
+
console.log(c.dim(currentLang === 'zh-TW'
|
|
585
|
+
? ' \u91CD\u555F AI \u4EE3\u7406\u5F8C\u5373\u53EF\u4F7F\u7528 11 \u500B panguard_* MCP \u5DE5\u5177\uFF1A'
|
|
586
|
+
: ' Restart your AI agent to use 11 panguard_* MCP tools:'));
|
|
587
|
+
// Platform-specific restart instructions
|
|
588
|
+
const restartHints = {
|
|
589
|
+
'claude-code': { en: 'Close and reopen your terminal', zh: '\u95DC\u9589\u4E26\u91CD\u65B0\u958B\u555F\u7D42\u7AEF\u6A5F' },
|
|
590
|
+
'claude-desktop': { en: 'Quit and reopen Claude Desktop', zh: '\u9000\u51FA\u4E26\u91CD\u65B0\u958B\u555F Claude Desktop' },
|
|
591
|
+
cursor: { en: 'Cmd+Shift+P (or Ctrl+Shift+P) > "Reload Window"', zh: 'Cmd+Shift+P > "Reload Window"' },
|
|
592
|
+
openclaw: { en: 'Close and reopen OpenClaw', zh: '\u95DC\u9589\u4E26\u91CD\u65B0\u958B\u555F OpenClaw' },
|
|
593
|
+
codex: { en: 'Restart the Codex CLI session', zh: '\u91CD\u65B0\u555F\u52D5 Codex CLI' },
|
|
594
|
+
workbuddy: { en: 'Close and reopen WorkBuddy', zh: '\u95DC\u9589\u4E26\u91CD\u65B0\u958B\u555F WorkBuddy' },
|
|
595
|
+
nemoclaw: { en: 'Close and reopen NemoClaw', zh: '\u95DC\u9589\u4E26\u91CD\u65B0\u958B\u555F NemoClaw' },
|
|
596
|
+
};
|
|
597
|
+
for (const p of unconfigured) {
|
|
598
|
+
const hint = restartHints[p.id];
|
|
599
|
+
const text = hint
|
|
600
|
+
? (currentLang === 'zh-TW' ? hint.zh : hint.en)
|
|
601
|
+
: (currentLang === 'zh-TW' ? '\u91CD\u65B0\u555F\u52D5\u61C9\u7528\u7A0B\u5F0F' : 'Restart the application');
|
|
602
|
+
console.log(c.dim(` ${p.name}: ${text}`));
|
|
603
|
+
}
|
|
604
|
+
console.log('');
|
|
605
|
+
console.log(c.dim(currentLang === 'zh-TW'
|
|
606
|
+
? ' \u8A66\u8A66\u554F AI: \u300C\u5BE9\u8A08\u9019\u500B\u5C08\u6848\u7684\u6280\u80FD\u300D\u6216\u300C\u6383\u63CF\u6211\u7684\u7CFB\u7D71\u300D'
|
|
607
|
+
: ' Try asking your AI: "audit the skills in this project" or "scan my system"'));
|
|
608
|
+
}
|
|
609
|
+
catch (err) {
|
|
610
|
+
detectSp.fail(currentLang === 'zh-TW'
|
|
611
|
+
? 'MCP \u8A2D\u5B9A\u5931\u6557'
|
|
612
|
+
: 'MCP setup failed');
|
|
613
|
+
console.log(formatError(err instanceof Error ? err.message : String(err), 'MCP Setup', currentLang === 'zh-TW' ? '\u8ACB\u91CD\u8A66' : 'Please retry'));
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
// ---------------------------------------------------------------------------
|
|
465
617
|
// [1] Security Scan
|
|
466
618
|
// ---------------------------------------------------------------------------
|
|
467
619
|
async function actionScan() {
|
|
@@ -515,27 +667,36 @@ async function actionScan() {
|
|
|
515
667
|
for (const f of result.findings) {
|
|
516
668
|
const sev = colorSeverity(f.severity).padEnd(10);
|
|
517
669
|
console.log(` ${sev} ${f.title}`);
|
|
518
|
-
|
|
519
|
-
|
|
670
|
+
// Show description if available
|
|
671
|
+
if (f.description) {
|
|
672
|
+
console.log(c.dim(` ${f.description}`));
|
|
673
|
+
}
|
|
674
|
+
if (f.manualFix && f.manualFix.length > 0) {
|
|
675
|
+
const fixLabel = currentLang === 'zh-TW' ? '\u4FEE\u5FA9:' : 'Fix:';
|
|
520
676
|
console.log(c.dim(` ${fixLabel}`));
|
|
521
677
|
for (const cmd of f.manualFix) {
|
|
522
678
|
console.log(c.dim(` $ ${cmd}`));
|
|
523
679
|
}
|
|
524
680
|
fixableCount++;
|
|
525
681
|
}
|
|
682
|
+
else if (f.remediation) {
|
|
683
|
+
// Show recommendation even if no auto-fix command
|
|
684
|
+
const recLabel = currentLang === 'zh-TW' ? '\u5EFA\u8B70:' : 'Recommendation:';
|
|
685
|
+
console.log(c.dim(` ${recLabel} ${f.remediation}`));
|
|
686
|
+
}
|
|
526
687
|
}
|
|
527
688
|
console.log('');
|
|
528
689
|
const issuesText = currentLang === 'zh-TW'
|
|
529
690
|
? `${result.findings.length} \u500B\u554F\u984C`
|
|
530
691
|
: `${result.findings.length} issue(s) found`;
|
|
531
692
|
console.log(c.dim(` ${issuesText}`));
|
|
532
|
-
if (
|
|
693
|
+
if (fixableCount > 0) {
|
|
533
694
|
const upgradeLines = currentLang === 'zh-TW'
|
|
534
695
|
? [
|
|
535
|
-
`\
|
|
696
|
+
`\u53EF\u81EA\u52D5\u4FEE\u5FA9:`,
|
|
536
697
|
`$ panguard scan --fix`,
|
|
537
698
|
]
|
|
538
|
-
: [`
|
|
699
|
+
: [`Auto-fix available:`, `$ panguard scan --fix`];
|
|
539
700
|
console.log('');
|
|
540
701
|
console.log(box(upgradeLines.join('\n'), { borderColor: c.sage }));
|
|
541
702
|
}
|
|
@@ -556,7 +717,8 @@ async function actionScan() {
|
|
|
556
717
|
console.log(` ${agentMsg}`);
|
|
557
718
|
const guardItems = [
|
|
558
719
|
{ key: '1', label: currentLang === 'zh-TW' ? '\u662F\uFF0C\u555F\u52D5 Guard \u9632\u8B77' : 'Yes, start Guard protection' },
|
|
559
|
-
{ key: '2', label: currentLang === 'zh-TW' ? '\
|
|
720
|
+
{ key: '2', label: currentLang === 'zh-TW' ? '\u5BE9\u8A08\u5DF2\u5B89\u88DD\u6280\u80FD\u7684\u5B89\u5168\u5A01\u8105' : 'Audit installed skills for threats' },
|
|
721
|
+
{ key: '3', label: currentLang === 'zh-TW' ? '\u4E0D\u7528\uFF0C\u56DE\u4E3B\u9078\u55AE' : 'No, return to main menu' },
|
|
560
722
|
];
|
|
561
723
|
renderCompactMenu('', guardItems);
|
|
562
724
|
const guardChoice = await waitForCompactChoice(guardItems, currentLang);
|
|
@@ -564,16 +726,22 @@ async function actionScan() {
|
|
|
564
726
|
await actionGuard();
|
|
565
727
|
return;
|
|
566
728
|
}
|
|
729
|
+
if (guardChoice?.key === '2') {
|
|
730
|
+
await actionAudit();
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
567
733
|
}
|
|
568
734
|
else {
|
|
569
735
|
nextSteps(currentLang === 'zh-TW'
|
|
570
736
|
? [
|
|
737
|
+
{ cmd: '[8] \u6280\u80FD\u5BE9\u8A08', desc: '\u5BE9\u8A08\u5DF2\u5B89\u88DD\u6280\u80FD\u7684\u5B89\u5168\u5A01\u8105' },
|
|
571
738
|
{ cmd: 'scan --full', desc: '\u57F7\u884C\u5B8C\u6574\u6383\u63CF' },
|
|
572
|
-
{ cmd: '
|
|
739
|
+
{ cmd: 'guard start', desc: '\u555F\u52D5\u5373\u6642\u9632\u8B77' },
|
|
573
740
|
]
|
|
574
741
|
: [
|
|
742
|
+
{ cmd: '[8] Skill Auditor', desc: 'Audit installed skills for threats' },
|
|
575
743
|
{ cmd: 'scan --full', desc: 'Run a comprehensive scan' },
|
|
576
|
-
{ cmd: '
|
|
744
|
+
{ cmd: 'guard start', desc: 'Enable real-time protection' },
|
|
577
745
|
], currentLang);
|
|
578
746
|
}
|
|
579
747
|
}
|
|
@@ -585,54 +753,21 @@ async function actionReport() {
|
|
|
585
753
|
'Panguard',
|
|
586
754
|
currentLang === 'zh-TW' ? '\u5408\u898F\u5831\u544A' : 'Compliance Report',
|
|
587
755
|
]);
|
|
588
|
-
const title = currentLang === 'zh-TW' ? '\u5408\u898F\u5831\u544A' : 'Compliance Report';
|
|
589
|
-
console.log(` ${theme.brandBold(title)}`);
|
|
590
|
-
console.log('');
|
|
591
|
-
const frameworkItems = [
|
|
592
|
-
{ key: '1', label: 'ISO 27001' },
|
|
593
|
-
{ key: '2', label: 'SOC 2' },
|
|
594
|
-
{
|
|
595
|
-
key: '3',
|
|
596
|
-
label: currentLang === 'zh-TW'
|
|
597
|
-
? '\u8CC7\u901A\u5B89\u5168\u7BA1\u7406\u6CD5'
|
|
598
|
-
: 'TW Cyber Security Act',
|
|
599
|
-
},
|
|
600
|
-
];
|
|
601
|
-
renderCompactMenu(currentLang === 'zh-TW' ? '\u9078\u64C7\u5408\u898F\u6846\u67B6' : 'Select Framework', frameworkItems);
|
|
602
|
-
const fwChoice = await waitForCompactChoice(frameworkItems, currentLang);
|
|
603
|
-
if (!fwChoice)
|
|
604
|
-
return;
|
|
605
|
-
const frameworkMap = {
|
|
606
|
-
'1': 'iso27001',
|
|
607
|
-
'2': 'soc2',
|
|
608
|
-
'3': 'tw_cyber_security_act',
|
|
609
|
-
};
|
|
610
|
-
const framework = frameworkMap[fwChoice.key] ?? 'iso27001';
|
|
611
|
-
const langItems = [
|
|
612
|
-
{
|
|
613
|
-
key: '1',
|
|
614
|
-
label: currentLang === 'zh-TW' ? '\u7E41\u9AD4\u4E2D\u6587 (zh-TW)' : 'Chinese (zh-TW)',
|
|
615
|
-
},
|
|
616
|
-
{ key: '2', label: currentLang === 'zh-TW' ? '\u82F1\u6587 (en)' : 'English (en)' },
|
|
617
|
-
];
|
|
618
756
|
console.log('');
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
757
|
+
if (currentLang === 'zh-TW') {
|
|
758
|
+
console.log(' \u5408\u898F\u5831\u544A \u2014 \u5373\u5C07\u63A8\u51FA');
|
|
759
|
+
console.log('');
|
|
760
|
+
console.log(' ISO 27001\u3001SOC 2\u3001\u8CC7\u5B89\u7BA1\u7406\u6CD5\u5408\u898F\u5831\u544A\u529F\u80FD\u958B\u767C\u4E2D\u3002');
|
|
761
|
+
console.log(' \u8FFD\u8E64\u9032\u5EA6: https://github.com/panguard-ai/panguard-ai');
|
|
762
|
+
}
|
|
763
|
+
else {
|
|
764
|
+
console.log(' Compliance Report \u2014 Coming Soon');
|
|
765
|
+
console.log('');
|
|
766
|
+
console.log(' ISO 27001, SOC 2, and TW Cyber Security Act compliance');
|
|
767
|
+
console.log(' reports are under active development.');
|
|
768
|
+
console.log(' Follow progress: https://github.com/panguard-ai/panguard-ai');
|
|
769
|
+
}
|
|
624
770
|
console.log('');
|
|
625
|
-
const { executeCli } = await import('@panguard-ai/panguard-report');
|
|
626
|
-
await executeCli(['generate', '--framework', framework, '--language', reportLang]);
|
|
627
|
-
nextSteps(currentLang === 'zh-TW'
|
|
628
|
-
? [
|
|
629
|
-
{ cmd: 'scan', desc: '\u57F7\u884C\u5B89\u5168\u6383\u63CF' },
|
|
630
|
-
{ cmd: 'guard start', desc: '\u555F\u52D5\u5373\u6642\u9632\u8B77' },
|
|
631
|
-
]
|
|
632
|
-
: [
|
|
633
|
-
{ cmd: 'scan', desc: 'Run a security scan' },
|
|
634
|
-
{ cmd: 'guard start', desc: 'Enable real-time protection' },
|
|
635
|
-
], currentLang);
|
|
636
771
|
}
|
|
637
772
|
// ---------------------------------------------------------------------------
|
|
638
773
|
// [3] Guard Engine
|
|
@@ -676,27 +811,24 @@ async function actionGuard() {
|
|
|
676
811
|
], currentLang);
|
|
677
812
|
break;
|
|
678
813
|
}
|
|
679
|
-
//
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
console.log('');
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
}
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
console.log(` ${c.dim('\u2500')} Notifications ${c.dim('(Solo $9/mo)')}`);
|
|
696
|
-
console.log(` ${c.dim('\u2500')} Log retention 7d+ ${c.dim('(Solo $9/mo)')}`);
|
|
697
|
-
}
|
|
698
|
-
console.log('');
|
|
814
|
+
// Show Guard capabilities
|
|
815
|
+
console.log(` ${c.sage('\u25C6')} Guard active${' '.repeat(30)}All Layers \u00B7 Free`);
|
|
816
|
+
console.log('');
|
|
817
|
+
if (currentLang === 'zh-TW') {
|
|
818
|
+
console.log(` ${c.safe('\u2713')} \u5DF2\u77E5\u653B\u64CA\u6A21\u5F0F\u81EA\u52D5\u5C01\u9396`);
|
|
819
|
+
console.log(` ${c.safe('\u2713')} Threat Cloud \u5A01\u8105\u60C5\u5831`);
|
|
820
|
+
console.log(` ${c.safe('\u2713')} AI \u5206\u6790`);
|
|
821
|
+
console.log(` ${c.safe('\u2713')} \u901A\u77E5\u7CFB\u7D71`);
|
|
822
|
+
console.log(` ${c.safe('\u2713')} \u65E5\u8A8C\u4FDD\u7559`);
|
|
823
|
+
}
|
|
824
|
+
else {
|
|
825
|
+
console.log(` ${c.safe('\u2713')} Auto-blocking for known attack patterns`);
|
|
826
|
+
console.log(` ${c.safe('\u2713')} Threat Cloud intelligence`);
|
|
827
|
+
console.log(` ${c.safe('\u2713')} AI analysis`);
|
|
828
|
+
console.log(` ${c.safe('\u2713')} Notifications`);
|
|
829
|
+
console.log(` ${c.safe('\u2713')} Log retention`);
|
|
699
830
|
}
|
|
831
|
+
console.log('');
|
|
700
832
|
// Spawn guard as background process
|
|
701
833
|
const { spawn: spawnProcess } = await import('node:child_process');
|
|
702
834
|
const { fileURLToPath: toPath } = await import('node:url');
|
|
@@ -802,46 +934,21 @@ async function actionTrap() {
|
|
|
802
934
|
'Panguard',
|
|
803
935
|
currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Honeypot System',
|
|
804
936
|
]);
|
|
805
|
-
const title = currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Honeypot System';
|
|
806
|
-
console.log(` ${theme.brandBold(title)}`);
|
|
807
937
|
console.log('');
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
{
|
|
814
|
-
key: '2',
|
|
815
|
-
label: currentLang === 'zh-TW'
|
|
816
|
-
? '\u5B8C\u6574\u914D\u7F6E (8 \u7A2E\u670D\u52D9)'
|
|
817
|
-
: 'Config (All 8 Services)',
|
|
818
|
-
},
|
|
819
|
-
{ key: '3', label: currentLang === 'zh-TW' ? '\u67E5\u770B\u72C0\u614B' : 'Status' },
|
|
820
|
-
{
|
|
821
|
-
key: '4',
|
|
822
|
-
label: currentLang === 'zh-TW' ? '\u653B\u64CA\u8005\u5206\u6790' : 'Attacker Profiles',
|
|
823
|
-
},
|
|
824
|
-
];
|
|
825
|
-
renderCompactMenu(currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Honeypot System', items);
|
|
826
|
-
const choice = await waitForCompactChoice(items, currentLang);
|
|
827
|
-
if (!choice)
|
|
828
|
-
return;
|
|
829
|
-
console.log('');
|
|
830
|
-
const { executeCli } = await import('@panguard-ai/panguard-trap');
|
|
831
|
-
switch (choice.key) {
|
|
832
|
-
case '1':
|
|
833
|
-
await executeCli(['config', '--services', 'ssh,http']);
|
|
834
|
-
break;
|
|
835
|
-
case '2':
|
|
836
|
-
await executeCli(['config', '--services', 'ssh,http,ftp,telnet,mysql,redis,smb,rdp']);
|
|
837
|
-
break;
|
|
838
|
-
case '3':
|
|
839
|
-
await executeCli(['status']);
|
|
840
|
-
break;
|
|
841
|
-
case '4':
|
|
842
|
-
await executeCli(['profiles']);
|
|
843
|
-
break;
|
|
938
|
+
if (currentLang === 'zh-TW') {
|
|
939
|
+
console.log(' \u871C\u7F50\u7CFB\u7D71 \u2014 \u5373\u5C07\u63A8\u51FA');
|
|
940
|
+
console.log('');
|
|
941
|
+
console.log(' \u8A98\u990C\u670D\u52D9\uFF08SSH\u3001HTTP\u3001FTP\uFF09\u5075\u6E2C\u8207\u5206\u6790\u653B\u64CA\u8005\u529F\u80FD\u958B\u767C\u4E2D\u3002');
|
|
942
|
+
console.log(' \u8FFD\u8E64\u9032\u5EA6: https://github.com/panguard-ai/panguard-ai');
|
|
844
943
|
}
|
|
944
|
+
else {
|
|
945
|
+
console.log(' Honeypot System \u2014 Coming Soon');
|
|
946
|
+
console.log('');
|
|
947
|
+
console.log(' Decoy services (SSH, HTTP, FTP) to detect and profile');
|
|
948
|
+
console.log(' attackers are under active development.');
|
|
949
|
+
console.log(' Follow progress: https://github.com/panguard-ai/panguard-ai');
|
|
950
|
+
}
|
|
951
|
+
console.log('');
|
|
845
952
|
}
|
|
846
953
|
// ---------------------------------------------------------------------------
|
|
847
954
|
// [5] Notifications
|
|
@@ -904,7 +1011,17 @@ async function actionThreat() {
|
|
|
904
1011
|
? ` \u5373\u5C07\u555F\u52D5\u5A01\u8105\u60C5\u5831 REST API \u4F3A\u670D\u5668 (port ${port})`
|
|
905
1012
|
: ` Starting Threat Cloud REST API server (port ${port})...`));
|
|
906
1013
|
console.log('');
|
|
907
|
-
|
|
1014
|
+
let ThreatCloudServer;
|
|
1015
|
+
try {
|
|
1016
|
+
const mod = '@panguard-ai/threat-cloud';
|
|
1017
|
+
const tc = await import(/* webpackIgnore: true */ mod);
|
|
1018
|
+
ThreatCloudServer = tc.ThreatCloudServer;
|
|
1019
|
+
}
|
|
1020
|
+
catch {
|
|
1021
|
+
console.log(c.red(' Threat Cloud server package is not available.'));
|
|
1022
|
+
console.log(c.dim(' Your Guard client connects to Threat Cloud automatically.'));
|
|
1023
|
+
return;
|
|
1024
|
+
}
|
|
908
1025
|
const sp = spinner(currentLang === 'zh-TW'
|
|
909
1026
|
? '\u6B63\u5728\u555F\u52D5 Threat Cloud API...'
|
|
910
1027
|
: 'Starting Threat Cloud API server...');
|
|
@@ -941,7 +1058,6 @@ async function actionThreat() {
|
|
|
941
1058
|
async function actionDemo() {
|
|
942
1059
|
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u529F\u80FD\u5C55\u793A' : 'Feature Demo']);
|
|
943
1060
|
const { runScan } = await import('@panguard-ai/panguard-scan');
|
|
944
|
-
const { generateComplianceReport, generateSummaryText } = await import('@panguard-ai/panguard-report');
|
|
945
1061
|
const title = currentLang === 'zh-TW' ? '\u529F\u80FD\u5C55\u793A' : 'Feature Demo';
|
|
946
1062
|
console.log(` ${theme.brandBold(title)}`);
|
|
947
1063
|
console.log(c.dim(currentLang === 'zh-TW'
|
|
@@ -982,59 +1098,14 @@ async function actionDemo() {
|
|
|
982
1098
|
});
|
|
983
1099
|
}
|
|
984
1100
|
console.log('');
|
|
985
|
-
// 2. Compliance Report
|
|
1101
|
+
// 2. Compliance Report (Coming Soon)
|
|
986
1102
|
console.log(c.dim(currentLang === 'zh-TW'
|
|
987
|
-
? ' 2. \u5408\u898F\u5831\u544A
|
|
988
|
-
: ' 2. Compliance Report
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
try {
|
|
994
|
-
const findings = [
|
|
995
|
-
{
|
|
996
|
-
findingId: 'DEMO-001',
|
|
997
|
-
severity: 'high',
|
|
998
|
-
title: 'Missing information security policy',
|
|
999
|
-
description: 'No formal information security policy document found.',
|
|
1000
|
-
category: 'policy',
|
|
1001
|
-
timestamp: new Date(),
|
|
1002
|
-
source: 'panguard-scan',
|
|
1003
|
-
},
|
|
1004
|
-
{
|
|
1005
|
-
findingId: 'DEMO-002',
|
|
1006
|
-
severity: 'medium',
|
|
1007
|
-
title: 'Unencrypted data at rest',
|
|
1008
|
-
description: 'Database storage does not use encryption at rest.',
|
|
1009
|
-
category: 'encryption',
|
|
1010
|
-
timestamp: new Date(),
|
|
1011
|
-
source: 'panguard-scan',
|
|
1012
|
-
},
|
|
1013
|
-
];
|
|
1014
|
-
const report = generateComplianceReport(findings, 'iso27001', 'en', {
|
|
1015
|
-
includeRecommendations: true,
|
|
1016
|
-
});
|
|
1017
|
-
const summary = generateSummaryText(report);
|
|
1018
|
-
reportSp.succeed(currentLang === 'zh-TW'
|
|
1019
|
-
? 'ISO 27001 \u5831\u544A\u5DF2\u7522\u751F'
|
|
1020
|
-
: 'ISO 27001 report generated');
|
|
1021
|
-
const summaryLines = summary.split('\n').slice(0, 10);
|
|
1022
|
-
for (const line of summaryLines) {
|
|
1023
|
-
console.log(` ${c.dim(line)}`);
|
|
1024
|
-
}
|
|
1025
|
-
console.log(c.dim(' ...'));
|
|
1026
|
-
results.push({
|
|
1027
|
-
name: currentLang === 'zh-TW' ? '\u5408\u898F\u5831\u544A' : 'Report',
|
|
1028
|
-
status: 'ok',
|
|
1029
|
-
});
|
|
1030
|
-
}
|
|
1031
|
-
catch (err) {
|
|
1032
|
-
reportSp.fail(`${err instanceof Error ? err.message : err}`);
|
|
1033
|
-
results.push({
|
|
1034
|
-
name: currentLang === 'zh-TW' ? '\u5408\u898F\u5831\u544A' : 'Report',
|
|
1035
|
-
status: 'fail',
|
|
1036
|
-
});
|
|
1037
|
-
}
|
|
1103
|
+
? ' 2. \u5408\u898F\u5831\u544A \u2014 \u5373\u5C07\u63A8\u51FA'
|
|
1104
|
+
: ' 2. Compliance Report \u2014 Coming Soon'));
|
|
1105
|
+
results.push({
|
|
1106
|
+
name: currentLang === 'zh-TW' ? '\u5408\u898F\u5831\u544A' : 'Report',
|
|
1107
|
+
status: 'warn',
|
|
1108
|
+
});
|
|
1038
1109
|
console.log('');
|
|
1039
1110
|
// 3. Guard Engine
|
|
1040
1111
|
console.log(c.dim(currentLang === 'zh-TW' ? ' 3. \u5B88\u8B77\u5F15\u64CE' : ' 3. Guard Engine'));
|
|
@@ -1057,24 +1128,14 @@ async function actionDemo() {
|
|
|
1057
1128
|
});
|
|
1058
1129
|
}
|
|
1059
1130
|
console.log('');
|
|
1060
|
-
// 4. Honeypot
|
|
1061
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
status: 'ok',
|
|
1069
|
-
});
|
|
1070
|
-
}
|
|
1071
|
-
catch (err) {
|
|
1072
|
-
console.log(` ${c.critical(err instanceof Error ? err.message : String(err))}`);
|
|
1073
|
-
results.push({
|
|
1074
|
-
name: currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Trap',
|
|
1075
|
-
status: 'fail',
|
|
1076
|
-
});
|
|
1077
|
-
}
|
|
1131
|
+
// 4. Honeypot (Coming Soon)
|
|
1132
|
+
console.log(c.dim(currentLang === 'zh-TW'
|
|
1133
|
+
? ' 4. \u871C\u7F50\u7CFB\u7D71 \u2014 \u5373\u5C07\u63A8\u51FA'
|
|
1134
|
+
: ' 4. Honeypot System \u2014 Coming Soon'));
|
|
1135
|
+
results.push({
|
|
1136
|
+
name: currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Trap',
|
|
1137
|
+
status: 'warn',
|
|
1138
|
+
});
|
|
1078
1139
|
console.log('');
|
|
1079
1140
|
// 5. Notifications
|
|
1080
1141
|
console.log(c.dim(currentLang === 'zh-TW' ? ' 5. \u901A\u77E5\u7CFB\u7D71' : ' 5. Notification System'));
|
|
@@ -1121,13 +1182,202 @@ async function actionDemo() {
|
|
|
1121
1182
|
], currentLang);
|
|
1122
1183
|
}
|
|
1123
1184
|
// ---------------------------------------------------------------------------
|
|
1124
|
-
//
|
|
1185
|
+
// [8] Skill Auditor
|
|
1125
1186
|
// ---------------------------------------------------------------------------
|
|
1126
|
-
async function
|
|
1127
|
-
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\
|
|
1128
|
-
const
|
|
1129
|
-
|
|
1130
|
-
|
|
1187
|
+
async function actionAudit() {
|
|
1188
|
+
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u6280\u80FD\u5BE9\u8A08' : 'Skill Auditor']);
|
|
1189
|
+
const title = currentLang === 'zh-TW' ? '\u6280\u80FD\u5BE9\u8A08' : 'Skill Auditor';
|
|
1190
|
+
console.log(` ${theme.brandBold(title)}`);
|
|
1191
|
+
console.log('');
|
|
1192
|
+
console.log(c.dim(currentLang === 'zh-TW'
|
|
1193
|
+
? ' \u6383\u63CF AI \u4EE3\u7406\u6280\u80FD\u76EE\u9304\uFF08\u5305\u542B SKILL.md\uFF09\u4EE5\u5075\u6E2C\u5B89\u5168\u554F\u984C\u3002'
|
|
1194
|
+
: ' Scan an AI agent skill directory (containing SKILL.md) for security issues.'));
|
|
1195
|
+
console.log('');
|
|
1196
|
+
// Ask for path to audit
|
|
1197
|
+
const pathItems = [
|
|
1198
|
+
{
|
|
1199
|
+
key: '1',
|
|
1200
|
+
label: currentLang === 'zh-TW'
|
|
1201
|
+
? '\u5BE9\u8A08\u7576\u524D\u76EE\u9304 (.)'
|
|
1202
|
+
: 'Audit current directory (.)',
|
|
1203
|
+
},
|
|
1204
|
+
{
|
|
1205
|
+
key: '2',
|
|
1206
|
+
label: currentLang === 'zh-TW'
|
|
1207
|
+
? '\u8F38\u5165\u81EA\u5B9A\u8DEF\u5F91'
|
|
1208
|
+
: 'Enter custom path',
|
|
1209
|
+
},
|
|
1210
|
+
];
|
|
1211
|
+
renderCompactMenu(currentLang === 'zh-TW' ? '\u9078\u64C7\u76EE\u6A19' : 'Select target', pathItems);
|
|
1212
|
+
const choice = await waitForCompactChoice(pathItems, currentLang);
|
|
1213
|
+
if (!choice)
|
|
1214
|
+
return;
|
|
1215
|
+
let targetPath = process.cwd();
|
|
1216
|
+
if (choice.key === '2') {
|
|
1217
|
+
// Read a custom path from the user via text prompt
|
|
1218
|
+
const readline = await import('node:readline');
|
|
1219
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1220
|
+
targetPath = await new Promise((resolve) => {
|
|
1221
|
+
const prompt = currentLang === 'zh-TW' ? ' \u8DEF\u5F91: ' : ' Path: ';
|
|
1222
|
+
rl.question(prompt, (answer) => {
|
|
1223
|
+
rl.close();
|
|
1224
|
+
resolve(answer.trim() || process.cwd());
|
|
1225
|
+
});
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
const path = await import('node:path');
|
|
1229
|
+
const resolvedPath = path.resolve(targetPath);
|
|
1230
|
+
console.log('');
|
|
1231
|
+
const sp = spinner(currentLang === 'zh-TW'
|
|
1232
|
+
? `\u6B63\u5728\u5BE9\u8A08 ${resolvedPath}...`
|
|
1233
|
+
: `Auditing ${resolvedPath}...`);
|
|
1234
|
+
try {
|
|
1235
|
+
const { auditSkill } = await import('@panguard-ai/panguard-skill-auditor');
|
|
1236
|
+
const report = await auditSkill(resolvedPath);
|
|
1237
|
+
sp.succeed(currentLang === 'zh-TW' ? '\u5BE9\u8A08\u5B8C\u6210' : 'Audit complete');
|
|
1238
|
+
// Display results
|
|
1239
|
+
const levelColors = {
|
|
1240
|
+
LOW: c.safe,
|
|
1241
|
+
MEDIUM: c.caution,
|
|
1242
|
+
HIGH: c.critical,
|
|
1243
|
+
CRITICAL: (s) => c.bold(c.critical(s)),
|
|
1244
|
+
};
|
|
1245
|
+
const colorFn = levelColors[report.riskLevel] ?? c.dim;
|
|
1246
|
+
console.log('');
|
|
1247
|
+
console.log(box([
|
|
1248
|
+
`${c.bold(currentLang === 'zh-TW' ? '\u6280\u80FD\u5BE9\u8A08\u5831\u544A' : 'Skill Audit Report')}`,
|
|
1249
|
+
'',
|
|
1250
|
+
`${currentLang === 'zh-TW' ? '\u6280\u80FD' : 'Skill'}: ${report.manifest?.name ?? 'Unknown'}${report.manifest?.metadata?.version ? ` v${report.manifest.metadata.version}` : ''}`,
|
|
1251
|
+
`${currentLang === 'zh-TW' ? '\u4F5C\u8005' : 'Author'}: ${report.manifest?.metadata?.author ?? 'Unknown'}`,
|
|
1252
|
+
`${currentLang === 'zh-TW' ? '\u98A8\u96AA\u5206\u6578' : 'Risk Score'}: ${colorFn(`${report.riskScore}/100 (${report.riskLevel})`)}`,
|
|
1253
|
+
`${currentLang === 'zh-TW' ? '\u6642\u9593' : 'Duration'}: ${report.durationMs}ms`,
|
|
1254
|
+
].join('\n'), { borderColor: c.sage }));
|
|
1255
|
+
console.log('');
|
|
1256
|
+
for (const check of report.checks) {
|
|
1257
|
+
const icon = check.status === 'pass'
|
|
1258
|
+
? c.safe('\u2713')
|
|
1259
|
+
: check.status === 'fail'
|
|
1260
|
+
? c.critical('\u2717')
|
|
1261
|
+
: check.status === 'warn'
|
|
1262
|
+
? c.caution('~')
|
|
1263
|
+
: c.dim('i');
|
|
1264
|
+
const statusLabel = check.status === 'pass'
|
|
1265
|
+
? 'PASS'
|
|
1266
|
+
: check.status === 'fail'
|
|
1267
|
+
? 'FAIL'
|
|
1268
|
+
: check.status === 'warn'
|
|
1269
|
+
? 'WARN'
|
|
1270
|
+
: 'INFO';
|
|
1271
|
+
console.log(` ${icon} [${statusLabel}] ${check.label}`);
|
|
1272
|
+
}
|
|
1273
|
+
console.log('');
|
|
1274
|
+
if (report.findings.length > 0) {
|
|
1275
|
+
console.log(c.bold(currentLang === 'zh-TW'
|
|
1276
|
+
? ` \u767C\u73FE ${report.findings.length} \u500B\u554F\u984C:`
|
|
1277
|
+
: ` ${report.findings.length} finding(s):`));
|
|
1278
|
+
console.log('');
|
|
1279
|
+
for (const finding of report.findings) {
|
|
1280
|
+
const sevColor = finding.severity === 'critical'
|
|
1281
|
+
? c.critical
|
|
1282
|
+
: finding.severity === 'high'
|
|
1283
|
+
? c.caution
|
|
1284
|
+
: c.dim;
|
|
1285
|
+
console.log(` ${sevColor(`[${finding.severity.toUpperCase()}]`)} ${finding.title}`);
|
|
1286
|
+
console.log(` ${c.dim(finding.description)}`);
|
|
1287
|
+
if (finding.location)
|
|
1288
|
+
console.log(` ${c.dim(`at ${finding.location}`)}`);
|
|
1289
|
+
console.log('');
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
nextSteps(currentLang === 'zh-TW'
|
|
1293
|
+
? [
|
|
1294
|
+
{ cmd: 'scan', desc: '\u57F7\u884C\u7CFB\u7D71\u5B89\u5168\u6383\u63CF' },
|
|
1295
|
+
{ cmd: 'guard start', desc: '\u555F\u52D5 24/7 \u5B88\u8B77\u9632\u8B77' },
|
|
1296
|
+
]
|
|
1297
|
+
: [
|
|
1298
|
+
{ cmd: 'scan', desc: 'Run a system security scan' },
|
|
1299
|
+
{ cmd: 'guard start', desc: 'Start 24/7 guard protection' },
|
|
1300
|
+
], currentLang);
|
|
1301
|
+
}
|
|
1302
|
+
catch (err) {
|
|
1303
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1304
|
+
const isNoSkill = errMsg.toLowerCase().includes('skill.md') || errMsg.toLowerCase().includes('not found') || errMsg.toLowerCase().includes('enoent');
|
|
1305
|
+
if (isNoSkill) {
|
|
1306
|
+
sp.warn(currentLang === 'zh-TW'
|
|
1307
|
+
? '\u6B64\u76EE\u9304\u672A\u627E\u5230 AI \u6280\u80FD (SKILL.md)'
|
|
1308
|
+
: 'No AI skills (SKILL.md) found in this directory');
|
|
1309
|
+
console.log('');
|
|
1310
|
+
console.log(c.dim(currentLang === 'zh-TW'
|
|
1311
|
+
? ' Skill Auditor \u6383\u63CF\u5305\u542B SKILL.md \u7684 AI \u6280\u80FD\u76EE\u9304\u3002'
|
|
1312
|
+
: ' Skill Auditor scans AI skill directories containing a SKILL.md file.'));
|
|
1313
|
+
console.log('');
|
|
1314
|
+
console.log(c.dim(currentLang === 'zh-TW'
|
|
1315
|
+
? ' \u5E38\u898B\u6280\u80FD\u4F4D\u7F6E\uFF1A'
|
|
1316
|
+
: ' Common skill locations:'));
|
|
1317
|
+
console.log(c.dim(' ~/.claude/skills/'));
|
|
1318
|
+
console.log(c.dim(' ./.claude/skills/'));
|
|
1319
|
+
console.log(c.dim(' ./.mcp/'));
|
|
1320
|
+
console.log(c.dim(' ./skills/'));
|
|
1321
|
+
console.log('');
|
|
1322
|
+
// Auto-scan known skill directories
|
|
1323
|
+
const fs = await import('node:fs');
|
|
1324
|
+
const path = await import('node:path');
|
|
1325
|
+
const skillDirs = [
|
|
1326
|
+
path.join(homedir(), '.claude', 'skills'),
|
|
1327
|
+
path.join(process.cwd(), '.claude', 'skills'),
|
|
1328
|
+
path.join(process.cwd(), '.mcp'),
|
|
1329
|
+
path.join(process.cwd(), 'skills'),
|
|
1330
|
+
];
|
|
1331
|
+
const foundDirs = [];
|
|
1332
|
+
for (const dir of skillDirs) {
|
|
1333
|
+
if (fs.existsSync(dir)) {
|
|
1334
|
+
// Check for subdirectories that might have SKILL.md
|
|
1335
|
+
try {
|
|
1336
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
1337
|
+
for (const entry of entries) {
|
|
1338
|
+
if (entry.isDirectory()) {
|
|
1339
|
+
const skillMdPath = path.join(dir, entry.name, 'SKILL.md');
|
|
1340
|
+
if (fs.existsSync(skillMdPath)) {
|
|
1341
|
+
foundDirs.push(path.join(dir, entry.name));
|
|
1342
|
+
}
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
// Also check the directory itself
|
|
1346
|
+
if (fs.existsSync(path.join(dir, 'SKILL.md'))) {
|
|
1347
|
+
foundDirs.push(dir);
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
catch {
|
|
1351
|
+
/* skip inaccessible dirs */
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
if (foundDirs.length > 0) {
|
|
1356
|
+
console.log(c.sage(currentLang === 'zh-TW'
|
|
1357
|
+
? ` \u5728\u7CFB\u7D71\u4E2D\u627E\u5230 ${foundDirs.length} \u500B\u6280\u80FD\uFF1A`
|
|
1358
|
+
: ` Found ${foundDirs.length} skill(s) on your system:`));
|
|
1359
|
+
for (const dir of foundDirs) {
|
|
1360
|
+
console.log(c.dim(` ${dir}`));
|
|
1361
|
+
}
|
|
1362
|
+
console.log('');
|
|
1363
|
+
console.log(c.dim(currentLang === 'zh-TW'
|
|
1364
|
+
? ` \u57F7\u884C\uFF1Apanguard audit skill <path> \u4F86\u5BE9\u8A08\u7279\u5B9A\u6280\u80FD`
|
|
1365
|
+
: ` Run: panguard audit skill <path> to audit a specific skill`));
|
|
1366
|
+
}
|
|
1367
|
+
else {
|
|
1368
|
+
console.log(c.dim(currentLang === 'zh-TW'
|
|
1369
|
+
? ' \u7CFB\u7D71\u4E2D\u672A\u627E\u5230\u5DF2\u5B89\u88DD\u7684 AI \u6280\u80FD\u3002'
|
|
1370
|
+
: ' No installed AI skills found on your system.'));
|
|
1371
|
+
console.log(c.dim(currentLang === 'zh-TW'
|
|
1372
|
+
? ' \u5728\u5305\u542B AI \u6280\u80FD\u7684\u5C08\u6848\u76EE\u9304\u4E2D\u57F7\u884C\u6B64\u6307\u4EE4\u3002'
|
|
1373
|
+
: ' Run this command inside a project directory that contains AI skills.'));
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
else {
|
|
1377
|
+
sp.fail(currentLang === 'zh-TW' ? '\u5BE9\u8A08\u5931\u6557' : 'Audit failed');
|
|
1378
|
+
console.log(formatError(errMsg, currentLang === 'zh-TW' ? '\u6280\u80FD\u5BE9\u8A08' : 'Skill Auditor', currentLang === 'zh-TW' ? '\u8ACB\u91CD\u8A66\u6216\u6AA2\u67E5\u65E5\u8A8C' : 'Please retry or check logs'));
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1131
1381
|
}
|
|
1132
1382
|
// ---------------------------------------------------------------------------
|
|
1133
1383
|
// Prompt commands: Hardening
|
|
@@ -1171,15 +1421,6 @@ async function actionStatus() {
|
|
|
1171
1421
|
await cmd.parseAsync(['status'], { from: 'user' });
|
|
1172
1422
|
}
|
|
1173
1423
|
// ---------------------------------------------------------------------------
|
|
1174
|
-
// Prompt commands: Login
|
|
1175
|
-
// ---------------------------------------------------------------------------
|
|
1176
|
-
async function actionLogin() {
|
|
1177
|
-
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5E33\u865F\u767B\u5165' : 'Login']);
|
|
1178
|
-
const { loginCommand } = await import('./commands/login.js');
|
|
1179
|
-
const cmd = loginCommand();
|
|
1180
|
-
await cmd.parseAsync(['login'], { from: 'user' });
|
|
1181
|
-
}
|
|
1182
|
-
// ---------------------------------------------------------------------------
|
|
1183
1424
|
// Prompt commands: Config
|
|
1184
1425
|
// ---------------------------------------------------------------------------
|
|
1185
1426
|
async function actionConfig() {
|