Skip to content

Add feature to define custom properties (name/value pairs) to the TM and elements #183

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions docs/advanced_template.md
Original file line number Diff line number Diff line change
@@ -4,6 +4,17 @@

{tm.description}

{tm.props:if:

## TM Properties

Key|Value|
|:----:|:----:|
{tm.props:call:getPair:|{{item.key}}|{{item.value}}|
}

}

## Dataflow Diagram - Level 0 DFD

![](sample.png)
@@ -100,6 +111,10 @@ Description|{{item.description}}|
In Scope|{{item.inScope}}|
Type|{{item:call:getElementType}}|
Finding Count|{{item:call:getFindingCount}}|
{{item.props:if:{{item.props:call:getPair:|{{{{item.key}}}}|{{{{item.value}}}}|
}}
}}


{{item.findings:if:

9 changes: 2 additions & 7 deletions docs/basic_template.md
Original file line number Diff line number Diff line change
@@ -7,19 +7,13 @@

 

{tm.assumptions:if:

|Assumptions|
{tm.assumptions:if:|Assumptions|
|-----------|
{tm.assumptions:repeat:|{{item}}|
}

 
 
 
}


## Dataflow Diagram - Level 0 DFD

![](sample.png)
@@ -67,3 +61,4 @@ Name|Description|Classification
 
</details>
}|

131 changes: 114 additions & 17 deletions docs/pytm/index.html
Original file line number Diff line number Diff line change
@@ -1635,6 +1635,7 @@ <h3>Instance variables</h3>
required=False,
doc=&#34;Location of the source code that describes this element relative to the directory of the model script.&#34;,
)
props = varDict(dict([]), doc=&#34;Custom name/value pairs containing data about this element.&#34;)

def __init__(self, name, **kwargs):
for key, value in kwargs.items():
@@ -1758,6 +1759,22 @@ <h3>Instance variables</h3>
return True
return False


def getProperty(self, prop):
&#34;&#34;&#34;getter method to extract data from props dict and avoid KeyError exceptions&#34;&#34;&#34;

if (self.props):
try:
value = self.props[prop]
print(&#34;getProperty:value = &#34; + value)
except KeyError:
value = None
else:
value = None

return value


def _attr_values(self):
klass = self.__class__
result = {}
@@ -1929,6 +1946,22 @@ <h3>Instance variables</h3>
return self.data.get(instance, self.default)</code></pre>
</details>
</dd>
<dt id="pytm.Element.props"><code class="name">var <span class="ident">props</span></code></dt>
<dd>
<div class="desc"><p>Custom name/value pairs containing data about this element.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def __get__(self, instance, owner):
# when x.d is called we get here
# instance = x
# owner = type(x)
if instance is None:
return self
return self.data.get(instance, self.default)</code></pre>
</details>
</dd>
<dt id="pytm.Element.sourceFiles"><code class="name">var <span class="ident">sourceFiles</span></code></dt>
<dd>
<div class="desc"><p>Location of the source code that describes this element relative to the directory of the model script.</p></div>
@@ -2036,6 +2069,30 @@ <h3>Methods</h3>
return self.source.inside(*boundaries) and self.sink.inBoundary is None</code></pre>
</details>
</dd>
<dt id="pytm.Element.getProperty"><code class="name flex">
<span>def <span class="ident">getProperty</span></span>(<span>self, prop)</span>
</code></dt>
<dd>
<div class="desc"><p>getter method to extract data from props dict and avoid KeyError exceptions</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def getProperty(self, prop):
&#34;&#34;&#34;getter method to extract data from props dict and avoid KeyError exceptions&#34;&#34;&#34;

if (self.props):
try:
value = self.props[prop]
print(&#34;getProperty:value = &#34; + value)
except KeyError:
value = None
else:
value = None

return value</code></pre>
</details>
</dd>
<dt id="pytm.Element.inside"><code class="name flex">
<span>def <span class="ident">inside</span></span>(<span>self, *boundaries)</span>
</code></dt>
@@ -3548,6 +3605,7 @@ <h3>Class variables</h3>
doc=&#34;&#34;&#34;How to handle duplicate Dataflow
with same properties, except name and notes&#34;&#34;&#34;,
)
props = varDict(dict([]), doc=&#34;Custom name/value pairs containing data about the model.&#34;)
assumptions = varStrings(
[],
required=False,
@@ -3572,6 +3630,7 @@ <h3>Class variables</h3>
cls._threats = []
cls._boundaries = []
cls._data = []
cls._threatsExcluded = []

def _init_threats(self):
TM._threats = []
@@ -3606,6 +3665,9 @@ <h3>Class variables</h3>
if not t.apply(e) and t.id not in override_ids:
continue

if t.id in TM._threatsExcluded:
continue

finding_count += 1
f = Finding(e, id=str(finding_count), threat=t)
logger.debug(f&#34;new finding: {f}&#34;)
@@ -3793,15 +3855,21 @@ <h3>Class variables</h3>
threats = encode_threat_data(TM._threats)
findings = encode_threat_data(self.findings)

elements = encode_element_threat_data(TM._elements)
assets = encode_element_threat_data(TM._assets)
actors = encode_element_threat_data(TM._actors)
boundaries = encode_element_threat_data(TM._boundaries)
flows = encode_element_threat_data(TM._flows)

data = {
&#34;tm&#34;: self,
&#34;dataflows&#34;: TM._flows,
&#34;dataflows&#34;: flows,
&#34;threats&#34;: threats,
&#34;findings&#34;: findings,
&#34;elements&#34;: TM._elements,
&#34;assets&#34;: TM._assets,
&#34;actors&#34;: TM._actors,
&#34;boundaries&#34;: TM._boundaries,
&#34;elements&#34;: elements,
&#34;assets&#34;: assets,
&#34;actors&#34;: actors,
&#34;boundaries&#34;: boundaries,
&#34;data&#34;: TM._data,
}

@@ -3815,6 +3883,9 @@ <h3>Class variables</h3>
if result.debug:
logger.setLevel(logging.DEBUG)

if result.exclude is not None:
TM._threatsExcluded = result.exclude.split(&#34;,&#34;)

if result.seq is True:
print(self.seq())

@@ -3839,9 +3910,6 @@ <h3>Class variables</h3>
if result.report is not None:
print(self.report(result.report))

if result.exclude is not None:
TM._threatsExcluded = result.exclude.split(&#34;,&#34;)

if result.describe is not None:
_describe_classes(result.describe.split())

@@ -3964,7 +4032,8 @@ <h3>Static methods</h3>
cls._assets = []
cls._threats = []
cls._boundaries = []
cls._data = []</code></pre>
cls._data = []
cls._threatsExcluded = []</code></pre>
</details>
</dd>
</dl>
@@ -4099,6 +4168,22 @@ <h3>Instance variables</h3>
return self.data.get(instance, self.default)</code></pre>
</details>
</dd>
<dt id="pytm.TM.props"><code class="name">var <span class="ident">props</span></code></dt>
<dd>
<div class="desc"><p>Custom name/value pairs containing data about the model.</p></div>
<details class="source">
<summary>
<span>Expand source code</span>
</summary>
<pre><code class="python">def __get__(self, instance, owner):
# when x.d is called we get here
# instance = x
# owner = type(x)
if instance is None:
return self
return self.data.get(instance, self.default)</code></pre>
</details>
</dd>
<dt id="pytm.TM.threatsFile"><code class="name">var <span class="ident">threatsFile</span></code></dt>
<dd>
<div class="desc"><p>JSON file with custom threats</p></div>
@@ -4155,6 +4240,9 @@ <h3>Methods</h3>
if result.debug:
logger.setLevel(logging.DEBUG)

if result.exclude is not None:
TM._threatsExcluded = result.exclude.split(&#34;,&#34;)

if result.seq is True:
print(self.seq())

@@ -4179,9 +4267,6 @@ <h3>Methods</h3>
if result.report is not None:
print(self.report(result.report))

if result.exclude is not None:
TM._threatsExcluded = result.exclude.split(&#34;,&#34;)

if result.describe is not None:
_describe_classes(result.describe.split())

@@ -4211,15 +4296,21 @@ <h3>Methods</h3>
threats = encode_threat_data(TM._threats)
findings = encode_threat_data(self.findings)

elements = encode_element_threat_data(TM._elements)
assets = encode_element_threat_data(TM._assets)
actors = encode_element_threat_data(TM._actors)
boundaries = encode_element_threat_data(TM._boundaries)
flows = encode_element_threat_data(TM._flows)

data = {
&#34;tm&#34;: self,
&#34;dataflows&#34;: TM._flows,
&#34;dataflows&#34;: flows,
&#34;threats&#34;: threats,
&#34;findings&#34;: findings,
&#34;elements&#34;: TM._elements,
&#34;assets&#34;: TM._assets,
&#34;actors&#34;: TM._actors,
&#34;boundaries&#34;: TM._boundaries,
&#34;elements&#34;: elements,
&#34;assets&#34;: assets,
&#34;actors&#34;: actors,
&#34;boundaries&#34;: boundaries,
&#34;data&#34;: TM._data,
}

@@ -4257,6 +4348,9 @@ <h3>Methods</h3>
if not t.apply(e) and t.id not in override_ids:
continue

if t.id in TM._threatsExcluded:
continue

finding_count += 1
f = Finding(e, id=str(finding_count), threat=t)
logger.debug(f&#34;new finding: {f}&#34;)
@@ -4670,6 +4764,7 @@ <h4><code><a title="pytm.Element" href="#pytm.Element">Element</a></code></h4>
<li><code><a title="pytm.Element.enters" href="#pytm.Element.enters">enters</a></code></li>
<li><code><a title="pytm.Element.exits" href="#pytm.Element.exits">exits</a></code></li>
<li><code><a title="pytm.Element.findings" href="#pytm.Element.findings">findings</a></code></li>
<li><code><a title="pytm.Element.getProperty" href="#pytm.Element.getProperty">getProperty</a></code></li>
<li><code><a title="pytm.Element.inBoundary" href="#pytm.Element.inBoundary">inBoundary</a></code></li>
<li><code><a title="pytm.Element.inScope" href="#pytm.Element.inScope">inScope</a></code></li>
<li><code><a title="pytm.Element.inside" href="#pytm.Element.inside">inside</a></code></li>
@@ -4679,6 +4774,7 @@ <h4><code><a title="pytm.Element" href="#pytm.Element">Element</a></code></h4>
<li><code><a title="pytm.Element.name" href="#pytm.Element.name">name</a></code></li>
<li><code><a title="pytm.Element.oneOf" href="#pytm.Element.oneOf">oneOf</a></code></li>
<li><code><a title="pytm.Element.overrides" href="#pytm.Element.overrides">overrides</a></code></li>
<li><code><a title="pytm.Element.props" href="#pytm.Element.props">props</a></code></li>
<li><code><a title="pytm.Element.sourceFiles" href="#pytm.Element.sourceFiles">sourceFiles</a></code></li>
</ul>
</li>
@@ -4807,6 +4903,7 @@ <h4><code><a title="pytm.TM" href="#pytm.TM">TM</a></code></h4>
<li><code><a title="pytm.TM.name" href="#pytm.TM.name">name</a></code></li>
<li><code><a title="pytm.TM.onDuplicates" href="#pytm.TM.onDuplicates">onDuplicates</a></code></li>
<li><code><a title="pytm.TM.process" href="#pytm.TM.process">process</a></code></li>
<li><code><a title="pytm.TM.props" href="#pytm.TM.props">props</a></code></li>
<li><code><a title="pytm.TM.report" href="#pytm.TM.report">report</a></code></li>
<li><code><a title="pytm.TM.reset" href="#pytm.TM.reset">reset</a></code></li>
<li><code><a title="pytm.TM.resolve" href="#pytm.TM.resolve">resolve</a></code></li>
27 changes: 27 additions & 0 deletions pytm/pytm.py
Original file line number Diff line number Diff line change
@@ -217,6 +217,13 @@ def __set__(self, instance, value):
super().__set__(instance, DataSet(value))


class varDict(var):
def __set__(self, instance, value):
if not isinstance(value, dict):
raise ValueError("expecting a dict, got a {}".format(type(value)))
super().__set__(instance, value)


class DataSet(set):
def __contains__(self, item):
if isinstance(item, str):
@@ -746,11 +753,13 @@ class TM:
doc="""How to handle duplicate Dataflow
with same properties, except name and notes""",
)
props = varDict(dict([]), doc="Custom name/value pairs containing data about the model.")
assumptions = varStrings(
[],
required=False,
doc="A list of assumptions about the design/model.",
)
custom_properties = dict([])

def __init__(self, name, **kwargs):
for key, value in kwargs.items():
@@ -1294,6 +1303,7 @@ class Element:
required=False,
doc="Location of the source code that describes this element relative to the directory of the model script.",
)
props = varDict(dict([]), doc="Custom name/value pairs containing data about this element.")
controls = varControls(None)

def __init__(self, name, **kwargs):
@@ -1419,6 +1429,22 @@ def inside(self, *boundaries):
return True
return False


def getProperty(self, prop):
"""getter method to extract data from props dict and avoid KeyError exceptions"""

if (self.props):
try:
value = self.props[prop]
print("getProperty:value = " + value)
except KeyError:
value = None
else:
value = None

return value


def _attr_values(self):
klass = self.__class__
result = {}
@@ -1868,6 +1894,7 @@ def serialize(obj, nested=False):
elif (
not nested
and not isinstance(value, str)
and not isinstance(value, dict)
and isinstance(value, Iterable)
):
value = [v.id if isinstance(v, Finding) else v.name for v in value]
Loading