synapse 2.169.0__py311-none-any.whl → 2.171.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.
- synapse/cortex.py +99 -3
- synapse/datamodel.py +5 -0
- synapse/lib/ast.py +70 -12
- synapse/lib/cell.py +76 -7
- synapse/lib/layer.py +75 -6
- synapse/lib/lmdbslab.py +17 -0
- synapse/lib/node.py +7 -0
- synapse/lib/snap.py +22 -4
- synapse/lib/storm.py +1 -1
- synapse/lib/stormlib/cortex.py +1 -1
- synapse/lib/stormlib/model.py +339 -40
- synapse/lib/stormtypes.py +58 -1
- synapse/lib/types.py +36 -1
- synapse/lib/version.py +2 -2
- synapse/lib/view.py +94 -15
- synapse/models/files.py +40 -0
- synapse/models/inet.py +8 -4
- synapse/models/infotech.py +355 -17
- synapse/tests/files/cpedata.json +525034 -0
- synapse/tests/test_cortex.py +108 -0
- synapse/tests/test_lib_ast.py +66 -0
- synapse/tests/test_lib_cell.py +112 -0
- synapse/tests/test_lib_layer.py +52 -1
- synapse/tests/test_lib_lmdbslab.py +36 -0
- synapse/tests/test_lib_scrape.py +72 -71
- synapse/tests/test_lib_snap.py +16 -1
- synapse/tests/test_lib_storm.py +118 -0
- synapse/tests/test_lib_stormlib_cortex.py +15 -0
- synapse/tests/test_lib_stormlib_model.py +427 -0
- synapse/tests/test_lib_stormtypes.py +147 -15
- synapse/tests/test_lib_types.py +21 -0
- synapse/tests/test_lib_view.py +77 -0
- synapse/tests/test_model_files.py +52 -0
- synapse/tests/test_model_inet.py +63 -1
- synapse/tests/test_model_infotech.py +187 -26
- synapse/tests/utils.py +42 -9
- {synapse-2.169.0.dist-info → synapse-2.171.0.dist-info}/METADATA +1 -1
- {synapse-2.169.0.dist-info → synapse-2.171.0.dist-info}/RECORD +41 -40
- {synapse-2.169.0.dist-info → synapse-2.171.0.dist-info}/LICENSE +0 -0
- {synapse-2.169.0.dist-info → synapse-2.171.0.dist-info}/WHEEL +0 -0
- {synapse-2.169.0.dist-info → synapse-2.171.0.dist-info}/top_level.txt +0 -0
synapse/lib/snap.py
CHANGED
|
@@ -183,6 +183,15 @@ class ProtoNode:
|
|
|
183
183
|
|
|
184
184
|
return s_common.novalu
|
|
185
185
|
|
|
186
|
+
async def hasData(self, name):
|
|
187
|
+
if name in self.nodedata:
|
|
188
|
+
return True
|
|
189
|
+
|
|
190
|
+
if self.node is not None:
|
|
191
|
+
return await self.node.hasData(name)
|
|
192
|
+
|
|
193
|
+
return False
|
|
194
|
+
|
|
186
195
|
async def setData(self, name, valu):
|
|
187
196
|
|
|
188
197
|
if await self.getData(name) == valu:
|
|
@@ -287,6 +296,15 @@ class ProtoNode:
|
|
|
287
296
|
if self.node is not None:
|
|
288
297
|
return self.node.getTagProp(tag, name)
|
|
289
298
|
|
|
299
|
+
def hasTagProp(self, tag, name):
|
|
300
|
+
if (tag, name) in self.tagprops:
|
|
301
|
+
return True
|
|
302
|
+
|
|
303
|
+
if self.node is not None:
|
|
304
|
+
return self.node.hasTagProp(tag, name)
|
|
305
|
+
|
|
306
|
+
return False
|
|
307
|
+
|
|
290
308
|
async def setTagProp(self, tag, name, valu):
|
|
291
309
|
|
|
292
310
|
tagnode = await self.addTag(tag)
|
|
@@ -322,7 +340,7 @@ class ProtoNode:
|
|
|
322
340
|
if self.node is not None:
|
|
323
341
|
return self.node.get(name)
|
|
324
342
|
|
|
325
|
-
async def _set(self, prop, valu, norminfo=None):
|
|
343
|
+
async def _set(self, prop, valu, norminfo=None, ignore_ro=False):
|
|
326
344
|
|
|
327
345
|
if prop.locked:
|
|
328
346
|
mesg = f'Prop {prop.full} is locked due to deprecation.'
|
|
@@ -360,7 +378,7 @@ class ProtoNode:
|
|
|
360
378
|
if curv == valu:
|
|
361
379
|
return False
|
|
362
380
|
|
|
363
|
-
if prop.info.get('ro') and curv is not None:
|
|
381
|
+
if not ignore_ro and prop.info.get('ro') and curv is not None:
|
|
364
382
|
mesg = f'Property is read only: {prop.full}.'
|
|
365
383
|
await self.ctx.snap._raiseOnStrict(s_exc.ReadOnlyProp, mesg)
|
|
366
384
|
return False
|
|
@@ -372,12 +390,12 @@ class ProtoNode:
|
|
|
372
390
|
|
|
373
391
|
return valu, norminfo
|
|
374
392
|
|
|
375
|
-
async def set(self, name, valu, norminfo=None):
|
|
393
|
+
async def set(self, name, valu, norminfo=None, ignore_ro=False):
|
|
376
394
|
prop = self.form.props.get(name)
|
|
377
395
|
if prop is None:
|
|
378
396
|
return False
|
|
379
397
|
|
|
380
|
-
retn = await self._set(prop, valu, norminfo=norminfo)
|
|
398
|
+
retn = await self._set(prop, valu, norminfo=norminfo, ignore_ro=ignore_ro)
|
|
381
399
|
if retn is False:
|
|
382
400
|
return False
|
|
383
401
|
|
synapse/lib/storm.py
CHANGED
|
@@ -2351,7 +2351,7 @@ class Runtime(s_base.Base):
|
|
|
2351
2351
|
if view is None:
|
|
2352
2352
|
raise s_exc.NoSuchView(mesg=f'No such view iden={viewiden}', iden=viewiden)
|
|
2353
2353
|
|
|
2354
|
-
self.
|
|
2354
|
+
self.confirm(('view', 'read'), gateiden=viewiden)
|
|
2355
2355
|
snap = await view.snap(self.user)
|
|
2356
2356
|
|
|
2357
2357
|
return snap
|
synapse/lib/stormlib/cortex.py
CHANGED
|
@@ -980,7 +980,7 @@ class HttpReq(s_stormtypes.StormType):
|
|
|
980
980
|
try:
|
|
981
981
|
return json.loads(self.rnfo.get('body'))
|
|
982
982
|
except (UnicodeDecodeError, json.JSONDecodeError) as e:
|
|
983
|
-
raise s_exc.StormRuntimeError(mesg='Failed to decode request body as JSON: {e}') from None
|
|
983
|
+
raise s_exc.StormRuntimeError(mesg=f'Failed to decode request body as JSON: {e}') from None
|
|
984
984
|
|
|
985
985
|
@s_stormtypes.stormfunc(readonly=True)
|
|
986
986
|
async def _methSendCode(self, code):
|
synapse/lib/stormlib/model.py
CHANGED
|
@@ -5,6 +5,19 @@ import synapse.lib.node as s_node
|
|
|
5
5
|
import synapse.lib.cache as s_cache
|
|
6
6
|
import synapse.lib.stormtypes as s_stormtypes
|
|
7
7
|
|
|
8
|
+
import synapse.models.infotech as s_infotech
|
|
9
|
+
|
|
10
|
+
RISK_HASVULN_VULNPROPS = (
|
|
11
|
+
'hardware',
|
|
12
|
+
'host',
|
|
13
|
+
'item',
|
|
14
|
+
'org',
|
|
15
|
+
'person',
|
|
16
|
+
'place',
|
|
17
|
+
'software',
|
|
18
|
+
'spec',
|
|
19
|
+
)
|
|
20
|
+
|
|
8
21
|
stormcmds = [
|
|
9
22
|
{
|
|
10
23
|
'name': 'model.edge.set',
|
|
@@ -311,7 +324,7 @@ class LibModel(s_stormtypes.Lib):
|
|
|
311
324
|
{'name': 'name', 'type': 'str', 'desc': 'The name of the tag prop to retrieve.', },
|
|
312
325
|
),
|
|
313
326
|
'returns': {'type': ['model:tagprop', 'null'],
|
|
314
|
-
'desc': 'The ``model:tagprop`` instance
|
|
327
|
+
'desc': 'The ``model:tagprop`` instance of the tag prop if present or null.',
|
|
315
328
|
}}},
|
|
316
329
|
)
|
|
317
330
|
|
|
@@ -694,8 +707,58 @@ class LibModelDeprecated(s_stormtypes.Lib):
|
|
|
694
707
|
gatekeys = ((self.runt.user.iden, ('model', 'deprecated', 'lock'), None),)
|
|
695
708
|
await self.runt.dyncall('cortex', todo, gatekeys=gatekeys)
|
|
696
709
|
|
|
710
|
+
class MigrationEditorMixin:
|
|
711
|
+
'''
|
|
712
|
+
Mixin helpers for migrating data within an editor context.
|
|
713
|
+
'''
|
|
714
|
+
|
|
715
|
+
async def copyData(self, src, proto, overwrite=False):
|
|
716
|
+
|
|
717
|
+
async for name in src.iterDataKeys():
|
|
718
|
+
if overwrite or not await proto.hasData(name):
|
|
719
|
+
self.runt.layerConfirm(('node', 'data', 'set', name))
|
|
720
|
+
valu = await src.getData(name)
|
|
721
|
+
await proto.setData(name, valu)
|
|
722
|
+
|
|
723
|
+
async def copyEdges(self, editor, src, proto):
|
|
724
|
+
|
|
725
|
+
verbs = set()
|
|
726
|
+
|
|
727
|
+
async for (verb, n2iden) in src.iterEdgesN1():
|
|
728
|
+
|
|
729
|
+
if verb not in verbs:
|
|
730
|
+
self.runt.layerConfirm(('node', 'edge', 'add', verb))
|
|
731
|
+
verbs.add(verb)
|
|
732
|
+
|
|
733
|
+
if await self.runt.snap.getNodeByBuid(s_common.uhex(n2iden)) is not None:
|
|
734
|
+
await proto.addEdge(verb, n2iden)
|
|
735
|
+
|
|
736
|
+
dstiden = proto.iden()
|
|
737
|
+
|
|
738
|
+
async for (verb, n1iden) in src.iterEdgesN2():
|
|
739
|
+
|
|
740
|
+
if verb not in verbs:
|
|
741
|
+
self.runt.layerConfirm(('node', 'edge', 'add', verb))
|
|
742
|
+
verbs.add(verb)
|
|
743
|
+
|
|
744
|
+
n1proto = await editor.getNodeByBuid(s_common.uhex(n1iden))
|
|
745
|
+
if n1proto is not None:
|
|
746
|
+
await n1proto.addEdge(verb, dstiden)
|
|
747
|
+
|
|
748
|
+
async def copyTags(self, src, proto, overwrite=False):
|
|
749
|
+
|
|
750
|
+
for name, valu in src.tags.items():
|
|
751
|
+
self.runt.layerConfirm(('node', 'tag', 'add', *name.split('.')))
|
|
752
|
+
await proto.addTag(name, valu=valu)
|
|
753
|
+
|
|
754
|
+
for tagname, tagprops in src.tagprops.items():
|
|
755
|
+
for propname, valu in tagprops.items():
|
|
756
|
+
if overwrite or not proto.hasTagProp(tagname, propname):
|
|
757
|
+
await proto.setTagProp(tagname, propname, valu) # use tag perms
|
|
758
|
+
|
|
759
|
+
|
|
697
760
|
@s_stormtypes.registry.registerLib
|
|
698
|
-
class LibModelMigration(s_stormtypes.Lib):
|
|
761
|
+
class LibModelMigration(s_stormtypes.Lib, MigrationEditorMixin):
|
|
699
762
|
'''
|
|
700
763
|
A Storm library containing migration tools.
|
|
701
764
|
'''
|
|
@@ -745,14 +808,8 @@ class LibModelMigration(s_stormtypes.Lib):
|
|
|
745
808
|
overwrite = await s_stormtypes.tobool(overwrite)
|
|
746
809
|
|
|
747
810
|
async with self.runt.snap.getEditor() as editor:
|
|
748
|
-
|
|
749
811
|
proto = editor.loadNode(dst)
|
|
750
|
-
|
|
751
|
-
async for name in src.iterDataKeys():
|
|
752
|
-
if overwrite or not await dst.hasData(name):
|
|
753
|
-
self.runt.layerConfirm(('node', 'data', 'set', name))
|
|
754
|
-
valu = await src.getData(name)
|
|
755
|
-
await proto.setData(name, valu)
|
|
812
|
+
await self.copyData(src, proto, overwrite=overwrite)
|
|
756
813
|
|
|
757
814
|
async def _methCopyEdges(self, src, dst):
|
|
758
815
|
|
|
@@ -764,30 +821,8 @@ class LibModelMigration(s_stormtypes.Lib):
|
|
|
764
821
|
snap = self.runt.snap
|
|
765
822
|
|
|
766
823
|
async with snap.getEditor() as editor:
|
|
767
|
-
|
|
768
824
|
proto = editor.loadNode(dst)
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
async for (verb, n2iden) in src.iterEdgesN1():
|
|
772
|
-
|
|
773
|
-
if verb not in verbs:
|
|
774
|
-
self.runt.layerConfirm(('node', 'edge', 'add', verb))
|
|
775
|
-
verbs.add(verb)
|
|
776
|
-
|
|
777
|
-
if await snap.getNodeByBuid(s_common.uhex(n2iden)) is not None:
|
|
778
|
-
await proto.addEdge(verb, n2iden)
|
|
779
|
-
|
|
780
|
-
dstiden = s_common.ehex(dst.buid)
|
|
781
|
-
|
|
782
|
-
async for (verb, n1iden) in src.iterEdgesN2():
|
|
783
|
-
|
|
784
|
-
if verb not in verbs:
|
|
785
|
-
self.runt.layerConfirm(('node', 'edge', 'add', verb))
|
|
786
|
-
verbs.add(verb)
|
|
787
|
-
|
|
788
|
-
n1proto = await editor.getNodeByBuid(s_common.uhex(n1iden))
|
|
789
|
-
if n1proto is not None:
|
|
790
|
-
await n1proto.addEdge(verb, dstiden)
|
|
825
|
+
await self.copyEdges(editor, src, proto)
|
|
791
826
|
|
|
792
827
|
async def _methCopyTags(self, src, dst, overwrite=False):
|
|
793
828
|
|
|
@@ -801,14 +836,278 @@ class LibModelMigration(s_stormtypes.Lib):
|
|
|
801
836
|
snap = self.runt.snap
|
|
802
837
|
|
|
803
838
|
async with snap.getEditor() as editor:
|
|
804
|
-
|
|
805
839
|
proto = editor.loadNode(dst)
|
|
840
|
+
await self.copyTags(src, proto, overwrite=overwrite)
|
|
841
|
+
|
|
842
|
+
@s_stormtypes.registry.registerLib
|
|
843
|
+
class LibModelMigrations(s_stormtypes.Lib, MigrationEditorMixin):
|
|
844
|
+
'''
|
|
845
|
+
A Storm library for selectively migrating nodes in the current view.
|
|
846
|
+
'''
|
|
847
|
+
_storm_locals = (
|
|
848
|
+
{'name': 'itSecCpe_2_170_0',
|
|
849
|
+
'desc': '''
|
|
850
|
+
Versions of Synapse prior to v2.169.0 did not correctly parse and
|
|
851
|
+
convert CPE strings from 2.2 -> 2.3 or 2.3 -> 2.2. This migration
|
|
852
|
+
attempts to re-normalize `it:sec:cpe` nodes that may be fixable.
|
|
853
|
+
|
|
854
|
+
NOTE: It is highly recommended to test the `it:sec:cpe` migrations
|
|
855
|
+
in a fork first and confirm the migration was successful without any
|
|
856
|
+
issues. Then run the migration in view deporder to migrate the
|
|
857
|
+
entire cortex. E.g.::
|
|
858
|
+
|
|
859
|
+
for $view in $lib.view.list(deporder=$lib.true) {
|
|
860
|
+
view.exec $view.iden {
|
|
861
|
+
for $n in $lib.layer.get().liftByProp(it:sec:cpe) {
|
|
862
|
+
$lib.model.migration.s.itSecCpe_2_170_0($n)
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
Upon completion of the migration, nodedata will contain a
|
|
868
|
+
`migration.s.itSecCpe_2_170_0` dict with information about the
|
|
869
|
+
migration status. This dict may contain the following:
|
|
870
|
+
|
|
871
|
+
- `status`: (required str) "success" or "failed"
|
|
872
|
+
- `reason`: (optional str) if "status" is "failed", this key will
|
|
873
|
+
explain why the migration failed.
|
|
874
|
+
- `valu`: (optional str) if this key is present, it will contain
|
|
875
|
+
an updated CPE2.3 string since the primary property cannot be
|
|
876
|
+
changed.
|
|
877
|
+
- `updated`: (optional list[str]) A list of properties that were
|
|
878
|
+
updated by the migration.
|
|
879
|
+
|
|
880
|
+
Failed or incorrect migrations may be helped by updating the :v2_2
|
|
881
|
+
property to be a valid CPE2.2 string and then re-running the
|
|
882
|
+
migration with `force=$lib.true`. If the primary property (CPE2.3)
|
|
883
|
+
is valid but incorrect, users may update the :v2_2 property and then
|
|
884
|
+
run the migration with `prefer_v22=$lib.true` to make the migration
|
|
885
|
+
use the `:v2_2` string instead of the primary property for the
|
|
886
|
+
migration process.
|
|
887
|
+
''',
|
|
888
|
+
'type': {'type': 'function', '_funcname': '_itSecCpe_2_170_0',
|
|
889
|
+
'args': (
|
|
890
|
+
{'name': 'n', 'type': 'node', 'desc': 'The it:sec:cpe node to migrate.'},
|
|
891
|
+
{'name': 'prefer_v22', 'type': 'bool', 'default': False,
|
|
892
|
+
'desc': '''
|
|
893
|
+
Try to renormalize using the :v2_2 prop instead of the
|
|
894
|
+
primary property. This can be especially useful when the
|
|
895
|
+
primary property is a valid but incorrect CPE string.
|
|
896
|
+
'''},
|
|
897
|
+
{'name': 'force', 'type': 'bool', 'default': False,
|
|
898
|
+
'desc': 'Perform fixups even if the primary property and :v2_2 are valid.'},
|
|
899
|
+
),
|
|
900
|
+
'returns': {'type': 'boolean', 'desc': 'Boolean indicating if the migration was successful.'}}},
|
|
901
|
+
{'name': 'riskHasVulnToVulnerable', 'desc': '''
|
|
902
|
+
Create a risk:vulnerable node from the provided risk:hasvuln node.
|
|
903
|
+
|
|
904
|
+
Edits will be made to the risk:vulnerable node in the current write layer.
|
|
905
|
+
|
|
906
|
+
If multiple vulnerable properties are set on the risk:hasvuln node
|
|
907
|
+
multiple risk:vulnerable nodes will be created (each with a unique guid).
|
|
908
|
+
Otherwise, a single risk:vulnerable node will be created with the same guid
|
|
909
|
+
as the provided risk:hasvuln node. Extended properties will not be migrated.
|
|
910
|
+
|
|
911
|
+
Tags, tag properties, edges, and node data will be copied
|
|
912
|
+
to the risk:vulnerable node. However, existing tag properties and
|
|
913
|
+
node data will not be overwritten.
|
|
914
|
+
''',
|
|
915
|
+
'type': {'type': 'function', '_funcname': '_riskHasVulnToVulnerable',
|
|
916
|
+
'args': (
|
|
917
|
+
{'name': 'n', 'type': 'node', 'desc': 'The risk:hasvuln node to migrate.'},
|
|
918
|
+
{'name': 'nodata', 'type': 'bool', 'default': False,
|
|
919
|
+
'desc': 'Do not copy nodedata to the risk:vulnerable node.'},
|
|
920
|
+
),
|
|
921
|
+
'returns': {'type': 'list', 'desc': 'A list of idens for the risk:vulnerable nodes.'}}},
|
|
922
|
+
)
|
|
923
|
+
_storm_lib_path = ('model', 'migration', 's')
|
|
924
|
+
|
|
925
|
+
def getObjLocals(self):
|
|
926
|
+
return {
|
|
927
|
+
'itSecCpe_2_170_0': self._itSecCpe_2_170_0,
|
|
928
|
+
'riskHasVulnToVulnerable': self._riskHasVulnToVulnerable,
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
async def _itSecCpe_2_170_0(self, n, prefer_v22=False, force=False):
|
|
932
|
+
|
|
933
|
+
if not isinstance(n, s_node.Node):
|
|
934
|
+
raise s_exc.BadArg(mesg='$lib.model.migration.s.itSecCpe_2_170_0() argument must be a node.')
|
|
935
|
+
|
|
936
|
+
if n.form.name != 'it:sec:cpe':
|
|
937
|
+
raise s_exc.BadArg(f'itSecCpeFix only accepts it:sec:cpe nodes, not {n.form.name}')
|
|
938
|
+
|
|
939
|
+
prefer_v22 = await s_stormtypes.tobool(prefer_v22)
|
|
940
|
+
force = await s_stormtypes.tobool(force)
|
|
941
|
+
|
|
942
|
+
layr = self.runt.snap.wlyr
|
|
943
|
+
# We only need to check :v2_2 since that's the only property that's
|
|
944
|
+
# writable. Everthing else is readonly. And we can do it here once
|
|
945
|
+
# instead of in the loop below which will cause a perf hit.
|
|
946
|
+
self.runt.confirmPropSet(n.form.prop('v2_2'), layriden=layr.iden)
|
|
947
|
+
|
|
948
|
+
curv = n.repr()
|
|
949
|
+
reprvalu = f'it:sec:cpe={curv}'
|
|
950
|
+
|
|
951
|
+
nodedata = await n.getData('migration.s.itSecCpe_2_170_0', {})
|
|
952
|
+
if nodedata.get('status') == 'success' and not force:
|
|
953
|
+
if self.runt.debug:
|
|
954
|
+
mesg = f'DEBUG: itSecCpe_2_170_0({reprvalu}): Node already migrated.'
|
|
955
|
+
await self.runt.printf(mesg)
|
|
956
|
+
return True
|
|
957
|
+
|
|
958
|
+
modl = self.runt.model.type('it:sec:cpe')
|
|
959
|
+
|
|
960
|
+
valu23 = None
|
|
961
|
+
valu22 = None
|
|
962
|
+
invalid = ''
|
|
963
|
+
|
|
964
|
+
# Check the primary property for validity.
|
|
965
|
+
cpe23 = s_infotech.cpe23_regex.match(curv)
|
|
966
|
+
if cpe23 is not None and cpe23.group() == curv:
|
|
967
|
+
valu23 = curv
|
|
968
|
+
|
|
969
|
+
# Check the v2_2 property for validity.
|
|
970
|
+
v2_2 = n.props.get('v2_2')
|
|
971
|
+
if v2_2 is not None:
|
|
972
|
+
rgx = s_infotech.cpe22_regex.match(v2_2)
|
|
973
|
+
if rgx is not None and rgx.group() == v2_2:
|
|
974
|
+
valu22 = v2_2
|
|
975
|
+
|
|
976
|
+
async with self.runt.snap.getNodeEditor(n) as proto:
|
|
977
|
+
|
|
978
|
+
# If both values are populated, this node is valid
|
|
979
|
+
if valu23 is not None and valu22 is not None and not force:
|
|
980
|
+
if self.runt.debug:
|
|
981
|
+
mesg = f'DEBUG: itSecCpe_2_170_0({reprvalu}): Node is valid, no migration necessary.'
|
|
982
|
+
await self.runt.printf(mesg)
|
|
983
|
+
|
|
984
|
+
await proto.setData('migration.s.itSecCpe_2_170_0', {
|
|
985
|
+
'status': 'success',
|
|
986
|
+
})
|
|
987
|
+
|
|
988
|
+
return True
|
|
989
|
+
|
|
990
|
+
if valu23 is None and valu22 is None:
|
|
991
|
+
reason = 'Unable to migrate due to invalid data. Primary property and :v2_2 are both invalid.'
|
|
992
|
+
# Invalid 2.3 string and no/invalid v2_2 prop. Nothing
|
|
993
|
+
# we can do here so log, mark, and go around.
|
|
994
|
+
mesg = f'itSecCpe_2_170_0({reprvalu}): {reason}'
|
|
995
|
+
await self.runt.warn(mesg)
|
|
996
|
+
|
|
997
|
+
await proto.setData('migration.s.itSecCpe_2_170_0', {
|
|
998
|
+
'status': 'failed',
|
|
999
|
+
'reason': reason,
|
|
1000
|
+
})
|
|
1001
|
+
|
|
1002
|
+
return False
|
|
1003
|
+
|
|
1004
|
+
if prefer_v22:
|
|
1005
|
+
valu = valu22 or valu23
|
|
1006
|
+
else:
|
|
1007
|
+
valu = valu23 or valu22
|
|
1008
|
+
|
|
1009
|
+
# Re-normalize the data from the 2.3 or 2.2 string, whichever was valid.
|
|
1010
|
+
norm, info = modl.norm(valu)
|
|
1011
|
+
subs = info.get('subs')
|
|
1012
|
+
|
|
1013
|
+
edits = []
|
|
1014
|
+
nodedata = {'status': 'success'}
|
|
1015
|
+
|
|
1016
|
+
if norm != curv:
|
|
1017
|
+
# The re-normed value is not the same as the current value.
|
|
1018
|
+
# Since we can't change the primary property, store the
|
|
1019
|
+
# updated value in nodedata.
|
|
1020
|
+
if self.runt.debug:
|
|
1021
|
+
mesg = f'DEBUG: itSecCpe_2_170_0({reprvalu}): Stored updated primary property value to nodedata: {curv} -> {norm}.'
|
|
1022
|
+
await self.runt.printf(mesg)
|
|
1023
|
+
|
|
1024
|
+
nodedata['valu'] = norm
|
|
1025
|
+
|
|
1026
|
+
# Iterate over the existing properties
|
|
1027
|
+
for propname, propcurv in n.props.items():
|
|
1028
|
+
subscurv = subs.get(propname)
|
|
1029
|
+
if subscurv is None:
|
|
1030
|
+
continue
|
|
1031
|
+
|
|
1032
|
+
if propname == 'v2_2' and isinstance(subscurv, list):
|
|
1033
|
+
subscurv = s_infotech.zipCpe22(subscurv)
|
|
1034
|
+
|
|
1035
|
+
# Values are the same, go around
|
|
1036
|
+
if propcurv == subscurv:
|
|
1037
|
+
continue
|
|
1038
|
+
|
|
1039
|
+
nodedata.setdefault('updated', [])
|
|
1040
|
+
nodedata['updated'].append(propname)
|
|
1041
|
+
|
|
1042
|
+
# Update the existing property with the re-normalized property value.
|
|
1043
|
+
await proto.set(propname, subscurv, ignore_ro=True)
|
|
1044
|
+
|
|
1045
|
+
await proto.setData('migration.s.itSecCpe_2_170_0', nodedata)
|
|
1046
|
+
|
|
1047
|
+
if self.runt.debug:
|
|
1048
|
+
if nodedata.get('updated'):
|
|
1049
|
+
mesg = f'DEBUG: itSecCpe_2_170_0({reprvalu}): Updated properties: {", ".join(nodedata["updated"])}.'
|
|
1050
|
+
await self.runt.printf(mesg)
|
|
1051
|
+
else:
|
|
1052
|
+
mesg = f'DEBUG: itSecCpe_2_170_0({reprvalu}): No property updates required.'
|
|
1053
|
+
await self.runt.printf(mesg)
|
|
1054
|
+
|
|
1055
|
+
return True
|
|
1056
|
+
|
|
1057
|
+
async def _riskHasVulnToVulnerable(self, n, nodata=False):
|
|
1058
|
+
|
|
1059
|
+
nodata = await s_stormtypes.tobool(nodata)
|
|
1060
|
+
|
|
1061
|
+
if not isinstance(n, s_node.Node):
|
|
1062
|
+
raise s_exc.BadArg(mesg='$lib.model.migration.s.riskHasVulnToVulnerable() argument must be a node.')
|
|
1063
|
+
|
|
1064
|
+
if n.form.name != 'risk:hasvuln':
|
|
1065
|
+
mesg = f'$lib.model.migration.s.riskHasVulnToVulnerable() only accepts risk:hasvuln nodes, not {n.form.name}'
|
|
1066
|
+
raise s_exc.BadArg(mesg=mesg)
|
|
1067
|
+
|
|
1068
|
+
retidens = []
|
|
1069
|
+
|
|
1070
|
+
if not (vuln := n.get('vuln')):
|
|
1071
|
+
return retidens
|
|
1072
|
+
|
|
1073
|
+
props = {
|
|
1074
|
+
'vuln': vuln,
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
links = {prop: valu for prop in RISK_HASVULN_VULNPROPS if (valu := n.get(prop)) is not None}
|
|
1078
|
+
|
|
1079
|
+
match len(links):
|
|
1080
|
+
case 0:
|
|
1081
|
+
return retidens
|
|
1082
|
+
case 1:
|
|
1083
|
+
guid = n.ndef[1]
|
|
1084
|
+
case _:
|
|
1085
|
+
guid = None
|
|
1086
|
+
|
|
1087
|
+
riskvuln = self.runt.model.form('risk:vulnerable')
|
|
1088
|
+
|
|
1089
|
+
self.runt.layerConfirm(riskvuln.addperm)
|
|
1090
|
+
self.runt.confirmPropSet(riskvuln.props['vuln'])
|
|
1091
|
+
self.runt.confirmPropSet(riskvuln.props['node'])
|
|
1092
|
+
|
|
1093
|
+
if (seen := n.get('.seen')):
|
|
1094
|
+
self.runt.confirmPropSet(riskvuln.props['.seen'])
|
|
1095
|
+
props['.seen'] = seen
|
|
1096
|
+
|
|
1097
|
+
async with self.runt.snap.getEditor() as editor:
|
|
1098
|
+
|
|
1099
|
+
for prop, valu in links.items():
|
|
1100
|
+
|
|
1101
|
+
pguid = guid if guid is not None else s_common.guid((guid, prop))
|
|
1102
|
+
pprops = props | {'node': (n.form.props[prop].type.name, valu)}
|
|
1103
|
+
|
|
1104
|
+
proto = await editor.addNode('risk:vulnerable', pguid, props=pprops)
|
|
1105
|
+
retidens.append(proto.iden())
|
|
1106
|
+
|
|
1107
|
+
await self.copyTags(n, proto, overwrite=False)
|
|
1108
|
+
await self.copyEdges(editor, n, proto)
|
|
806
1109
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
await proto.addTag(name, valu=valu)
|
|
1110
|
+
if not nodata:
|
|
1111
|
+
await self.copyData(n, proto, overwrite=False)
|
|
810
1112
|
|
|
811
|
-
|
|
812
|
-
for propname, valu in tagprops.items():
|
|
813
|
-
if overwrite or not dst.hasTagProp(tagname, propname):
|
|
814
|
-
await proto.setTagProp(tagname, propname, valu) # use tag perms
|
|
1113
|
+
return retidens
|
synapse/lib/stormtypes.py
CHANGED
|
@@ -6721,6 +6721,20 @@ class Layer(Prim):
|
|
|
6721
6721
|
''',
|
|
6722
6722
|
'type': {'type': 'function', '_funcname': 'getStorNodes',
|
|
6723
6723
|
'returns': {'name': 'Yields', 'type': 'list', 'desc': 'Tuple of buid, sode values.', }}},
|
|
6724
|
+
{'name': 'getStorNodesByForm', 'desc': '''
|
|
6725
|
+
Get buid, sode tuples representing the data stored in the layer for a given form.
|
|
6726
|
+
|
|
6727
|
+
Notes:
|
|
6728
|
+
The storage nodes represent **only** the data stored in the layer
|
|
6729
|
+
and may not represent whole nodes. If the only data stored in the layer for
|
|
6730
|
+
a given buid is an N2 edge reference, a storage node will not be returned.
|
|
6731
|
+
''',
|
|
6732
|
+
'type': {'type': 'function', '_funcname': 'getStorNodesByForm',
|
|
6733
|
+
'args': (
|
|
6734
|
+
{'name': 'form', 'type': 'str',
|
|
6735
|
+
'desc': 'The name of the form to get storage nodes for.'},
|
|
6736
|
+
),
|
|
6737
|
+
'returns': {'name': 'Yields', 'type': 'list', 'desc': 'Tuple of buid, sode values.', }}},
|
|
6724
6738
|
{'name': 'getMirrorStatus', 'desc': '''
|
|
6725
6739
|
Return a dictionary of the mirror synchronization status for the layer.
|
|
6726
6740
|
''',
|
|
@@ -6902,6 +6916,7 @@ class Layer(Prim):
|
|
|
6902
6916
|
'getFormCounts': self._methGetFormcount,
|
|
6903
6917
|
'getStorNode': self.getStorNode,
|
|
6904
6918
|
'getStorNodes': self.getStorNodes,
|
|
6919
|
+
'getStorNodesByForm': self.getStorNodesByForm,
|
|
6905
6920
|
'getEdgesByN1': self.getEdgesByN1,
|
|
6906
6921
|
'getEdgesByN2': self.getEdgesByN2,
|
|
6907
6922
|
'getMirrorStatus': self.getMirrorStatus,
|
|
@@ -7198,6 +7213,19 @@ class Layer(Prim):
|
|
|
7198
7213
|
async for item in layr.getStorNodes():
|
|
7199
7214
|
yield item
|
|
7200
7215
|
|
|
7216
|
+
@stormfunc(readonly=True)
|
|
7217
|
+
async def getStorNodesByForm(self, form):
|
|
7218
|
+
form = await tostr(form)
|
|
7219
|
+
if self.runt.snap.core.model.form(form) is None:
|
|
7220
|
+
raise s_exc.NoSuchForm.init(form)
|
|
7221
|
+
|
|
7222
|
+
layriden = self.valu.get('iden')
|
|
7223
|
+
await self.runt.reqUserCanReadLayer(layriden)
|
|
7224
|
+
layr = self.runt.snap.core.getLayer(layriden)
|
|
7225
|
+
|
|
7226
|
+
async for item in layr.getStorNodesByForm(form):
|
|
7227
|
+
yield item
|
|
7228
|
+
|
|
7201
7229
|
@stormfunc(readonly=True)
|
|
7202
7230
|
async def getEdges(self):
|
|
7203
7231
|
layriden = self.valu.get('iden')
|
|
@@ -7452,6 +7480,12 @@ class View(Prim):
|
|
|
7452
7480
|
{'name': 'name', 'type': 'str', 'desc': 'The name of the new view.', 'default': None, },
|
|
7453
7481
|
),
|
|
7454
7482
|
'returns': {'type': 'view', 'desc': 'The ``view`` object for the new View.', }}},
|
|
7483
|
+
{'name': 'insertParentFork', 'desc': 'Insert a new View between a forked View and its parent.',
|
|
7484
|
+
'type': {'type': 'function', '_funcname': '_methViewInsertParentFork',
|
|
7485
|
+
'args': (
|
|
7486
|
+
{'name': 'name', 'type': 'str', 'desc': 'The name of the new View.', 'default': None},
|
|
7487
|
+
),
|
|
7488
|
+
'returns': {'type': 'view', 'desc': 'The ``view`` object for the new View.', }}},
|
|
7455
7489
|
{'name': 'pack', 'desc': 'Get the View definition.',
|
|
7456
7490
|
'type': {'type': 'function', '_funcname': '_methViewPack',
|
|
7457
7491
|
'returns': {'type': 'dict', 'desc': 'Dictionary containing the View definition.', }}},
|
|
@@ -7653,7 +7687,6 @@ class View(Prim):
|
|
|
7653
7687
|
return {
|
|
7654
7688
|
'set': self._methViewSet,
|
|
7655
7689
|
'get': self._methViewGet,
|
|
7656
|
-
'fork': self._methViewFork,
|
|
7657
7690
|
'pack': self._methViewPack,
|
|
7658
7691
|
'repr': self._methViewRepr,
|
|
7659
7692
|
'merge': self._methViewMerge,
|
|
@@ -7668,6 +7701,9 @@ class View(Prim):
|
|
|
7668
7701
|
'getTagPropCount': self._methGetTagPropCount,
|
|
7669
7702
|
'getPropArrayCount': self._methGetPropArrayCount,
|
|
7670
7703
|
|
|
7704
|
+
'fork': self._methViewFork,
|
|
7705
|
+
'insertParentFork': self._methViewInsertParentFork,
|
|
7706
|
+
|
|
7671
7707
|
'getMerges': self.getMerges,
|
|
7672
7708
|
'delMergeVote': self.delMergeVote,
|
|
7673
7709
|
'setMergeVote': self.setMergeVote,
|
|
@@ -7919,6 +7955,27 @@ class View(Prim):
|
|
|
7919
7955
|
|
|
7920
7956
|
return View(self.runt, newv, path=self.path)
|
|
7921
7957
|
|
|
7958
|
+
async def _methViewInsertParentFork(self, name=None):
|
|
7959
|
+
useriden = self.runt.user.iden
|
|
7960
|
+
viewiden = self.valu.get('iden')
|
|
7961
|
+
|
|
7962
|
+
name = await tostr(name, noneok=True)
|
|
7963
|
+
|
|
7964
|
+
self.runt.reqAdmin(gateiden=viewiden)
|
|
7965
|
+
|
|
7966
|
+
view = self.runt.snap.core.reqView(viewiden)
|
|
7967
|
+
if not view.isafork():
|
|
7968
|
+
mesg = f'View ({viewiden}) is not a fork, cannot insert a new fork between it and parent.'
|
|
7969
|
+
raise s_exc.BadState(mesg=mesg)
|
|
7970
|
+
|
|
7971
|
+
self.runt.confirm(('view', 'add'))
|
|
7972
|
+
self.runt.confirm(('view', 'read'), gateiden=view.parent.iden)
|
|
7973
|
+
self.runt.confirm(('view', 'fork'), gateiden=view.parent.iden)
|
|
7974
|
+
|
|
7975
|
+
newv = await view.insertParentFork(useriden, name=name)
|
|
7976
|
+
|
|
7977
|
+
return View(self.runt, newv, path=self.path)
|
|
7978
|
+
|
|
7922
7979
|
async def _methViewMerge(self, force=False):
|
|
7923
7980
|
'''
|
|
7924
7981
|
Merge a forked view back into its parent.
|
synapse/lib/types.py
CHANGED
|
@@ -1408,6 +1408,31 @@ class Ndef(Type):
|
|
|
1408
1408
|
self.setNormFunc(list, self._normPyTuple)
|
|
1409
1409
|
self.setNormFunc(tuple, self._normPyTuple)
|
|
1410
1410
|
|
|
1411
|
+
self.formfilter = None
|
|
1412
|
+
|
|
1413
|
+
self.forms = self.opts.get('forms')
|
|
1414
|
+
self.ifaces = self.opts.get('interfaces')
|
|
1415
|
+
|
|
1416
|
+
if self.forms or self.ifaces:
|
|
1417
|
+
if self.forms is not None:
|
|
1418
|
+
forms = set(self.forms)
|
|
1419
|
+
|
|
1420
|
+
if self.ifaces is not None:
|
|
1421
|
+
ifaces = set(self.ifaces)
|
|
1422
|
+
|
|
1423
|
+
def filtfunc(form):
|
|
1424
|
+
if self.forms is not None and form.name in forms:
|
|
1425
|
+
return False
|
|
1426
|
+
|
|
1427
|
+
if self.ifaces is not None:
|
|
1428
|
+
for iface in form.ifaces.keys():
|
|
1429
|
+
if iface in ifaces:
|
|
1430
|
+
return False
|
|
1431
|
+
|
|
1432
|
+
return True
|
|
1433
|
+
|
|
1434
|
+
self.formfilter = filtfunc
|
|
1435
|
+
|
|
1411
1436
|
def _normStormNode(self, valu):
|
|
1412
1437
|
return self._normPyTuple(valu.ndef)
|
|
1413
1438
|
|
|
@@ -1421,6 +1446,16 @@ class Ndef(Type):
|
|
|
1421
1446
|
if form is None:
|
|
1422
1447
|
raise s_exc.NoSuchForm.init(formname)
|
|
1423
1448
|
|
|
1449
|
+
if self.formfilter is not None and self.formfilter(form):
|
|
1450
|
+
mesg = f'Ndef of form {formname} is not allowed as a value for {self.name} with form filter'
|
|
1451
|
+
if self.forms is not None:
|
|
1452
|
+
mesg += f' forms={self.forms}'
|
|
1453
|
+
|
|
1454
|
+
if self.ifaces is not None:
|
|
1455
|
+
mesg += f' interfaces={self.ifaces}'
|
|
1456
|
+
|
|
1457
|
+
raise s_exc.BadTypeValu(valu=formname, name=self.name, mesg=mesg, forms=self.forms, interfaces=self.ifaces)
|
|
1458
|
+
|
|
1424
1459
|
formnorm, forminfo = form.type.norm(formvalu)
|
|
1425
1460
|
norm = (form.name, formnorm)
|
|
1426
1461
|
|
|
@@ -1727,7 +1762,7 @@ class Str(Type):
|
|
|
1727
1762
|
|
|
1728
1763
|
def _normPyFloat(self, valu):
|
|
1729
1764
|
deci = s_common.hugectx.create_decimal(str(valu))
|
|
1730
|
-
return format(deci, 'f')
|
|
1765
|
+
return self._normPyStr(format(deci, 'f'))
|
|
1731
1766
|
|
|
1732
1767
|
def _normPyStr(self, valu):
|
|
1733
1768
|
|
synapse/lib/version.py
CHANGED
|
@@ -223,6 +223,6 @@ def reqVersion(valu, reqver,
|
|
|
223
223
|
##############################################################################
|
|
224
224
|
# The following are touched during the release process by bumpversion.
|
|
225
225
|
# Do not modify these directly.
|
|
226
|
-
version = (2,
|
|
226
|
+
version = (2, 171, 0)
|
|
227
227
|
verstring = '.'.join([str(x) for x in version])
|
|
228
|
-
commit = '
|
|
228
|
+
commit = 'c89839928860a36f70377a8cefd09aaa4b12595a'
|