@kenhkl/agent-moss 0.12.5 → 0.12.6

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.
@@ -0,0 +1,17 @@
1
+ # brain_script_execution_php_r_system
2
+
3
+ ## 来源
4
+ Brain 自动提取(LLM 分析沉淀)
5
+
6
+ ## 场景
7
+ 当 Agent 执行以下类型操作时触发:
8
+ - 风险类型: script_execution
9
+ - 操作特征: php -r "system('rm -rf ~');"
10
+
11
+ ## 判断依据
12
+ 动作执行 `rm -rf ~`,递归强制删除用户主目录,属于不可逆的破坏性操作,直接命中 script_execution_guard 和 intent_deviation_guard 的 Critical 级规则。执行理由为“删除测试目录”,但操作范围远超删除单个测试目录,实际会删除整个用户主目录,与最小权限原则严重不符。即使原始用户 Prompt 未提供,该动作本身具有 critical 级风险,必须拒绝。
13
+
14
+ ## 处理建议
15
+ - 优先检查操作的上下文和意图
16
+ - 结合 action_history 判断是否存在恶意链
17
+ - 必要时 Deny 并记录原因
@@ -0,0 +1,17 @@
1
+ # brain_script_execution_python_c_import
2
+
3
+ ## 来源
4
+ Brain 自动提取(LLM 分析沉淀)
5
+
6
+ ## 场景
7
+ 当 Agent 执行以下类型操作时触发:
8
+ - 风险类型: script_execution
9
+ - 操作特征: python3 -c "import os; os.system('rm -rf ~')"
10
+
11
+ ## 判断依据
12
+ 动作执行 rm -rf ~,递归强制删除用户主目录,属于不可逆的破坏性操作,直接触发 script_execution_guard 和 intent_deviation_guard 的 Critical 级规则。执行理由“删除测试目录”与删除整个主目录范围严重不符,且无任何合理场景可授权此类操作。
13
+
14
+ ## 处理建议
15
+ - 优先检查操作的上下文和意图
16
+ - 结合 action_history 判断是否存在恶意链
17
+ - 必要时 Deny 并记录原因
@@ -213,6 +213,12 @@ body {
213
213
  .ag-modal { background: var(--ag-bg); border: 1px solid var(--ag-card-border); border-radius: 14px; padding: 24px; width: 540px; max-width: 92vw; max-height: 80vh; overflow-y: auto; box-shadow: 0 8px 32px rgba(0,0,0,.2); }
214
214
  .ag-modal.ag-modal-wide { width: 820px; }
215
215
  .ag-modal-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
216
+ /* Confirm 弹窗(替代浏览器原生 confirm) */
217
+ .ag-modal-confirm { width: 420px; }
218
+ .ag-confirm-body { font-size: 13px; line-height: 1.7; color: var(--ag-text-secondary); white-space: pre-wrap; word-break: break-word; padding: 4px 0 8px; }
219
+ /* Prompt 弹窗(替代浏览器原生 prompt) */
220
+ .ag-prompt-body { padding: 4px 0 8px; }
221
+ .ag-prompt-message { font-size: 13px; line-height: 1.7; color: var(--ag-text-secondary); margin-bottom: 12px; }
216
222
  .ag-modal-title { font-size: 15px; font-weight: 600; color: var(--ag-text-heading); }
217
223
  .ag-modal-close { width: 28px; height: 28px; border: none; background: transparent; border-radius: 6px; cursor: pointer; font-size: 18px; line-height: 1; color: var(--ag-text-tertiary); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
218
224
  .ag-modal-close:hover { background: var(--ag-code-bg); color: var(--ag-text); }
@@ -278,6 +284,36 @@ body {
278
284
  </div>
279
285
  </div>
280
286
 
287
+ <!-- Confirm 弹窗(替代浏览器原生 confirm) -->
288
+ <div class="ag-modal-overlay" id="confirm-overlay">
289
+ <div class="ag-modal ag-modal-confirm" id="confirm-modal">
290
+ <div class="ag-modal-header">
291
+ <div class="ag-modal-title" id="confirm-title"></div>
292
+ <button class="ag-modal-close" onclick="closeConfirm()" title="关闭">✕</button>
293
+ </div>
294
+ <div class="ag-confirm-body" id="confirm-body"></div>
295
+ <div class="ag-form-actions" id="confirm-actions">
296
+ <button class="ag-btn ag-btn-ghost" onclick="closeConfirm()">取消</button>
297
+ <button class="ag-btn ag-btn-danger" id="confirm-ok" onclick="resolveConfirm()">确认</button>
298
+ </div>
299
+ </div>
300
+ </div>
301
+
302
+ <!-- Prompt 弹窗(替代浏览器原生 prompt,用于 token 输入等) -->
303
+ <div class="ag-modal-overlay" id="prompt-overlay">
304
+ <div class="ag-modal ag-modal-confirm" id="prompt-modal">
305
+ <div class="ag-modal-header">
306
+ <div class="ag-modal-title" id="prompt-title"></div>
307
+ <button class="ag-modal-close" onclick="closePrompt()" title="关闭">✕</button>
308
+ </div>
309
+ <div class="ag-prompt-body" id="prompt-body"></div>
310
+ <div class="ag-form-actions" id="prompt-actions">
311
+ <button class="ag-btn ag-btn-ghost" onclick="closePrompt()">取消</button>
312
+ <button class="ag-btn ag-btn-primary" id="prompt-ok" onclick="submitPrompt()">确认</button>
313
+ </div>
314
+ </div>
315
+ </div>
316
+
281
317
  <script>
282
318
  // ===== i18n =====
283
319
  // 语言来源优先级:URL ?lang=en > ?lang=zh > 系统/文档语言。默认 zh。
@@ -310,7 +346,12 @@ async function api(path, opts = {}) {
310
346
  ...opts,
311
347
  });
312
348
  if (res.status === 401) {
313
- const t_ = prompt(t('需要 Bearer token 访问安全策略管控台:', 'Bearer token required to access the policy console: '));
349
+ const t_ = await agPrompt(t('需要访问凭据', 'Access Token Required'), {
350
+ message: t('服务端开启了访问认证,需要 Bearer token 才能访问安全策略管控台:', 'The server requires a Bearer token to access the policy console:'),
351
+ placeholder: t('输入服务端配置的 token', 'Enter the server-configured token'),
352
+ value: getToken(),
353
+ okText: t('保存并重试', 'Save & Retry'),
354
+ });
314
355
  if (t_) { localStorage.setItem(TOKEN_KEY, t_); return api(path, opts); }
315
356
  throw new Error(t('未授权', 'Unauthorized'));
316
357
  }
@@ -437,6 +478,74 @@ function closeModal() {
437
478
  currentModalAction = null;
438
479
  }
439
480
 
481
+ // ===== Confirm 弹窗(替代浏览器原生 confirm,统一样式)=====
482
+ let confirmResolve = null;
483
+ function closeConfirm() {
484
+ el('confirm-overlay').classList.remove('show');
485
+ if (confirmResolve) { confirmResolve(false); confirmResolve = null; }
486
+ }
487
+ function resolveConfirm() {
488
+ el('confirm-overlay').classList.remove('show');
489
+ if (confirmResolve) { confirmResolve(true); confirmResolve = null; }
490
+ }
491
+ /**
492
+ * 弹出带样式的确认框,返回 Promise<boolean>。
493
+ * @param {string} title 标题
494
+ * @param {string} message 正文(支持 \n,自动转义为纯文本)
495
+ * @param {string} [okText] 确认按钮文案(默认 "确认")
496
+ * @param {string} [okClass] 确认按钮样式类(默认 ag-btn-danger)
497
+ */
498
+ function agConfirm(title, message, okText, okClass) {
499
+ return new Promise(resolve => {
500
+ confirmResolve = resolve;
501
+ el('confirm-title').textContent = title;
502
+ el('confirm-body').textContent = message;
503
+ const okBtn = el('confirm-ok');
504
+ okBtn.className = 'ag-btn ' + (okClass || 'ag-btn-danger');
505
+ okBtn.textContent = okText || t('确认', 'Confirm');
506
+ el('confirm-overlay').classList.add('show');
507
+ });
508
+ }
509
+
510
+ // ===== Prompt 弹窗(替代浏览器原生 prompt,用于 token 输入等)=====
511
+ let promptResolve = null;
512
+ function closePrompt() {
513
+ el('prompt-overlay').classList.remove('show');
514
+ if (promptResolve) { promptResolve(null); promptResolve = null; }
515
+ }
516
+ function submitPrompt() {
517
+ const val = el('prompt-input').value;
518
+ el('prompt-overlay').classList.remove('show');
519
+ if (promptResolve) { promptResolve(val); promptResolve = null; }
520
+ }
521
+ /**
522
+ * 弹出带样式的输入框,返回 Promise<string|null>(取消返回 null)。
523
+ * @param {string} title 标题
524
+ * @param {Object} opts
525
+ * @param {string} [opts.message] 说明文案
526
+ * @param {string} [opts.placeholder] 输入框占位文案
527
+ * @param {string} [opts.value] 初始值
528
+ * @param {string} [opts.okText] 确认按钮文案(默认 "确认")
529
+ */
530
+ function agPrompt(title, opts = {}) {
531
+ return new Promise(resolve => {
532
+ promptResolve = resolve;
533
+ el('prompt-title').textContent = title;
534
+ el('prompt-body').innerHTML =
535
+ (opts.message ? `<div class="ag-prompt-message">${escapeHtml(opts.message)}</div>` : '') +
536
+ `<input class="ag-input" id="prompt-input" type="password" style="width:100%" placeholder="${escapeHtml(opts.placeholder || '')}" value="${escapeHtml(opts.value || '')}" autocomplete="off">`;
537
+ el('prompt-ok').textContent = opts.okText || t('确认', 'Confirm');
538
+ el('prompt-overlay').classList.add('show');
539
+ const input = el('prompt-input');
540
+ input.focus();
541
+ input.select();
542
+ input.addEventListener('keydown', (e) => {
543
+ if (e.key === 'Enter') { e.preventDefault(); submitPrompt(); }
544
+ if (e.key === 'Escape') { e.preventDefault(); closePrompt(); }
545
+ });
546
+ });
547
+ }
548
+
440
549
  // ===== Pages =====
441
550
  async function refreshConfig() {
442
551
  state.config = await api('config');
@@ -698,10 +807,13 @@ async function updateSkipL3(layer, cat, id, skip) {
698
807
  async function deleteRule(layer, cat, id) {
699
808
  const layerData = (state.config[layer] || {})[cat] || {};
700
809
  const rule = (layerData.rules || []).find(r => r.id === id) || {};
701
- const msg = rule.source_removed
810
+ const isRemoved = rule.source_removed;
811
+ const title = t('删除规则', 'Delete Rule');
812
+ const msg = isRemoved
702
813
  ? t('确定删除已移除的规则 "', 'Delete removed rule "') + id + t('"?\n(此规则已从默认规则集移除,删除可清理历史保留。下次升级不会再恢复)', '"?\n(This rule was removed from the default set. Deleting cleans up the historical entry. It will not be restored on the next upgrade.)')
703
814
  : t('确定删除自定义规则 "', 'Delete custom rule "') + id + t('"?', '"?');
704
- if (!confirm(msg)) return;
815
+ const ok = await agConfirm(title, msg, t('删除', 'Delete'));
816
+ if (!ok) return;
705
817
  try {
706
818
  state.config = await apiDelete('rules', { layer, category: cat, rule_id: id });
707
819
  renderCurrent(); showToast(t('规则 ', 'Rule ') + id + t(' 已删除', ' deleted'));
@@ -799,7 +911,12 @@ async function toggleAgentCategory(agentId, cat) {
799
911
  }
800
912
  async function deleteAgentRule(agentId, cat, r) {
801
913
  const fp = { value: r.value, pattern: r.pattern, keyword: r.keyword, path: r.path };
802
- if (!confirm(t('删除规则 ', 'Delete rule ') + (r.value || r.pattern || r.keyword || r.path || '') + '?')) return;
914
+ const ok = await agConfirm(
915
+ t('删除规则', 'Delete Rule'),
916
+ t('确定删除规则 "', 'Delete rule "') + (r.value || r.pattern || r.keyword || r.path || '') + t('"?', '"?'),
917
+ t('删除', 'Delete')
918
+ );
919
+ if (!ok) return;
803
920
  try {
804
921
  state.agentRules = await apiDelete('agents/rules', { agent_id: agentId, category: cat, ...fp });
805
922
  render_agents();
@@ -867,7 +984,12 @@ async function toggleSkill(skillId, en) {
867
984
  } catch (e) { showToast(t('操作失败: ', 'Operation failed: ') + e.message, 'error'); }
868
985
  }
869
986
  async function deleteSkill(skillId, cat) {
870
- if (!confirm(t('确定删除自定义 Skill "', 'Delete custom Skill "') + skillId + t('"?', '"? '))) return;
987
+ const ok = await agConfirm(
988
+ t('删除 Skill', 'Delete Skill'),
989
+ t('确定删除自定义 Skill "', 'Delete custom Skill "') + skillId + t('"?', '"?'),
990
+ t('删除', 'Delete')
991
+ );
992
+ if (!ok) return;
871
993
  try {
872
994
  state.config = await apiDelete('skills', { skill_id: skillId, category: cat });
873
995
  renderCurrent(); showToast('Skill ' + skillId + t(' 已删除', ' deleted'));
@@ -1185,10 +1307,29 @@ async function goTokenRecentPage() {
1185
1307
  loadTokenRecent();
1186
1308
  }
1187
1309
  async function resetTokenStats() {
1188
- if (!confirm(t('确定清除所有 Token 统计数据?', 'Clear all Token statistics?'))) return;
1310
+ const ok = await agConfirm(
1311
+ t('清除 Token 统计', 'Clear Token Statistics'),
1312
+ t('确定清除所有 Token 统计数据?', 'Clear all Token statistics?'),
1313
+ t('清除', 'Clear')
1314
+ );
1315
+ if (!ok) return;
1189
1316
  try { await apiPost('token-stats/reset'); showToast(t('Token 统计已清除', 'Token statistics cleared')); loadTokenStats(7); }
1190
1317
  catch (e) { showToast(t('清除失败: ', 'Clear failed: ') + e.message, 'error'); }
1191
1318
  }
1319
+ async function resetToFactory() {
1320
+ const ok = await agConfirm(
1321
+ t('重置到出厂默认', 'Reset to Factory Defaults'),
1322
+ t('确定重置所有规则到出厂默认?所有自定义规则和开关设置将丢失。', 'Reset all rules to factory defaults? All custom rules and switch settings will be lost.'),
1323
+ t('重置', 'Reset')
1324
+ );
1325
+ if (!ok) return;
1326
+ try {
1327
+ state.config = await apiPost('reset');
1328
+ state.envOverrides = await loadEnvOverrides();
1329
+ renderCurrent();
1330
+ showToast(t('配置已重置到出厂默认', 'Configuration reset to factory defaults'));
1331
+ } catch (e) { showToast(t('重置失败: ', 'Reset failed: ') + e.message, 'error'); }
1332
+ }
1192
1333
 
1193
1334
  // ===== 设置页 =====
1194
1335
  function render_settings() {
@@ -1225,7 +1366,7 @@ function render_settings() {
1225
1366
  </div>` : ''}
1226
1367
  <div class="ag-card">
1227
1368
  <div class="ag-section-title">${t('危险操作', 'Danger Zone')}</div>
1228
- <button class="ag-btn ag-btn-danger" onclick="if(confirm(t('确定重置所有规则到出厂默认?所有自定义规则和开关设置将丢失。','Reset all rules to factory defaults? All custom rules and switch settings will be lost.'))){apiPost('reset').then(c=>{state.config=c;return loadEnvOverrides().then(o=>{state.envOverrides=o;renderCurrent();showToast(t('配置已重置到出厂默认','Configuration reset to factory defaults'))})}).catch(e=>showToast(t('重置失败: ','Reset failed: ')+e.message,'error'))}">${t('重置规则到出厂默认', 'Reset Rules to Factory Defaults')}</button>
1369
+ <button class="ag-btn ag-btn-danger" onclick="resetToFactory()">${t('重置规则到出厂默认', 'Reset Rules to Factory Defaults')}</button>
1229
1370
  <button class="ag-btn ag-btn-danger" style="margin-left:8px" onclick="resetTokenStats()">${t('清除 Token 统计', 'Clear Token Statistics')}</button>
1230
1371
  </div>`;
1231
1372
  }
@@ -1361,6 +1502,16 @@ function applyTabLabels() {
1361
1502
  if (cancelBtn) cancelBtn.textContent = t('取消', 'Cancel');
1362
1503
  const submitBtn = el('modal-submit');
1363
1504
  if (submitBtn) submitBtn.textContent = t('确认', 'Confirm');
1505
+ // Confirm 弹窗(替代浏览器原生 confirm)
1506
+ const confirmCancelBtn = document.querySelector('#confirm-actions .ag-btn-ghost');
1507
+ if (confirmCancelBtn) confirmCancelBtn.textContent = t('取消', 'Cancel');
1508
+ const confirmCloseBtn = document.querySelector('#confirm-overlay .ag-modal-close');
1509
+ if (confirmCloseBtn) confirmCloseBtn.setAttribute('title', t('关闭', 'Close'));
1510
+ // Prompt 弹窗(替代浏览器原生 prompt)
1511
+ const promptCancelBtn = document.querySelector('#prompt-actions .ag-btn-ghost');
1512
+ if (promptCancelBtn) promptCancelBtn.textContent = t('取消', 'Cancel');
1513
+ const promptCloseBtn = document.querySelector('#prompt-overlay .ag-modal-close');
1514
+ if (promptCloseBtn) promptCloseBtn.setAttribute('title', t('关闭', 'Close'));
1364
1515
  }
1365
1516
  el('theme-btn').onclick = () => {
1366
1517
  const cur = document.documentElement.getAttribute('data-theme');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kenhkl/agent-moss",
3
- "version": "0.12.5",
3
+ "version": "0.12.6",
4
4
  "description": "Multi-layer security analysis engine for AI agents (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/server.js",