Skip to content
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

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

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
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
Expand Up @@ -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)
Expand Down Expand Up @@ -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:

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

 

{tm.assumptions:if:

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

 
 
 
}


## Dataflow Diagram - Level 0 DFD

![](sample.png)
Expand Down Expand Up @@ -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
Expand Up @@ -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():
Expand Down Expand Up @@ -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 = {}
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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,
Expand All @@ -3572,6 +3630,7 @@ <h3>Class variables</h3>
cls._threats = []
cls._boundaries = []
cls._data = []
cls._threatsExcluded = []

def _init_threats(self):
TM._threats = []
Expand Down Expand Up @@ -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;)
Expand Down Expand Up @@ -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,
}

Expand All @@ -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())

Expand All @@ -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())

Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand Down Expand Up @@ -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())

Expand All @@ -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())

Expand Down Expand Up @@ -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,
}

Expand Down Expand Up @@ -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;)
Expand Down Expand Up @@ -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>
Expand All @@ -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>
Expand Down Expand Up @@ -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>
Expand Down
27 changes: 27 additions & 0 deletions pytm/pytm.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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():
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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 = {}
Expand Down Expand Up @@ -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]
Expand Down
Loading