synapse 2.186.0__py311-none-any.whl → 2.188.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 +133 -9
- synapse/datamodel.py +20 -4
- synapse/exc.py +14 -1
- synapse/lib/ast.py +6 -4
- synapse/lib/auth.py +9 -0
- synapse/lib/hive.py +1 -1
- synapse/lib/httpapi.py +2 -1
- synapse/lib/modelrev.py +771 -11
- synapse/lib/nexus.py +6 -0
- synapse/lib/node.py +5 -3
- synapse/lib/scrape.py +18 -104
- synapse/lib/spooled.py +26 -3
- synapse/lib/storm.py +51 -28
- synapse/lib/stormlib/model.py +320 -250
- synapse/lib/stormlib/modelext.py +31 -0
- synapse/lib/stormlib/scrape.py +1 -4
- synapse/lib/stormtypes.py +53 -11
- synapse/lib/version.py +2 -2
- synapse/lib/view.py +9 -3
- synapse/models/base.py +27 -0
- synapse/models/files.py +22 -0
- synapse/models/inet.py +49 -4
- synapse/models/infotech.py +49 -22
- synapse/models/orgs.py +64 -2
- synapse/models/proj.py +1 -6
- synapse/models/risk.py +65 -0
- synapse/tests/test_cortex.py +21 -0
- synapse/tests/test_lib_agenda.py +13 -0
- synapse/tests/test_lib_auth.py +15 -0
- synapse/tests/test_lib_cell.py +2 -1
- synapse/tests/test_lib_httpapi.py +6 -0
- synapse/tests/test_lib_modelrev.py +918 -379
- synapse/tests/test_lib_nexus.py +26 -0
- synapse/tests/test_lib_scrape.py +14 -6
- synapse/tests/test_lib_spooled.py +34 -0
- synapse/tests/test_lib_storm.py +48 -0
- synapse/tests/test_lib_stormlib_model.py +0 -270
- synapse/tests/test_lib_stormlib_modelext.py +76 -1
- synapse/tests/test_lib_stormlib_scrape.py +0 -8
- synapse/tests/test_lib_stormtypes.py +12 -1
- synapse/tests/test_lib_trigger.py +8 -0
- synapse/tests/test_lib_view.py +24 -0
- synapse/tests/test_model_base.py +11 -0
- synapse/tests/test_model_files.py +19 -0
- synapse/tests/test_model_inet.py +33 -0
- synapse/tests/test_model_infotech.py +14 -11
- synapse/tests/test_model_orgs.py +39 -0
- synapse/tests/test_model_proj.py +11 -1
- synapse/tests/test_model_risk.py +32 -0
- synapse/tools/changelog.py +11 -3
- {synapse-2.186.0.dist-info → synapse-2.188.0.dist-info}/METADATA +1 -1
- {synapse-2.186.0.dist-info → synapse-2.188.0.dist-info}/RECORD +55 -58
- 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.188.0.dist-info}/LICENSE +0 -0
- {synapse-2.186.0.dist-info → synapse-2.188.0.dist-info}/WHEEL +0 -0
- {synapse-2.186.0.dist-info → synapse-2.188.0.dist-info}/top_level.txt +0 -0
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
|
synapse/lib/stormlib/modelext.py
CHANGED
|
@@ -105,6 +105,21 @@ class LibModelExt(s_stormtypes.Lib):
|
|
|
105
105
|
'desc': 'The form of the n2 node. May be "*" or null to specify "any".'},
|
|
106
106
|
),
|
|
107
107
|
'returns': {'type': 'null'}}},
|
|
108
|
+
{'name': 'addType', 'desc': 'Add an extended type definition to the data model.',
|
|
109
|
+
'type': {'type': 'function', '_funcname': 'addType',
|
|
110
|
+
'args': (
|
|
111
|
+
{'name': 'typename', 'type': 'str', 'desc': 'The name of the type to add.'},
|
|
112
|
+
{'name': 'basetype', 'type': 'str', 'desc': 'The base type the type is derived from.'},
|
|
113
|
+
{'name': 'typeopts', 'type': 'dict', 'desc': 'A Synapse type opts dictionary.'},
|
|
114
|
+
{'name': 'typeinfo', 'type': 'dict', 'desc': 'A Synapse type info dictionary.'},
|
|
115
|
+
),
|
|
116
|
+
'returns': {'type': 'null'}}},
|
|
117
|
+
{'name': 'delType', 'desc': 'Remove an extended type definition from the model.',
|
|
118
|
+
'type': {'type': 'function', '_funcname': 'delType',
|
|
119
|
+
'args': (
|
|
120
|
+
{'name': 'typename', 'type': 'str', 'desc': 'The extended type to remove.'},
|
|
121
|
+
),
|
|
122
|
+
'returns': {'type': 'null'}}},
|
|
108
123
|
)
|
|
109
124
|
_storm_lib_path = ('model', 'ext')
|
|
110
125
|
|
|
@@ -122,6 +137,8 @@ class LibModelExt(s_stormtypes.Lib):
|
|
|
122
137
|
'addExtModel': self.addExtModel,
|
|
123
138
|
'addEdge': self.addEdge,
|
|
124
139
|
'delEdge': self.delEdge,
|
|
140
|
+
'addType': self.addType,
|
|
141
|
+
'delType': self.delType,
|
|
125
142
|
}
|
|
126
143
|
|
|
127
144
|
# TODO type docs in the new convention
|
|
@@ -210,6 +227,7 @@ class LibModelExt(s_stormtypes.Lib):
|
|
|
210
227
|
return await self.runt.snap.core.getExtModel()
|
|
211
228
|
|
|
212
229
|
async def addExtModel(self, model):
|
|
230
|
+
self.runt.reqAdmin()
|
|
213
231
|
model = await s_stormtypes.toprim(model)
|
|
214
232
|
return await self.runt.snap.core.addExtModel(model)
|
|
215
233
|
|
|
@@ -249,3 +267,16 @@ class LibModelExt(s_stormtypes.Lib):
|
|
|
249
267
|
|
|
250
268
|
s_stormtypes.confirm(('model', 'edge', 'del'))
|
|
251
269
|
await self.runt.snap.core.delEdge((n1form, verb, n2form))
|
|
270
|
+
|
|
271
|
+
async def addType(self, typename, basetype, typeopts, typeinfo):
|
|
272
|
+
typename = await s_stormtypes.tostr(typename)
|
|
273
|
+
basetype = await s_stormtypes.tostr(basetype)
|
|
274
|
+
typeopts = await s_stormtypes.toprim(typeopts)
|
|
275
|
+
typeinfo = await s_stormtypes.toprim(typeinfo)
|
|
276
|
+
s_stormtypes.confirm(('model', 'type', 'add', typename))
|
|
277
|
+
await self.runt.snap.core.addType(typename, basetype, typeopts, typeinfo)
|
|
278
|
+
|
|
279
|
+
async def delType(self, typename):
|
|
280
|
+
typename = await s_stormtypes.tostr(typename)
|
|
281
|
+
s_stormtypes.confirm(('model', 'type', 'del', typename))
|
|
282
|
+
await self.runt.snap.core.delType(typename)
|
synapse/lib/stormlib/scrape.py
CHANGED
|
@@ -155,10 +155,7 @@ class LibScrape(s_stormtypes.Lib):
|
|
|
155
155
|
if fangs:
|
|
156
156
|
_fangs = {src: dst for (src, dst) in fangs}
|
|
157
157
|
_fangre = s_scrape.genFangRegex(_fangs)
|
|
158
|
-
|
|
159
|
-
scrape_text, offsets = s_scrape.refang_text2(text, re=_fangre, fangs=_fangs)
|
|
160
|
-
else:
|
|
161
|
-
scrape_text, offsets = await s_coro.semafork(s_scrape.refang_text2, text, re=_fangre, fangs=_fangs)
|
|
158
|
+
scrape_text, offsets = await s_coro.semafork(s_scrape.refang_text2, text, re=_fangre, fangs=_fangs)
|
|
162
159
|
|
|
163
160
|
async for info in s_scrape.genMatchesAsync(scrape_text, regx, opts=opts):
|
|
164
161
|
valu = info.pop('valu')
|