@lobehub/lobehub 2.0.0-next.94 → 2.0.0-next.96

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 (63) hide show
  1. package/.github/workflows/issue-auto-comments.yml +0 -19
  2. package/CHANGELOG.md +50 -0
  3. package/changelog/v1.json +18 -0
  4. package/locales/ar/common.json +21 -0
  5. package/locales/ar/hotkey.json +4 -0
  6. package/locales/bg-BG/common.json +21 -0
  7. package/locales/bg-BG/hotkey.json +4 -0
  8. package/locales/de-DE/common.json +21 -0
  9. package/locales/de-DE/hotkey.json +4 -0
  10. package/locales/en-US/common.json +21 -0
  11. package/locales/en-US/hotkey.json +4 -0
  12. package/locales/es-ES/common.json +21 -0
  13. package/locales/es-ES/hotkey.json +4 -0
  14. package/locales/fa-IR/common.json +21 -0
  15. package/locales/fa-IR/hotkey.json +4 -0
  16. package/locales/fr-FR/common.json +21 -0
  17. package/locales/fr-FR/hotkey.json +4 -0
  18. package/locales/it-IT/common.json +21 -0
  19. package/locales/it-IT/hotkey.json +4 -0
  20. package/locales/ja-JP/common.json +21 -0
  21. package/locales/ja-JP/hotkey.json +4 -0
  22. package/locales/ko-KR/common.json +21 -0
  23. package/locales/ko-KR/hotkey.json +4 -0
  24. package/locales/nl-NL/common.json +21 -0
  25. package/locales/nl-NL/hotkey.json +4 -0
  26. package/locales/pl-PL/common.json +21 -0
  27. package/locales/pl-PL/hotkey.json +4 -0
  28. package/locales/pt-BR/common.json +21 -0
  29. package/locales/pt-BR/hotkey.json +4 -0
  30. package/locales/ru-RU/common.json +21 -0
  31. package/locales/ru-RU/hotkey.json +4 -0
  32. package/locales/tr-TR/common.json +21 -0
  33. package/locales/tr-TR/hotkey.json +4 -0
  34. package/locales/vi-VN/common.json +21 -0
  35. package/locales/vi-VN/hotkey.json +4 -0
  36. package/locales/zh-CN/common.json +21 -0
  37. package/locales/zh-CN/hotkey.json +4 -0
  38. package/locales/zh-TW/common.json +21 -0
  39. package/locales/zh-TW/hotkey.json +4 -0
  40. package/package.json +3 -1
  41. package/packages/agent-runtime/src/core/InterventionChecker.ts +85 -0
  42. package/packages/agent-runtime/src/core/__tests__/InterventionChecker.test.ts +492 -22
  43. package/packages/agent-runtime/src/core/defaultSecurityBlacklist.ts +335 -0
  44. package/packages/agent-runtime/src/core/index.ts +1 -0
  45. package/packages/agent-runtime/src/types/state.ts +10 -1
  46. package/packages/const/src/hotkeys.ts +6 -0
  47. package/packages/conversation-flow/src/__tests__/indexing.test.ts +513 -0
  48. package/packages/conversation-flow/src/__tests__/structuring.test.ts +600 -0
  49. package/packages/types/src/hotkey.ts +1 -0
  50. package/packages/types/src/tool/intervention.ts +38 -0
  51. package/src/app/[variants]/(main)/settings/_layout/Desktop/index.tsx +41 -8
  52. package/src/app/[variants]/(main)/settings/provider/(list)/ProviderGrid/Card.tsx +6 -4
  53. package/src/app/[variants]/(main)/settings/provider/(list)/ProviderGrid/index.tsx +16 -4
  54. package/src/app/[variants]/(main)/settings/provider/(list)/index.tsx +15 -3
  55. package/src/app/[variants]/(main)/settings/provider/detail/index.tsx +23 -15
  56. package/src/features/Conversation/MarkdownElements/remarkPlugins/createRemarkSelfClosingTagPlugin.test.ts +25 -0
  57. package/src/features/Conversation/MarkdownElements/remarkPlugins/createRemarkSelfClosingTagPlugin.ts +28 -0
  58. package/src/layout/GlobalProvider/Cmdk.tsx +470 -0
  59. package/src/layout/GlobalProvider/CmdkLazy.tsx +17 -0
  60. package/src/layout/GlobalProvider/index.tsx +2 -0
  61. package/src/locales/default/common.ts +21 -0
  62. package/src/locales/default/hotkey.ts +4 -0
  63. package/src/store/chat/agents/GeneralChatAgent.ts +22 -8
@@ -1,20 +1,35 @@
1
- import type { HumanInterventionConfig } from '@lobechat/types';
1
+ import type { HumanInterventionConfig, SecurityBlacklistConfig } from '@lobechat/types';
2
2
  import { describe, expect, it } from 'vitest';
3
3
 
4
4
  import { InterventionChecker } from '../InterventionChecker';
5
+ import { DEFAULT_SECURITY_BLACKLIST } from '../defaultSecurityBlacklist';
5
6
 
6
7
  describe('InterventionChecker', () => {
7
8
  describe('shouldIntervene', () => {
8
9
  it('should return never when config is undefined', () => {
9
- const result = InterventionChecker.shouldIntervene({ config: undefined, toolArgs: {} });
10
+ const result = InterventionChecker.shouldIntervene({
11
+ config: undefined,
12
+ securityBlacklist: [], // Disable blacklist for this test
13
+ toolArgs: {},
14
+ });
10
15
  expect(result).toBe('never');
11
16
  });
12
17
 
13
18
  it('should return the policy when config is a simple string', () => {
14
- expect(InterventionChecker.shouldIntervene({ config: 'never', toolArgs: {} })).toBe('never');
15
- expect(InterventionChecker.shouldIntervene({ config: 'required', toolArgs: {} })).toBe(
16
- 'required',
17
- );
19
+ expect(
20
+ InterventionChecker.shouldIntervene({
21
+ config: 'never',
22
+ securityBlacklist: [], // Disable blacklist for this test
23
+ toolArgs: {},
24
+ }),
25
+ ).toBe('never');
26
+ expect(
27
+ InterventionChecker.shouldIntervene({
28
+ config: 'required',
29
+ securityBlacklist: [], // Disable blacklist for this test
30
+ toolArgs: {},
31
+ }),
32
+ ).toBe('required');
18
33
  });
19
34
 
20
35
  it('should match rules in order and return first match', () => {
@@ -24,14 +39,26 @@ describe('InterventionChecker', () => {
24
39
  { policy: 'required' }, // Default rule
25
40
  ];
26
41
 
27
- expect(InterventionChecker.shouldIntervene({ config, toolArgs: { command: 'ls:' } })).toBe(
28
- 'never',
29
- );
30
42
  expect(
31
- InterventionChecker.shouldIntervene({ config, toolArgs: { command: 'git commit:' } }),
43
+ InterventionChecker.shouldIntervene({
44
+ config,
45
+ securityBlacklist: [], // Disable blacklist for this test
46
+ toolArgs: { command: 'ls:' },
47
+ }),
48
+ ).toBe('never');
49
+ expect(
50
+ InterventionChecker.shouldIntervene({
51
+ config,
52
+ securityBlacklist: [], // Disable blacklist for this test
53
+ toolArgs: { command: 'git commit:' },
54
+ }),
32
55
  ).toBe('required');
33
56
  expect(
34
- InterventionChecker.shouldIntervene({ config, toolArgs: { command: 'rm -rf /' } }),
57
+ InterventionChecker.shouldIntervene({
58
+ config,
59
+ securityBlacklist: [], // Disable blacklist for this test
60
+ toolArgs: { command: 'rm -rf /' },
61
+ }),
35
62
  ).toBe('required');
36
63
  });
37
64
 
@@ -40,6 +67,7 @@ describe('InterventionChecker', () => {
40
67
 
41
68
  const result = InterventionChecker.shouldIntervene({
42
69
  config,
70
+ securityBlacklist: [], // Disable blacklist for this test
43
71
  toolArgs: { command: 'rm -rf /' },
44
72
  });
45
73
  expect(result).toBe('required');
@@ -61,6 +89,7 @@ describe('InterventionChecker', () => {
61
89
  expect(
62
90
  InterventionChecker.shouldIntervene({
63
91
  config,
92
+ securityBlacklist: [], // Disable blacklist for this test
64
93
  toolArgs: {
65
94
  command: 'git add:.',
66
95
  path: '/Users/project/file.ts',
@@ -72,6 +101,7 @@ describe('InterventionChecker', () => {
72
101
  expect(
73
102
  InterventionChecker.shouldIntervene({
74
103
  config,
104
+ securityBlacklist: [], // Disable blacklist for this test
75
105
  toolArgs: {
76
106
  command: 'git add:.',
77
107
  path: '/tmp/file.ts',
@@ -88,6 +118,7 @@ describe('InterventionChecker', () => {
88
118
 
89
119
  const result = InterventionChecker.shouldIntervene({
90
120
  config,
121
+ securityBlacklist: [], // Disable blacklist for this test
91
122
  toolArgs: { command: 'anything' },
92
123
  });
93
124
  expect(result).toBe('required');
@@ -218,6 +249,393 @@ describe('InterventionChecker', () => {
218
249
  });
219
250
  });
220
251
 
252
+ describe('checkSecurityBlacklist', () => {
253
+ it('should return not blocked when blacklist is empty', () => {
254
+ const result = InterventionChecker.checkSecurityBlacklist([], { command: 'rm -rf /' });
255
+ expect(result.blocked).toBe(false);
256
+ expect(result.reason).toBeUndefined();
257
+ });
258
+
259
+ describe('with DEFAULT_SECURITY_BLACKLIST', () => {
260
+ it('should block dangerous rm -rf ~/ command', () => {
261
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
262
+ command: 'rm -rf ~/',
263
+ });
264
+ expect(result.blocked).toBe(true);
265
+ expect(result.reason).toBe('Recursive deletion of home directory is extremely dangerous');
266
+ });
267
+
268
+ it('should block rm -rf on macOS home directory', () => {
269
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
270
+ command: 'rm -rf /Users/alice',
271
+ });
272
+ expect(result.blocked).toBe(true);
273
+ expect(result.reason).toBe('Recursive deletion of home directory is extremely dangerous');
274
+ });
275
+
276
+ it('should block rm -rf on Linux home directory', () => {
277
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
278
+ command: 'rm -rf /home/alice',
279
+ });
280
+ expect(result.blocked).toBe(true);
281
+ expect(result.reason).toBe('Recursive deletion of home directory is extremely dangerous');
282
+ });
283
+
284
+ it('should block rm -rf with $HOME variable', () => {
285
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
286
+ command: 'rm -rf $HOME',
287
+ });
288
+ expect(result.blocked).toBe(true);
289
+ expect(result.reason).toBe('Recursive deletion of home directory is extremely dangerous');
290
+ });
291
+
292
+ it('should block rm -rf / command', () => {
293
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
294
+ command: 'rm -rf /',
295
+ });
296
+ expect(result.blocked).toBe(true);
297
+ expect(result.reason).toBe('Recursive deletion of root directory will destroy the system');
298
+ });
299
+
300
+ it('should allow safe rm commands', () => {
301
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
302
+ command: 'rm -rf /tmp/test-folder',
303
+ });
304
+ expect(result.blocked).toBe(false);
305
+ });
306
+
307
+ it('should block fork bomb', () => {
308
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
309
+ command: ':(){ :|:& };:',
310
+ });
311
+ expect(result.blocked).toBe(true);
312
+ expect(result.reason).toBe('Fork bomb can crash the system');
313
+ });
314
+
315
+ it('should block dangerous dd commands to disk devices', () => {
316
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
317
+ command: 'dd if=/dev/zero of=/dev/sda',
318
+ });
319
+ expect(result.blocked).toBe(true);
320
+ expect(result.reason).toBe('Writing random data to disk devices can destroy data');
321
+ });
322
+
323
+ it('should block reading .env files via command', () => {
324
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
325
+ command: 'cat .env',
326
+ });
327
+ expect(result.blocked).toBe(true);
328
+ expect(result.reason).toBe(
329
+ 'Reading .env files may leak sensitive credentials and API keys',
330
+ );
331
+ });
332
+
333
+ it('should block reading .env files via path', () => {
334
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
335
+ path: '/project/.env.local',
336
+ });
337
+ expect(result.blocked).toBe(true);
338
+ expect(result.reason).toBe(
339
+ 'Reading .env files may leak sensitive credentials and API keys',
340
+ );
341
+ });
342
+
343
+ it('should block reading SSH private keys via command', () => {
344
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
345
+ command: 'cat ~/.ssh/id_rsa',
346
+ });
347
+ expect(result.blocked).toBe(true);
348
+ expect(result.reason).toBe('Reading SSH private keys can compromise system security');
349
+ });
350
+
351
+ it('should block reading SSH private keys via path', () => {
352
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
353
+ path: '/home/user/.ssh/id_ed25519',
354
+ });
355
+ expect(result.blocked).toBe(true);
356
+ expect(result.reason).toBe('Reading SSH private keys can compromise system security');
357
+ });
358
+
359
+ it('should allow reading SSH public keys', () => {
360
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
361
+ command: 'cat ~/.ssh/id_rsa.pub',
362
+ });
363
+ expect(result.blocked).toBe(false);
364
+ });
365
+
366
+ it('should block reading AWS credentials via command', () => {
367
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
368
+ command: 'cat ~/.aws/credentials',
369
+ });
370
+ expect(result.blocked).toBe(true);
371
+ expect(result.reason).toBe('Accessing AWS credentials can leak cloud access keys');
372
+ });
373
+
374
+ it('should block reading AWS credentials via path', () => {
375
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
376
+ path: '/home/user/.aws/credentials',
377
+ });
378
+ expect(result.blocked).toBe(true);
379
+ expect(result.reason).toBe('Accessing AWS credentials can leak cloud access keys');
380
+ });
381
+
382
+ it('should block reading Docker config', () => {
383
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
384
+ command: 'less ~/.docker/config.json',
385
+ });
386
+ expect(result.blocked).toBe(true);
387
+ expect(result.reason).toBe('Reading Docker config may expose registry credentials');
388
+ });
389
+
390
+ it('should block reading Kubernetes config', () => {
391
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
392
+ path: '/home/user/.kube/config',
393
+ });
394
+ expect(result.blocked).toBe(true);
395
+ expect(result.reason).toBe('Reading Kubernetes config may expose cluster credentials');
396
+ });
397
+
398
+ it('should block reading Git credentials', () => {
399
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
400
+ command: 'cat ~/.git-credentials',
401
+ });
402
+ expect(result.blocked).toBe(true);
403
+ expect(result.reason).toBe('Reading Git credentials file may leak access tokens');
404
+ });
405
+
406
+ it('should block reading npm token file', () => {
407
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
408
+ path: '/home/user/.npmrc',
409
+ });
410
+ expect(result.blocked).toBe(true);
411
+ expect(result.reason).toBe(
412
+ 'Reading npm token file may expose package registry credentials',
413
+ );
414
+ });
415
+
416
+ it('should block reading shell history files', () => {
417
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
418
+ command: 'cat ~/.bash_history',
419
+ });
420
+ expect(result.blocked).toBe(true);
421
+ expect(result.reason).toBe(
422
+ 'Reading history files may expose sensitive commands and credentials',
423
+ );
424
+ });
425
+
426
+ it('should block reading GCP credentials', () => {
427
+ const result = InterventionChecker.checkSecurityBlacklist(DEFAULT_SECURITY_BLACKLIST, {
428
+ path: '/home/user/.config/gcloud/application_default_credentials.json',
429
+ });
430
+ expect(result.blocked).toBe(true);
431
+ expect(result.reason).toBe('Reading GCP credentials may leak cloud service account keys');
432
+ });
433
+ });
434
+
435
+ describe('with custom blacklist', () => {
436
+ it('should work with multiple parameter matching', () => {
437
+ const blacklist: SecurityBlacklistConfig = [
438
+ {
439
+ description: 'Dangerous operation on system files',
440
+ match: {
441
+ command: { pattern: 'rm.*', type: 'regex' },
442
+ path: '/etc/*',
443
+ },
444
+ },
445
+ ];
446
+
447
+ // Both match - should block
448
+ expect(
449
+ InterventionChecker.checkSecurityBlacklist(blacklist, {
450
+ command: 'rm -rf',
451
+ path: '/etc/passwd',
452
+ }).blocked,
453
+ ).toBe(true);
454
+
455
+ // Only command matches - should not block
456
+ expect(
457
+ InterventionChecker.checkSecurityBlacklist(blacklist, {
458
+ command: 'rm -rf',
459
+ path: '/tmp/file',
460
+ }).blocked,
461
+ ).toBe(false);
462
+
463
+ // Only path matches - should not block
464
+ expect(
465
+ InterventionChecker.checkSecurityBlacklist(blacklist, {
466
+ command: 'cat',
467
+ path: '/etc/passwd',
468
+ }).blocked,
469
+ ).toBe(false);
470
+ });
471
+ });
472
+ });
473
+
474
+ describe('shouldIntervene with security blacklist', () => {
475
+ describe('with default blacklist behavior', () => {
476
+ it('should block dangerous commands even in auto-run mode', () => {
477
+ // Even with config set to 'never', default blacklist should override
478
+ const result = InterventionChecker.shouldIntervene({
479
+ config: 'never',
480
+ // Not passing securityBlacklist - should use DEFAULT_SECURITY_BLACKLIST
481
+ toolArgs: { command: 'rm -rf ~/' },
482
+ });
483
+
484
+ expect(result).toBe('required');
485
+ });
486
+
487
+ it('should block dangerous commands even with no config', () => {
488
+ // Even with no config (which normally means 'never'), default blacklist should override
489
+ const result = InterventionChecker.shouldIntervene({
490
+ config: undefined,
491
+ // Not passing securityBlacklist - should use DEFAULT_SECURITY_BLACKLIST
492
+ toolArgs: { command: 'rm -rf /' },
493
+ });
494
+
495
+ expect(result).toBe('required');
496
+ });
497
+
498
+ it('should allow safe commands to follow normal intervention rules', () => {
499
+ // Safe command should follow normal config
500
+ const result = InterventionChecker.shouldIntervene({
501
+ config: 'never',
502
+ // Not passing securityBlacklist - should use DEFAULT_SECURITY_BLACKLIST
503
+ toolArgs: { command: 'ls -la' },
504
+ });
505
+
506
+ expect(result).toBe('never');
507
+ });
508
+
509
+ it('should block reading sensitive files', () => {
510
+ // Test with actual default blacklist for sensitive file reading
511
+ const result = InterventionChecker.shouldIntervene({
512
+ config: 'never',
513
+ // Not passing securityBlacklist - should use DEFAULT_SECURITY_BLACKLIST
514
+ toolArgs: { command: 'cat .env' },
515
+ });
516
+
517
+ expect(result).toBe('required');
518
+ });
519
+ });
520
+
521
+ describe('with custom blacklist replacement', () => {
522
+ it('should use custom blacklist instead of default when provided', () => {
523
+ const customBlacklist: SecurityBlacklistConfig = [
524
+ {
525
+ description: 'Block all npm commands in production',
526
+ match: {
527
+ command: { pattern: 'npm.*', type: 'regex' },
528
+ },
529
+ },
530
+ ];
531
+
532
+ // Custom blacklist blocks npm but not rm
533
+ expect(
534
+ InterventionChecker.shouldIntervene({
535
+ config: 'never',
536
+ securityBlacklist: customBlacklist,
537
+ toolArgs: { command: 'npm install' },
538
+ }),
539
+ ).toBe('required');
540
+
541
+ // rm is not in custom blacklist, should follow config
542
+ expect(
543
+ InterventionChecker.shouldIntervene({
544
+ config: 'never',
545
+ securityBlacklist: customBlacklist,
546
+ toolArgs: { command: 'rm -rf ~/' },
547
+ }),
548
+ ).toBe('never');
549
+ });
550
+
551
+ it('should support extending default blacklist with custom rules', () => {
552
+ const extendedBlacklist: SecurityBlacklistConfig = [
553
+ ...DEFAULT_SECURITY_BLACKLIST,
554
+ {
555
+ description: 'Block access to production database',
556
+ match: {
557
+ command: { pattern: '.*psql.*production.*', type: 'regex' },
558
+ },
559
+ },
560
+ ];
561
+
562
+ // Default rule still works
563
+ expect(
564
+ InterventionChecker.shouldIntervene({
565
+ config: 'never',
566
+ securityBlacklist: extendedBlacklist,
567
+ toolArgs: { command: 'rm -rf ~/' },
568
+ }),
569
+ ).toBe('required');
570
+
571
+ // Custom rule works
572
+ expect(
573
+ InterventionChecker.shouldIntervene({
574
+ config: 'never',
575
+ securityBlacklist: extendedBlacklist,
576
+ toolArgs: { command: 'psql -h production.db' },
577
+ }),
578
+ ).toBe('required');
579
+
580
+ // Safe commands pass
581
+ expect(
582
+ InterventionChecker.shouldIntervene({
583
+ config: 'never',
584
+ securityBlacklist: extendedBlacklist,
585
+ toolArgs: { command: 'psql -h localhost' },
586
+ }),
587
+ ).toBe('never');
588
+ });
589
+
590
+ it('should allow disabling security blacklist by passing empty array', () => {
591
+ // Dangerous command should not be blocked when blacklist is empty
592
+ const result = InterventionChecker.shouldIntervene({
593
+ config: 'never',
594
+ securityBlacklist: [], // Explicitly disable blacklist
595
+ toolArgs: { command: 'rm -rf ~/' },
596
+ });
597
+
598
+ expect(result).toBe('never');
599
+ });
600
+
601
+ it('should support project-specific blacklist rules', () => {
602
+ const projectBlacklist: SecurityBlacklistConfig = [
603
+ {
604
+ description: 'Block modifying package.json in CI',
605
+ match: {
606
+ path: { pattern: '.*/package\\.json$', type: 'regex' },
607
+ command: { pattern: '(vim|nano|vi|emacs|code|sed).*', type: 'regex' },
608
+ },
609
+ },
610
+ ];
611
+
612
+ // Should block editing package.json
613
+ expect(
614
+ InterventionChecker.shouldIntervene({
615
+ config: 'never',
616
+ securityBlacklist: projectBlacklist,
617
+ toolArgs: {
618
+ command: 'vim package.json',
619
+ path: '/project/package.json',
620
+ },
621
+ }),
622
+ ).toBe('required');
623
+
624
+ // Should allow reading package.json
625
+ expect(
626
+ InterventionChecker.shouldIntervene({
627
+ config: 'never',
628
+ securityBlacklist: projectBlacklist,
629
+ toolArgs: {
630
+ command: 'cat package.json',
631
+ path: '/project/package.json',
632
+ },
633
+ }),
634
+ ).toBe('never');
635
+ });
636
+ });
637
+ });
638
+
221
639
  describe('Integration scenarios', () => {
222
640
  it('should handle Bash tool scenario', () => {
223
641
  const config: HumanInterventionConfig = [
@@ -229,24 +647,44 @@ describe('InterventionChecker', () => {
229
647
  ];
230
648
 
231
649
  // Safe commands - never
232
- expect(InterventionChecker.shouldIntervene({ config, toolArgs: { command: 'ls:' } })).toBe(
233
- 'never',
234
- );
650
+ expect(
651
+ InterventionChecker.shouldIntervene({
652
+ config,
653
+ securityBlacklist: [], // Disable blacklist for this test
654
+ toolArgs: { command: 'ls:' },
655
+ }),
656
+ ).toBe('never');
235
657
 
236
658
  // Git commands - require
237
659
  expect(
238
- InterventionChecker.shouldIntervene({ config, toolArgs: { command: 'git add:.' } }),
660
+ InterventionChecker.shouldIntervene({
661
+ config,
662
+ securityBlacklist: [], // Disable blacklist for this test
663
+ toolArgs: { command: 'git add:.' },
664
+ }),
239
665
  ).toBe('required');
240
666
  expect(
241
- InterventionChecker.shouldIntervene({ config, toolArgs: { command: 'git commit:-m' } }),
667
+ InterventionChecker.shouldIntervene({
668
+ config,
669
+ securityBlacklist: [], // Disable blacklist for this test
670
+ toolArgs: { command: 'git commit:-m' },
671
+ }),
242
672
  ).toBe('required');
243
673
 
244
674
  // Dangerous commands - require
245
- expect(InterventionChecker.shouldIntervene({ config, toolArgs: { command: 'rm:-rf' } })).toBe(
246
- 'required',
247
- );
248
675
  expect(
249
- InterventionChecker.shouldIntervene({ config, toolArgs: { command: 'npm install' } }),
676
+ InterventionChecker.shouldIntervene({
677
+ config,
678
+ securityBlacklist: [], // Disable blacklist for this test
679
+ toolArgs: { command: 'rm:-rf' },
680
+ }),
681
+ ).toBe('required');
682
+ expect(
683
+ InterventionChecker.shouldIntervene({
684
+ config,
685
+ securityBlacklist: [], // Disable blacklist for this test
686
+ toolArgs: { command: 'npm install' },
687
+ }),
250
688
  ).toBe('required');
251
689
  });
252
690
 
@@ -260,13 +698,18 @@ describe('InterventionChecker', () => {
260
698
  expect(
261
699
  InterventionChecker.shouldIntervene({
262
700
  config,
701
+ securityBlacklist: [], // Disable blacklist for this test
263
702
  toolArgs: { path: '/Users/project/file.ts' },
264
703
  }),
265
704
  ).toBe('never');
266
705
 
267
706
  // Outside project - require
268
707
  expect(
269
- InterventionChecker.shouldIntervene({ config, toolArgs: { path: '/tmp/file.ts' } }),
708
+ InterventionChecker.shouldIntervene({
709
+ config,
710
+ securityBlacklist: [], // Disable blacklist for this test
711
+ toolArgs: { path: '/tmp/file.ts' },
712
+ }),
270
713
  ).toBe('required');
271
714
  });
272
715
 
@@ -274,8 +717,35 @@ describe('InterventionChecker', () => {
274
717
  const config: HumanInterventionConfig = 'required';
275
718
 
276
719
  expect(
277
- InterventionChecker.shouldIntervene({ config, toolArgs: { url: 'https://example.com' } }),
720
+ InterventionChecker.shouldIntervene({
721
+ config,
722
+ securityBlacklist: [], // Disable blacklist for this test
723
+ toolArgs: { url: 'https://example.com' },
724
+ }),
278
725
  ).toBe('required');
279
726
  });
727
+
728
+ it('should handle security blacklist overriding user config', () => {
729
+ const config: HumanInterventionConfig = 'never';
730
+ const blacklist: SecurityBlacklistConfig = DEFAULT_SECURITY_BLACKLIST;
731
+
732
+ // Dangerous command blocked even with 'never' config
733
+ expect(
734
+ InterventionChecker.shouldIntervene({
735
+ config,
736
+ securityBlacklist: blacklist,
737
+ toolArgs: { command: 'rm -rf /' },
738
+ }),
739
+ ).toBe('required');
740
+
741
+ // Safe command follows config
742
+ expect(
743
+ InterventionChecker.shouldIntervene({
744
+ config,
745
+ securityBlacklist: blacklist,
746
+ toolArgs: { command: 'ls -la' },
747
+ }),
748
+ ).toBe('never');
749
+ });
280
750
  });
281
751
  });