Skip to content

Commit

Permalink
Merge pull request #155 from izar/update_template
Browse files Browse the repository at this point in the history
TemplateEngine improvements, updated template.md
  • Loading branch information
izar authored Oct 31, 2021
2 parents 755318b + 598df02 commit f13629c
Show file tree
Hide file tree
Showing 10 changed files with 285 additions and 11 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ plantuml.jar
tm/
/sqldump
/tests/output_current.json
/tests/output_current.md
/tests/1.txt
/tests/0.txt
/tests/.config.pytm
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ The `tm.py` is an example model. You can run it to generate the report and diagr

```
mkdir -p tm
./tm.py --report docs/template.md | pandoc -f markdown -t html > tm/report.html
./tm.py --report docs/basic_template.md | pandoc -f markdown -t html > tm/report.html
./tm.py --dfd | dot -Tpng -o tm/dfd.png
./tm.py --seq | java -Djava.awt.headless=true -jar $PLANTUML_PATH -tpng -pipe > tm/seq.png
```
Expand Down Expand Up @@ -213,7 +213,7 @@ The diagrams and findings can be included in the template to create a final repo

```bash

tm.py --report docs/template.md | pandoc -f markdown -t html > report.html
tm.py --report docs/basic_template.md | pandoc -f markdown -t html > report.html

```
The templating format used in the report template is very simple:
Expand Down
10 changes: 7 additions & 3 deletions docs/Stylesheet.css
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ hr {
opacity: .5;
}
table {
margin: .75rem 0;
padding: 0;
margin: .75rem 0 0 1rem;
padding: 0;
width: 50%;
text-align: left;
white-space: nowrap;
Expand Down Expand Up @@ -76,4 +76,8 @@ table tr td {
text-align: left;
border: 1px solid #ccc;
}
/* @end */

details {
margin-left: 2rem
}
/* @end */
160 changes: 160 additions & 0 deletions docs/advanced_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
<link href="docs/Stylesheet.css" rel="stylesheet"></link>

## System Description

{tm.description}

## Dataflow Diagram - Level 0 DFD

![](sample.png)

&nbsp;

## Dataflows

Name|From|To |Data|Protocol|Port
|:----:|:----:|:---:|:----:|:--------:|:----:|
{dataflows:repeat:|{{item.display_name:call:}}|{{item.source.name}}|{{item.sink.name}}|{{item.data}}|{{item.protocol}}|{{item.dstPort}}|
}

## Data Dictionary

Name|Description|Classification|Carried|Processed
|:----:|:--------:|:----:|:----|:----|
{data:repeat:|{{item.name}}|{{item.description}}|{{item.classification.name}}|{{item.carriedBy:repeat:{{{{item.name}}}}<br>}}|{{item.processedBy:repeat:{{{{item.name}}}}<br>}}|
}

## Actors

{actors:repeat:
Name|{{item.name}}
|:----|:----|
Description|{{item.description}}|
Is Admin|{{item.isAdmin}}
Finding Count|{{item:call:getFindingCount}}|

{{item.findings:if:

**Threats**

{{item.findings:repeat:
<details>
<summary> {{{{item.id}}}} -- {{{{item.threat_id}}}} -- {{{{item.description}}}}</summary>
<h6> Targeted Element </h6>
<p> {{{{item.target}}}} </p>
<h6> Severity </h6>
<p>{{{{item.severity}}}}</p>
<h6>Example Instances</h6>
<p>{{{{item.example}}}}</p>
<h6>Mitigations</h6>
<p>{{{{item.mitigations}}}}</p>
<h6>References</h6>
<p>{{{{item.references}}}}</p>
&emsp;
</details>
}}
}}
}

## Boundaries

{boundaries:repeat:
Name|{{item.name}}
|:----|:----|
Description|{{item.description}}|
In Scope|{{item.inScope}}|
Immediate Parent|{{item.parents:if:{{item:call:getParentName}}}}{{item.parents:not:N/A, primary boundary}}|
All Parents|{{item.parents:call:{{{{item.display_name:call:}}}}, }}|
Classification|{{item.maxClassification}}|
Finding Count|{{item:call:getFindingCount}}|

{{item.findings:if:

**Threats**

{{item.findings:repeat:
<details>
<summary> {{{{item.id}}}} -- {{{{item.threat_id}}}} -- {{{{item.description}}}}</summary>
<h6> Targeted Element </h6>
<p> {{{{item.target}}}} </p>
<h6> Severity </h6>
<p>{{{{item.severity}}}}</p>
<h6>Example Instances</h6>
<p>{{{{item.example}}}}</p>
<h6>Mitigations</h6>
<p>{{{{item.mitigations}}}}</p>
<h6>References</h6>
<p>{{{{item.references}}}}</p>
&emsp;
</details>
}}
}}
}

## Assets

{assets:repeat:
Name|{{item.name}}|
|:----|:----|
Description|{{item.description}}|
In Scope|{{item.inScope}}|
Type|{{item:call:getElementType}}|
Finding Count|{{item:call:getFindingCount}}|

{{item.findings:if:

**Threats**

{{item.findings:repeat:
<details>
<summary> {{{{item.id}}}} -- {{{{item.threat_id}}}} -- {{{{item.description}}}}</summary>
<h6> Targeted Element </h6>
<p> {{{{item.target}}}} </p>
<h6> Severity </h6>
<p>{{{{item.severity}}}}</p>
<h6>Example Instances</h6>
<p>{{{{item.example}}}}</p>
<h6>Mitigations</h6>
<p>{{{{item.mitigations}}}}</p>
<h6>References</h6>
<p>{{{{item.references}}}}</p>
&nbsp;
</details>
}}
}}
}

## Data Flows

{dataflows:repeat:
Name|{{item.name}}
|:----|:----|
Description|{{item.description}}|
Sink|{{item.sink}}|
Source|{{item.source}}|
Is Response|{{item.isResponse}}|
In Scope|{{item.inScope}}|
Finding Count|{{item:call:getFindingCount}}|

{{item.findings:if:

**Threats**

{{item.findings:repeat:
<details>
<summary> {{{{item.id}}}} -- {{{{item.threat_id}}}} -- {{{{item.description}}}}</summary>
<h6> Targeted Element </h6>
<p> {{{{item.target}}}} </p>
<h6> Severity </h6>
<p>{{{{item.severity}}}}</p>
<h6>Example Instances</h6>
<p>{{{{item.example}}}}</p>
<h6>Mitigations</h6>
<p>{{{{item.mitigations}}}}</p>
<h6>References</h6>
<p>{{{{item.references}}}}</p>
&emsp;
</details>
}}
}}
}
2 changes: 1 addition & 1 deletion docs/template.md → docs/basic_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Name|Description|Classification

|{findings:repeat:
<details>
<summary> {{item.id}} -- {{item.description}}</summary>
<summary> {{item.threat_id}} -- {{item.description}}</summary>
<h6> Targeted Element </h6>
<p> {{item.target}} </p>
<h6> Severity </h6>
Expand Down
39 changes: 39 additions & 0 deletions pytm/report_util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

class ReportUtils:
@staticmethod
def getParentName(element):
from pytm import Boundary
if (isinstance(element, Boundary)):
parent = element.inBoundary
if (parent is not None):
return parent.name
else:
return str("")
else:
return "ERROR: getParentName method is not valid for " + element.__class__.__name__


@staticmethod
def getNamesOfParents(element):
from pytm import Boundary
if (isinstance(element, Boundary)):
parents = [p.name for p in element.parents()]
return parents
else:
return "ERROR: getNamesOfParents method is not valid for " + element.__class__.__name__

@staticmethod
def getFindingCount(element):
from pytm import Element
if (isinstance(element, Element)):
return str(len(list(element.findings)))
else:
return "ERROR: getFindingCount method is not valid for " + element.__class__.__name__

@staticmethod
def getElementType(element):
from pytm import Element
if (isinstance(element, Element)):
return str(element.__class__.__name__)
else:
return "ERROR: getElementType method is not valid for " + element.__class__.__name__
73 changes: 69 additions & 4 deletions pytm/template_engine.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# shamelessly lifted from https://makina-corpus.com/blog/metier/2016/the-worlds-simplest-python-template-engine
# but modified to include support to call methods which return lists, to call external utility methods, use
# if operator with methods and added a not operator.

import string

Expand All @@ -7,14 +9,77 @@ class SuperFormatter(string.Formatter):
"""World's simplest Template engine."""

def format_field(self, value, spec):

spec_parts = spec.split(":")
if spec.startswith("repeat"):
# Example usage, format, count of spec_parts, exampple format
# object:repeat:template 2 {item.findings:repeat:{{item.id}}, }

template = spec.partition(":")[-1]
if type(value) is dict:
value = value.items()
return "".join([self.format(template, item=item) for item in value])
elif spec == "call":
return value()
elif spec.startswith("if"):
return (value and spec.partition(":")[-1]) or ""

elif spec.startswith("call:") and hasattr(value, "__call__"):
# Example usage, format, exampple format
# methood:call {item.display_name:call:}
# methood:call:template {item.parents:call:{{item.name}}, }
result = value()

if type(result) is list:
template = spec.partition(":")[-1]
return "".join([self.format(template, item=item) for item in result])

return result

elif spec.startswith("call:"):
# Example usage, format, exampple format
# object:call:method_name {item:call:getFindingCount}
# object:call:method_name:template {item:call:getNamesOfParents:
# {{item}}
# }

method_name = spec_parts[1]

result = self.call_util_method(method_name, value)

if type(result) is list:
template = spec.partition(":")[-1]
template = template.partition(":")[-1]
return "".join([self.format(template, item=item) for item in result])

return result

elif (spec.startswith("if") or spec.startswith("not")):
# Example usage, format, exampple format
# object.bool:if:template {item.inScope:if:Is in scope}
# object:if:template {item.findings:if:Has Findings}
# object.method:if:template {item.parents:if:Has Parents}
#
# object.bool:not:template {item.inScope:not:Is not in scope}
# object:not:template {item.findings:not:Has No Findings}
# object.method:not:template {item.parents:not:Has No Parents}

template = spec.partition(":")[-1]
if (hasattr(value, "__call__")):
result = value()
else:
result = value

if (spec.startswith("if")):
return (result and template or "")
else:
return (not result and template or "")

else:
return super(SuperFormatter, self).format_field(value, spec)

def call_util_method(self, method_name, object):
module_name = "pytm.report_util"
klass_name = "ReportUtils"
module = __import__(module_name, fromlist=['ReportUtils'])
klass = getattr(module, klass_name)
method = getattr(klass, method_name)

result = method(object)
return result
Binary file modified sample.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 4 additions & 1 deletion tests/test_pytmfunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,10 @@ def test_report(self):
Dataflow(worker, db, "Query for tasks")

self.assertTrue(tm.check())
output = tm.report("docs/template.md")
output = tm.report("docs/basic_template.md")

with open(os.path.join(dir_path, "output_current.md"), "w") as x:
x.write(output)

with open(os.path.join(dir_path, "output_current.md"), "w") as x:
x.write(output)
Expand Down
2 changes: 2 additions & 0 deletions tm.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
]

internet = Boundary("Internet")

server_db = Boundary("Server/DB")
server_db.levels = [2]

vpc = Boundary("AWS VPC")

user = Actor("User")
Expand Down

0 comments on commit f13629c

Please sign in to comment.