@panguard-ai/panguard 0.3.0 → 0.3.2
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 +504 -244
- 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 +24 -12
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
|
}
|
|
@@ -546,17 +707,43 @@ async function actionScan() {
|
|
|
546
707
|
: 'No security issues found';
|
|
547
708
|
console.log(` ${c.safe(noIssues)}`);
|
|
548
709
|
}
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
{
|
|
559
|
-
|
|
710
|
+
// Agent auto-suggestion: offer to activate Guard if not running
|
|
711
|
+
const guardRunning = isGuardRunning().running;
|
|
712
|
+
if (!guardRunning) {
|
|
713
|
+
console.log('');
|
|
714
|
+
const agentMsg = currentLang === 'zh-TW'
|
|
715
|
+
? `${c.sage('\u25C6')} \u5373\u6642\u9632\u8B77\u5C1A\u672A\u555F\u52D5\u3002\u8981\u73FE\u5728\u555F\u7528\u55CE\uFF1F`
|
|
716
|
+
: `${c.sage('\u25C6')} Real-time protection is not active. Enable now?`;
|
|
717
|
+
console.log(` ${agentMsg}`);
|
|
718
|
+
const guardItems = [
|
|
719
|
+
{ key: '1', label: currentLang === 'zh-TW' ? '\u662F\uFF0C\u555F\u52D5 Guard \u9632\u8B77' : 'Yes, start Guard protection' },
|
|
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' },
|
|
722
|
+
];
|
|
723
|
+
renderCompactMenu('', guardItems);
|
|
724
|
+
const guardChoice = await waitForCompactChoice(guardItems, currentLang);
|
|
725
|
+
if (guardChoice?.key === '1') {
|
|
726
|
+
await actionGuard();
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
if (guardChoice?.key === '2') {
|
|
730
|
+
await actionAudit();
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
else {
|
|
735
|
+
nextSteps(currentLang === 'zh-TW'
|
|
736
|
+
? [
|
|
737
|
+
{ cmd: '[8] \u6280\u80FD\u5BE9\u8A08', desc: '\u5BE9\u8A08\u5DF2\u5B89\u88DD\u6280\u80FD\u7684\u5B89\u5168\u5A01\u8105' },
|
|
738
|
+
{ cmd: 'scan --full', desc: '\u57F7\u884C\u5B8C\u6574\u6383\u63CF' },
|
|
739
|
+
{ cmd: 'guard start', desc: '\u555F\u52D5\u5373\u6642\u9632\u8B77' },
|
|
740
|
+
]
|
|
741
|
+
: [
|
|
742
|
+
{ cmd: '[8] Skill Auditor', desc: 'Audit installed skills for threats' },
|
|
743
|
+
{ cmd: 'scan --full', desc: 'Run a comprehensive scan' },
|
|
744
|
+
{ cmd: 'guard start', desc: 'Enable real-time protection' },
|
|
745
|
+
], currentLang);
|
|
746
|
+
}
|
|
560
747
|
}
|
|
561
748
|
// ---------------------------------------------------------------------------
|
|
562
749
|
// [2] Compliance Report
|
|
@@ -566,54 +753,21 @@ async function actionReport() {
|
|
|
566
753
|
'Panguard',
|
|
567
754
|
currentLang === 'zh-TW' ? '\u5408\u898F\u5831\u544A' : 'Compliance Report',
|
|
568
755
|
]);
|
|
569
|
-
const title = currentLang === 'zh-TW' ? '\u5408\u898F\u5831\u544A' : 'Compliance Report';
|
|
570
|
-
console.log(` ${theme.brandBold(title)}`);
|
|
571
756
|
console.log('');
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
return;
|
|
586
|
-
const frameworkMap = {
|
|
587
|
-
'1': 'iso27001',
|
|
588
|
-
'2': 'soc2',
|
|
589
|
-
'3': 'tw_cyber_security_act',
|
|
590
|
-
};
|
|
591
|
-
const framework = frameworkMap[fwChoice.key] ?? 'iso27001';
|
|
592
|
-
const langItems = [
|
|
593
|
-
{
|
|
594
|
-
key: '1',
|
|
595
|
-
label: currentLang === 'zh-TW' ? '\u7E41\u9AD4\u4E2D\u6587 (zh-TW)' : 'Chinese (zh-TW)',
|
|
596
|
-
},
|
|
597
|
-
{ key: '2', label: currentLang === 'zh-TW' ? '\u82F1\u6587 (en)' : 'English (en)' },
|
|
598
|
-
];
|
|
599
|
-
console.log('');
|
|
600
|
-
renderCompactMenu(currentLang === 'zh-TW' ? '\u5831\u544A\u8A9E\u8A00' : 'Report Language', langItems);
|
|
601
|
-
const langChoice = await waitForCompactChoice(langItems, currentLang);
|
|
602
|
-
if (!langChoice)
|
|
603
|
-
return;
|
|
604
|
-
const reportLang = langChoice.key === '1' ? 'zh-TW' : 'en';
|
|
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
|
+
}
|
|
605
770
|
console.log('');
|
|
606
|
-
const { executeCli } = await import('@panguard-ai/panguard-report');
|
|
607
|
-
await executeCli(['generate', '--framework', framework, '--language', reportLang]);
|
|
608
|
-
nextSteps(currentLang === 'zh-TW'
|
|
609
|
-
? [
|
|
610
|
-
{ cmd: 'scan', desc: '\u57F7\u884C\u5B89\u5168\u6383\u63CF' },
|
|
611
|
-
{ cmd: 'guard start', desc: '\u555F\u52D5\u5373\u6642\u9632\u8B77' },
|
|
612
|
-
]
|
|
613
|
-
: [
|
|
614
|
-
{ cmd: 'scan', desc: 'Run a security scan' },
|
|
615
|
-
{ cmd: 'guard start', desc: 'Enable real-time protection' },
|
|
616
|
-
], currentLang);
|
|
617
771
|
}
|
|
618
772
|
// ---------------------------------------------------------------------------
|
|
619
773
|
// [3] Guard Engine
|
|
@@ -657,27 +811,24 @@ async function actionGuard() {
|
|
|
657
811
|
], currentLang);
|
|
658
812
|
break;
|
|
659
813
|
}
|
|
660
|
-
//
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
console.log('');
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
console.log(` ${c.dim('\u2500')} \u901A\u77E5\u7CFB\u7D71 ${c.dim('(Solo $9/\u6708)')}`);
|
|
670
|
-
console.log(` ${c.dim('\u2500')} \u65E5\u8A8C\u4FDD\u7559 7 \u5929+ ${c.dim('(Solo $9/\u6708)')}`);
|
|
671
|
-
}
|
|
672
|
-
else {
|
|
673
|
-
console.log(` ${c.safe('\u2713')} Auto-blocking for known attack patterns`);
|
|
674
|
-
console.log(` ${c.safe('\u2713')} Threat Cloud intelligence`);
|
|
675
|
-
console.log(` ${c.dim('\u2500')} AI analysis ${c.dim('(Solo $9/mo)')}`);
|
|
676
|
-
console.log(` ${c.dim('\u2500')} Notifications ${c.dim('(Solo $9/mo)')}`);
|
|
677
|
-
console.log(` ${c.dim('\u2500')} Log retention 7d+ ${c.dim('(Solo $9/mo)')}`);
|
|
678
|
-
}
|
|
679
|
-
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`);
|
|
680
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`);
|
|
830
|
+
}
|
|
831
|
+
console.log('');
|
|
681
832
|
// Spawn guard as background process
|
|
682
833
|
const { spawn: spawnProcess } = await import('node:child_process');
|
|
683
834
|
const { fileURLToPath: toPath } = await import('node:url');
|
|
@@ -783,46 +934,21 @@ async function actionTrap() {
|
|
|
783
934
|
'Panguard',
|
|
784
935
|
currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Honeypot System',
|
|
785
936
|
]);
|
|
786
|
-
const title = currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Honeypot System';
|
|
787
|
-
console.log(` ${theme.brandBold(title)}`);
|
|
788
937
|
console.log('');
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
{
|
|
795
|
-
key: '2',
|
|
796
|
-
label: currentLang === 'zh-TW'
|
|
797
|
-
? '\u5B8C\u6574\u914D\u7F6E (8 \u7A2E\u670D\u52D9)'
|
|
798
|
-
: 'Config (All 8 Services)',
|
|
799
|
-
},
|
|
800
|
-
{ key: '3', label: currentLang === 'zh-TW' ? '\u67E5\u770B\u72C0\u614B' : 'Status' },
|
|
801
|
-
{
|
|
802
|
-
key: '4',
|
|
803
|
-
label: currentLang === 'zh-TW' ? '\u653B\u64CA\u8005\u5206\u6790' : 'Attacker Profiles',
|
|
804
|
-
},
|
|
805
|
-
];
|
|
806
|
-
renderCompactMenu(currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Honeypot System', items);
|
|
807
|
-
const choice = await waitForCompactChoice(items, currentLang);
|
|
808
|
-
if (!choice)
|
|
809
|
-
return;
|
|
810
|
-
console.log('');
|
|
811
|
-
const { executeCli } = await import('@panguard-ai/panguard-trap');
|
|
812
|
-
switch (choice.key) {
|
|
813
|
-
case '1':
|
|
814
|
-
await executeCli(['config', '--services', 'ssh,http']);
|
|
815
|
-
break;
|
|
816
|
-
case '2':
|
|
817
|
-
await executeCli(['config', '--services', 'ssh,http,ftp,telnet,mysql,redis,smb,rdp']);
|
|
818
|
-
break;
|
|
819
|
-
case '3':
|
|
820
|
-
await executeCli(['status']);
|
|
821
|
-
break;
|
|
822
|
-
case '4':
|
|
823
|
-
await executeCli(['profiles']);
|
|
824
|
-
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');
|
|
825
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('');
|
|
826
952
|
}
|
|
827
953
|
// ---------------------------------------------------------------------------
|
|
828
954
|
// [5] Notifications
|
|
@@ -885,7 +1011,17 @@ async function actionThreat() {
|
|
|
885
1011
|
? ` \u5373\u5C07\u555F\u52D5\u5A01\u8105\u60C5\u5831 REST API \u4F3A\u670D\u5668 (port ${port})`
|
|
886
1012
|
: ` Starting Threat Cloud REST API server (port ${port})...`));
|
|
887
1013
|
console.log('');
|
|
888
|
-
|
|
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
|
+
}
|
|
889
1025
|
const sp = spinner(currentLang === 'zh-TW'
|
|
890
1026
|
? '\u6B63\u5728\u555F\u52D5 Threat Cloud API...'
|
|
891
1027
|
: 'Starting Threat Cloud API server...');
|
|
@@ -922,7 +1058,6 @@ async function actionThreat() {
|
|
|
922
1058
|
async function actionDemo() {
|
|
923
1059
|
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u529F\u80FD\u5C55\u793A' : 'Feature Demo']);
|
|
924
1060
|
const { runScan } = await import('@panguard-ai/panguard-scan');
|
|
925
|
-
const { generateComplianceReport, generateSummaryText } = await import('@panguard-ai/panguard-report');
|
|
926
1061
|
const title = currentLang === 'zh-TW' ? '\u529F\u80FD\u5C55\u793A' : 'Feature Demo';
|
|
927
1062
|
console.log(` ${theme.brandBold(title)}`);
|
|
928
1063
|
console.log(c.dim(currentLang === 'zh-TW'
|
|
@@ -963,59 +1098,14 @@ async function actionDemo() {
|
|
|
963
1098
|
});
|
|
964
1099
|
}
|
|
965
1100
|
console.log('');
|
|
966
|
-
// 2. Compliance Report
|
|
1101
|
+
// 2. Compliance Report (Coming Soon)
|
|
967
1102
|
console.log(c.dim(currentLang === 'zh-TW'
|
|
968
|
-
? ' 2. \u5408\u898F\u5831\u544A
|
|
969
|
-
: ' 2. Compliance Report
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
try {
|
|
975
|
-
const findings = [
|
|
976
|
-
{
|
|
977
|
-
findingId: 'DEMO-001',
|
|
978
|
-
severity: 'high',
|
|
979
|
-
title: 'Missing information security policy',
|
|
980
|
-
description: 'No formal information security policy document found.',
|
|
981
|
-
category: 'policy',
|
|
982
|
-
timestamp: new Date(),
|
|
983
|
-
source: 'panguard-scan',
|
|
984
|
-
},
|
|
985
|
-
{
|
|
986
|
-
findingId: 'DEMO-002',
|
|
987
|
-
severity: 'medium',
|
|
988
|
-
title: 'Unencrypted data at rest',
|
|
989
|
-
description: 'Database storage does not use encryption at rest.',
|
|
990
|
-
category: 'encryption',
|
|
991
|
-
timestamp: new Date(),
|
|
992
|
-
source: 'panguard-scan',
|
|
993
|
-
},
|
|
994
|
-
];
|
|
995
|
-
const report = generateComplianceReport(findings, 'iso27001', 'en', {
|
|
996
|
-
includeRecommendations: true,
|
|
997
|
-
});
|
|
998
|
-
const summary = generateSummaryText(report);
|
|
999
|
-
reportSp.succeed(currentLang === 'zh-TW'
|
|
1000
|
-
? 'ISO 27001 \u5831\u544A\u5DF2\u7522\u751F'
|
|
1001
|
-
: 'ISO 27001 report generated');
|
|
1002
|
-
const summaryLines = summary.split('\n').slice(0, 10);
|
|
1003
|
-
for (const line of summaryLines) {
|
|
1004
|
-
console.log(` ${c.dim(line)}`);
|
|
1005
|
-
}
|
|
1006
|
-
console.log(c.dim(' ...'));
|
|
1007
|
-
results.push({
|
|
1008
|
-
name: currentLang === 'zh-TW' ? '\u5408\u898F\u5831\u544A' : 'Report',
|
|
1009
|
-
status: 'ok',
|
|
1010
|
-
});
|
|
1011
|
-
}
|
|
1012
|
-
catch (err) {
|
|
1013
|
-
reportSp.fail(`${err instanceof Error ? err.message : err}`);
|
|
1014
|
-
results.push({
|
|
1015
|
-
name: currentLang === 'zh-TW' ? '\u5408\u898F\u5831\u544A' : 'Report',
|
|
1016
|
-
status: 'fail',
|
|
1017
|
-
});
|
|
1018
|
-
}
|
|
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
|
+
});
|
|
1019
1109
|
console.log('');
|
|
1020
1110
|
// 3. Guard Engine
|
|
1021
1111
|
console.log(c.dim(currentLang === 'zh-TW' ? ' 3. \u5B88\u8B77\u5F15\u64CE' : ' 3. Guard Engine'));
|
|
@@ -1038,24 +1128,14 @@ async function actionDemo() {
|
|
|
1038
1128
|
});
|
|
1039
1129
|
}
|
|
1040
1130
|
console.log('');
|
|
1041
|
-
// 4. Honeypot
|
|
1042
|
-
console.log(c.dim(currentLang === 'zh-TW'
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
status: 'ok',
|
|
1050
|
-
});
|
|
1051
|
-
}
|
|
1052
|
-
catch (err) {
|
|
1053
|
-
console.log(` ${c.critical(err instanceof Error ? err.message : String(err))}`);
|
|
1054
|
-
results.push({
|
|
1055
|
-
name: currentLang === 'zh-TW' ? '\u871C\u7F50\u7CFB\u7D71' : 'Trap',
|
|
1056
|
-
status: 'fail',
|
|
1057
|
-
});
|
|
1058
|
-
}
|
|
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
|
+
});
|
|
1059
1139
|
console.log('');
|
|
1060
1140
|
// 5. Notifications
|
|
1061
1141
|
console.log(c.dim(currentLang === 'zh-TW' ? ' 5. \u901A\u77E5\u7CFB\u7D71' : ' 5. Notification System'));
|
|
@@ -1102,13 +1182,202 @@ async function actionDemo() {
|
|
|
1102
1182
|
], currentLang);
|
|
1103
1183
|
}
|
|
1104
1184
|
// ---------------------------------------------------------------------------
|
|
1105
|
-
//
|
|
1185
|
+
// [8] Skill Auditor
|
|
1106
1186
|
// ---------------------------------------------------------------------------
|
|
1107
|
-
async function
|
|
1108
|
-
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\
|
|
1109
|
-
const
|
|
1110
|
-
|
|
1111
|
-
|
|
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
|
+
}
|
|
1112
1381
|
}
|
|
1113
1382
|
// ---------------------------------------------------------------------------
|
|
1114
1383
|
// Prompt commands: Hardening
|
|
@@ -1152,15 +1421,6 @@ async function actionStatus() {
|
|
|
1152
1421
|
await cmd.parseAsync(['status'], { from: 'user' });
|
|
1153
1422
|
}
|
|
1154
1423
|
// ---------------------------------------------------------------------------
|
|
1155
|
-
// Prompt commands: Login
|
|
1156
|
-
// ---------------------------------------------------------------------------
|
|
1157
|
-
async function actionLogin() {
|
|
1158
|
-
breadcrumb(['Panguard', currentLang === 'zh-TW' ? '\u5E33\u865F\u767B\u5165' : 'Login']);
|
|
1159
|
-
const { loginCommand } = await import('./commands/login.js');
|
|
1160
|
-
const cmd = loginCommand();
|
|
1161
|
-
await cmd.parseAsync(['login'], { from: 'user' });
|
|
1162
|
-
}
|
|
1163
|
-
// ---------------------------------------------------------------------------
|
|
1164
1424
|
// Prompt commands: Config
|
|
1165
1425
|
// ---------------------------------------------------------------------------
|
|
1166
1426
|
async function actionConfig() {
|