Skip to content

Commit 7cabf89

Browse files
authored
Merge pull request #148 from izar/data_object_from_dataflow
Create full Data object from Dataflow.data string
2 parents f7b5d5f + a0671d8 commit 7cabf89

8 files changed

Lines changed: 121 additions & 61 deletions

File tree

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -155,20 +155,20 @@ my_lambda_to_db.dstPort = 3306
155155
user_to_web = Dataflow(user, web, "User enters comments (*)")
156156
user_to_web.protocol = "HTTP"
157157
user_to_web.dstPort = 80
158-
user_to_web.data = 'Comments in HTML or Markdown'
158+
user_to_web.data = Data('Comments in HTML or Markdown', classification=Classification.PUBLIC)
159159

160160
web_to_user = Dataflow(web, user, "Comments saved (*)")
161161
web_to_user.protocol = "HTTP"
162-
web_to_user.data = 'Ack of saving or error message, in JSON'
163162

164163
web_to_db = Dataflow(web, db, "Insert query with comments")
165164
web_to_db.protocol = "MySQL"
166165
web_to_db.dstPort = 3306
167-
web_to_db.data = 'MySQL insert statement, all literals'
168-
166+
# this is a BAD way of defining a data object, here for a demo on how it
167+
# will appear on the sample report. Use Data objects.
168+
db_to_web.data = 'Results of insert op'
169169
db_to_web = Dataflow(db, web, "Comments contents")
170170
db_to_web.protocol = "MySQL"
171-
db_to_web.data = 'Results of insert op'
171+
172172

173173
tm.process()
174174

pytm/pytm.py

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -187,13 +187,23 @@ def __set__(self, instance, value):
187187
class varData(var):
188188
def __set__(self, instance, value):
189189
if isinstance(value, str):
190-
value = [Data(value)]
190+
value = [
191+
Data(
192+
name="undefined",
193+
description=value,
194+
classification=Classification.UNKNOWN,
195+
)
196+
]
197+
sys.stderr.write(
198+
f"FIXME: a dataflow is using a string as the Data attribute. This has been deprecated and Data objects should be created instead.\n"
199+
)
200+
191201
if not isinstance(value, Iterable):
192202
value = [value]
193203
for i, e in enumerate(value):
194204
if not isinstance(e, Data):
195205
raise ValueError(
196-
"expecting a list of Data, item number {} is a {}".format(
206+
"expecting a list of pytm.Data, item number {} is a {}".format(
197207
i, type(e)
198208
)
199209
)
@@ -387,7 +397,9 @@ def _apply_defaults(flows, data):
387397
try:
388398
e.overrides = e.sink.overrides
389399
e.overrides.extend(
390-
f for f in e.source.overrides if f.threat_id not in (f.threat_id for f in e.overrides)
400+
f
401+
for f in e.source.overrides
402+
if f.threat_id not in (f.threat_id for f in e.overrides)
391403
)
392404
except ValueError:
393405
pass
@@ -712,7 +724,7 @@ def _add_threats(self):
712724
TM._threats.append(Threat(**i))
713725

714726
def resolve(self):
715-
finding_count = 0;
727+
finding_count = 0
716728
findings = []
717729
elements = defaultdict(list)
718730
for e in TM._elements:
@@ -723,7 +735,9 @@ def resolve(self):
723735
# if element is a dataflow filter out overrides from source and sink
724736
# because they will be always applied there anyway
725737
try:
726-
override_ids -= set(f.threat_id for f in e.source.overrides + e.sink.overrides)
738+
override_ids -= set(
739+
f.threat_id for f in e.source.overrides + e.sink.overrides
740+
)
727741
except AttributeError:
728742
pass
729743

@@ -914,7 +928,6 @@ def report(self, template_path):
914928
with open(template_path) as file:
915929
template = file.read()
916930

917-
918931
threats = encode_threat_data(TM._threats)
919932
findings = encode_threat_data(self.findings)
920933

@@ -1243,8 +1256,9 @@ class Data:
12431256

12441257
name = varString("", required=True)
12451258
description = varString("")
1259+
format = varString("")
12461260
classification = varClassification(
1247-
Classification.PUBLIC,
1261+
Classification.UNKNOWN,
12481262
required=True,
12491263
doc="Level of classification for this piece of data",
12501264
)
@@ -1310,7 +1324,7 @@ class Asset(Element):
13101324
port = varInt(-1, doc="Default TCP port for incoming data flows")
13111325
isEncrypted = varBool(False, doc="Requires incoming data flow to be encrypted")
13121326
protocol = varString("", doc="Default network protocol for incoming data flows")
1313-
data = varData([], doc="Default type of data in incoming data flows")
1327+
data = varData([], doc="pytm.Data object(s) in incoming data flows")
13141328
inputs = varElements([], doc="incoming Dataflows")
13151329
outputs = varElements([], doc="outgoing Dataflows")
13161330
onAWS = varBool(False)
@@ -1354,6 +1368,7 @@ def __init__(self, name, **kwargs):
13541368
super().__init__(name, **kwargs)
13551369
TM._assets.append(self)
13561370

1371+
13571372
class Lambda(Asset):
13581373
"""A lambda function running in a Function-as-a-Service (FaaS) environment"""
13591374

@@ -1512,7 +1527,7 @@ class Actor(Element):
15121527

15131528
port = varInt(-1, doc="Default TCP port for outgoing data flows")
15141529
protocol = varString("", doc="Default network protocol for outgoing data flows")
1515-
data = varData([], doc="Default type of data in outgoing data flows")
1530+
data = varData([], doc="pytm.Data object(s) in outgoing data flows")
15161531
inputs = varElements([], doc="incoming Dataflows")
15171532
outputs = varElements([], doc="outgoing Dataflows")
15181533
authenticatesDestination = varBool(
@@ -1607,7 +1622,7 @@ class Dataflow(Element):
16071622
doc="TLS version used.",
16081623
)
16091624
protocol = varString("", doc="Protocol used in this data flow")
1610-
data = varData([], doc="Default type of data in incoming data flows")
1625+
data = varData([], doc="pytm.Data object(s) in incoming data flows")
16111626
authenticatesDestination = varBool(
16121627
False,
16131628
doc="""Verifies the identity of the destination,
@@ -1792,34 +1807,35 @@ def serialize(obj, nested=False):
17921807
result[i.lstrip("_")] = value
17931808
return result
17941809

1810+
17951811
def encode_threat_data(obj):
17961812
"""Used to html encode threat data from a list of threats or findings"""
17971813
encoded_threat_data = []
17981814

17991815
attrs = [
1800-
"description",
1801-
"details",
1802-
"severity",
1803-
"mitigations",
1804-
"example",
1805-
"id",
1806-
"target",
1807-
"references",
1808-
"condition",
1816+
"description",
1817+
"details",
1818+
"severity",
1819+
"mitigations",
1820+
"example",
1821+
"id",
1822+
"target",
1823+
"references",
1824+
"condition",
18091825
]
18101826

18111827
for e in obj:
18121828
t = copy.deepcopy(e)
18131829

1814-
if (isinstance(t, Finding)):
1830+
if isinstance(t, Finding):
18151831
attrs.append("threat_id")
18161832

18171833
for a in attrs:
18181834
v = getattr(e, a)
18191835

1820-
if (isinstance(v, int)):
1836+
if isinstance(v, int):
18211837
t._safeset(a, v)
1822-
elif (isinstance(v, tuple)):
1838+
elif isinstance(v, tuple):
18231839
t._safeset(a, v)
18241840
else:
18251841
t._safeset(a, html.escape(v))
@@ -1828,6 +1844,7 @@ def encode_threat_data(obj):
18281844

18291845
return encoded_threat_data
18301846

1847+
18311848
def get_args():
18321849
_parser = argparse.ArgumentParser()
18331850

0 commit comments

Comments
 (0)