synapse 2.185.0__py311-none-any.whl → 2.187.0__py311-none-any.whl

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.

Potentially problematic release.


This version of synapse might be problematic. Click here for more details.

Files changed (43) hide show
  1. synapse/cortex.py +5 -4
  2. synapse/exc.py +2 -0
  3. synapse/lib/ast.py +1 -1
  4. synapse/lib/cell.py +65 -2
  5. synapse/lib/drive.py +45 -10
  6. synapse/lib/hive.py +1 -1
  7. synapse/lib/modelrev.py +771 -11
  8. synapse/lib/snap.py +0 -6
  9. synapse/lib/spooled.py +26 -3
  10. synapse/lib/storm.py +7 -0
  11. synapse/lib/stormlib/model.py +320 -250
  12. synapse/lib/stormtypes.py +36 -10
  13. synapse/lib/types.py +6 -0
  14. synapse/lib/version.py +2 -2
  15. synapse/models/infotech.py +49 -22
  16. synapse/models/risk.py +3 -0
  17. synapse/tests/test_cortex.py +10 -5
  18. synapse/tests/test_lib_base.py +2 -2
  19. synapse/tests/test_lib_cell.py +16 -4
  20. synapse/tests/test_lib_modelrev.py +918 -379
  21. synapse/tests/test_lib_spooled.py +34 -0
  22. synapse/tests/test_lib_stormlib_model.py +0 -270
  23. synapse/tests/test_lib_stormtypes.py +11 -0
  24. synapse/tests/test_model_infotech.py +14 -11
  25. synapse/tests/test_model_risk.py +2 -0
  26. synapse/tests/test_tools_snapshot.py +47 -0
  27. synapse/tools/aha/clone.py +3 -1
  28. synapse/tools/aha/easycert.py +1 -1
  29. synapse/tools/aha/enroll.py +3 -1
  30. synapse/tools/aha/provision/service.py +3 -1
  31. synapse/tools/aha/provision/user.py +3 -1
  32. synapse/tools/changelog.py +11 -3
  33. synapse/tools/livebackup.py +3 -1
  34. synapse/tools/promote.py +9 -3
  35. synapse/tools/snapshot.py +69 -0
  36. {synapse-2.185.0.dist-info → synapse-2.187.0.dist-info}/METADATA +1 -1
  37. {synapse-2.185.0.dist-info → synapse-2.187.0.dist-info}/RECORD +40 -41
  38. {synapse-2.185.0.dist-info → synapse-2.187.0.dist-info}/WHEEL +1 -1
  39. synapse/assets/__init__.py +0 -35
  40. synapse/assets/storm/migrations/model-0.2.28.storm +0 -355
  41. synapse/tests/test_assets.py +0 -25
  42. {synapse-2.185.0.dist-info → synapse-2.187.0.dist-info}/LICENSE +0 -0
  43. {synapse-2.185.0.dist-info → synapse-2.187.0.dist-info}/top_level.txt +0 -0
@@ -28,11 +28,34 @@ class SpooledTest(s_test.SynTest):
28
28
 
29
29
  await sset.add(10)
30
30
 
31
+ newset = await sset.copy()
32
+ self.len(1, newset)
33
+ self.true(10 in newset)
34
+ self.false(newset.fallback)
35
+
31
36
  # Trigger fallback
32
37
  await sset.add(20)
33
38
  await sset.add(30)
34
39
  await sset.add(None)
35
40
 
41
+ newset = await sset.copy()
42
+ self.true(10 in newset)
43
+ self.true(20 in newset)
44
+ self.true(30 in newset)
45
+ self.true(None in newset)
46
+ self.len(4, newset)
47
+
48
+ await newset.clear()
49
+ self.false(10 in newset)
50
+ self.false(20 in newset)
51
+ self.false(30 in newset)
52
+ self.false(None in newset)
53
+ self.len(0, newset)
54
+
55
+ self.true(os.path.isdir(newset.slab.path))
56
+ await newset.fini()
57
+ self.false(os.path.isdir(newset.slab.path))
58
+
36
59
  self.len(4, sset)
37
60
 
38
61
  await sset.add(20)
@@ -68,6 +91,17 @@ class SpooledTest(s_test.SynTest):
68
91
  self.true(os.path.isdir(sset.slab.path))
69
92
  self.true(os.path.abspath(sset.slab.path).startswith(dirn))
70
93
 
94
+ newset = await sset.copy()
95
+ self.true(os.path.isdir(newset.slab.path))
96
+ self.true(os.path.abspath(newset.slab.path).startswith(dirn))
97
+
98
+ # Slabs should get removed on fini
99
+ self.false(os.path.isdir(sset.slab.path))
100
+
101
+ self.true(os.path.isdir(newset.slab.path))
102
+ await newset.fini()
103
+ self.false(os.path.isdir(newset.slab.path))
104
+
71
105
  async def test_spooled_dict(self):
72
106
 
73
107
  async def runtest(x):
@@ -403,24 +403,7 @@ class StormlibModelTest(s_test.SynTest):
403
403
  q = 'test:str=src $n=$node -> { test:str=deny $lib.model.migration.copyTags($n, $node) }'
404
404
  await self.asyncraises(s_exc.AuthDeny, core.nodes(q, opts=aslow))
405
405
 
406
- with self.raises(s_exc.NoSuchProp) as exc:
407
- await core.callStorm('$lib.model.migration.liftByPropValuNoNorm(formname, propname, valu)')
408
- self.eq(exc.exception.get('mesg'), 'Could not find prop: formname:propname')
409
-
410
- with self.raises(s_exc.AuthDeny) as exc:
411
- await core.callStorm('$lib.model.migration.liftByPropValuNoNorm(it:prod:soft, cpe, valu)')
412
- self.eq(exc.exception.get('mesg'), '$lib.model.migration.liftByPropValuNoNorm() is restricted to model migrations only.')
413
-
414
- with self.raises(s_exc.BadArg) as exc:
415
- await core.callStorm('$lib.model.migration.setNodePropValuNoNorm(notanode, propname, valu)')
416
- self.eq(exc.exception.get('mesg'), '$lib.model.migration.setNodePropValuNoNorm() argument must be a node.')
417
-
418
- with self.raises(s_exc.AuthDeny) as exc:
419
- await core.callStorm('test:str $lib.model.migration.setNodePropValuNoNorm($node, propname, valu)')
420
- self.eq(exc.exception.get('mesg'), '$lib.model.migration.setNodePropValuNoNorm() is restricted to model migrations only.')
421
-
422
406
  # copy extended properties
423
-
424
407
  await self.asyncraises(s_exc.BadArg, core.nodes('test:str=src $lib.model.migration.copyExtProps($node, newp)'))
425
408
  await self.asyncraises(s_exc.BadArg, core.nodes('test:str=dst $lib.model.migration.copyExtProps(newp, $node)'))
426
409
 
@@ -442,259 +425,6 @@ class StormlibModelTest(s_test.SynTest):
442
425
  self.len(1, nodes)
443
426
  self.eq(nodes[0].get('_foo'), 'foobarbaz')
444
427
 
445
- async def test_model_migration_s_itSecCpe_2_170_0(self):
446
-
447
- async with self.getRegrCore('itSecCpe_2_170_0', maxvers=(0, 2, 27)) as core:
448
- # Migrate it:sec:cpe nodes with a valid CPE2.3, valid CPE2.2
449
- q = 'it:sec:cpe +#test.cpe.23valid +#test.cpe.22valid'
450
- nodes = await core.nodes(q)
451
- self.len(2, nodes)
452
- self.eq(
453
- [
454
- ('it:sec:cpe', 'cpe:2.3:a:abine:donottrackme_-_mobile_privacy:1.1.8:*:*:*:*:android:*:*'),
455
- ('it:sec:cpe', 'cpe:2.3:a:01generator:pireospay:-:*:*:*:*:prestashop:*:*')
456
- ],
457
- [node.ndef for node in nodes]
458
- )
459
-
460
- q = '''
461
- it:sec:cpe +#test.cpe.23valid +#test.cpe.22valid
462
- $lib.debug=$lib.true
463
- $lib.model.migration.s.itSecCpe_2_170_0($node)
464
- $node.data.load(migration.s.itSecCpe_2_170_0)
465
- '''
466
- nodes = await core.nodes(q)
467
-
468
- data = nodes[0].nodedata['migration.s.itSecCpe_2_170_0']
469
- self.nn(data)
470
- self.eq(data['status'], 'success')
471
- self.none(data.get('reason'))
472
-
473
- data = nodes[1].nodedata['migration.s.itSecCpe_2_170_0']
474
- self.nn(data)
475
- self.eq(data['status'], 'success')
476
- self.none(data.get('reason'))
477
-
478
- async with self.getRegrCore('itSecCpe_2_170_0', maxvers=(0, 2, 27)) as core:
479
- # Migrate it:sec:cpe nodes with a valid CPE2.3, invalid CPE2.2
480
- q = '''
481
- it:sec:cpe +#test.cpe.23valid +#test.cpe.22invalid
482
- $lib.debug=$lib.true
483
- $lib.model.migration.s.itSecCpe_2_170_0($node)
484
- '''
485
- nodes = await core.nodes(q)
486
- self.len(3, nodes)
487
- self.eq(
488
- [
489
- ('it:sec:cpe', 'cpe:2.3:a:1c:1c\\:enterprise:-:*:*:*:*:*:*:*'),
490
- ('it:sec:cpe', 'cpe:2.3:o:zyxel:nas542_firmware:5.21\\%28aazf.15\\%29co:*:*:*:*:*:*:*'),
491
- ('it:sec:cpe', 'cpe:2.3:a:abinitio:control\\>center:-:*:*:*:*:*:*:*'),
492
- ],
493
- [node.ndef for node in nodes]
494
- )
495
-
496
- q = '''
497
- it:sec:cpe +#test.cpe.23valid +#test.cpe.22invalid
498
- $node.data.load(migration.s.itSecCpe_2_170_0)
499
- '''
500
- nodes = await core.nodes(q)
501
- self.len(3, nodes)
502
-
503
- data = nodes[0].nodedata['migration.s.itSecCpe_2_170_0']
504
- self.nn(data)
505
- self.eq(data['status'], 'success')
506
- self.eq(data['updated'], ['v2_2', 'product'])
507
- self.eq(nodes[0].get('v2_2'), 'cpe:/a:1c:1c%3aenterprise:-')
508
- self.eq(nodes[0].get('product'), '1c:enterprise')
509
-
510
- data = nodes[1].nodedata['migration.s.itSecCpe_2_170_0']
511
- self.nn(data)
512
- self.eq(data['status'], 'success')
513
- self.none(data.get('valu'))
514
- self.eq(data['updated'], ['v2_2', 'version'])
515
- self.eq(nodes[1].get('v2_2'), 'cpe:/o:zyxel:nas542_firmware:5.21%2528aazf.15%2529co')
516
- self.eq(nodes[1].get('version'), '5.21%28aazf.15%29co')
517
-
518
- data = nodes[2].nodedata['migration.s.itSecCpe_2_170_0']
519
- self.nn(data)
520
- self.eq(data['status'], 'success')
521
- self.eq(data['updated'], ['v2_2', 'product'])
522
- self.eq(nodes[2].get('v2_2'), 'cpe:/a:abinitio:control%3ecenter:-')
523
- self.eq(nodes[2].get('product'), 'control>center')
524
-
525
- # The migration of this node was not correct because the CPE2.3 string (primary property) is valid but was
526
- # not created correctly due to a bad CPE2.2 input value. Now we update :v2_2 to be correct, and re-run the
527
- # migration. This time, we specify `prefer_v22=True` and `force=True` so the migration will use the updated
528
- # :v2_2 prop for reparsing the strings. Force will cause the migration to continue past the check where both
529
- # the primary property and :v2_2 are valid.
530
- q = '''
531
- it:sec:cpe:product=nas542_firmware [ :v2_2="cpe:/o:zyxel:nas542_firmware:5.21%28aazf.15%29co" ]
532
- $lib.debug=$lib.true
533
- $lib.model.migration.s.itSecCpe_2_170_0($node, prefer_v22=$lib.true, force=$lib.true)
534
- '''
535
- nodes = await core.nodes(q)
536
- self.len(1, nodes)
537
-
538
- # Lift the updated node and check the migration did what was expected.
539
- q = '''
540
- it:sec:cpe:product=nas542_firmware
541
- $node.data.load(migration.s.itSecCpe_2_170_0)
542
- '''
543
- nodes = await core.nodes(q)
544
- self.len(1, nodes)
545
-
546
- data = nodes[0].nodedata['migration.s.itSecCpe_2_170_0']
547
- self.nn(data)
548
- self.eq(data['status'], 'success')
549
- self.eq(data['updated'], ['version'])
550
- self.eq(data['valu'], 'cpe:2.3:o:zyxel:nas542_firmware:5.21\\(aazf.15\\)co:*:*:*:*:*:*:*')
551
- self.eq(nodes[0].get('v2_2'), 'cpe:/o:zyxel:nas542_firmware:5.21%28aazf.15%29co')
552
- self.eq(nodes[0].get('version'), '5.21(aazf.15)co')
553
-
554
- async with self.getRegrCore('itSecCpe_2_170_0', maxvers=(0, 2, 27)) as core:
555
- # Migrate it:sec:cpe nodes with a invalid CPE2.3, valid CPE2.2
556
- q = '''
557
- it:sec:cpe +#test.cpe.23invalid +#test.cpe.22valid
558
- $lib.debug=$lib.true
559
- $lib.model.migration.s.itSecCpe_2_170_0($node)
560
- '''
561
- nodes = await core.nodes(q)
562
- self.len(4, nodes)
563
- self.eq(
564
- [
565
- ('it:sec:cpe', 'cpe:2.3:h:d\\-link:dir\\-850l:*:*:*:*:*:*:*:*'),
566
- ('it:sec:cpe', 'cpe:2.3:a:acurax:under_construction_%2f_maintenance_mode:-::~~~wordpress~~:*:*:*:*:*'),
567
- ('it:sec:cpe', 'cpe:2.3:a:10web:social_feed_for_instagram:1.0.0::~~premium~wordpress~~:*:*:*:*:*'),
568
- ('it:sec:cpe', 'cpe:2.3:o:zyxel:nas326_firmware:5.21%28aazf.14%29c0:*:*:*:*:*:*:*'),
569
- ],
570
- [node.ndef for node in nodes]
571
- )
572
-
573
- q = '''
574
- it:sec:cpe +#test.cpe.23invalid +#test.cpe.22valid
575
- $node.data.load(migration.s.itSecCpe_2_170_0)
576
- '''
577
- nodes = await core.nodes(q)
578
- self.len(4, nodes)
579
-
580
- data = nodes[0].nodedata['migration.s.itSecCpe_2_170_0']
581
- self.nn(data)
582
- self.eq(data['status'], 'success')
583
- self.eq(data['updated'], ['vendor', 'product'])
584
- self.eq(data['valu'], 'cpe:2.3:h:d-link:dir-850l:*:*:*:*:*:*:*:*')
585
- self.eq(nodes[0].get('vendor'), 'd-link')
586
- self.eq(nodes[0].get('product'), 'dir-850l')
587
-
588
- data = nodes[1].nodedata['migration.s.itSecCpe_2_170_0']
589
- self.nn(data)
590
- self.eq(data['status'], 'success')
591
- self.eq(data['updated'], ['product', 'update', 'edition', 'target_sw'])
592
- self.eq(data['valu'], 'cpe:2.3:a:acurax:under_construction_\\/_maintenance_mode:-:*:*:*:*:wordpress:*:*')
593
- self.eq(nodes[1].get('product'), 'under_construction_/_maintenance_mode')
594
- self.eq(nodes[1].get('update'), '*')
595
- self.eq(nodes[1].get('edition'), '*')
596
- self.eq(nodes[1].get('target_sw'), 'wordpress')
597
-
598
- data = nodes[2].nodedata['migration.s.itSecCpe_2_170_0']
599
- self.nn(data)
600
- self.eq(data['status'], 'success')
601
- self.eq(data['updated'], ['update', 'edition', 'sw_edition', 'target_sw'])
602
- self.eq(data['valu'], 'cpe:2.3:a:10web:social_feed_for_instagram:1.0.0:*:*:*:premium:wordpress:*:*')
603
- self.eq(nodes[2].get('update'), '*')
604
- self.eq(nodes[2].get('edition'), '*')
605
- self.eq(nodes[2].get('sw_edition'), 'premium')
606
- self.eq(nodes[2].get('target_sw'), 'wordpress')
607
-
608
- data = nodes[3].nodedata['migration.s.itSecCpe_2_170_0']
609
- self.nn(data)
610
- self.eq(data['status'], 'success')
611
- self.eq(data['updated'], ['version'])
612
- self.eq(data['valu'], 'cpe:2.3:o:zyxel:nas326_firmware:5.21\\(aazf.14\\)c0:*:*:*:*:*:*:*')
613
- self.eq(nodes[3].get('version'), '5.21(aazf.14)c0')
614
-
615
- async with self.getRegrCore('itSecCpe_2_170_0', maxvers=(0, 2, 27)) as core:
616
- # Migrate it:sec:cpe nodes with a invalid CPE2.3, invalid CPE2.2
617
- q = '''
618
- it:sec:cpe +#test.cpe.23invalid +#test.cpe.22invalid
619
- $lib.debug=$lib.true
620
- $lib.model.migration.s.itSecCpe_2_170_0($node)
621
- '''
622
- msgs = await core.stormlist(q)
623
- mesg = 'itSecCpe_2_170_0(it:sec:cpe=cpe:2.3:a:openbsd:openssh:8.2p1 ubuntu-4ubuntu0.2:*:*:*:*:*:*:*): '
624
- mesg += 'Unable to migrate due to invalid data. Primary property and :v2_2 are both invalid.'
625
- self.stormIsInWarn(mesg, msgs)
626
-
627
- ndefs = [m[1][0] for m in msgs if m[0] == 'node']
628
- self.eq(
629
- [
630
- ('it:sec:cpe', 'cpe:2.3:a:openbsd:openssh:7.4\r\n:*:*:*:*:*:*:*'),
631
- ('it:sec:cpe', 'cpe:2.3:a:openbsd:openssh:8.2p1 ubuntu-4ubuntu0.2:*:*:*:*:*:*:*')
632
- ],
633
- ndefs
634
- )
635
-
636
- q = '''
637
- it:sec:cpe +#test.cpe.23invalid +#test.cpe.22invalid
638
- $node.data.load(migration.s.itSecCpe_2_170_0)
639
- '''
640
- nodes = await core.nodes(q)
641
- self.len(2, nodes)
642
-
643
- for node in nodes:
644
- data = node.nodedata['migration.s.itSecCpe_2_170_0']
645
- self.eq(data, {
646
- 'status': 'failed',
647
- 'reason': 'Unable to migrate due to invalid data. Primary property and :v2_2 are both invalid.',
648
- })
649
-
650
- # Now update the :v2_2 on one of the nodes and migrate again
651
- q = '''
652
- it:sec:cpe:version^=8.2p1 [ :v2_2="cpe:/a:openbsd:openssh:8.2p1_ubuntu-4ubuntu0.2" ]
653
- $lib.debug=$lib.true
654
- $lib.model.migration.s.itSecCpe_2_170_0($node)
655
- '''
656
- msgs = await core.stormlist(q)
657
- self.stormHasNoWarnErr(msgs)
658
-
659
- q = '''
660
- it:sec:cpe:version^=8.2p1
661
- $node.data.load(migration.s.itSecCpe_2_170_0)
662
- '''
663
- nodes = await core.nodes(q)
664
- self.len(1, nodes)
665
-
666
- data = nodes[0].nodedata['migration.s.itSecCpe_2_170_0']
667
- self.nn(data)
668
- self.eq(data['status'], 'success')
669
- self.eq(data['updated'], ['version'])
670
- self.eq(data['valu'], 'cpe:2.3:a:openbsd:openssh:8.2p1_ubuntu-4ubuntu0.2:*:*:*:*:*:*:*')
671
- self.eq(nodes[0].get('version'), '8.2p1_ubuntu-4ubuntu0.2')
672
-
673
- # Run the migration again to make sure we identify already migrated
674
- # nodes correctly and bail early.
675
- q = '''
676
- it:sec:cpe:version^=8.2p1
677
- $lib.debug=$lib.true
678
- $lib.model.migration.s.itSecCpe_2_170_0($node)
679
- '''
680
- msgs = await core.stormlist(q)
681
- self.stormIsInPrint('DEBUG: itSecCpe_2_170_0(it:sec:cpe=cpe:2.3:a:openbsd:openssh:8.2p1 ubuntu-4ubuntu0.2:*:*:*:*:*:*:*): Node already migrated.', msgs)
682
-
683
- q = '''
684
- it:sec:cpe:version^=8.2p1
685
- $lib.debug=$lib.true
686
- $lib.model.migration.s.itSecCpe_2_170_0($node, force=$lib.true)
687
- '''
688
- msgs = await core.stormlist(q)
689
- self.stormIsInPrint('DEBUG: itSecCpe_2_170_0(it:sec:cpe=cpe:2.3:a:openbsd:openssh:8.2p1 ubuntu-4ubuntu0.2:*:*:*:*:*:*:*): No property updates required.', msgs)
690
-
691
- async with self.getTestCore() as core:
692
- with self.raises(s_exc.BadArg):
693
- await core.callStorm('$lib.model.migration.s.itSecCpe_2_170_0(newp)')
694
-
695
- with self.raises(s_exc.BadArg):
696
- await core.callStorm('[ inet:fqdn=vertex.link ] $lib.model.migration.s.itSecCpe_2_170_0($node)')
697
-
698
428
  async def test_stormlib_model_migrations_risk_hasvuln_vulnerable(self):
699
429
 
700
430
  async with self.getTestCore() as core:
@@ -907,6 +907,17 @@ class StormTypesTest(s_test.SynTest):
907
907
  'refs',
908
908
  '20153b758f9d5eaaa38e4f4a65c36da797c3e59e549620fa7c4895e1a920991f'), edges)
909
909
 
910
+ data = await core.callStorm('''
911
+ $data = ({})
912
+ inet:user=visi
913
+ for ($name, $valu) in $lib.layer.get().getNodeData($node.iden()) { $data.$name = $valu }
914
+ return($data)
915
+ ''')
916
+ foo = data.get('foo')
917
+ self.nn(foo)
918
+ self.nn(foo.get('asof'))
919
+ self.eq('bar', foo.get('data'))
920
+
910
921
  msgs = await core.stormlist('$lib.print($lib.null)')
911
922
  self.stormIsInPrint('$lib.null', msgs)
912
923
  self.stormNotInPrint('None', msgs)
@@ -1778,8 +1778,17 @@ class InfotechModelTest(s_t_utils.SynTest):
1778
1778
  with self.raises(s_exc.BadTypeValu):
1779
1779
  nodes = await core.nodes('[it:sec:cpe=cpe:2.3:1:2:3:4:5:6:7:8:9:10:11:12]')
1780
1780
 
1781
- nodes = await core.nodes('[ it:sec:cpe=cpe:2.3:a:vertex:synapse ]')
1782
- self.eq(nodes[0].ndef, ('it:sec:cpe', 'cpe:2.3:a:vertex:synapse:*:*:*:*:*:*:*:*'))
1781
+ with self.raises(s_exc.BadTypeValu):
1782
+ await core.nodes('[ it:sec:cpe=cpe:2.3:a:vertex:synapse ]')
1783
+
1784
+ with self.raises(s_exc.BadTypeValu):
1785
+ await core.callStorm(r'$lib.cast(it:sec:cpe, "cpe:2.3:a:openbsd:openssh:7.4\r\n:*:*:*:*:*:*:*")')
1786
+
1787
+ with self.raises(s_exc.BadTypeValu):
1788
+ await core.callStorm(r'$lib.cast(it:sec:cpe:v2_2, "cpe:/a:01generator:pireospay\r\n:-::~~~prestashop~~")')
1789
+
1790
+ with self.raises(s_exc.BadTypeValu):
1791
+ await core.callStorm('$lib.cast(it:sec:cpe:v2_2, "cpe:2.3:*")')
1783
1792
 
1784
1793
  nodes = await core.nodes('''[
1785
1794
  it:sec:cpe=cpe:2.3:a:microsoft:internet_explorer:8.0.6001:beta:*:*:*:*:*:*
@@ -1792,14 +1801,8 @@ class InfotechModelTest(s_t_utils.SynTest):
1792
1801
  self.eq(nodes[0].get('version'), '8.0.6001')
1793
1802
  self.eq(nodes[0].get('update'), 'beta')
1794
1803
 
1795
- nodes = await core.nodes("[ it:sec:cpe='cpe:2.3:a:openbsd:openssh:7.4\r\n:*:*:*:*:*:*:*' ]")
1796
- self.len(1, nodes)
1797
- self.eq(nodes[0].ndef, ('it:sec:cpe', 'cpe:2.3:a:openbsd:openssh:7.4:*:*:*:*:*:*:*'))
1798
- self.eq(nodes[0].get('part'), 'a')
1799
- self.eq(nodes[0].get('product'), 'openssh')
1800
- self.eq(nodes[0].get('vendor'), 'openbsd')
1801
- self.eq(nodes[0].get('version'), '7.4')
1802
- self.eq(nodes[0].get('v2_2'), 'cpe:/a:openbsd:openssh:7.4')
1804
+ with self.raises(s_exc.BadTypeValu):
1805
+ await core.nodes("[ it:sec:cpe='cpe:2.3:a:openbsd:openssh:7.4\r\n:*:*:*:*:*:*:*' ]")
1803
1806
 
1804
1807
  nodes = await core.nodes(r'[ it:sec:cpe="cpe:2.3:o:cisco:ios:12.1\(22\)ea1a:*:*:*:*:*:*:*" ]')
1805
1808
  self.len(1, nodes)
@@ -1923,7 +1926,7 @@ class InfotechModelTest(s_t_utils.SynTest):
1923
1926
  nodes = await core.nodes(q, opts={'vars': {'valu': valu}})
1924
1927
  self.len(1, nodes)
1925
1928
  node = nodes[0]
1926
- self.eq(node.ndef[1], valu.lower())
1929
+ self.eq(node.ndef[1], valu.lower(), msg=valu.lower())
1927
1930
 
1928
1931
  async def test_infotech_c2config(self):
1929
1932
  async with self.getTestCore() as core:
@@ -577,6 +577,7 @@ class RiskModelTest(s_t_utils.SynTest):
577
577
  :techniques=(*,)
578
578
  :tag=cno.mal.cobaltstrike
579
579
  :mitre:attack:software=S0001
580
+ :id=" AAAbbb123 "
580
581
 
581
582
  :sophistication=high
582
583
  :availability=public
@@ -593,6 +594,7 @@ class RiskModelTest(s_t_utils.SynTest):
593
594
  self.eq(1643673600000, nodes[0].get('reporter:discovered'))
594
595
  self.eq(1675209600000, nodes[0].get('reporter:published'))
595
596
  self.eq('S0001', nodes[0].get('mitre:attack:software'))
597
+ self.eq('AAAbbb123', nodes[0].get('id'))
596
598
 
597
599
  self.eq('cobaltstrike', nodes[0].get('soft:name'))
598
600
  self.eq(('beacon',), nodes[0].get('soft:names'))
@@ -0,0 +1,47 @@
1
+ from unittest import mock
2
+
3
+ import synapse.lib.output as s_output
4
+ import synapse.tools.snapshot as s_tools_snapshot
5
+
6
+ import synapse.tests.utils as s_t_utils
7
+
8
+ class PromoteToolTest(s_t_utils.SynTest):
9
+
10
+ async def test_tool_snapshot(self):
11
+
12
+ async with self.getTestCore() as core:
13
+
14
+ lurl = core.getLocalUrl()
15
+
16
+ self.eq(0, await s_tools_snapshot.main(('freeze', '--svcurl', lurl)))
17
+ self.true(core.paused)
18
+
19
+ outp = s_output.OutPutStr()
20
+ self.eq(1, await s_tools_snapshot.main(('freeze', '--svcurl', lurl), outp=outp))
21
+ self.isin('ERROR BadState', str(outp))
22
+
23
+ self.eq(0, await s_tools_snapshot.main(('resume', '--svcurl', lurl)))
24
+ self.false(core.paused)
25
+
26
+ outp = s_output.OutPutStr()
27
+ self.eq(1, await s_tools_snapshot.main(('resume', '--svcurl', lurl), outp=outp))
28
+ self.isin('ERROR BadState', str(outp))
29
+
30
+ outp = s_output.OutPutStr()
31
+ async with core.nexslock:
32
+ argv = ('freeze', '--svcurl', lurl, '--timeout', '1')
33
+ self.eq(1, await s_tools_snapshot.main(argv, outp=outp))
34
+ self.isin('ERROR TimeOut', str(outp))
35
+
36
+ def boom():
37
+ raise Exception('boom')
38
+
39
+ outp = s_output.OutPutStr()
40
+ with mock.patch('os.sync', boom):
41
+ self.eq(1, await s_tools_snapshot.main(('freeze', '--svcurl', lurl), outp=outp))
42
+ self.false(core.paused)
43
+ self.isin('ERROR SynErr: boom', str(outp))
44
+
45
+ outp = s_output.OutPutStr()
46
+ self.eq(1, await s_tools_snapshot.main(('freeze', '--svcurl', 'newp://newp'), outp=outp))
47
+ self.isin('ERROR BadUrl', str(outp))
@@ -18,7 +18,9 @@ Examples:
18
18
 
19
19
  async def main(argv, outp=s_output.stdout):
20
20
 
21
- pars = argparse.ArgumentParser(prog='synapse.tools.aha.clone', description=descr)
21
+ pars = argparse.ArgumentParser(prog='synapse.tools.aha.clone', description=descr,
22
+ formatter_class=argparse.RawDescriptionHelpFormatter)
23
+
22
24
  pars.add_argument('dnsname', help='The DNS name of the new AHA server.')
23
25
  pars.add_argument('--port', type=int, default=27492, help='The port that the new AHA server should listen on.')
24
26
  pars.add_argument('--url', default='cell:///vertex/storage', help='The telepath URL to connect to the AHA service.')
@@ -51,7 +51,7 @@ async def _main(argv, outp):
51
51
 
52
52
  def getArgParser():
53
53
  desc = 'CLI tool to generate simple x509 certificates from an Aha server.'
54
- pars = argparse.ArgumentParser(prog='aha.easycert', description=desc)
54
+ pars = argparse.ArgumentParser(prog='synapse.tools.aha.easycert', description=desc)
55
55
 
56
56
  pars.add_argument('-a', '--aha', required=True, # type=str,
57
57
  help='Aha server to connect too.')
@@ -21,7 +21,9 @@ Examples:
21
21
 
22
22
  async def main(argv, outp=s_output.stdout):
23
23
 
24
- pars = argparse.ArgumentParser(prog='provision', description=descr)
24
+ pars = argparse.ArgumentParser(prog='synapse.tools.aha.enroll', description=descr,
25
+ formatter_class=argparse.RawDescriptionHelpFormatter)
26
+
25
27
  pars.add_argument('onceurl', help='The one-time use AHA user enrollment URL.')
26
28
  opts = pars.parse_args(argv)
27
29
 
@@ -23,7 +23,9 @@ Examples:
23
23
 
24
24
  async def main(argv, outp=s_output.stdout):
25
25
 
26
- pars = argparse.ArgumentParser(prog='synapse.tools.aha.provision.service', description=descr)
26
+ pars = argparse.ArgumentParser(prog='synapse.tools.aha.provision.service', description=descr,
27
+ formatter_class=argparse.RawDescriptionHelpFormatter)
28
+
27
29
  pars.add_argument('--url', default='cell:///vertex/storage', help='The telepath URL to connect to the AHA service.')
28
30
  pars.add_argument('--user', help='Provision the new service with the username.')
29
31
  pars.add_argument('--cellyaml', help='Specify the path to a YAML file containing config options for the service.')
@@ -22,7 +22,9 @@ Examples:
22
22
 
23
23
  async def main(argv, outp=s_output.stdout):
24
24
 
25
- pars = argparse.ArgumentParser(prog='synapse.tools.aha.provision.user', description=descr)
25
+ pars = argparse.ArgumentParser(prog='synapse.tools.aha.provision.user', description=descr,
26
+ formatter_class=argparse.RawDescriptionHelpFormatter)
27
+
26
28
  pars.add_argument('--url', default='cell:///vertex/storage', help='The telepath URL to connect to the AHA service.')
27
29
  pars.add_argument('--again', default=False, action='store_true', help='Generate a new enroll URL for an existing user.')
28
30
  pars.add_argument('--only-url', help='Only output the URL upon successful execution',
@@ -883,9 +883,17 @@ async def format(opts: argparse.Namespace,
883
883
  text = text + f'\n{header}\n{"-" * len(header)}'
884
884
  dataz.sort(key=lambda x: x.get('prs'))
885
885
  for data in dataz:
886
- desc = data.get('desc')
887
- for line in textwrap.wrap(desc, initial_indent='- ', subsequent_indent=' ', width=opts.width):
888
- text = f'{text}\n{line}'
886
+ desc = data.get('desc') # type: str
887
+ desc_lines = desc.splitlines()
888
+ for i, chunk in enumerate(desc_lines):
889
+ if i == 0:
890
+ for line in textwrap.wrap(chunk, initial_indent='- ', subsequent_indent=' ', width=opts.width):
891
+ text = f'{text}\n{line}'
892
+ else:
893
+ text = text + '\n'
894
+ for line in textwrap.wrap(chunk, initial_indent=' ', subsequent_indent=' ', width=opts.width):
895
+ text = f'{text}\n{line}'
896
+
889
897
  if not opts.hide_prs:
890
898
  for pr in data.get('prs'):
891
899
  text = f'{text}\n (`#{pr} <https://github.com/vertexproject/synapse/pull/{pr}>`_)'
@@ -20,7 +20,9 @@ Examples:
20
20
 
21
21
  async def main(argv, outp=s_output.stdout):
22
22
 
23
- pars = argparse.ArgumentParser(prog='livebackup', description=descr)
23
+ pars = argparse.ArgumentParser(prog='synapse.tools.livebackup', description=descr,
24
+ formatter_class=argparse.RawDescriptionHelpFormatter)
25
+
24
26
  pars.add_argument('--url', default='cell:///vertex/storage', help='The telepath URL of the Synapse service.')
25
27
  pars.add_argument('--name', default=None, help='Specify a name for the backup. Defaults to an automatically generated timestamp.')
26
28
 
synapse/tools/promote.py CHANGED
@@ -18,9 +18,15 @@ Example (being run from a Cortex mirror docker container):
18
18
 
19
19
  async def main(argv, outp=s_output.stdout):
20
20
 
21
- pars = argparse.ArgumentParser(prog='provision', description=descr)
22
- pars.add_argument('--svcurl', default='cell:///vertex/storage', help='The telepath URL of the Synapse service.')
23
- pars.add_argument('--failure', default=False, action='store_true', help='Promotion is due to leader being offline. Graceful handoff is not possible.')
21
+ pars = argparse.ArgumentParser(prog='synapse.tools.promote', description=descr,
22
+ formatter_class=argparse.RawDescriptionHelpFormatter)
23
+
24
+ pars.add_argument('--svcurl', default='cell:///vertex/storage',
25
+ help='The telepath URL of the Synapse service.')
26
+
27
+ pars.add_argument('--failure', default=False, action='store_true',
28
+ help='Promotion is due to leader being offline. Graceful handoff is not possible.')
29
+
24
30
  # TODO pars.add_argument('--timeout', type=float, default=30.0, help='The maximum timeout to wait for the mirror to catch up.')
25
31
 
26
32
  opts = pars.parse_args(argv)
@@ -0,0 +1,69 @@
1
+ import sys
2
+ import asyncio
3
+ import logging
4
+ import argparse
5
+
6
+ import synapse.exc as s_exc
7
+ import synapse.telepath as s_telepath
8
+
9
+ import synapse.lib.output as s_output
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ desc = '''
14
+ Command line tool to freeze/resume service operations to allow
15
+ system admins to generate a transactionally consistent volume
16
+ snapshot using 3rd party tools.
17
+
18
+ The use pattern should be::
19
+
20
+ python -m synapse.tools.snapshot freeze
21
+
22
+ <generate volume snapshot using 3rd party tools>
23
+
24
+ python -m synapse.tools.snapshot resume
25
+
26
+ The tool will set the process exit code to 0 on success.
27
+ '''
28
+
29
+ async def main(argv, outp=s_output.stdout):
30
+
31
+ pars = argparse.ArgumentParser('synapse.tools.snapshot',
32
+ description=desc,
33
+ formatter_class=argparse.RawDescriptionHelpFormatter)
34
+
35
+ subs = pars.add_subparsers(required=True, title='commands', dest='cmd')
36
+
37
+ freeze = subs.add_parser('freeze', help='Suspend edits and sync changes to disk.')
38
+ freeze.add_argument('--timeout', type=int, default=120,
39
+ help='Maximum time to wait for the nexus lock.')
40
+
41
+ freeze.add_argument('--svcurl', default='cell:///vertex/storage',
42
+ help='The telepath URL of the Synapse service.')
43
+
44
+ resume = subs.add_parser('resume', help='Resume edits and continue normal operation.')
45
+ resume.add_argument('--svcurl', default='cell:///vertex/storage',
46
+ help='The telepath URL of the Synapse service.')
47
+
48
+ opts = pars.parse_args(argv)
49
+
50
+ try:
51
+ async with s_telepath.withTeleEnv():
52
+
53
+ async with await s_telepath.openurl(opts.svcurl) as proxy:
54
+
55
+ if opts.cmd == 'freeze':
56
+ await proxy.freeze(timeout=opts.timeout)
57
+ return 0
58
+
59
+ if opts.cmd == 'resume':
60
+ await proxy.resume()
61
+ return 0
62
+
63
+ except s_exc.SynErr as e:
64
+ mesg = e.errinfo.get('mesg')
65
+ outp.printf(f'ERROR {e.__class__.__name__}: {mesg}')
66
+ return 1
67
+
68
+ if __name__ == '__main__': # pragma: no cover
69
+ sys.exit(asyncio.run(main(sys.argv[1:])))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: synapse
3
- Version: 2.185.0
3
+ Version: 2.187.0
4
4
  Summary: Synapse Intelligence Analysis Framework
5
5
  Author-email: The Vertex Project LLC <root@vertex.link>
6
6
  License: Apache License 2.0