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.

Files changed (58) hide show
  1. synapse/cortex.py +133 -9
  2. synapse/datamodel.py +20 -4
  3. synapse/exc.py +14 -1
  4. synapse/lib/ast.py +6 -4
  5. synapse/lib/auth.py +9 -0
  6. synapse/lib/hive.py +1 -1
  7. synapse/lib/httpapi.py +2 -1
  8. synapse/lib/modelrev.py +771 -11
  9. synapse/lib/nexus.py +6 -0
  10. synapse/lib/node.py +5 -3
  11. synapse/lib/scrape.py +18 -104
  12. synapse/lib/spooled.py +26 -3
  13. synapse/lib/storm.py +51 -28
  14. synapse/lib/stormlib/model.py +320 -250
  15. synapse/lib/stormlib/modelext.py +31 -0
  16. synapse/lib/stormlib/scrape.py +1 -4
  17. synapse/lib/stormtypes.py +53 -11
  18. synapse/lib/version.py +2 -2
  19. synapse/lib/view.py +9 -3
  20. synapse/models/base.py +27 -0
  21. synapse/models/files.py +22 -0
  22. synapse/models/inet.py +49 -4
  23. synapse/models/infotech.py +49 -22
  24. synapse/models/orgs.py +64 -2
  25. synapse/models/proj.py +1 -6
  26. synapse/models/risk.py +65 -0
  27. synapse/tests/test_cortex.py +21 -0
  28. synapse/tests/test_lib_agenda.py +13 -0
  29. synapse/tests/test_lib_auth.py +15 -0
  30. synapse/tests/test_lib_cell.py +2 -1
  31. synapse/tests/test_lib_httpapi.py +6 -0
  32. synapse/tests/test_lib_modelrev.py +918 -379
  33. synapse/tests/test_lib_nexus.py +26 -0
  34. synapse/tests/test_lib_scrape.py +14 -6
  35. synapse/tests/test_lib_spooled.py +34 -0
  36. synapse/tests/test_lib_storm.py +48 -0
  37. synapse/tests/test_lib_stormlib_model.py +0 -270
  38. synapse/tests/test_lib_stormlib_modelext.py +76 -1
  39. synapse/tests/test_lib_stormlib_scrape.py +0 -8
  40. synapse/tests/test_lib_stormtypes.py +12 -1
  41. synapse/tests/test_lib_trigger.py +8 -0
  42. synapse/tests/test_lib_view.py +24 -0
  43. synapse/tests/test_model_base.py +11 -0
  44. synapse/tests/test_model_files.py +19 -0
  45. synapse/tests/test_model_inet.py +33 -0
  46. synapse/tests/test_model_infotech.py +14 -11
  47. synapse/tests/test_model_orgs.py +39 -0
  48. synapse/tests/test_model_proj.py +11 -1
  49. synapse/tests/test_model_risk.py +32 -0
  50. synapse/tools/changelog.py +11 -3
  51. {synapse-2.186.0.dist-info → synapse-2.188.0.dist-info}/METADATA +1 -1
  52. {synapse-2.186.0.dist-info → synapse-2.188.0.dist-info}/RECORD +55 -58
  53. synapse/assets/__init__.py +0 -35
  54. synapse/assets/storm/migrations/model-0.2.28.storm +0 -355
  55. synapse/tests/test_assets.py +0 -25
  56. {synapse-2.186.0.dist-info → synapse-2.188.0.dist-info}/LICENSE +0 -0
  57. {synapse-2.186.0.dist-info → synapse-2.188.0.dist-info}/WHEEL +0 -0
  58. {synapse-2.186.0.dist-info → synapse-2.188.0.dist-info}/top_level.txt +0 -0
synapse/lib/modelrev.py CHANGED
@@ -2,14 +2,18 @@ import regex
2
2
  import logging
3
3
 
4
4
  import synapse.exc as s_exc
5
- import synapse.assets as s_assets
6
5
  import synapse.common as s_common
7
6
 
7
+ import synapse.lib.cache as s_cache
8
8
  import synapse.lib.layer as s_layer
9
+ import synapse.lib.msgpack as s_msgpack
10
+ import synapse.lib.spooled as s_spooled
11
+
12
+ import synapse.models.infotech as s_infotech
9
13
 
10
14
  logger = logging.getLogger(__name__)
11
15
 
12
- maxvers = (0, 2, 30)
16
+ maxvers = (0, 2, 31)
13
17
 
14
18
  class ModelRev:
15
19
 
@@ -45,6 +49,7 @@ class ModelRev:
45
49
  # Model revision 0.2.28 skipped
46
50
  ((0, 2, 29), self.revModel_0_2_29),
47
51
  ((0, 2, 30), self.revModel_0_2_30),
52
+ ((0, 2, 31), self.revModel_0_2_31),
48
53
  )
49
54
 
50
55
  async def _uniqSortArray(self, todoprops, layers):
@@ -791,15 +796,6 @@ class ModelRev:
791
796
  async def revModel_0_2_27(self, layers):
792
797
  await self._normPropValu(layers, 'it:dev:repo:commit:id')
793
798
 
794
- async def revModel_0_2_28(self, layers):
795
-
796
- opts = {'vars': {
797
- 'layridens': [layr.iden for layr in layers],
798
- }}
799
-
800
- text = s_assets.getStorm('migrations', 'model-0.2.28.storm')
801
- await self.runStorm(text, opts=opts)
802
-
803
799
  async def revModel_0_2_29(self, layers):
804
800
  await self._propToForm(layers, 'ou:industry:type', 'ou:industry:type:taxonomy')
805
801
 
@@ -814,6 +810,11 @@ class ModelRev:
814
810
  await self._normFormSubs(layers, 'inet:ipv6', cmprvalu='2001:20::/28')
815
811
  await self._normFormSubs(layers, 'inet:ipv6', cmprvalu='2001:30::/28')
816
812
 
813
+ async def revModel_0_2_31(self, layers):
814
+ migr = await ModelMigration_0_2_31.anit(self.core, layers)
815
+ await migr.revModel_0_2_31()
816
+ await self._normFormSubs(layers, 'it:sec:cpe')
817
+
817
818
  async def runStorm(self, text, opts=None):
818
819
  '''
819
820
  Run storm code in a schedcoro and log the output messages.
@@ -1205,3 +1206,762 @@ class ModelRev:
1205
1206
  }
1206
1207
  '''
1207
1208
  await self.runStorm(storm, opts=opts)
1209
+
1210
+ class ModelMigration_0_2_31:
1211
+ @classmethod
1212
+ async def anit(cls, core, layers):
1213
+ self = cls()
1214
+
1215
+ self.core = core
1216
+ self.layers = layers
1217
+
1218
+ self.meta = {'time': s_common.now(), 'user': self.core.auth.rootuser.iden}
1219
+
1220
+ self.editcount = 0
1221
+ self.nodeedits = {}
1222
+
1223
+ self.nodes = await s_spooled.Dict.anit(dirn=self.core.dirn)
1224
+ self.todos = await s_spooled.Set.anit(dirn=self.core.dirn)
1225
+
1226
+ self.core.onfini(self.nodes)
1227
+ self.core.onfini(self.todos)
1228
+
1229
+ try:
1230
+ await self.core.getCoreQueue('model_0_2_31:nodes')
1231
+ self.hasq = True
1232
+ except s_exc.NoSuchName:
1233
+ self.hasq = False
1234
+
1235
+ return self
1236
+
1237
+ async def _queueEdit(self, layriden, edit):
1238
+ self.nodeedits.setdefault(layriden, {})
1239
+ buid, formname, edits = edit
1240
+ self.nodeedits[layriden].setdefault(buid, (buid, formname, []))
1241
+ self.nodeedits[layriden][buid][2].extend(edits)
1242
+ self.editcount += 1
1243
+
1244
+ if self.editcount >= 1000: # pragma: no cover
1245
+ await self._flushEdits()
1246
+
1247
+ async def _flushEdits(self):
1248
+ for layriden, layredits in self.nodeedits.items():
1249
+ layer = self.core.getLayer(layriden)
1250
+ if layer is None: # pragma: no cover
1251
+ continue
1252
+
1253
+ await layer.storNodeEditsNoLift(list(layredits.values()), self.meta)
1254
+
1255
+ self.editcount = 0
1256
+ self.nodeedits = {}
1257
+
1258
+ # NOTE: For the edit* functions below, we only need precise state tracking for nodes and properties. Don't precisely
1259
+ # track the rest.
1260
+ async def editNodeAdd(self, layriden, buid, formname, formvalu, stortype):
1261
+ if not self.nodes.has(buid):
1262
+
1263
+ node = {
1264
+ 'formname': formname,
1265
+ 'formvalu': formvalu,
1266
+ 'sodes': {},
1267
+ 'nodedata': {},
1268
+ 'n1edges': {},
1269
+ 'n2edges': {},
1270
+ }
1271
+ await self.nodes.set(buid, node)
1272
+
1273
+ await self._queueEdit(layriden,
1274
+ (buid, formname, (
1275
+ (s_layer.EDIT_NODE_ADD, (formvalu, stortype), ()),
1276
+ )),
1277
+ )
1278
+
1279
+ async def editPropSet(self, layriden, buid, formname, propname, newvalu, oldvalu, stortype):
1280
+ assert self.nodes.has(buid)
1281
+ node = self.getNode(buid)
1282
+
1283
+ sode = node['sodes'].get(layriden, {})
1284
+ node['sodes'][layriden] = sode
1285
+
1286
+ props = sode.get('props', {})
1287
+ sode['props'] = props
1288
+
1289
+ if oldvalu is not None:
1290
+ assert props.get(propname) == (oldvalu, stortype), f'GOT: {props.get(propname)} EXPECTED: {(oldvalu, stortype)}'
1291
+ props[propname] = (newvalu, stortype)
1292
+
1293
+ await self.nodes.set(buid, node)
1294
+
1295
+ await self._queueEdit(layriden,
1296
+ (buid, formname, (
1297
+ (s_layer.EDIT_PROP_SET, (propname, newvalu, oldvalu, stortype), ()),
1298
+ )),
1299
+ )
1300
+
1301
+ async def editTagSet(self, layriden, buid, formname, tagname, newvalu, oldvalu):
1302
+ await self._queueEdit(layriden,
1303
+ (buid, formname, (
1304
+ (s_layer.EDIT_TAG_SET, (tagname, newvalu, oldvalu), ()),
1305
+ )),
1306
+ )
1307
+
1308
+ async def editTagpropSet(self, layriden, buid, formname, tagname, propname, newvalu, oldvalu, stortype):
1309
+ await self._queueEdit(layriden,
1310
+ (buid, formname, (
1311
+ (s_layer.EDIT_TAGPROP_SET, (tagname, propname, newvalu, oldvalu, stortype), ()),
1312
+ )),
1313
+ )
1314
+
1315
+ async def editNodedataSet(self, layriden, buid, formname, name, newvalu, oldvalu):
1316
+ await self._queueEdit(layriden,
1317
+ (buid, formname, (
1318
+ (s_layer.EDIT_NODEDATA_SET, (name, newvalu, oldvalu), ()),
1319
+ )),
1320
+ )
1321
+
1322
+ async def editEdgeAdd(self, layriden, buid, formname, verb, iden):
1323
+ await self._queueEdit(layriden,
1324
+ (buid, formname, (
1325
+ (s_layer.EDIT_EDGE_ADD, (verb, iden), ()),
1326
+ )),
1327
+ )
1328
+
1329
+ async def editNodeDel(self, layriden, buid, formname, formvalu):
1330
+ assert self.nodes.has(buid)
1331
+ node = self.nodes.pop(buid)
1332
+
1333
+ for layriden in node['layers']:
1334
+ await self._queueEdit(layriden,
1335
+ (buid, formname, (
1336
+ (s_layer.EDIT_NODE_DEL, formvalu, ()),
1337
+ )),
1338
+ )
1339
+
1340
+ async def editPropDel(self, layriden, buid, formname, propname, propvalu, stortype):
1341
+ assert self.nodes.has(buid)
1342
+ node = self.getNode(buid)
1343
+
1344
+ sode = node['sodes'][layriden]
1345
+ props = sode.get('props', {})
1346
+
1347
+ assert props.get(propname) == (propvalu, stortype), f'GOT: {props.get(propname)} EXPECTED: {(propvalu, stortype)}'
1348
+
1349
+ props.pop(propname)
1350
+
1351
+ await self.nodes.set(buid, node)
1352
+
1353
+ await self._queueEdit(layriden,
1354
+ (buid, formname, (
1355
+ (s_layer.EDIT_PROP_DEL, (propname, propvalu, stortype), ()),
1356
+ )),
1357
+ )
1358
+
1359
+ async def editTagDel(self, layriden, buid, formname, tagname, tagvalu):
1360
+ await self._queueEdit(layriden,
1361
+ (buid, formname, (
1362
+ (s_layer.EDIT_TAG_DEL, (tagname, tagvalu), ()),
1363
+ )),
1364
+ )
1365
+
1366
+ async def editTagpropDel(self, layriden, buid, formname, tagname, propname, propvalu, stortype):
1367
+ await self._queueEdit(layriden,
1368
+ (buid, formname, (
1369
+ (s_layer.EDIT_TAGPROP_DEL, (tagname, propname, propvalu, stortype), ()),
1370
+ )),
1371
+ )
1372
+
1373
+ async def editNodedataDel(self, layriden, buid, formname, name, valu):
1374
+ await self._queueEdit(layriden,
1375
+ (buid, formname, (
1376
+ (s_layer.EDIT_NODEDATA_DEL, (name, valu), ()),
1377
+ )),
1378
+ )
1379
+
1380
+ async def editEdgeDel(self, layriden, buid, formname, verb, iden):
1381
+ await self._queueEdit(layriden,
1382
+ (buid, formname, (
1383
+ (s_layer.EDIT_EDGE_DEL, (verb, iden), ()),
1384
+ )),
1385
+ )
1386
+
1387
+ def getNode(self, buid):
1388
+ node = self.nodes.get(buid, {})
1389
+ if not node:
1390
+ node.setdefault('refs', {})
1391
+ node.setdefault('sodes', {})
1392
+ node.setdefault('layers', [])
1393
+ node.setdefault('n1edges', {})
1394
+ node.setdefault('n2edges', {})
1395
+ node.setdefault('verdict', None)
1396
+ node.setdefault('nodedata', {})
1397
+ return node
1398
+
1399
+ async def _loadNode(self, layer, buid, node=None):
1400
+ if node is None:
1401
+ node = self.getNode(buid)
1402
+
1403
+ sode = await layer.getStorNode(buid)
1404
+ if sode:
1405
+ node['sodes'].setdefault(layer.iden, {})
1406
+ node['sodes'][layer.iden] = sode
1407
+
1408
+ if (formvalu := sode.get('valu')) is not None:
1409
+ if node.get('formvalu') is None:
1410
+ node['formvalu'] = formvalu[0]
1411
+ node['formname'] = sode.get('form')
1412
+
1413
+ if layer.iden not in node['layers']:
1414
+ layers = list(node['layers'])
1415
+ layers.append(layer.iden)
1416
+ node['layers'] = layers
1417
+
1418
+ # Get nodedata
1419
+ nodedata = [k async for k in layer.iterNodeData(buid)]
1420
+ if nodedata:
1421
+ node['nodedata'][layer.iden] = nodedata
1422
+
1423
+ # Collect N1 edges
1424
+ n1edges = [k async for k in layer.iterNodeEdgesN1(buid)]
1425
+ if n1edges:
1426
+ node['n1edges'][layer.iden] = n1edges
1427
+
1428
+ # Collect N2 edges
1429
+ n2edges = []
1430
+ async for verb, iden in layer.iterNodeEdgesN2(buid):
1431
+ n2edges.append((verb, iden))
1432
+
1433
+ await self.todos.add(('getvalu', (s_common.uhex(iden), False)))
1434
+
1435
+ if n2edges:
1436
+ node['n2edges'][layer.iden] = n2edges
1437
+
1438
+ await self.nodes.set(buid, node)
1439
+ return node
1440
+
1441
+ async def revModel_0_2_31(self):
1442
+
1443
+ form = self.core.model.form('it:sec:cpe')
1444
+
1445
+ logger.info(f'Collecting and classifying it:sec:cpe nodes in {len(self.layers)} layers')
1446
+
1447
+ # Pick up and classify all bad CPE nodes
1448
+ for idx, layer in enumerate(self.layers):
1449
+ logger.debug(f'Classifying nodes in layer {idx}')
1450
+
1451
+ async for buid, sode in layer.getStorNodesByForm('it:sec:cpe'):
1452
+
1453
+ verdict = 'remove'
1454
+
1455
+ # Delete invalid v2_2 props while we're iterating
1456
+ props = sode.get('props', {})
1457
+ if (v2_2 := props.get('v2_2')) is not None:
1458
+ propvalu, stortype = v2_2
1459
+ if not s_infotech.isValidCpe22(propvalu):
1460
+ await self._queueEdit(
1461
+ layer.iden,
1462
+ (buid, 'it:sec:cpe', (
1463
+ (s_layer.EDIT_PROP_DEL, ('v2_2', propvalu, stortype), ()),
1464
+ ))
1465
+ )
1466
+ else:
1467
+ verdict = 'migrate'
1468
+
1469
+ if (formvalu := sode.get('valu')) is None:
1470
+ continue
1471
+
1472
+ formvalu = formvalu[0]
1473
+ if s_infotech.isValidCpe23(formvalu):
1474
+ continue
1475
+
1476
+ node = self.getNode(buid)
1477
+ node['formvalu'] = formvalu
1478
+ node['formname'] = 'it:sec:cpe'
1479
+ node['verdict'] = verdict
1480
+ layers = list(node['layers'])
1481
+ layers.append(layer.iden)
1482
+ node['layers'] = layers
1483
+
1484
+ await self.nodes.set(buid, node)
1485
+
1486
+ await self._flushEdits()
1487
+
1488
+ invalid = len(self.nodes)
1489
+ logger.info(f'Processing {invalid} invalid it:sec:cpe nodes in {len(self.layers)} layers')
1490
+
1491
+ # Pick up all related CPE node info. The majority of the work happens in this loop
1492
+ for idx, layer in enumerate(self.layers):
1493
+ logger.debug(f'Processing nodes in layer {idx}')
1494
+
1495
+ for buid, node in self.nodes.items():
1496
+ await self._loadNode(layer, buid, node=node)
1497
+
1498
+ formvalu = node.get('formvalu')
1499
+ formname = node.get('formname')
1500
+ formndef = (formname, formvalu)
1501
+
1502
+ refs = node['refs'].get(layer.iden, [])
1503
+
1504
+ for refinfo in self.getRefInfo(formname):
1505
+ (refform, refprop, reftype, isarray, isro) = refinfo
1506
+
1507
+ if reftype == 'ndef':
1508
+ propvalu = formndef
1509
+ else:
1510
+ propvalu = formvalu
1511
+
1512
+ async for refbuid, refsode in self.getSodeByPropValuNoNorm(layer, refform, refprop, propvalu):
1513
+ # Save the reference info
1514
+ refs.append((s_common.ehex(refbuid), refinfo))
1515
+
1516
+ # Add a todo to get valu and refs to the new nodes
1517
+ await self.todos.add(('getvalu', (refbuid, True)))
1518
+
1519
+ if refs:
1520
+ node['refs'][layer.iden] = refs
1521
+
1522
+ await self.nodes.set(buid, node)
1523
+
1524
+ logger.info('Processing invalid it:sec:cpe node references (this may happen multiple times)')
1525
+
1526
+ # Collect sources, direct references, second-degree references, etc.
1527
+ while len(self.todos):
1528
+ # Copy the list of todos and then clear the original list. This makes it so we will process all the todos
1529
+ # but we can add new todos (that will iterate over all the layers) below to gather supporting data as
1530
+ # needed.
1531
+ todotmp = await self.todos.copy()
1532
+ await self.todos.clear()
1533
+
1534
+ for idx, layer in enumerate(self.layers):
1535
+ logger.debug(f'Processing references in layer {idx}')
1536
+
1537
+ async for entry in todotmp:
1538
+ match entry:
1539
+ case ('getvalu', (buid, fullnode)):
1540
+ if fullnode:
1541
+ node = await self._loadNode(layer, buid)
1542
+ formvalu = node.get('formvalu')
1543
+ if formvalu is None:
1544
+ continue
1545
+
1546
+ formname = node.get('formname')
1547
+
1548
+ await self.todos.add(('getrefs', (buid, formname, formvalu)))
1549
+ else:
1550
+ sode = await layer.getStorNode(buid)
1551
+
1552
+ if (formvalu := sode.get('valu')) is None:
1553
+ continue
1554
+
1555
+ formvalu = formvalu[0]
1556
+ formname = sode.get('form')
1557
+
1558
+ node = self.getNode(buid)
1559
+ node['formvalu'] = formvalu
1560
+ node['formname'] = formname
1561
+ layers = list(node['layers'])
1562
+ layers.append(layer.iden)
1563
+ node['layers'] = layers
1564
+
1565
+ await self.nodes.set(buid, node)
1566
+
1567
+ case ('getrefs', (buid, formname, formvalu)):
1568
+
1569
+ node = self.getNode(buid)
1570
+ formndef = (formname, formvalu)
1571
+
1572
+ node.setdefault('refs', {})
1573
+ refs = node['refs'].get(layer.iden, [])
1574
+
1575
+ for refinfo in self.getRefInfo(formname):
1576
+ (refform, refprop, reftype, isarray, isro) = refinfo
1577
+
1578
+ if reftype == 'ndef':
1579
+ propvalu = formndef
1580
+ else:
1581
+ propvalu = formvalu
1582
+
1583
+ async for refbuid, refsode in self.getSodeByPropValuNoNorm(layer, refform, refprop, propvalu):
1584
+ # Save the reference info
1585
+ refs.append((s_common.ehex(refbuid), refinfo))
1586
+
1587
+ # Add a todo to get valu and refs to the new nodes
1588
+ await self.todos.add(('getvalu', (refbuid, True)))
1589
+
1590
+ if refs:
1591
+ node['refs'][layer.iden] = refs
1592
+
1593
+ await self.nodes.set(buid, node)
1594
+
1595
+ await todotmp.fini()
1596
+
1597
+ logger.info(f'Migrating/removing {invalid} invalid it:sec:cpe nodes')
1598
+
1599
+ count = 0
1600
+ removed = 0
1601
+ migrated = 0
1602
+ for buid, node in self.nodes.items():
1603
+ action = node.get('verdict')
1604
+
1605
+ if action is None:
1606
+ continue
1607
+
1608
+ if action == 'migrate':
1609
+ propvalu = None
1610
+ for layriden, sode in node.get('sodes').items():
1611
+ props = sode.get('props', {})
1612
+ propvalu, stortype = props.get('v2_2', (None, None))
1613
+ if propvalu is not None:
1614
+ break
1615
+
1616
+ newvalu, _ = form.type.norm(propvalu)
1617
+ await self.moveNode(buid, newvalu)
1618
+
1619
+ migrated += 1
1620
+
1621
+ elif action == 'remove':
1622
+ newvalu = None
1623
+ # Before removing the node, iterate over the sodes looking for a good :v2_2 value
1624
+ for layriden, sode in node.get('sodes').items():
1625
+ props = sode.get('props', {})
1626
+ propvalu, stortype = props.get('v2_2', (None, None))
1627
+ if propvalu is None:
1628
+ continue
1629
+
1630
+ newvalu, _ = form.type.norm(propvalu)
1631
+ # This prop is going to be the new primary value so delete the secondary prop
1632
+ await self.editPropDel(layriden, buid, 'it:sec:cpe', 'v2_2', propvalu, stortype)
1633
+
1634
+ # Oh yeah! Migrate the node instead of removing it
1635
+ await self.moveNode(buid, newvalu)
1636
+
1637
+ migrated += 1
1638
+ break
1639
+
1640
+ else:
1641
+ await self.removeNode(buid)
1642
+ removed += 1
1643
+
1644
+ count = migrated + removed
1645
+ if count % 1000 == 0: # pragma: no cover
1646
+ logger.info(f'Processed {count} it:sec:cpe nodes')
1647
+
1648
+ await self._flushEdits()
1649
+
1650
+ logger.info(f'Finished processing {count} it:sec:cpe nodes: {migrated} migrated, {removed} removed')
1651
+
1652
+ await self.todos.fini()
1653
+ await self.nodes.fini()
1654
+
1655
+ @s_cache.memoizemethod()
1656
+ def getRoProps(self, formname):
1657
+ roprops = []
1658
+
1659
+ form = self.core.model.form(formname)
1660
+ for propname, prop in form.props.items():
1661
+ if prop.info.get('ro', False):
1662
+ roprops.append(propname)
1663
+
1664
+ return roprops
1665
+
1666
+ @s_cache.memoizemethod()
1667
+ def getRefInfo(self, formname):
1668
+ props = []
1669
+ props.extend(self.core.model.getPropsByType(formname))
1670
+ props.extend(self.core.model.getPropsByType('array'))
1671
+ props.extend(self.core.model.getPropsByType('ndef'))
1672
+
1673
+ props = [k for k in props if k.form.name != formname]
1674
+
1675
+ refinfo = []
1676
+ for prop in props:
1677
+
1678
+ if prop.form.name == formname: # pragma: no cover
1679
+ continue
1680
+
1681
+ proptype = prop.type
1682
+
1683
+ if prop.type.isarray:
1684
+ proptype = prop.type.arraytype
1685
+
1686
+ if proptype.name not in (formname, 'ndef'):
1687
+ continue
1688
+
1689
+ refinfo.append((
1690
+ prop.form.name,
1691
+ prop.name,
1692
+ proptype.name,
1693
+ prop.type.isarray,
1694
+ prop.info.get('ro', False)
1695
+ ))
1696
+
1697
+ return refinfo
1698
+
1699
+ async def removeNode(self, buid):
1700
+ assert self.nodes.has(buid)
1701
+ node = self.getNode(buid)
1702
+
1703
+ await self.storeNode(buid)
1704
+
1705
+ formname = node.get('formname')
1706
+ formvalu = node.get('formvalu')
1707
+ formndef = (formname, formvalu)
1708
+ refs = node.get('refs')
1709
+
1710
+ # Delete references
1711
+ for reflayr, reflist in refs.items():
1712
+ for refiden, refinfo in reflist:
1713
+ refbuid = s_common.uhex(refiden)
1714
+ (refform, refprop, reftype, isarray, isro) = refinfo
1715
+
1716
+ if reftype == 'ndef':
1717
+ propvalu = formndef
1718
+ else:
1719
+ propvalu = formvalu
1720
+
1721
+ if isro:
1722
+ await self.removeNode(refbuid)
1723
+ continue
1724
+
1725
+ refnode = self.getNode(refbuid)
1726
+ refsode = refnode['sodes'].get(reflayr)
1727
+
1728
+ curv, stortype = refsode['props'].get(refprop, (None, None))
1729
+
1730
+ if isarray:
1731
+
1732
+ _curv = curv
1733
+
1734
+ newv = list(_curv).copy()
1735
+
1736
+ while propvalu in newv:
1737
+ newv.remove(propvalu)
1738
+
1739
+ if not newv:
1740
+ await self.editPropDel(reflayr, refbuid, refform, refprop, curv, stortype)
1741
+
1742
+ else:
1743
+ await self.editPropSet(reflayr, refbuid, refform, refprop, newv, curv, stortype)
1744
+
1745
+ else:
1746
+ await self.editPropDel(reflayr, refbuid, refform, refprop, curv, stortype)
1747
+
1748
+ await self.delNode(buid)
1749
+
1750
+ async def storeNode(self, buid):
1751
+ assert self.nodes.has(buid)
1752
+ node = self.getNode(buid)
1753
+
1754
+ formname = node.get('formname')
1755
+ formvalu = node.get('formvalu')
1756
+
1757
+ sources = set()
1758
+ # Resolve sources
1759
+ n2edges = {}
1760
+ for layriden, edges in node['n2edges'].items():
1761
+ n2edges.setdefault(layriden, [])
1762
+
1763
+ for verb, n2iden in edges:
1764
+ n2buid = s_common.uhex(n2iden)
1765
+ assert self.nodes.has(n2buid)
1766
+ n2node = self.nodes.get(n2buid)
1767
+ if n2node is None: # pragma: no cover
1768
+ continue
1769
+
1770
+ n2edges[layriden].append((verb, n2iden, n2node['formname']))
1771
+
1772
+ if verb == 'seen':
1773
+ formvalu = n2node.get('formvalu')
1774
+ assert formvalu is not None
1775
+ sources.add(formvalu)
1776
+
1777
+ # Make some changes before serializing
1778
+ item = s_msgpack.deepcopy(node)
1779
+ item.pop('verdict', None)
1780
+ item['iden'] = s_common.ehex(buid)
1781
+ item['sources'] = list(sources)
1782
+ item['n2edges'] = n2edges
1783
+
1784
+ roprops = self.getRoProps(formname)
1785
+ for layriden, sode in node['sodes'].items():
1786
+ props = sode.get('props')
1787
+ if props is None: # pragma: no cover
1788
+ continue
1789
+
1790
+ props = {name: valu for name, valu in list(props.items()) if name not in roprops}
1791
+ if props:
1792
+ item['sodes'][layriden]['props'] = props
1793
+ else:
1794
+ item['sodes'][layriden].pop('props')
1795
+
1796
+ if not self.hasq:
1797
+ await self.core.addCoreQueue('model_0_2_31:nodes', {})
1798
+ self.hasq = True
1799
+
1800
+ await self.core.coreQueuePuts('model_0_2_31:nodes', (item,))
1801
+
1802
+ async def getSodeByPropValuNoNorm(self, layer, formname, propname, valu, cmpr='='):
1803
+ prop = self.core.model.reqProp(f'{formname}:{propname}')
1804
+
1805
+ stortype = prop.type.stortype
1806
+
1807
+ # Normally we'd call proptype.getStorCmprs() here to get the cmprvals
1808
+ # but getStorCmprs() calls norm() which we're trying to avoid so build
1809
+ # cmprvals manually here.
1810
+
1811
+ if prop.type.isarray:
1812
+ stortype &= (~s_layer.STOR_FLAG_ARRAY)
1813
+ liftfunc = layer.liftByPropArray
1814
+ else:
1815
+ liftfunc = layer.liftByPropValu
1816
+
1817
+ cmprvals = ((cmpr, valu, stortype),)
1818
+
1819
+ async for _, buid, sode in liftfunc(formname, propname, cmprvals):
1820
+ yield buid, sode
1821
+
1822
+ async def delNode(self, buid):
1823
+ assert self.nodes.has(buid)
1824
+ node = self.getNode(buid)
1825
+
1826
+ formname = node.get('formname')
1827
+ formvalu = node.get('formvalu')
1828
+
1829
+ # Edits
1830
+ for layriden, sode in node['sodes'].items():
1831
+ props = sode.get('props', {}).copy()
1832
+ for propname, propvalu in props.items():
1833
+ propvalu, stortype = propvalu
1834
+ await self.editPropDel(layriden, buid, formname, propname, propvalu, stortype)
1835
+
1836
+ tags = sode.get('tags', {})
1837
+ for tagname, tagvalu in tags.items():
1838
+ await self.editTagDel(layriden, buid, formname, tagname, tagvalu)
1839
+
1840
+ tagprops = sode.get('tagprops', {})
1841
+ for tagname, propvalus in tagprops.items():
1842
+ for propname, propvalu in propvalus.items():
1843
+ propvalu, stortype = propvalu
1844
+ await self.editTagpropDel(layriden, buid, formname, tagname, propname, propvalu, stortype)
1845
+
1846
+ # Nodedata
1847
+ for layriden, data in node['nodedata'].items():
1848
+ for name, valu in data:
1849
+ await self.editNodedataDel(layriden, buid, formname, name, valu)
1850
+
1851
+ # Edges
1852
+ for layriden, edges in node['n1edges'].items():
1853
+ for verb, iden in edges:
1854
+ await self.editEdgeDel(layriden, buid, formname, verb, iden)
1855
+
1856
+ for layriden, edges in node['n2edges'].items():
1857
+ for verb, iden in edges:
1858
+ n2buid = s_common.uhex(iden)
1859
+
1860
+ n2node = self.nodes.get(n2buid)
1861
+ if n2node is None: # pragma: no cover
1862
+ continue
1863
+
1864
+ n2form = n2node.get('formname')
1865
+ await self.editEdgeDel(layriden, n2buid, n2form, verb, s_common.ehex(buid))
1866
+
1867
+ # Node
1868
+ await self.editNodeDel(layriden, buid, formname, formvalu)
1869
+
1870
+ async def moveNode(self, buid, newvalu):
1871
+ assert self.nodes.has(buid)
1872
+ node = self.getNode(buid)
1873
+
1874
+ formname = node.get('formname')
1875
+ formvalu = node.get('formvalu')
1876
+ refs = node.get('refs')
1877
+
1878
+ oldndef = (formname, formvalu)
1879
+ newndef = (formname, newvalu)
1880
+ newbuid = s_common.buid((formname, newvalu))
1881
+
1882
+ form = self.core.model.reqForm(formname)
1883
+
1884
+ # Node
1885
+ for layriden in node['layers']:
1886
+ # Create the new node in the same layers as the old node
1887
+ await self.editNodeAdd(layriden, newbuid, formname, newvalu, form.type.stortype)
1888
+
1889
+ # Edits
1890
+ for layriden, sode in node['sodes'].items():
1891
+ props = sode.get('props', {})
1892
+ for propname, propvalu in props.items():
1893
+ propvalu, stortype = propvalu
1894
+ await self.editPropSet(layriden, newbuid, formname, propname, propvalu, None, stortype)
1895
+
1896
+ tags = sode.get('tags', {})
1897
+ for tagname, tagvalu in tags.items():
1898
+ await self.editTagSet(layriden, newbuid, formname, tagname, tagvalu, None)
1899
+
1900
+ tagprops = sode.get('tagprops', {})
1901
+ for tagname, propvalus in tagprops.items():
1902
+ for propname, propvalu in propvalus.items():
1903
+ propvalu, stortype = propvalu
1904
+
1905
+ await self.editTagpropSet(layriden, newbuid, formname, tagname, propname, propvalu, None, stortype)
1906
+
1907
+ # Nodedata
1908
+ for layriden, data in node['nodedata'].items():
1909
+ for name, valu in data:
1910
+ await self.editNodedataSet(layriden, newbuid, formname, name, valu, None)
1911
+
1912
+ # Edges
1913
+ for layriden, edges in node['n1edges'].items():
1914
+ for verb, iden in edges:
1915
+ await self.editEdgeAdd(layriden, newbuid, formname, verb, iden)
1916
+
1917
+ for layriden, edges in node['n2edges'].items():
1918
+ for verb, iden in edges:
1919
+ n2buid = s_common.uhex(iden)
1920
+
1921
+ n2node = self.nodes.get(n2buid)
1922
+ if n2node is None: # pragma: no cover
1923
+ continue
1924
+
1925
+ n2form = n2node.get('formname')
1926
+ await self.editEdgeAdd(layriden, n2buid, n2form, verb, s_common.ehex(newbuid))
1927
+
1928
+ # Move references
1929
+ for reflayr, reflist in refs.items():
1930
+ for refiden, refinfo in reflist:
1931
+ refbuid = s_common.uhex(refiden)
1932
+ (refform, refprop, reftype, isarray, isro) = refinfo
1933
+
1934
+ if isro:
1935
+ await self.removeNode(refbuid)
1936
+ continue
1937
+
1938
+ if reftype == 'ndef':
1939
+ oldpropv = oldndef
1940
+ newpropv = newndef
1941
+ else:
1942
+ oldpropv = formvalu
1943
+ newpropv = newvalu
1944
+
1945
+ refnode = self.getNode(refbuid)
1946
+ refsode = refnode['sodes'].get(reflayr)
1947
+
1948
+ curv, stortype = refsode.get('props', {}).get(refprop, (None, None))
1949
+ assert stortype is not None
1950
+
1951
+ if isarray:
1952
+
1953
+ _curv = curv
1954
+
1955
+ newv = list(_curv).copy()
1956
+
1957
+ while oldpropv in newv:
1958
+ newv.remove(oldpropv)
1959
+
1960
+ newv.append(newpropv)
1961
+
1962
+ await self.editPropSet(reflayr, refbuid, refform, refprop, newv, curv, stortype)
1963
+
1964
+ else:
1965
+ await self.editPropSet(reflayr, refbuid, refform, refprop, newpropv, curv, stortype)
1966
+
1967
+ await self.delNode(buid)