mal-toolbox 0.1.8__tar.gz → 0.1.10__tar.gz
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.
- {mal_toolbox-0.1.8/mal_toolbox.egg-info → mal_toolbox-0.1.10}/PKG-INFO +7 -1
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/README.md +6 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10/mal_toolbox.egg-info}/PKG-INFO +7 -1
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/__init__.py +2 -2
- mal_toolbox-0.1.10/maltoolbox/attackgraph/__init__.py +8 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/attackgraph/analyzers/apriori.py +9 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/attackgraph/attacker.py +23 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/attackgraph/attackgraph.py +57 -6
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/attackgraph/node.py +43 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/attackgraph/query.py +43 -2
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/language/__init__.py +2 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/language/classes_factory.py +3 -2
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/pyproject.toml +2 -3
- mal_toolbox-0.1.8/maltoolbox/attackgraph/__init__.py +0 -3
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/AUTHORS +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/LICENSE +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/mal_toolbox.egg-info/SOURCES.txt +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/mal_toolbox.egg-info/dependency_links.txt +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/mal_toolbox.egg-info/requires.txt +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/mal_toolbox.egg-info/top_level.txt +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/__main__.py +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/attackgraph/analyzers/__init__.py +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/default.conf +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/exceptions.py +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/file_utils.py +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/ingestors/__init__.py +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/ingestors/neo4j.py +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/language/compiler/__init__.py +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/language/compiler/mal_lexer.py +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/language/compiler/mal_parser.py +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/language/compiler/mal_visitor.py +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/language/languagegraph.py +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/model.py +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/translators/__init__.py +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/translators/securicad.py +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/translators/updater.py +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/maltoolbox/wrappers.py +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/setup.cfg +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/tests/test_model.py +0 -0
- {mal_toolbox-0.1.8 → mal_toolbox-0.1.10}/tests/test_wrappers.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mal-toolbox
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.10
|
|
4
4
|
Summary: A collection of tools used to create MAL models and attack graphs.
|
|
5
5
|
Author-email: Andrei Buhaiu <buhaiu@kth.se>, Giuseppe Nebbione <nebbione@kth.se>, Nikolaos Kakouros <nkak@kth.se>, Jakob Nyberg <jaknyb@kth.se>, Joakim Loxdal <loxdal@kth.se>
|
|
6
6
|
License: Apache Software License
|
|
@@ -102,6 +102,12 @@ and it can be used to visualise the instance model and the attack graph.
|
|
|
102
102
|
|
|
103
103
|
# Usage
|
|
104
104
|
|
|
105
|
+
## Installation
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
pip install mal-toolbox
|
|
109
|
+
```
|
|
110
|
+
|
|
105
111
|
## Configuration
|
|
106
112
|
A default configuration file `default.conf` can be found in the package
|
|
107
113
|
directory. This contains the default values to use for logging and can also be
|
|
@@ -75,6 +75,12 @@ and it can be used to visualise the instance model and the attack graph.
|
|
|
75
75
|
|
|
76
76
|
# Usage
|
|
77
77
|
|
|
78
|
+
## Installation
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
pip install mal-toolbox
|
|
82
|
+
```
|
|
83
|
+
|
|
78
84
|
## Configuration
|
|
79
85
|
A default configuration file `default.conf` can be found in the package
|
|
80
86
|
directory. This contains the default values to use for logging and can also be
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: mal-toolbox
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.10
|
|
4
4
|
Summary: A collection of tools used to create MAL models and attack graphs.
|
|
5
5
|
Author-email: Andrei Buhaiu <buhaiu@kth.se>, Giuseppe Nebbione <nebbione@kth.se>, Nikolaos Kakouros <nkak@kth.se>, Jakob Nyberg <jaknyb@kth.se>, Joakim Loxdal <loxdal@kth.se>
|
|
6
6
|
License: Apache Software License
|
|
@@ -102,6 +102,12 @@ and it can be used to visualise the instance model and the attack graph.
|
|
|
102
102
|
|
|
103
103
|
# Usage
|
|
104
104
|
|
|
105
|
+
## Installation
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
pip install mal-toolbox
|
|
109
|
+
```
|
|
110
|
+
|
|
105
111
|
## Configuration
|
|
106
112
|
A default configuration file `default.conf` can be found in the package
|
|
107
113
|
directory. This contains the default values to use for logging and can also be
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
-
# MAL Toolbox v0.1.
|
|
2
|
+
# MAL Toolbox v0.1.10
|
|
3
3
|
# Copyright 2024, Andrei Buhaiu.
|
|
4
4
|
#
|
|
5
5
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
@@ -21,7 +21,7 @@ MAL-Toolbox Framework
|
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
23
|
__title__ = 'maltoolbox'
|
|
24
|
-
__version__ = '0.1.
|
|
24
|
+
__version__ = '0.1.10'
|
|
25
25
|
__authors__ = ['Andrei Buhaiu',
|
|
26
26
|
'Giuseppe Nebbione',
|
|
27
27
|
'Nikolaos Kakouros',
|
|
@@ -51,6 +51,15 @@ def propagate_necessity_from_node(node: AttackGraphNode) -> None:
|
|
|
51
51
|
'Propagate necessity from "%s"(%d) with necessity status %s.',
|
|
52
52
|
node.full_name, node.id, node.is_necessary
|
|
53
53
|
)
|
|
54
|
+
|
|
55
|
+
if node.ttc and 'name' in node.ttc:
|
|
56
|
+
if node.ttc['name'] not in ['Enabled', 'Disabled']:
|
|
57
|
+
# Do not propagate unnecessary state from nodes that have a TTC
|
|
58
|
+
# probability distribution associated with them.
|
|
59
|
+
# TODO: Evaluate this more carefully, how do we want to have TTCs
|
|
60
|
+
# impact necessity and viability.
|
|
61
|
+
return
|
|
62
|
+
|
|
54
63
|
for child in node.children:
|
|
55
64
|
original_value = child.is_necessary
|
|
56
65
|
if child.type == 'or':
|
|
@@ -4,6 +4,7 @@ MAL-Toolbox Attack Graph Attacker Class
|
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
6
|
from dataclasses import dataclass, field
|
|
7
|
+
import copy
|
|
7
8
|
import logging
|
|
8
9
|
|
|
9
10
|
from typing import Optional
|
|
@@ -41,6 +42,28 @@ class Attacker:
|
|
|
41
42
|
def __repr__(self) -> str:
|
|
42
43
|
return str(self.to_dict())
|
|
43
44
|
|
|
45
|
+
def __deepcopy__(self, memo) -> Attacker:
|
|
46
|
+
"""Deep copy an Attacker"""
|
|
47
|
+
|
|
48
|
+
# Check if the object is already in the memo dictionary
|
|
49
|
+
if id(self) in memo:
|
|
50
|
+
return memo[id(self)]
|
|
51
|
+
|
|
52
|
+
copied_attacker = Attacker(
|
|
53
|
+
id = self.id,
|
|
54
|
+
name = self.name,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# Remember that self was already copied
|
|
58
|
+
memo[id(self)] = copied_attacker
|
|
59
|
+
|
|
60
|
+
copied_attacker.entry_points = copy.deepcopy(
|
|
61
|
+
self.entry_points, memo = memo)
|
|
62
|
+
copied_attacker.reached_attack_steps = copy.deepcopy(
|
|
63
|
+
self.reached_attack_steps, memo = memo)
|
|
64
|
+
|
|
65
|
+
return copied_attacker
|
|
66
|
+
|
|
44
67
|
def compromise(self, node: AttackGraphNode) -> None:
|
|
45
68
|
"""
|
|
46
69
|
Have the attacke compromise the node given as a parameter.
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
MAL-Toolbox Attack Graph Module
|
|
3
3
|
"""
|
|
4
4
|
from __future__ import annotations
|
|
5
|
+
import copy
|
|
5
6
|
import logging
|
|
6
7
|
import json
|
|
7
8
|
|
|
@@ -224,6 +225,52 @@ class AttackGraph():
|
|
|
224
225
|
'attackers': serialized_attackers,
|
|
225
226
|
}
|
|
226
227
|
|
|
228
|
+
def __deepcopy__(self, memo):
|
|
229
|
+
|
|
230
|
+
# Check if the object is already in the memo dictionary
|
|
231
|
+
if id(self) in memo:
|
|
232
|
+
return memo[id(self)]
|
|
233
|
+
|
|
234
|
+
copied_attackgraph = AttackGraph(self.lang_graph)
|
|
235
|
+
copied_attackgraph.model = self.model
|
|
236
|
+
|
|
237
|
+
copied_attackgraph.nodes = []
|
|
238
|
+
|
|
239
|
+
# Deep copy nodes
|
|
240
|
+
for node in self.nodes:
|
|
241
|
+
copied_node = copy.deepcopy(node, memo)
|
|
242
|
+
copied_attackgraph.nodes.append(copied_node)
|
|
243
|
+
|
|
244
|
+
# Re-link node references
|
|
245
|
+
for node in self.nodes:
|
|
246
|
+
if node.parents:
|
|
247
|
+
memo[id(node)].parents = copy.deepcopy(node.parents, memo)
|
|
248
|
+
if node.children:
|
|
249
|
+
memo[id(node)].children = copy.deepcopy(node.children, memo)
|
|
250
|
+
|
|
251
|
+
# Deep copy attackers and references to them
|
|
252
|
+
copied_attackgraph.attackers = copy.deepcopy(self.attackers, memo)
|
|
253
|
+
|
|
254
|
+
# Re-link attacker references
|
|
255
|
+
for node in self.nodes:
|
|
256
|
+
if node.compromised_by:
|
|
257
|
+
memo[id(node)].compromised_by = copy.deepcopy(
|
|
258
|
+
node.compromised_by, memo)
|
|
259
|
+
|
|
260
|
+
# Copy lookup dicts
|
|
261
|
+
copied_attackgraph._id_to_attacker = \
|
|
262
|
+
copy.deepcopy(self._id_to_attacker, memo)
|
|
263
|
+
copied_attackgraph._id_to_node = \
|
|
264
|
+
copy.deepcopy(self._id_to_node, memo)
|
|
265
|
+
copied_attackgraph._full_name_to_node = \
|
|
266
|
+
copy.deepcopy(self._full_name_to_node, memo)
|
|
267
|
+
|
|
268
|
+
# Copy counters
|
|
269
|
+
copied_attackgraph.next_node_id = self.next_node_id
|
|
270
|
+
copied_attackgraph.next_attacker_id = self.next_attacker_id
|
|
271
|
+
|
|
272
|
+
return copied_attackgraph
|
|
273
|
+
|
|
227
274
|
def save_to_file(self, filename: str) -> None:
|
|
228
275
|
"""Save to json/yml depending on extension"""
|
|
229
276
|
logger.debug('Save attack graph to file "%s".', filename)
|
|
@@ -329,7 +376,10 @@ class AttackGraph():
|
|
|
329
376
|
attacker = ag_attacker,
|
|
330
377
|
attacker_id = int(attacker['id']),
|
|
331
378
|
entry_points = attacker['entry_points'].keys(),
|
|
332
|
-
reached_attack_steps =
|
|
379
|
+
reached_attack_steps = [
|
|
380
|
+
int(node_id) # Convert to int since they can be strings
|
|
381
|
+
for node_id in attacker['reached_attack_steps'].keys()
|
|
382
|
+
]
|
|
333
383
|
)
|
|
334
384
|
|
|
335
385
|
return attack_graph
|
|
@@ -382,7 +432,7 @@ class AttackGraph():
|
|
|
382
432
|
The attack step node that matches the given full name.
|
|
383
433
|
"""
|
|
384
434
|
|
|
385
|
-
logger.debug(f'Looking up node with
|
|
435
|
+
logger.debug(f'Looking up node with full name "{full_name}"')
|
|
386
436
|
return self._full_name_to_node.get(full_name)
|
|
387
437
|
|
|
388
438
|
def get_attacker_by_id(self, attacker_id: int) -> Optional[Attacker]:
|
|
@@ -441,7 +491,7 @@ class AttackGraph():
|
|
|
441
491
|
continue
|
|
442
492
|
attacker.compromise(ag_node)
|
|
443
493
|
|
|
444
|
-
attacker.entry_points = attacker.reached_attack_steps
|
|
494
|
+
attacker.entry_points = list(attacker.reached_attack_steps)
|
|
445
495
|
|
|
446
496
|
def _generate_graph(self) -> None:
|
|
447
497
|
"""
|
|
@@ -587,7 +637,7 @@ class AttackGraph():
|
|
|
587
637
|
if logger.isEnabledFor(logging.DEBUG):
|
|
588
638
|
# Avoid running json.dumps when not in debug
|
|
589
639
|
logger.debug(f'Add node \"{node.full_name}\" '
|
|
590
|
-
'with id:{node_id}:\n' \
|
|
640
|
+
f'with id:{node_id}:\n' \
|
|
591
641
|
+ json.dumps(node.to_dict(), indent = 2))
|
|
592
642
|
|
|
593
643
|
if node.id in self._id_to_node:
|
|
@@ -644,7 +694,8 @@ class AttackGraph():
|
|
|
644
694
|
attacker.name,
|
|
645
695
|
attacker_id)
|
|
646
696
|
else:
|
|
647
|
-
logger.debug('Add attacker "%s" without id.'
|
|
697
|
+
logger.debug('Add attacker "%s" without id.',
|
|
698
|
+
attacker.name)
|
|
648
699
|
|
|
649
700
|
|
|
650
701
|
attacker.id = attacker_id or self.next_attacker_id
|
|
@@ -653,7 +704,7 @@ class AttackGraph():
|
|
|
653
704
|
|
|
654
705
|
self.next_attacker_id = max(attacker.id + 1, self.next_attacker_id)
|
|
655
706
|
for node_id in reached_attack_steps:
|
|
656
|
-
node = self.get_node_by_id(
|
|
707
|
+
node = self.get_node_by_id(node_id)
|
|
657
708
|
if node:
|
|
658
709
|
attacker.compromise(node)
|
|
659
710
|
else:
|
|
@@ -3,6 +3,7 @@ MAL-Toolbox Attack Graph Node Dataclass
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from __future__ import annotations
|
|
6
|
+
import copy
|
|
6
7
|
from dataclasses import field, dataclass
|
|
7
8
|
from typing import Any, Optional
|
|
8
9
|
|
|
@@ -69,6 +70,48 @@ class AttackGraphNode:
|
|
|
69
70
|
def __repr__(self) -> str:
|
|
70
71
|
return str(self.to_dict())
|
|
71
72
|
|
|
73
|
+
def __deepcopy__(self, memo) -> AttackGraphNode:
|
|
74
|
+
"""Deep copy an attackgraph node
|
|
75
|
+
|
|
76
|
+
The deepcopy will copy over node specific information, such as type,
|
|
77
|
+
name, etc., but it will not copy attack graph relations such as
|
|
78
|
+
parents, children, or attackers it is compromised by. These references
|
|
79
|
+
should be recreated when deepcopying the attack graph itself.
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
# Check if the object is already in the memo dictionary
|
|
84
|
+
if id(self) in memo:
|
|
85
|
+
return memo[id(self)]
|
|
86
|
+
|
|
87
|
+
copied_node = AttackGraphNode(
|
|
88
|
+
self.type,
|
|
89
|
+
self.name,
|
|
90
|
+
self.ttc,
|
|
91
|
+
self.id,
|
|
92
|
+
self.asset,
|
|
93
|
+
[],
|
|
94
|
+
[],
|
|
95
|
+
self.defense_status,
|
|
96
|
+
self.existence_status,
|
|
97
|
+
self.is_viable,
|
|
98
|
+
self.is_necessary,
|
|
99
|
+
[],
|
|
100
|
+
self.mitre_info,
|
|
101
|
+
[],
|
|
102
|
+
{},
|
|
103
|
+
{}
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
copied_node.tags = copy.deepcopy(self.tags, memo)
|
|
107
|
+
copied_node.attributes = copy.deepcopy(self.attributes, memo)
|
|
108
|
+
copied_node.extras = copy.deepcopy(self.extras, memo)
|
|
109
|
+
|
|
110
|
+
# Remember that self was already copied
|
|
111
|
+
memo[id(self)] = copied_node
|
|
112
|
+
|
|
113
|
+
return copied_node
|
|
114
|
+
|
|
72
115
|
def is_compromised(self) -> bool:
|
|
73
116
|
"""
|
|
74
117
|
Return True if any attackers have compromised this node.
|
|
@@ -37,10 +37,21 @@ def is_node_traversable_by_attacker(
|
|
|
37
37
|
attacker.id
|
|
38
38
|
)
|
|
39
39
|
if not node.is_viable:
|
|
40
|
+
logger.debug(
|
|
41
|
+
'"%s"(%d) is not traversable because it is non-viable',
|
|
42
|
+
node.full_name,
|
|
43
|
+
node.id,
|
|
44
|
+
)
|
|
40
45
|
return False
|
|
41
46
|
|
|
42
47
|
match(node.type):
|
|
43
48
|
case 'or':
|
|
49
|
+
logger.debug(
|
|
50
|
+
'"%s"(%d) is traversable because it is viable and '
|
|
51
|
+
'of type "or".',
|
|
52
|
+
node.full_name,
|
|
53
|
+
node.id
|
|
54
|
+
)
|
|
44
55
|
return True
|
|
45
56
|
|
|
46
57
|
case 'and':
|
|
@@ -49,14 +60,44 @@ def is_node_traversable_by_attacker(
|
|
|
49
60
|
not parent.is_compromised_by(attacker):
|
|
50
61
|
# If the parent is not present in the attacks steps
|
|
51
62
|
# already reached and is necessary.
|
|
63
|
+
logger.debug(
|
|
64
|
+
'"%s"(%d) is not traversable because while it is '
|
|
65
|
+
'viable, and of type "and", its necessary parent '
|
|
66
|
+
'"%s(%d)" has not already been compromised.',
|
|
67
|
+
node.full_name,
|
|
68
|
+
node.id,
|
|
69
|
+
parent.full_name,
|
|
70
|
+
parent.id
|
|
71
|
+
)
|
|
52
72
|
return False
|
|
73
|
+
logger.debug(
|
|
74
|
+
'"%s"(%d) is traversable because it is viable, '
|
|
75
|
+
'of type "and", and all of its necessary parents have '
|
|
76
|
+
'already been compromised.',
|
|
77
|
+
node.full_name,
|
|
78
|
+
node.id
|
|
79
|
+
)
|
|
53
80
|
return True
|
|
54
81
|
|
|
55
|
-
case 'exist' | 'notExist' |
|
|
82
|
+
case 'exist' | 'notExist' | 'defense':
|
|
83
|
+
logger.warning(
|
|
84
|
+
'Nodes of type "exist", "notExist", and "defense" are never '
|
|
85
|
+
'marked as traversable. However, we do not normally check '
|
|
86
|
+
'if they are traversable. Node "%s"(%d) of type "%s" was '
|
|
87
|
+
'checked for traversability.',
|
|
88
|
+
node.full_name,
|
|
89
|
+
node.id,
|
|
90
|
+
node.type
|
|
91
|
+
)
|
|
56
92
|
return False
|
|
57
93
|
|
|
58
94
|
case _:
|
|
59
|
-
logger.error(
|
|
95
|
+
logger.error(
|
|
96
|
+
'Node "%s"(%d) has an unknown type "%s".',
|
|
97
|
+
node.full_name,
|
|
98
|
+
node.id,
|
|
99
|
+
node.type
|
|
100
|
+
)
|
|
60
101
|
return False
|
|
61
102
|
|
|
62
103
|
def get_attack_surface(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"""
|
|
2
2
|
MAL-Toolbox Language Classes Factory Module
|
|
3
|
+
Uses python_jsonschema_objects to generate python classes from a MAL language
|
|
3
4
|
"""
|
|
4
5
|
from __future__ import annotations
|
|
5
6
|
import json
|
|
@@ -25,7 +26,7 @@ class LanguageClassesFactory:
|
|
|
25
26
|
|
|
26
27
|
def _generate_assets(self) -> None:
|
|
27
28
|
"""
|
|
28
|
-
Generate JSON Schema for
|
|
29
|
+
Generate JSON Schema for asset types in the language specification.
|
|
29
30
|
"""
|
|
30
31
|
for asset in self.lang_graph.assets:
|
|
31
32
|
logger.debug('Creating %s asset JSON schema entry.', asset.name)
|
|
@@ -67,7 +68,7 @@ class LanguageClassesFactory:
|
|
|
67
68
|
|
|
68
69
|
def _generate_associations(self) -> None:
|
|
69
70
|
"""
|
|
70
|
-
Generate JSON Schema for
|
|
71
|
+
Generate JSON Schema for association types in the language specification.
|
|
71
72
|
"""
|
|
72
73
|
def create_association_entry(assoc: SchemaGeneratedClass):
|
|
73
74
|
logger.debug('Creating %s association JSON schema entry.', assoc.name)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mal-toolbox"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.10"
|
|
4
4
|
authors = [
|
|
5
5
|
{ name="Andrei Buhaiu", email="buhaiu@kth.se" },
|
|
6
6
|
{ name="Giuseppe Nebbione", email="nebbione@kth.se" },
|
|
@@ -53,7 +53,6 @@ filterwarnings = "ignore:invalid escape sequence"
|
|
|
53
53
|
exclude = [
|
|
54
54
|
'maltoolbox/ingestors',
|
|
55
55
|
'maltoolbox/translators',
|
|
56
|
-
'maltoolbox/language/compiler'
|
|
57
|
-
'venv'
|
|
56
|
+
'maltoolbox/language/compiler'
|
|
58
57
|
]
|
|
59
58
|
allow_redefinition = true
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|