@goplus/agentguard 1.0.14 → 1.1.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.
Files changed (91) hide show
  1. package/README.md +33 -2
  2. package/dist/cli.d.ts +3 -0
  3. package/dist/cli.d.ts.map +1 -0
  4. package/dist/cli.js +172 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/cloud/client.d.ts +19 -0
  7. package/dist/cloud/client.d.ts.map +1 -0
  8. package/dist/cloud/client.js +86 -0
  9. package/dist/cloud/client.js.map +1 -0
  10. package/dist/config.d.ts +31 -0
  11. package/dist/config.d.ts.map +1 -0
  12. package/dist/config.js +131 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/index.d.ts +7 -0
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +25 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/installers.d.ts +10 -0
  19. package/dist/installers.d.ts.map +1 -0
  20. package/dist/installers.js +137 -0
  21. package/dist/installers.js.map +1 -0
  22. package/dist/mcp-server.js +3 -2
  23. package/dist/mcp-server.js.map +1 -1
  24. package/dist/postinstall.d.ts +3 -0
  25. package/dist/postinstall.d.ts.map +1 -0
  26. package/dist/postinstall.js +13 -0
  27. package/dist/postinstall.js.map +1 -0
  28. package/dist/runtime/audit.d.ts +10 -0
  29. package/dist/runtime/audit.d.ts.map +1 -0
  30. package/dist/runtime/audit.js +94 -0
  31. package/dist/runtime/audit.js.map +1 -0
  32. package/dist/runtime/evaluator.d.ts +3 -0
  33. package/dist/runtime/evaluator.d.ts.map +1 -0
  34. package/dist/runtime/evaluator.js +197 -0
  35. package/dist/runtime/evaluator.js.map +1 -0
  36. package/dist/runtime/policy.d.ts +12 -0
  37. package/dist/runtime/policy.d.ts.map +1 -0
  38. package/dist/runtime/policy.js +81 -0
  39. package/dist/runtime/policy.js.map +1 -0
  40. package/dist/runtime/protect.d.ts +22 -0
  41. package/dist/runtime/protect.d.ts.map +1 -0
  42. package/dist/runtime/protect.js +172 -0
  43. package/dist/runtime/protect.js.map +1 -0
  44. package/dist/runtime/redaction.d.ts +6 -0
  45. package/dist/runtime/redaction.d.ts.map +1 -0
  46. package/dist/runtime/redaction.js +103 -0
  47. package/dist/runtime/redaction.js.map +1 -0
  48. package/dist/runtime/types.d.ts +62 -0
  49. package/dist/runtime/types.d.ts.map +1 -0
  50. package/dist/runtime/types.js +3 -0
  51. package/dist/runtime/types.js.map +1 -0
  52. package/dist/scanner/rules/trojan.js +1 -1
  53. package/dist/scanner/rules/trojan.js.map +1 -1
  54. package/dist/tests/cloud-live.test.d.ts +2 -0
  55. package/dist/tests/cloud-live.test.d.ts.map +1 -0
  56. package/dist/tests/cloud-live.test.js +68 -0
  57. package/dist/tests/cloud-live.test.js.map +1 -0
  58. package/dist/tests/installer.test.d.ts +2 -0
  59. package/dist/tests/installer.test.d.ts.map +1 -0
  60. package/dist/tests/installer.test.js +32 -0
  61. package/dist/tests/installer.test.js.map +1 -0
  62. package/dist/tests/runtime-cloud.test.d.ts +2 -0
  63. package/dist/tests/runtime-cloud.test.d.ts.map +1 -0
  64. package/dist/tests/runtime-cloud.test.js +202 -0
  65. package/dist/tests/runtime-cloud.test.js.map +1 -0
  66. package/dist/version.d.ts +2 -0
  67. package/dist/version.d.ts.map +1 -0
  68. package/dist/version.js +7 -0
  69. package/dist/version.js.map +1 -0
  70. package/docs/SECURITY-POLICY.md +558 -0
  71. package/docs/architecture.md +54 -0
  72. package/docs/claude-code.md +41 -0
  73. package/docs/cloud-connect.md +73 -0
  74. package/docs/cloud-native-api.md +526 -0
  75. package/docs/codex.md +38 -0
  76. package/docs/goplus-api.md +38 -0
  77. package/docs/mcp-server.md +39 -0
  78. package/docs/openclaw.md +41 -0
  79. package/docs/privacy-boundary.md +37 -0
  80. package/docs/sdk.md +83 -0
  81. package/docs/trust-cli.md +58 -0
  82. package/examples/openclaw-docker/Dockerfile +10 -0
  83. package/examples/openclaw-docker/README.md +16 -0
  84. package/examples/openclaw-docker/docker-compose.yml +8 -0
  85. package/examples/openclaw-docker/plugin.ts +8 -0
  86. package/package.json +7 -2
  87. package/skills/agentguard/SKILL.md +157 -61
  88. package/skills/agentguard/{scripts/package.json → package.json} +2 -1
  89. package/skills/agentguard/patrol-checks.md +12 -2
  90. package/skills/agentguard/scan-rules.md +1 -1
  91. package/skills/agentguard/scripts/checkup-report.js +71 -30
@@ -75,6 +75,7 @@ Detailed commands, patterns, and thresholds for the 8 patrol checks. This docume
75
75
 
76
76
  ### Permission Checks
77
77
 
78
+ **macOS/Linux:**
78
79
  ```bash
79
80
  # SSH directory — should be 700
80
81
  stat -f "%Lp" ~/.ssh/ 2>/dev/null || stat -c "%a" ~/.ssh/ 2>/dev/null
@@ -82,10 +83,19 @@ stat -f "%Lp" ~/.ssh/ 2>/dev/null || stat -c "%a" ~/.ssh/ 2>/dev/null
82
83
  stat -f "%Lp" ~/.gnupg/ 2>/dev/null || stat -c "%a" ~/.gnupg/ 2>/dev/null
83
84
  ```
84
85
 
86
+ **Windows (use icacls instead of stat):**
87
+ ```powershell
88
+ icacls $env:USERPROFILE\.ssh 2>$null
89
+ icacls $env:USERPROFILE\.gnupg 2>$null
90
+ ```
91
+
85
92
  | Condition | Severity |
86
93
  |-----------|----------|
87
- | `~/.ssh/` permissions > 700 | HIGH |
88
- | `~/.gnupg/` permissions > 700 | MEDIUM |
94
+ | macOS/Linux: `~/.ssh/` exists AND permissions > 700 | HIGH |
95
+ | macOS/Linux: `~/.gnupg/` exists AND permissions > 700 | MEDIUM |
96
+ | Windows: `~/.ssh/` exists AND ACL grants access to Everyone/Users/Authenticated Users | HIGH |
97
+ | Windows: `~/.gnupg/` exists AND ACL grants access to Everyone/Users/Authenticated Users | MEDIUM |
98
+ | Directory does not exist (stat/icacls returns empty) | N/A — not a finding |
89
99
 
90
100
  ---
91
101
 
@@ -292,7 +292,7 @@ Detects trojanized binary distribution patterns. Flags when 2+ of the following
292
292
  - Version-like patterns (`x.0.0.0`)
293
293
  - Values > 255 in any octet
294
294
 
295
- ## Rule 24: SOCIAL_ENGINEERING (MEDIUM)
295
+ ## Rule 24: SOCIAL_ENGINEERING (HIGH)
296
296
  **Files**: `*.md`
297
297
 
298
298
  | Pattern | Description |
@@ -15,9 +15,17 @@
15
15
  import { writeFileSync, readFileSync, existsSync } from 'node:fs';
16
16
  import { join, dirname } from 'node:path';
17
17
  import { tmpdir, homedir } from 'node:os';
18
- import { exec } from 'node:child_process';
18
+ import open from 'open';
19
19
  import { fileURLToPath } from 'node:url';
20
20
 
21
+ const DIM_META = {
22
+ code_safety: { icon: 'find_in_page', name: 'Skill & Code Safety', zh: '技能与代码安全' },
23
+ credential_safety: { icon: 'key', name: 'Credential & Secrets', zh: '凭证与密钥安全' },
24
+ network_exposure: { icon: 'lan', name: 'Network & System', zh: '网络与系统暴露' },
25
+ runtime_protection: { icon: 'shield', name: 'Runtime Protection', zh: '运行时防护' },
26
+ web3_safety: { icon: 'token', name: 'Web3 Safety', zh: 'Web3 安全' },
27
+ };
28
+
21
29
  // Try to load favicon from agentguard-server or fallback
22
30
  const __dirname = dirname(fileURLToPath(import.meta.url));
23
31
  let faviconB64 = '';
@@ -29,13 +37,27 @@ for (const p of iconPaths) {
29
37
  if (existsSync(p)) { faviconB64 = readFileSync(p).toString('base64'); break; }
30
38
  }
31
39
 
32
- let input = '';
33
- process.stdin.setEncoding('utf8');
34
- process.stdin.on('data', (chunk) => { input += chunk; });
35
- process.stdin.on('end', () => {
36
- try { generateReport(JSON.parse(input)); }
37
- catch (err) { process.stderr.write(`Error: ${err.message}\n`); process.exit(1); }
38
- });
40
+ // Support --file <path> argument to read JSON from file (cross-platform friendly)
41
+ const fileArgIdx = process.argv.indexOf('--file');
42
+ if (fileArgIdx !== -1 && process.argv[fileArgIdx + 1]) {
43
+ const filePath = process.argv[fileArgIdx + 1];
44
+ try {
45
+ const content = readFileSync(filePath, 'utf8');
46
+ generateReport(JSON.parse(content));
47
+ } catch (err) {
48
+ process.stderr.write(`Error reading ${filePath}: ${err.message}\n`);
49
+ process.exit(1);
50
+ }
51
+ } else {
52
+ // Fallback: read JSON from stdin (pipe)
53
+ let input = '';
54
+ process.stdin.setEncoding('utf8');
55
+ process.stdin.on('data', (chunk) => { input += chunk; });
56
+ process.stdin.on('end', () => {
57
+ try { generateReport(JSON.parse(input)); }
58
+ catch (err) { process.stderr.write(`Error: ${err.message}\n`); process.exit(1); }
59
+ });
60
+ }
39
61
 
40
62
  // ---------------------------------------------------------------------------
41
63
  // Helpers
@@ -73,13 +95,7 @@ function getTier(score) {
73
95
  ])};
74
96
  }
75
97
 
76
- const DIM_META = {
77
- code_safety: { icon: 'find_in_page', name: 'Skill & Code Safety', zh: '技能与代码安全' },
78
- credential_safety: { icon: 'key', name: 'Credential & Secrets', zh: '凭证与密钥安全' },
79
- network_exposure: { icon: 'lan', name: 'Network & System', zh: '网络与系统暴露' },
80
- runtime_protection: { icon: 'shield', name: 'Runtime Protection', zh: '运行时防护' },
81
- web3_safety: { icon: 'token', name: 'Web3 Safety', zh: 'Web3 安全' },
82
- };
98
+
83
99
 
84
100
  function esc(s) { return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;'); }
85
101
 
@@ -730,7 +746,8 @@ function generateReport(data) {
730
746
  autoRecs.push({ severity: 'LOW', text: 'Enable auto-scan on session start: export AGENTGUARD_AUTO_SCAN=1', zh: '启用会话启动时自动扫描:export AGENTGUARD_AUTO_SCAN=1' });
731
747
 
732
748
  // Merge: user recs first, then auto recs (dedup by text similarity)
733
- const allRecs = [...recommendations];
749
+ // Guard against malformed entries where text may be undefined (#37)
750
+ const allRecs = recommendations.filter(r => r && typeof r.text === 'string');
734
751
  for (const ar of autoRecs) {
735
752
  if (!allRecs.some(r => r.text.toLowerCase().includes(ar.text.slice(0, 30).toLowerCase()))) {
736
753
  allRecs.push(ar);
@@ -747,11 +764,10 @@ function generateReport(data) {
747
764
  const sc = sevColor(sev);
748
765
  const zhText = r.zh || r.text;
749
766
  return `
750
- <div class="flex items-center gap-3 px-4 py-3 rounded-lg hover:bg-[#262a31]/30 transition-colors group">
767
+ <div class="flex items-center gap-3 px-4 py-3 rounded-lg">
751
768
  <span class="w-5 text-xs font-headline font-bold text-[#849588]/60">${i+1}</span>
752
769
  <span class="px-2 py-0.5 rounded text-[9px] font-bold uppercase tracking-wide text-white shrink-0" style="background:${sc}">${sev}</span>
753
770
  <span class="text-sm text-[#b9cbbd] leading-snug rec-text" data-en="${esc(r.text)}" data-zh="${esc(zhText)}">${esc(r.text)}</span>
754
- <span class="material-symbols-outlined text-sm text-[#849588]/0 group-hover:text-[#849588]/50 transition-colors ml-auto shrink-0">chevron_right</span>
755
771
  </div>`;
756
772
  }).join('')}</div>`
757
773
  : '<div class="text-center py-12 text-[#849588]" data-i18n="no_recs">No recommendations.</div>';
@@ -759,12 +775,7 @@ function generateReport(data) {
759
775
  // ── AI Analysis report ──
760
776
  const analysisText = data.analysis || '';
761
777
  const analysisHtml = analysisText
762
- ? `<div class="relative group">
763
- <div class="bg-[#0a0e14] border border-[#3a4a3f]/10 rounded-xl p-5 text-sm text-[#b9cbbd] leading-relaxed whitespace-pre-line" id="analysisText">${esc(analysisText)}</div>
764
- <button onclick="copyReport()" id="copyBtn" class="absolute top-3 right-3 flex items-center gap-1.5 px-3 py-1.5 bg-[#262a31] border border-[#3a4a3f]/30 rounded-lg text-[11px] font-semibold text-[#849588] hover:text-[#dfe2eb] hover:border-[#849588]/50 transition-all opacity-0 group-hover:opacity-100">
765
- <span class="material-symbols-outlined text-sm" id="copyIcon">content_copy</span><span id="copyLabel" data-i18n="copy_report">Copy Report</span>
766
- </button>
767
- </div>`
778
+ ? `<div class="bg-[#0a0e14] border border-[#3a4a3f]/10 rounded-xl p-5 text-sm text-[#b9cbbd] leading-relaxed whitespace-pre-line" id="analysisText">${esc(analysisText)}</div>`
768
779
  : '';
769
780
 
770
781
  // ── Health status label ──
@@ -919,6 +930,9 @@ body{background:#0a0e14;color:#dfe2eb;font-family:'Inter',sans-serif}
919
930
  <p class="text-[10px] font-label uppercase tracking-[0.2em] text-[#849588] mb-1" data-i18n="sec_analysis">Security Analysis</p>
920
931
  <h1 class="text-2xl font-headline font-bold text-[#f5fff5] tracking-tight flex items-center gap-3">
921
932
  <span class="material-symbols-outlined" style="color:${tier.color}">analytics</span><span data-i18n="diag_report">Diagnostic Report</span>
933
+ <button onclick="copyReport()" id="copyBtn" class="flex items-center gap-1 px-2 py-1 bg-[#262a31] border border-[#3a4a3f]/30 rounded-lg text-[11px] font-semibold text-[#849588] hover:text-[#dfe2eb] hover:border-[#849588]/50 transition-all ml-1">
934
+ <span class="material-symbols-outlined text-sm" id="copyIcon">content_copy</span><span id="copyLabel" class="hidden sm:inline" data-i18n="copy_report">Copy Report</span>
935
+ </button>
922
936
  </h1>
923
937
  </div>
924
938
  <div class="bg-[#262a31] border border-[#3a4a3f]/15 rounded-lg px-4 py-2 flex items-center gap-2">
@@ -1036,7 +1050,7 @@ body{background:#0a0e14;color:#dfe2eb;font-family:'Inter',sans-serif}
1036
1050
  i18n.zh.quote=quotes_zh['${tier.grade}']||quotes_zh.B;
1037
1051
  i18n.en.quote='"${tier.quote.replace(/'/g,"\\'")}\"';
1038
1052
 
1039
- let curLang='en';
1053
+ let curLang=(${JSON.stringify(data.analysis||'')}).match(/[\u4e00-\u9fff]/) ? 'zh' : 'en';
1040
1054
  window.toggleLang=function(){
1041
1055
  curLang=curLang==='en'?'zh':'en';
1042
1056
  document.getElementById('langLabel').textContent=curLang==='en'?'中文':'EN';
@@ -1054,6 +1068,22 @@ body{background:#0a0e14;color:#dfe2eb;font-family:'Inter',sans-serif}
1054
1068
  });
1055
1069
  };
1056
1070
 
1071
+ // Apply initial language on load (auto-detect Chinese from analysis content)
1072
+ if(curLang==='zh'){
1073
+ document.getElementById('langLabel').textContent='EN';
1074
+ document.querySelectorAll('[data-i18n]').forEach(el=>{
1075
+ const key=el.getAttribute('data-i18n');
1076
+ if(key==='findings_range'){
1077
+ const range=el.getAttribute('data-range'),total=el.getAttribute('data-total');
1078
+ el.textContent='发现 — '+range+' / 共 '+total;
1079
+ } else if(i18n.zh[key]!=null)el.textContent=i18n.zh[key];
1080
+ });
1081
+ document.querySelectorAll('.finding-dim,.finding-text,.clean-dims,.rec-text').forEach(el=>{
1082
+ const t=el.getAttribute('data-zh');
1083
+ if(t)el.textContent=t;
1084
+ });
1085
+ }
1086
+
1057
1087
  // Dimension data for share card (must be before shareReport)
1058
1088
  const _dims=${JSON.stringify(Object.fromEntries(Object.entries(DIM_META).map(([k])=>[k,dimensions[k]||{score:null,na:false}])))};
1059
1089
 
@@ -1334,11 +1364,22 @@ body{background:#0a0e14;color:#dfe2eb;font-family:'Inter',sans-serif}
1334
1364
 
1335
1365
  const outPath = join(tmpdir(), `agentguard-checkup-${Date.now()}.html`);
1336
1366
  writeFileSync(outPath, html, 'utf8');
1337
- console.log(outPath);
1338
1367
 
1339
- const cmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
1340
- exec(`${cmd} "${outPath}"`, (err) => {
1341
- if (err) process.stderr.write(`Could not open browser: ${err.message}\n`);
1368
+ // Skip browser open for headless/bot environments (Qclaw, OpenClaw, CI)
1369
+ const isHeadless = process.env.OPENCLAW_STATE_DIR || process.env.QCLAW || process.env.CI;
1370
+
1371
+ // Flush stdout before doing anything else — on Windows/Linux in non-TTY/pipe
1372
+ // mode, console.log() is non-blocking and process.exit() can terminate before
1373
+ // the buffer is flushed, causing the caller (Claude) to receive an empty path.
1374
+ // Flush stdout before doing anything else — on Windows/Linux in non-TTY/pipe
1375
+ // mode, console.log() is non-blocking and process.exit() can terminate before
1376
+ // the buffer is flushed, causing the caller (Claude) to receive an empty path.
1377
+ process.stdout.write(outPath + '\n', () => {
1378
+ if (!isHeadless) {
1379
+ open(outPath).catch(err => process.stderr.write(`Could not open browser: ${err.message}\n`));
1380
+ }
1381
+ // Hard exit after 3s — guards against exec child process hanging and
1382
+ // blocking Node from exiting naturally (e.g. xdg-open on misconfigured Linux).
1383
+ setTimeout(() => process.exit(0), 3000).unref();
1342
1384
  });
1343
- setTimeout(() => process.exit(0), 2000);
1344
1385
  }