synapse 2.186.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.
- synapse/cortex.py +2 -2
- synapse/lib/hive.py +1 -1
- synapse/lib/modelrev.py +771 -11
- synapse/lib/spooled.py +26 -3
- synapse/lib/storm.py +7 -0
- synapse/lib/stormlib/model.py +320 -250
- synapse/lib/stormtypes.py +36 -10
- synapse/lib/version.py +2 -2
- synapse/models/infotech.py +49 -22
- synapse/tests/test_lib_cell.py +1 -0
- synapse/tests/test_lib_modelrev.py +918 -379
- synapse/tests/test_lib_spooled.py +34 -0
- synapse/tests/test_lib_stormlib_model.py +0 -270
- synapse/tests/test_lib_stormtypes.py +11 -0
- synapse/tests/test_model_infotech.py +14 -11
- synapse/tools/changelog.py +11 -3
- {synapse-2.186.0.dist-info → synapse-2.187.0.dist-info}/METADATA +1 -1
- {synapse-2.186.0.dist-info → synapse-2.187.0.dist-info}/RECORD +21 -24
- synapse/assets/__init__.py +0 -35
- synapse/assets/storm/migrations/model-0.2.28.storm +0 -355
- synapse/tests/test_assets.py +0 -25
- {synapse-2.186.0.dist-info → synapse-2.187.0.dist-info}/LICENSE +0 -0
- {synapse-2.186.0.dist-info → synapse-2.187.0.dist-info}/WHEEL +0 -0
- {synapse-2.186.0.dist-info → synapse-2.187.0.dist-info}/top_level.txt +0 -0
synapse/lib/spooled.py
CHANGED
|
@@ -7,6 +7,8 @@ import synapse.lib.const as s_const
|
|
|
7
7
|
import synapse.lib.msgpack as s_msgpack
|
|
8
8
|
import synapse.lib.lmdbslab as s_lmdbslab
|
|
9
9
|
|
|
10
|
+
MAX_SPOOL_SIZE = 10000
|
|
11
|
+
|
|
10
12
|
class Spooled(s_base.Base):
|
|
11
13
|
'''
|
|
12
14
|
A Base class that can be used to implement objects which fallback to lmdb.
|
|
@@ -15,7 +17,7 @@ class Spooled(s_base.Base):
|
|
|
15
17
|
together. Under memory pressure, these objects have a better shot of getting paged out.
|
|
16
18
|
'''
|
|
17
19
|
|
|
18
|
-
async def __anit__(self, dirn=None, size=
|
|
20
|
+
async def __anit__(self, dirn=None, size=MAX_SPOOL_SIZE, cell=None):
|
|
19
21
|
'''
|
|
20
22
|
Args:
|
|
21
23
|
dirn(Optional[str]): base directory used for backing slab. If None, system temporary directory is used
|
|
@@ -55,7 +57,7 @@ class Set(Spooled):
|
|
|
55
57
|
A minimal set-like implementation that will spool to a slab on large growth.
|
|
56
58
|
'''
|
|
57
59
|
|
|
58
|
-
async def __anit__(self, dirn=None, size=
|
|
60
|
+
async def __anit__(self, dirn=None, size=MAX_SPOOL_SIZE, cell=None):
|
|
59
61
|
await Spooled.__anit__(self, dirn=dirn, size=size, cell=cell)
|
|
60
62
|
self.realset = set()
|
|
61
63
|
self.len = 0
|
|
@@ -84,6 +86,27 @@ class Set(Spooled):
|
|
|
84
86
|
|
|
85
87
|
return len(self.realset)
|
|
86
88
|
|
|
89
|
+
async def copy(self):
|
|
90
|
+
newset = await Set.anit(dirn=self.dirn, size=self.size, cell=self.cell)
|
|
91
|
+
|
|
92
|
+
if self.fallback:
|
|
93
|
+
await newset._initFallBack()
|
|
94
|
+
await self.slab.copydb(None, newset.slab)
|
|
95
|
+
newset.len = self.len
|
|
96
|
+
|
|
97
|
+
else:
|
|
98
|
+
newset.realset = self.realset.copy()
|
|
99
|
+
|
|
100
|
+
return newset
|
|
101
|
+
|
|
102
|
+
async def clear(self):
|
|
103
|
+
if self.fallback:
|
|
104
|
+
self.len = 0
|
|
105
|
+
await self.slab.trash()
|
|
106
|
+
await self._initFallBack()
|
|
107
|
+
else:
|
|
108
|
+
self.realset.clear()
|
|
109
|
+
|
|
87
110
|
async def add(self, valu):
|
|
88
111
|
|
|
89
112
|
if self.fallback:
|
|
@@ -117,7 +140,7 @@ class Set(Spooled):
|
|
|
117
140
|
|
|
118
141
|
class Dict(Spooled):
|
|
119
142
|
|
|
120
|
-
async def __anit__(self, dirn=None, size=
|
|
143
|
+
async def __anit__(self, dirn=None, size=MAX_SPOOL_SIZE, cell=None):
|
|
121
144
|
|
|
122
145
|
await Spooled.__anit__(self, dirn=dirn, size=size, cell=cell)
|
|
123
146
|
self.realdict = {}
|
synapse/lib/storm.py
CHANGED
|
@@ -5979,6 +5979,13 @@ class RunAsCmd(Cmd):
|
|
|
5979
5979
|
|
|
5980
5980
|
NOTE: This command requires admin privileges.
|
|
5981
5981
|
|
|
5982
|
+
NOTE: Heavy objects (for example a View or Layer) are bound to the context which they
|
|
5983
|
+
are instantiated in and methods on them will be run using the user in that
|
|
5984
|
+
context. This means that executing a method on a variable containing a heavy
|
|
5985
|
+
object which was instantiated outside of the runas command and then used
|
|
5986
|
+
within the runas command will check the permissions of the outer user, not
|
|
5987
|
+
the one specified by the runas command.
|
|
5988
|
+
|
|
5982
5989
|
Examples:
|
|
5983
5990
|
|
|
5984
5991
|
// Create a node as another user.
|
synapse/lib/stormlib/model.py
CHANGED
|
@@ -2,12 +2,11 @@ import synapse.exc as s_exc
|
|
|
2
2
|
import synapse.common as s_common
|
|
3
3
|
|
|
4
4
|
import synapse.lib.node as s_node
|
|
5
|
+
import synapse.lib.time as s_time
|
|
5
6
|
import synapse.lib.cache as s_cache
|
|
6
7
|
import synapse.lib.layer as s_layer
|
|
7
8
|
import synapse.lib.stormtypes as s_stormtypes
|
|
8
9
|
|
|
9
|
-
import synapse.models.infotech as s_infotech
|
|
10
|
-
|
|
11
10
|
RISK_HASVULN_VULNPROPS = (
|
|
12
11
|
'hardware',
|
|
13
12
|
'host',
|
|
@@ -818,8 +817,6 @@ class LibModelMigration(s_stormtypes.Lib, MigrationEditorMixin):
|
|
|
818
817
|
'copyEdges': self._methCopyEdges,
|
|
819
818
|
'copyTags': self._methCopyTags,
|
|
820
819
|
'copyExtProps': self._methCopyExtProps,
|
|
821
|
-
'liftByPropValuNoNorm': self._methLiftByPropValuNoNorm,
|
|
822
|
-
'setNodePropValuNoNorm': self._methSetNodePropValuNoNorm,
|
|
823
820
|
}
|
|
824
821
|
|
|
825
822
|
async def _methCopyData(self, src, dst, overwrite=False):
|
|
@@ -876,125 +873,12 @@ class LibModelMigration(s_stormtypes.Lib, MigrationEditorMixin):
|
|
|
876
873
|
proto = editor.loadNode(dst)
|
|
877
874
|
await self.copyExtProps(src, proto)
|
|
878
875
|
|
|
879
|
-
async def _methLiftByPropValuNoNorm(self, formname, propname, valu, cmpr='=', reverse=False):
|
|
880
|
-
'''
|
|
881
|
-
No storm docs for this on purpose. It is restricted for use during model migrations only.
|
|
882
|
-
'''
|
|
883
|
-
formname = await s_stormtypes.tostr(formname)
|
|
884
|
-
propname = await s_stormtypes.tostr(propname)
|
|
885
|
-
valu = await s_stormtypes.toprim(valu)
|
|
886
|
-
|
|
887
|
-
prop = self.runt.snap.core.model.prop(f'{formname}:{propname}')
|
|
888
|
-
if prop is None:
|
|
889
|
-
mesg = f'Could not find prop: {formname}:{propname}'
|
|
890
|
-
raise s_exc.NoSuchProp(mesg=mesg, formname=formname, propname=propname)
|
|
891
|
-
|
|
892
|
-
if not self.runt.snap.core.migration:
|
|
893
|
-
mesg = '$lib.model.migration.liftByPropValuNoNorm() is restricted to model migrations only.'
|
|
894
|
-
raise s_exc.AuthDeny(mesg=mesg, user=self.runt.user.iden, username=self.runt.user.name)
|
|
895
|
-
|
|
896
|
-
stortype = prop.type.stortype
|
|
897
|
-
|
|
898
|
-
# Normally we'd call proptype.getStorCmprs() here to get the cmprvals
|
|
899
|
-
# but getStorCmprs() calls norm() which we're trying to avoid so build
|
|
900
|
-
# cmprvals manually here.
|
|
901
|
-
|
|
902
|
-
if prop.type.isarray:
|
|
903
|
-
stortype &= (~s_layer.STOR_FLAG_ARRAY)
|
|
904
|
-
liftfunc = self.runt.snap.wlyr.liftByPropArray
|
|
905
|
-
else:
|
|
906
|
-
liftfunc = self.runt.snap.wlyr.liftByPropValu
|
|
907
|
-
|
|
908
|
-
cmprvals = ((cmpr, valu, stortype),)
|
|
909
|
-
|
|
910
|
-
layriden = self.runt.snap.wlyr.iden
|
|
911
|
-
async for _, buid, sode in liftfunc(formname, propname, cmprvals, reverse=reverse):
|
|
912
|
-
yield await self.runt.snap._joinStorNode(buid, {layriden: sode})
|
|
913
|
-
|
|
914
|
-
async def _methSetNodePropValuNoNorm(self, n, propname, valu):
|
|
915
|
-
'''
|
|
916
|
-
No storm docs for this on purpose. It is restricted for use during model migrations only.
|
|
917
|
-
'''
|
|
918
|
-
|
|
919
|
-
# NB: I'm sure there are all kinds of edges cases that this function doesn't account for. At the time of it's
|
|
920
|
-
# creation, this was intended to be used to update array properties with bad it:sec:cpe values in them. It works
|
|
921
|
-
# for that use case (see model migration 0.2.28). Any additional use of this function should perform heavy
|
|
922
|
-
# testing.
|
|
923
|
-
|
|
924
|
-
if not isinstance(n, s_node.Node):
|
|
925
|
-
raise s_exc.BadArg(mesg='$lib.model.migration.setNodePropValuNoNorm() argument must be a node.')
|
|
926
|
-
|
|
927
|
-
if not self.runt.snap.core.migration:
|
|
928
|
-
mesg = '$lib.model.migration.setNodePropValuNoNorm() is restricted to model migrations only.'
|
|
929
|
-
raise s_exc.AuthDeny(mesg=mesg, user=self.runt.user.iden, username=self.runt.user.name)
|
|
930
|
-
|
|
931
|
-
propname = await s_stormtypes.tostr(propname)
|
|
932
|
-
valu = await s_stormtypes.toprim(valu)
|
|
933
|
-
|
|
934
|
-
async with self.runt.snap.getNodeEditor(n) as proto:
|
|
935
|
-
await proto.set(propname, valu, norminfo={})
|
|
936
|
-
|
|
937
|
-
return n
|
|
938
|
-
|
|
939
876
|
@s_stormtypes.registry.registerLib
|
|
940
877
|
class LibModelMigrations(s_stormtypes.Lib, MigrationEditorMixin):
|
|
941
878
|
'''
|
|
942
879
|
A Storm library for selectively migrating nodes in the current view.
|
|
943
880
|
'''
|
|
944
881
|
_storm_locals = (
|
|
945
|
-
{'name': 'itSecCpe_2_170_0',
|
|
946
|
-
'desc': '''
|
|
947
|
-
Versions of Synapse prior to v2.169.0 did not correctly parse and
|
|
948
|
-
convert CPE strings from 2.2 -> 2.3 or 2.3 -> 2.2. This migration
|
|
949
|
-
attempts to re-normalize `it:sec:cpe` nodes that may be fixable.
|
|
950
|
-
|
|
951
|
-
NOTE: It is highly recommended to test the `it:sec:cpe` migrations
|
|
952
|
-
in a fork first and confirm the migration was successful without any
|
|
953
|
-
issues. Then run the migration in view deporder to migrate the
|
|
954
|
-
entire cortex. E.g.::
|
|
955
|
-
|
|
956
|
-
for $view in $lib.view.list(deporder=$lib.true) {
|
|
957
|
-
view.exec $view.iden {
|
|
958
|
-
for $n in $lib.layer.get().liftByProp(it:sec:cpe) {
|
|
959
|
-
$lib.model.migration.s.itSecCpe_2_170_0($n)
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
Upon completion of the migration, nodedata will contain a
|
|
965
|
-
`migration.s.itSecCpe_2_170_0` dict with information about the
|
|
966
|
-
migration status. This dict may contain the following:
|
|
967
|
-
|
|
968
|
-
- `status`: (required str) "success" or "failed"
|
|
969
|
-
- `reason`: (optional str) if "status" is "failed", this key will
|
|
970
|
-
explain why the migration failed.
|
|
971
|
-
- `valu`: (optional str) if this key is present, it will contain
|
|
972
|
-
an updated CPE2.3 string since the primary property cannot be
|
|
973
|
-
changed.
|
|
974
|
-
- `updated`: (optional list[str]) A list of properties that were
|
|
975
|
-
updated by the migration.
|
|
976
|
-
|
|
977
|
-
Failed or incorrect migrations may be helped by updating the :v2_2
|
|
978
|
-
property to be a valid CPE2.2 string and then re-running the
|
|
979
|
-
migration with `force=$lib.true`. If the primary property (CPE2.3)
|
|
980
|
-
is valid but incorrect, users may update the :v2_2 property and then
|
|
981
|
-
run the migration with `prefer_v22=$lib.true` to make the migration
|
|
982
|
-
use the `:v2_2` string instead of the primary property for the
|
|
983
|
-
migration process.
|
|
984
|
-
''',
|
|
985
|
-
'type': {'type': 'function', '_funcname': '_itSecCpe_2_170_0',
|
|
986
|
-
'args': (
|
|
987
|
-
{'name': 'n', 'type': 'node', 'desc': 'The it:sec:cpe node to migrate.'},
|
|
988
|
-
{'name': 'prefer_v22', 'type': 'bool', 'default': False,
|
|
989
|
-
'desc': '''
|
|
990
|
-
Try to renormalize using the :v2_2 prop instead of the
|
|
991
|
-
primary property. This can be especially useful when the
|
|
992
|
-
primary property is a valid but incorrect CPE string.
|
|
993
|
-
'''},
|
|
994
|
-
{'name': 'force', 'type': 'bool', 'default': False,
|
|
995
|
-
'desc': 'Perform fixups even if the primary property and :v2_2 are valid.'},
|
|
996
|
-
),
|
|
997
|
-
'returns': {'type': 'boolean', 'desc': 'Boolean indicating if the migration was successful.'}}},
|
|
998
882
|
{'name': 'riskHasVulnToVulnerable', 'desc': '''
|
|
999
883
|
Create a risk:vulnerable node from the provided risk:hasvuln node.
|
|
1000
884
|
|
|
@@ -1076,142 +960,9 @@ class LibModelMigrations(s_stormtypes.Lib, MigrationEditorMixin):
|
|
|
1076
960
|
|
|
1077
961
|
def getObjLocals(self):
|
|
1078
962
|
return {
|
|
1079
|
-
'itSecCpe_2_170_0': self._itSecCpe_2_170_0,
|
|
1080
|
-
'itSecCpe_2_170_0_internal': self._itSecCpe_2_170_0_internal,
|
|
1081
963
|
'riskHasVulnToVulnerable': self._riskHasVulnToVulnerable,
|
|
1082
964
|
}
|
|
1083
965
|
|
|
1084
|
-
async def _itSecCpe_2_170_0(self, n, prefer_v22=False, force=False):
|
|
1085
|
-
info = await self._itSecCpe_2_170_0_internal(n, prefer_v22=prefer_v22, force=force, set_nodedata=True)
|
|
1086
|
-
return info.get('status') == 'success'
|
|
1087
|
-
|
|
1088
|
-
async def _itSecCpe_2_170_0_internal(self, n, prefer_v22=False, force=False, set_nodedata=False):
|
|
1089
|
-
|
|
1090
|
-
if not isinstance(n, s_node.Node):
|
|
1091
|
-
raise s_exc.BadArg(mesg='$lib.model.migration.s.itSecCpe_2_170_0() argument must be a node.')
|
|
1092
|
-
|
|
1093
|
-
if n.form.name != 'it:sec:cpe':
|
|
1094
|
-
raise s_exc.BadArg(f'itSecCpeFix only accepts it:sec:cpe nodes, not {n.form.name}')
|
|
1095
|
-
|
|
1096
|
-
prefer_v22 = await s_stormtypes.tobool(prefer_v22)
|
|
1097
|
-
force = await s_stormtypes.tobool(force)
|
|
1098
|
-
|
|
1099
|
-
layr = self.runt.snap.wlyr
|
|
1100
|
-
# We only need to check :v2_2 since that's the only property that's
|
|
1101
|
-
# writable. Everthing else is readonly. And we can do it here once
|
|
1102
|
-
# instead of in the loop below which will cause a perf hit.
|
|
1103
|
-
self.runt.confirmPropSet(n.form.prop('v2_2'), layriden=layr.iden)
|
|
1104
|
-
|
|
1105
|
-
curv = n.repr()
|
|
1106
|
-
reprvalu = f'it:sec:cpe={curv}'
|
|
1107
|
-
|
|
1108
|
-
nodedata = await n.getData('migration.s.itSecCpe_2_170_0', {})
|
|
1109
|
-
if nodedata.get('status') == 'success' and not force:
|
|
1110
|
-
if self.runt.debug:
|
|
1111
|
-
mesg = f'DEBUG: itSecCpe_2_170_0({reprvalu}): Node already migrated.'
|
|
1112
|
-
await self.runt.printf(mesg)
|
|
1113
|
-
return nodedata
|
|
1114
|
-
|
|
1115
|
-
modl = self.runt.model.type('it:sec:cpe')
|
|
1116
|
-
|
|
1117
|
-
valu23 = None
|
|
1118
|
-
valu22 = None
|
|
1119
|
-
|
|
1120
|
-
# Check the primary property for validity.
|
|
1121
|
-
cpe23 = s_infotech.cpe23_regex.match(curv)
|
|
1122
|
-
if cpe23 is not None and cpe23.group() == curv:
|
|
1123
|
-
valu23 = curv
|
|
1124
|
-
|
|
1125
|
-
# Check the v2_2 property for validity.
|
|
1126
|
-
v2_2 = n.props.get('v2_2')
|
|
1127
|
-
if v2_2 is not None:
|
|
1128
|
-
rgx = s_infotech.cpe22_regex.match(v2_2)
|
|
1129
|
-
if rgx is not None and rgx.group() == v2_2:
|
|
1130
|
-
valu22 = v2_2
|
|
1131
|
-
|
|
1132
|
-
async with self.runt.snap.getNodeEditor(n) as proto:
|
|
1133
|
-
|
|
1134
|
-
# If both values are populated, this node is valid
|
|
1135
|
-
if valu23 is not None and valu22 is not None and not force:
|
|
1136
|
-
if self.runt.debug:
|
|
1137
|
-
mesg = f'DEBUG: itSecCpe_2_170_0({reprvalu}): Node is valid, no migration necessary.'
|
|
1138
|
-
await self.runt.printf(mesg)
|
|
1139
|
-
|
|
1140
|
-
nodedata = {'status': 'success'}
|
|
1141
|
-
if set_nodedata:
|
|
1142
|
-
await proto.setData('migration.s.itSecCpe_2_170_0', nodedata)
|
|
1143
|
-
|
|
1144
|
-
return nodedata
|
|
1145
|
-
|
|
1146
|
-
if valu23 is None and valu22 is None:
|
|
1147
|
-
reason = 'Unable to migrate due to invalid data. Primary property and :v2_2 are both invalid.'
|
|
1148
|
-
# Invalid 2.3 string and no/invalid v2_2 prop. Nothing
|
|
1149
|
-
# we can do here so log, mark, and go around.
|
|
1150
|
-
mesg = f'itSecCpe_2_170_0({reprvalu}): {reason}'
|
|
1151
|
-
await self.runt.warn(mesg)
|
|
1152
|
-
|
|
1153
|
-
nodedata = {
|
|
1154
|
-
'status': 'failed',
|
|
1155
|
-
'reason': reason,
|
|
1156
|
-
}
|
|
1157
|
-
if set_nodedata:
|
|
1158
|
-
await proto.setData('migration.s.itSecCpe_2_170_0', nodedata)
|
|
1159
|
-
|
|
1160
|
-
return nodedata
|
|
1161
|
-
|
|
1162
|
-
if prefer_v22:
|
|
1163
|
-
valu = valu22 or valu23
|
|
1164
|
-
else:
|
|
1165
|
-
valu = valu23 or valu22
|
|
1166
|
-
|
|
1167
|
-
# Re-normalize the data from the 2.3 or 2.2 string, whichever was valid.
|
|
1168
|
-
norm, info = modl.norm(valu)
|
|
1169
|
-
subs = info.get('subs')
|
|
1170
|
-
|
|
1171
|
-
nodedata = {'status': 'success'}
|
|
1172
|
-
|
|
1173
|
-
if norm != curv:
|
|
1174
|
-
# The re-normed value is not the same as the current value.
|
|
1175
|
-
# Since we can't change the primary property, store the
|
|
1176
|
-
# updated value in nodedata.
|
|
1177
|
-
if self.runt.debug:
|
|
1178
|
-
mesg = f'DEBUG: itSecCpe_2_170_0({reprvalu}): Stored updated primary property value to nodedata: {curv} -> {norm}.'
|
|
1179
|
-
await self.runt.printf(mesg)
|
|
1180
|
-
|
|
1181
|
-
nodedata['valu'] = norm
|
|
1182
|
-
|
|
1183
|
-
# Iterate over the existing properties
|
|
1184
|
-
for propname, propcurv in n.props.items():
|
|
1185
|
-
subscurv = subs.get(propname)
|
|
1186
|
-
if subscurv is None:
|
|
1187
|
-
continue
|
|
1188
|
-
|
|
1189
|
-
if propname == 'v2_2' and isinstance(subscurv, list):
|
|
1190
|
-
subscurv = s_infotech.zipCpe22(subscurv)
|
|
1191
|
-
|
|
1192
|
-
# Values are the same, go around
|
|
1193
|
-
if propcurv == subscurv:
|
|
1194
|
-
continue
|
|
1195
|
-
|
|
1196
|
-
nodedata.setdefault('updated', [])
|
|
1197
|
-
nodedata['updated'].append(propname)
|
|
1198
|
-
|
|
1199
|
-
# Update the existing property with the re-normalized property value.
|
|
1200
|
-
await proto.set(propname, subscurv, ignore_ro=True)
|
|
1201
|
-
|
|
1202
|
-
if set_nodedata:
|
|
1203
|
-
await proto.setData('migration.s.itSecCpe_2_170_0', nodedata)
|
|
1204
|
-
|
|
1205
|
-
if self.runt.debug:
|
|
1206
|
-
if nodedata.get('updated'):
|
|
1207
|
-
mesg = f'DEBUG: itSecCpe_2_170_0({reprvalu}): Updated properties: {", ".join(nodedata["updated"])}.'
|
|
1208
|
-
await self.runt.printf(mesg)
|
|
1209
|
-
else:
|
|
1210
|
-
mesg = f'DEBUG: itSecCpe_2_170_0({reprvalu}): No property updates required.'
|
|
1211
|
-
await self.runt.printf(mesg)
|
|
1212
|
-
|
|
1213
|
-
return nodedata
|
|
1214
|
-
|
|
1215
966
|
async def _riskHasVulnToVulnerable(self, n, nodata=False):
|
|
1216
967
|
|
|
1217
968
|
nodata = await s_stormtypes.tobool(nodata)
|
|
@@ -1269,3 +1020,322 @@ class LibModelMigrations(s_stormtypes.Lib, MigrationEditorMixin):
|
|
|
1269
1020
|
await self.copyData(n, proto, overwrite=False)
|
|
1270
1021
|
|
|
1271
1022
|
return retidens
|
|
1023
|
+
|
|
1024
|
+
@s_stormtypes.registry.registerLib
|
|
1025
|
+
class LibModelMigrations_0_2_31(s_stormtypes.Lib):
|
|
1026
|
+
'''
|
|
1027
|
+
A Storm library with helper functions for the 0.2.31 model it:sec:cpe migration.
|
|
1028
|
+
'''
|
|
1029
|
+
_storm_locals = (
|
|
1030
|
+
{'name': 'listNodes', 'desc': 'Yield queued nodes.',
|
|
1031
|
+
'type': {'type': 'function', '_funcname': '_methListNodes',
|
|
1032
|
+
'args': (
|
|
1033
|
+
{'name': 'form', 'type': 'form', 'default': None,
|
|
1034
|
+
'desc': 'Only yield entries matching the specified form.'},
|
|
1035
|
+
{'name': 'source', 'type': 'str', 'default': None,
|
|
1036
|
+
'desc': 'Only yield entries that were seen by the specified source.'},
|
|
1037
|
+
{'name': 'offset', 'type': 'int', 'default': 0,
|
|
1038
|
+
'desc': 'Skip this many entries.'},
|
|
1039
|
+
{'name': 'size', 'type': 'int', 'default': None,
|
|
1040
|
+
'desc': 'Only yield up to this many entries.'},
|
|
1041
|
+
),
|
|
1042
|
+
'returns': {'name': 'Yields', 'type': 'list',
|
|
1043
|
+
'desc': 'A tuple of (offset, form, valu, sources) values for the specified node.', }}},
|
|
1044
|
+
{'name': 'printNode', 'desc': 'Print detailed queued node information.',
|
|
1045
|
+
'type': {'type': 'function', '_funcname': '_methPrintNode',
|
|
1046
|
+
'args': (
|
|
1047
|
+
{'name': 'offset', 'type': 'into', 'desc': 'The offset of the queued node to print.'},
|
|
1048
|
+
),
|
|
1049
|
+
'returns': {'type': 'null'}}},
|
|
1050
|
+
{'name': 'repairNode', 'desc': 'Repair a queued node.',
|
|
1051
|
+
'type': {'type': 'function', '_funcname': '_methRepairNode',
|
|
1052
|
+
'args': (
|
|
1053
|
+
{'name': 'offset', 'type': 'str', 'desc': 'The node queue offset to repair.'},
|
|
1054
|
+
{'name': 'newvalu', 'type': 'any', 'desc': 'The new (corrected) node value.'},
|
|
1055
|
+
{'name': 'remove', 'type': 'boolean', 'default': False,
|
|
1056
|
+
'desc': 'Specify whether to delete the repaired node from the queue.'},
|
|
1057
|
+
),
|
|
1058
|
+
'returns': {'type': 'dict', 'desc': 'The queue node information'}}},
|
|
1059
|
+
)
|
|
1060
|
+
_storm_lib_path = ('model', 'migration', 's', 'model_0_2_31')
|
|
1061
|
+
|
|
1062
|
+
def getObjLocals(self):
|
|
1063
|
+
return {
|
|
1064
|
+
'listNodes': self._methListNodes,
|
|
1065
|
+
'printNode': self._methPrintNode,
|
|
1066
|
+
'repairNode': self._methRepairNode,
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
async def _hasCoreQueue(self, name):
|
|
1070
|
+
try:
|
|
1071
|
+
await self.runt.snap.core.getCoreQueue(name)
|
|
1072
|
+
return True
|
|
1073
|
+
except s_exc.NoSuchName:
|
|
1074
|
+
return False
|
|
1075
|
+
|
|
1076
|
+
async def _methListNodes(self, form=None, source=None, offset=0, size=None):
|
|
1077
|
+
form = await s_stormtypes.tostr(form, noneok=True)
|
|
1078
|
+
source = await s_stormtypes.tostr(source, noneok=True)
|
|
1079
|
+
offset = await s_stormtypes.toint(offset)
|
|
1080
|
+
size = await s_stormtypes.toint(size, noneok=True)
|
|
1081
|
+
|
|
1082
|
+
if not await self._hasCoreQueue('model_0_2_31:nodes'):
|
|
1083
|
+
await self.runt.printf('Queue model_0_2_31:nodes not found, no nodes to list.')
|
|
1084
|
+
return
|
|
1085
|
+
|
|
1086
|
+
nodes = self.runt.snap.core.coreQueueGets('model_0_2_31:nodes', offs=offset, cull=False, size=size)
|
|
1087
|
+
async for offs, node in nodes:
|
|
1088
|
+
if form is not None and node['formname'] != form:
|
|
1089
|
+
continue
|
|
1090
|
+
|
|
1091
|
+
if source is not None and source not in node['sources']:
|
|
1092
|
+
continue
|
|
1093
|
+
|
|
1094
|
+
yield (offs, node['formname'], node['formvalu'], node['sources'])
|
|
1095
|
+
|
|
1096
|
+
async def _methPrintNode(self, offset):
|
|
1097
|
+
offset = await s_stormtypes.toint(offset)
|
|
1098
|
+
|
|
1099
|
+
if not await self._hasCoreQueue('model_0_2_31:nodes'):
|
|
1100
|
+
await self.runt.printf('Queue model_0_2_31:nodes not found, no nodes to print.')
|
|
1101
|
+
return
|
|
1102
|
+
|
|
1103
|
+
node = await self.runt.snap.core.coreQueueGet('model_0_2_31:nodes', offs=offset, cull=False)
|
|
1104
|
+
if not node:
|
|
1105
|
+
await self.runt.warn(f'Queued node with offset {offset} not found.')
|
|
1106
|
+
return
|
|
1107
|
+
|
|
1108
|
+
node = node[1]
|
|
1109
|
+
|
|
1110
|
+
await self.runt.printf(f'{node["formname"]}={repr(node["formvalu"])}')
|
|
1111
|
+
|
|
1112
|
+
for layriden, sode in node['sodes'].items():
|
|
1113
|
+
await self.runt.printf(f' layer: {layriden}')
|
|
1114
|
+
|
|
1115
|
+
for propname, propvalu in sode.get('props', {}).items():
|
|
1116
|
+
if propname == '.seen':
|
|
1117
|
+
mintime, maxtime = propvalu[0]
|
|
1118
|
+
mindt = s_time.repr(mintime)
|
|
1119
|
+
maxdt = s_time.repr(maxtime)
|
|
1120
|
+
await self.runt.printf(f' .seen = ({mindt}, {maxdt})')
|
|
1121
|
+
else:
|
|
1122
|
+
await self.runt.printf(f' :{propname} = {propvalu[0]}')
|
|
1123
|
+
|
|
1124
|
+
for tagname, tagvalu in sode.get('tags', {}).items():
|
|
1125
|
+
if tagvalu == (None, None):
|
|
1126
|
+
await self.runt.printf(f' #{tagname}')
|
|
1127
|
+
else:
|
|
1128
|
+
mintime, maxtime = tagvalu
|
|
1129
|
+
mindt = s_time.repr(mintime)
|
|
1130
|
+
maxdt = s_time.repr(maxtime)
|
|
1131
|
+
await self.runt.printf(f' #{tagname} = ({mindt}, {maxdt})')
|
|
1132
|
+
|
|
1133
|
+
for tagprop, tagpropvalu in sode.get('tagprops', {}).items():
|
|
1134
|
+
for prop, valu in tagpropvalu.items():
|
|
1135
|
+
await self.runt.printf(f' #{tagprop}:{prop} = {valu[0]}')
|
|
1136
|
+
|
|
1137
|
+
if sources := node['sources']:
|
|
1138
|
+
await self.runt.printf(f' sources: {sorted(sources)}')
|
|
1139
|
+
|
|
1140
|
+
if noderefs := node['refs']:
|
|
1141
|
+
await self.runt.printf(' refs:')
|
|
1142
|
+
|
|
1143
|
+
for layriden, reflist in noderefs.items():
|
|
1144
|
+
await self.runt.printf(f' layer: {layriden}')
|
|
1145
|
+
for iden, refinfo in reflist:
|
|
1146
|
+
form, prop, *_ = refinfo
|
|
1147
|
+
await self.runt.printf(f' - {form}:{prop} (iden: {iden}')
|
|
1148
|
+
|
|
1149
|
+
n1edges = node['n1edges']
|
|
1150
|
+
n2edges = node['n2edges']
|
|
1151
|
+
|
|
1152
|
+
if n1edges or n2edges:
|
|
1153
|
+
await self.runt.printf(' edges:')
|
|
1154
|
+
|
|
1155
|
+
for layriden, edges in n1edges.items():
|
|
1156
|
+
for verb, iden in edges:
|
|
1157
|
+
await self.runt.printf(f' -({verb})> {iden}')
|
|
1158
|
+
|
|
1159
|
+
for layriden, edges in n2edges.items():
|
|
1160
|
+
for verb, iden, n2form in edges:
|
|
1161
|
+
await self.runt.printf(f' <({verb})- {iden}')
|
|
1162
|
+
|
|
1163
|
+
async def _repairNode(self, offset, newvalu):
|
|
1164
|
+
item = await self.runt.snap.core.coreQueueGet('model_0_2_31:nodes', offset, cull=False)
|
|
1165
|
+
if item is None:
|
|
1166
|
+
await self.runt.warn(f'Queued node with offset {offset} not found.')
|
|
1167
|
+
return False
|
|
1168
|
+
|
|
1169
|
+
node = item[1]
|
|
1170
|
+
|
|
1171
|
+
nodeform = node['formname']
|
|
1172
|
+
form = self.runt.snap.core.model.form(nodeform)
|
|
1173
|
+
|
|
1174
|
+
norm, info = form.type.norm(newvalu)
|
|
1175
|
+
|
|
1176
|
+
buid = s_common.buid((nodeform, norm))
|
|
1177
|
+
|
|
1178
|
+
nodeedits = {}
|
|
1179
|
+
|
|
1180
|
+
for layriden in node['layers']:
|
|
1181
|
+
nodeedits.setdefault(layriden, {})
|
|
1182
|
+
|
|
1183
|
+
layer = self.runt.snap.core.getLayer(layriden)
|
|
1184
|
+
if layer is None: # pragma: no cover
|
|
1185
|
+
await self.runt.warn(f'Layer does not exist to recreate node: {layriden}.')
|
|
1186
|
+
return False
|
|
1187
|
+
|
|
1188
|
+
await self.runt.printf(f'Repairing node at offset {offset} from {node["formvalu"]} -> {norm}')
|
|
1189
|
+
|
|
1190
|
+
# Create the node in the right layers
|
|
1191
|
+
for layriden in node['layers']:
|
|
1192
|
+
nodeedits[layriden][buid] = (
|
|
1193
|
+
buid, nodeform, [
|
|
1194
|
+
(s_layer.EDIT_NODE_ADD, (norm, form.type.stortype), ()),
|
|
1195
|
+
])
|
|
1196
|
+
|
|
1197
|
+
for propname, propvalu in info.get('subs', {}).items():
|
|
1198
|
+
prop = form.prop(propname)
|
|
1199
|
+
if prop is None:
|
|
1200
|
+
continue
|
|
1201
|
+
|
|
1202
|
+
stortype = prop.type.stortype
|
|
1203
|
+
|
|
1204
|
+
nodeedits[layriden][buid][2].append(
|
|
1205
|
+
(s_layer.EDIT_PROP_SET, (propname, propvalu, None, stortype), ()),
|
|
1206
|
+
)
|
|
1207
|
+
|
|
1208
|
+
for layriden, sode in node['sodes'].items():
|
|
1209
|
+
nodeedits.setdefault(layriden, {})
|
|
1210
|
+
nodeedits[layriden].setdefault(buid, (buid, nodeform, []))
|
|
1211
|
+
|
|
1212
|
+
for propname, propvalu in sode.get('props', {}).items():
|
|
1213
|
+
propvalu, stortype = propvalu
|
|
1214
|
+
|
|
1215
|
+
nodeedits[layriden][buid][2].append(
|
|
1216
|
+
(s_layer.EDIT_PROP_SET, (propname, propvalu, None, stortype), ()),
|
|
1217
|
+
)
|
|
1218
|
+
|
|
1219
|
+
for tagname, tagvalu in sode.get('tags', {}).items():
|
|
1220
|
+
nodeedits[layriden][buid][2].append(
|
|
1221
|
+
(s_layer.EDIT_TAG_SET, (tagname, tagvalu, None), ()),
|
|
1222
|
+
)
|
|
1223
|
+
|
|
1224
|
+
for tagprop, tagpropvalu in sode.get('tagprops', {}).items():
|
|
1225
|
+
for propname, propvalu in tagpropvalu.items():
|
|
1226
|
+
propvalu, stortype = propvalu
|
|
1227
|
+
nodeedits[layriden][buid][2].append(
|
|
1228
|
+
(s_layer.EDIT_TAGPROP_SET, (tagname, propname, propvalu, None, stortype), ()),
|
|
1229
|
+
)
|
|
1230
|
+
|
|
1231
|
+
for layriden, data in node['nodedata'].items():
|
|
1232
|
+
nodeedits.setdefault(layriden, {})
|
|
1233
|
+
nodeedits[layriden].setdefault(buid, (buid, nodeform, []))
|
|
1234
|
+
|
|
1235
|
+
for name, valu in data:
|
|
1236
|
+
nodeedits[layriden][buid][2].append(
|
|
1237
|
+
(s_layer.EDIT_NODEDATA_SET, (name, valu, None), ()),
|
|
1238
|
+
)
|
|
1239
|
+
|
|
1240
|
+
for layriden, edges in node['n1edges'].items():
|
|
1241
|
+
nodeedits.setdefault(layriden, {})
|
|
1242
|
+
nodeedits[layriden].setdefault(buid, (buid, nodeform, []))
|
|
1243
|
+
|
|
1244
|
+
for verb, iden in edges:
|
|
1245
|
+
nodeedits[layriden][buid][2].append(
|
|
1246
|
+
(s_layer.EDIT_EDGE_ADD, (verb, iden), ()),
|
|
1247
|
+
)
|
|
1248
|
+
|
|
1249
|
+
for layriden, edges in node['n2edges'].items():
|
|
1250
|
+
n1iden = s_common.ehex(buid)
|
|
1251
|
+
|
|
1252
|
+
for verb, iden, n2form in edges:
|
|
1253
|
+
n2buid = s_common.uhex(iden)
|
|
1254
|
+
|
|
1255
|
+
nodeedits.setdefault(layriden, {})
|
|
1256
|
+
nodeedits[layriden].setdefault(n2buid, (n2buid, n2form, []))
|
|
1257
|
+
|
|
1258
|
+
nodeedits[layriden][n2buid][2].append(
|
|
1259
|
+
(s_layer.EDIT_EDGE_ADD, (verb, n1iden), ()),
|
|
1260
|
+
)
|
|
1261
|
+
|
|
1262
|
+
for layriden, reflist in node['refs'].items():
|
|
1263
|
+
layer = self.runt.snap.core.getLayer(layriden)
|
|
1264
|
+
if layer is None:
|
|
1265
|
+
continue
|
|
1266
|
+
|
|
1267
|
+
for iden, refinfo in reflist:
|
|
1268
|
+
refform, refprop, reftype, isarray, isro = refinfo
|
|
1269
|
+
|
|
1270
|
+
if isro:
|
|
1271
|
+
continue
|
|
1272
|
+
|
|
1273
|
+
refbuid = s_common.uhex(iden)
|
|
1274
|
+
|
|
1275
|
+
nodeedits.setdefault(layriden, {})
|
|
1276
|
+
nodeedits[layriden].setdefault(refbuid, (refbuid, refform, []))
|
|
1277
|
+
|
|
1278
|
+
if reftype == 'ndef':
|
|
1279
|
+
propvalu = (nodeform, norm)
|
|
1280
|
+
else:
|
|
1281
|
+
propvalu = norm
|
|
1282
|
+
|
|
1283
|
+
stortype = self.runt.snap.core.model.type(reftype).stortype
|
|
1284
|
+
|
|
1285
|
+
if isarray:
|
|
1286
|
+
|
|
1287
|
+
sode = await layer.getStorNode(refbuid)
|
|
1288
|
+
if not sode:
|
|
1289
|
+
continue
|
|
1290
|
+
|
|
1291
|
+
props = sode.get('props', {})
|
|
1292
|
+
|
|
1293
|
+
curv, _ = props.get(refprop, (None, None))
|
|
1294
|
+
_curv = curv
|
|
1295
|
+
|
|
1296
|
+
if _curv is None:
|
|
1297
|
+
_curv = []
|
|
1298
|
+
|
|
1299
|
+
newv = list(_curv).copy()
|
|
1300
|
+
newv.append(propvalu)
|
|
1301
|
+
|
|
1302
|
+
nodeedits[layriden][refbuid][2].append(
|
|
1303
|
+
(s_layer.EDIT_PROP_SET, (refprop, newv, curv, stortype | s_layer.STOR_FLAG_ARRAY), ()),
|
|
1304
|
+
)
|
|
1305
|
+
|
|
1306
|
+
else:
|
|
1307
|
+
|
|
1308
|
+
nodeedits[layriden][refbuid][2].append(
|
|
1309
|
+
(s_layer.EDIT_PROP_SET, (refprop, propvalu, None, stortype), ()),
|
|
1310
|
+
)
|
|
1311
|
+
|
|
1312
|
+
meta = {'time': s_common.now(), 'user': self.runt.snap.core.auth.rootuser.iden}
|
|
1313
|
+
|
|
1314
|
+
# Process all layer edits as a single batch
|
|
1315
|
+
for layriden, edits in nodeedits.items():
|
|
1316
|
+
layer = self.runt.snap.core.getLayer(layriden)
|
|
1317
|
+
if layer is None: # pragma: no cover
|
|
1318
|
+
continue
|
|
1319
|
+
|
|
1320
|
+
await layer.storNodeEditsNoLift(list(edits.values()), meta)
|
|
1321
|
+
|
|
1322
|
+
return True
|
|
1323
|
+
|
|
1324
|
+
async def _methRepairNode(self, offset, newvalu, remove=False):
|
|
1325
|
+
ok = False
|
|
1326
|
+
|
|
1327
|
+
if not await self._hasCoreQueue('model_0_2_31:nodes'):
|
|
1328
|
+
await self.runt.printf('Queue model_0_2_31:nodes not found, no nodes to repair.')
|
|
1329
|
+
return False
|
|
1330
|
+
|
|
1331
|
+
try:
|
|
1332
|
+
ok = await self._repairNode(offset, newvalu)
|
|
1333
|
+
except s_exc.SynErr as exc: # pragma: no cover
|
|
1334
|
+
mesg = exc.get('mesg')
|
|
1335
|
+
await self.runt.warn(f'Error when restoring node {offset}: {mesg}')
|
|
1336
|
+
|
|
1337
|
+
if ok and remove:
|
|
1338
|
+
await self.runt.printf(f'Removing queued node: {offset}.')
|
|
1339
|
+
await self.runt.snap.core.coreQueuePop('model_0_2_31:nodes', offset)
|
|
1340
|
+
|
|
1341
|
+
return ok
|