From b794ddf277b8a1d0525f6c326d5f2c95ab3c9283 Mon Sep 17 00:00:00 2001 From: Nick Ozmore Date: Wed, 7 Apr 2021 10:12:59 -0400 Subject: [PATCH 1/8] Add sections for Actors and Assets, add finding_id to Findings --- docs/template.md | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/docs/template.md b/docs/template.md index 55a7a03..9ff7fae 100644 --- a/docs/template.md +++ b/docs/template.md @@ -31,14 +31,35 @@ Name|Description|Classification   +## Actors +  + +Name|Description|Classification +|:----:|:--------:|:----:| +{actors:repeat:|{{item.name}}|{{item.description}}|{{item.isAdmin}}| +} + +  + +## Assets +  + +Name|Description|Classification +|:----:|:--------:|:----:| +{assets:repeat:|{{item.name}}|{{item.description}}|{{item.__class__.__name__}}| +} + +  + + ## Potential Threats     -|{findings:repeat: +{findings:repeat:
- {{item.id}} -- {{item.description}} + {{item.id}} -- {{item.threat_id}} -- {{item.description}}
Targeted Element

{{item.target}}

Severity
From a9c060a6f28b26ee3daba9243fb5b6d4d04e92fc Mon Sep 17 00:00:00 2001 From: Nick Ozmore Date: Wed, 7 Apr 2021 17:57:55 -0400 Subject: [PATCH 2/8] Template Reporting Improvements --- docs/template.md | 106 +++++++++++++++++++++++++++++++--------- pytm/report_util.py | 33 +++++++++++++ pytm/template_engine.py | 29 ++++++++++- tm.py | 10 ++++ 4 files changed, 154 insertions(+), 24 deletions(-) create mode 100644 pytm/report_util.py diff --git a/docs/template.md b/docs/template.md index 9ff7fae..86e9a12 100644 --- a/docs/template.md +++ b/docs/template.md @@ -21,12 +21,15 @@ Name|From|To |Data|Protocol|Port {dataflows:repeat:|{{item.name}}|{{item.source.name}}|{{item.sink.name}}|{{item.data}}|{{item.protocol}}|{{item.dstPort}}| } +
+
+ ## Data Dictionary   -Name|Description|Classification -|:----:|:--------:|:----:| -{data:repeat:|{{item.name}}|{{item.description}}|{{item.classification.name}}| +Name|Description|Classification|Carried|Processed +|:----:|:--------:|:----:|:----|:----| +{data:repeat:|{{item.name}}|{{item.description}}|{{item.classification.name}}|{{item.carriedBy:repeat:{{{{item.name}}}}
}}|{{item.processedBy:repeat:{{{{item.name}}}}
}}| }   @@ -34,44 +37,103 @@ Name|Description|Classification ## Actors   -Name|Description|Classification +Name|Description|isAdmin |:----:|:--------:|:----:| {actors:repeat:|{{item.name}}|{{item.description}}|{{item.isAdmin}}| } -  +
+
-## Assets -  +## Boundaries + +{boundaries:repeat: +Element|{{item.name}} +|:----|:----| +Description|{{item.description}}| +InScope|{{item.inScope}}| +Parent|{{item:utils:getParentName}}| +Parents|{{item.parents:call:{{{{item.name}}}}, }}| +Classification|{{item.maxClassification}}| + +
+
-Name|Description|Classification -|:----:|:--------:|:----:| -{assets:repeat:|{{item.name}}|{{item.description}}|{{item.__class__.__name__}}| } -  +## Assets +{assets:repeat: -## Potential Threats +
+
-  -  +Element|{{item.name}} +|:----|:----| +Description|{{item.description}}| +InScope|{{item.inScope}}| +Type|{{item.__class__.__name__}}| +Finding Count|{{item:utils:countFindings}}| + +###### Threats -{findings:repeat: +{{item.findings:repeat:
- {{item.id}} -- {{item.threat_id}} -- {{item.description}} + {{{{item.id}}}} -- {{{{item.threat_id}}}} -- {{{{item.description}}}}
Targeted Element
-

{{item.target}}

+

{{{{item.target}}}}

Severity
-

{{item.severity}}

+

{{{{item.severity}}}}

Example Instances
-

{{item.example}}

+

{{{{item.example}}}}

Mitigations
-

{{item.mitigations}}

+

{{{{item.mitigations}}}}

References
-

{{item.references}}

+

{{{{item.references}}}}

     
-}| +}} +} + +
+
+ +## Data Flows + +{dataflows:repeat(e): + +
+
+ +Dataflow|{{item.name}} +|:----|:----| +Description|{{item.description}}| +InScope|{{item.inScope}}| +Finding Count|{{item:utils:countFindings}}| + +###### Threats + +{{item.findings:repeat: +
+ {{{{item.id}}}} -- {{{{item.threat_id}}}} -- {{{{item.description}}}} +
Targeted Element
+

{{{{item.target}}}}

+
Severity
+

{{{{item.severity}}}}

+
Example Instances
+

{{{{item.example}}}}

+
Mitigations
+

{{{{item.mitigations}}}}

+
References
+

{{{{item.references}}}}

+   +   +   +
+}} + +} + +  diff --git a/pytm/report_util.py b/pytm/report_util.py new file mode 100644 index 0000000..8de6f7e --- /dev/null +++ b/pytm/report_util.py @@ -0,0 +1,33 @@ + +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 printParents(element): + from pytm import Boundary + if (isinstance(element, Boundary)): + parents = map(lambda b: b.name, element.parents()) + return list(parents) + else: + return "ERROR: printParents method is not valid for " + element.__class__.__name__ + + @staticmethod + def countFindings(element): + from pytm import Element + if (isinstance(element, Element)): + return str(len(list(element.findings))) + else: + return "ERROR: countFindings method is not valid for " + element.___class___.___name___ + + \ No newline at end of file diff --git a/pytm/template_engine.py b/pytm/template_engine.py index 916939f..1f2a712 100644 --- a/pytm/template_engine.py +++ b/pytm/template_engine.py @@ -12,8 +12,33 @@ def format_field(self, value, spec): 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("call"): + 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("utils"): + + spec_parts = spec.split(":") + + method_name = spec_parts[1] + template = spec_parts[-1] + + 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(value) + + if type(result) is list: + return "".join([self.format(template, item=item) for item in result]) + + return result + elif spec.startswith("if"): return (value and spec.partition(":")[-1]) or "" else: diff --git a/tm.py b/tm.py index 7dc3270..b756d7e 100755 --- a/tm.py +++ b/tm.py @@ -17,10 +17,20 @@ tm.isOrdered = True tm.mergeResponses = True +all = Boundary("TM Boundary") internet = Boundary("Internet") +internet.inBoundary = all + +company = Boundary("Company") +company.inBoundary = all + server_db = Boundary("Server/DB") server_db.levels = [2] +server_db.inBoundary = company + vpc = Boundary("AWS VPC") +vpc.inBoundary = all + user = Actor("User") user.inBoundary = internet From 8cfa40a420703f5d9abb7a544e853f3b84f41d53 Mon Sep 17 00:00:00 2001 From: Nick Ozmore Date: Wed, 7 Apr 2021 18:21:28 -0400 Subject: [PATCH 3/8] updated tests --- .gitignore | 1 + tests/output.md | 212 +++++++++++++++++++++++++++++++++++++++-- tests/test_pytmfunc.py | 3 + 3 files changed, 209 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 24bf114..f67268c 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/tests/output.md b/tests/output.md index 23805a0..5529787 100644 --- a/tests/output.md +++ b/tests/output.md @@ -26,19 +26,217 @@ Name|From|To |Data|Protocol|Port |Query for tasks|Task queue worker|SQL Database|[]||-1| +
+
+ ## Data Dictionary   -Name|Description|Classification -|:----:|:--------:|:----:| -|auth cookie||PUBLIC| +Name|Description|Classification|Carried|Processed +|:----:|:--------:|:----:|:----|:----| +|auth cookie||PUBLIC|User enters comments (*)
|User
Web Server
|   -## Potential Threats - -  +## Actors   -|| +Name|Description|isAdmin +|:----:|:--------:|:----:| +|User||False| + + +
+
+ +## Boundaries + + +Element|Internet +|:----|:----| +Description|| +InScope|True| +Parent|| +Parents|| +Classification|Classification.UNKNOWN| + +
+
+ + +Element|Server/DB +|:----|:----| +Description|| +InScope|True| +Parent|| +Parents|| +Classification|Classification.UNKNOWN| + +
+
+ + + +## Assets + + + +
+
+ +Element|Web Server +|:----|:----| +Description|| +InScope|True| +Type|Server| +Finding Count|0| + +###### Threats + + + + +
+
+ +Element|Lambda func +|:----|:----| +Description|| +InScope|True| +Type|Lambda| +Finding Count|0| + +###### Threats + + + + +
+
+ +Element|Task queue worker +|:----|:----| +Description|| +InScope|True| +Type|Process| +Finding Count|0| + +###### Threats + + + + +
+
+ +Element|SQL Database +|:----|:----| +Description|| +InScope|True| +Type|Datastore| +Finding Count|0| + +###### Threats + + + + +
+
+ +## Data Flows + + + +
+
+ +Dataflow|User enters comments (*) +|:----|:----| +Description|| +InScope|True| +Finding Count|0| + +###### Threats + + + + + +
+
+ +Dataflow|Insert query with comments +|:----|:----| +Description|| +InScope|True| +Finding Count|0| + +###### Threats + + + + + +
+
+ +Dataflow|Call func +|:----|:----| +Description|| +InScope|True| +Finding Count|0| + +###### Threats + + + + + +
+
+ +Dataflow|Retrieve comments +|:----|:----| +Description|| +InScope|True| +Finding Count|0| + +###### Threats + + + + + +
+
+ +Dataflow|Show comments (*) +|:----|:----| +Description|| +InScope|True| +Finding Count|0| + +###### Threats + + + + + +
+
+ +Dataflow|Query for tasks +|:----|:----| +Description|| +InScope|True| +Finding Count|0| + +###### Threats + + + + + +  diff --git a/tests/test_pytmfunc.py b/tests/test_pytmfunc.py index d6f7fa5..e43b058 100644 --- a/tests/test_pytmfunc.py +++ b/tests/test_pytmfunc.py @@ -354,6 +354,9 @@ def test_report(self): self.assertTrue(tm.check()) output = tm.report("docs/template.md") + with open(os.path.join(dir_path, "output_current.md"), "w") as x: + x.write(output) + self.maxDiff = None self.assertEqual(output.strip(), expected.strip()) From ae607f37aab224af5f6d466b2bdf0394484f5dc2 Mon Sep 17 00:00:00 2001 From: Nick Ozmore Date: Thu, 8 Apr 2021 10:08:54 -0400 Subject: [PATCH 4/8] Changed util method names, added getElementType method, and bug fix on error message --- docs/template.md | 6 +++--- pytm/report_util.py | 17 +++++++++++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/docs/template.md b/docs/template.md index 86e9a12..f284780 100644 --- a/docs/template.md +++ b/docs/template.md @@ -72,8 +72,8 @@ Element|{{item.name}} |:----|:----| Description|{{item.description}}| InScope|{{item.inScope}}| -Type|{{item.__class__.__name__}}| -Finding Count|{{item:utils:countFindings}}| +Type|{{item:utils:getElementType}}| +Finding Count|{{item:utils:getFindingCount}}| ###### Threats @@ -111,7 +111,7 @@ Dataflow|{{item.name}} |:----|:----| Description|{{item.description}}| InScope|{{item.inScope}}| -Finding Count|{{item:utils:countFindings}}| +Finding Count|{{item:utils:getFindingCount}}| ###### Threats diff --git a/pytm/report_util.py b/pytm/report_util.py index 8de6f7e..0a3d804 100644 --- a/pytm/report_util.py +++ b/pytm/report_util.py @@ -14,20 +14,29 @@ def getParentName(element): @staticmethod - def printParents(element): + def getNamesOfParents(element): from pytm import Boundary if (isinstance(element, Boundary)): parents = map(lambda b: b.name, element.parents()) return list(parents) else: - return "ERROR: printParents method is not valid for " + element.__class__.__name__ + return "ERROR: getNamesOfParents method is not valid for " + element.__class__.__name__ @staticmethod - def countFindings(element): + def getFindingCount(element): from pytm import Element if (isinstance(element, Element)): return str(len(list(element.findings))) else: - return "ERROR: countFindings method is not valid for " + element.___class___.___name___ + 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__ + \ No newline at end of file From 4b6e2b8b384327c81bcdd46fa5f3225598e5b832 Mon Sep 17 00:00:00 2001 From: Nick Ozmore Date: Sat, 10 Apr 2021 01:10:26 -0400 Subject: [PATCH 5/8] Code, template cleanup, refactored template engine operations, addressed review comments and deep code suggestion --- docs/Stylesheet.css | 10 ++- docs/template.md | 123 ++++++++++++++++------------ pytm/report_util.py | 7 +- pytm/template_engine.py | 88 +++++++++++++++----- tests/output.md | 174 +++++++++++++--------------------------- 5 files changed, 203 insertions(+), 199 deletions(-) diff --git a/docs/Stylesheet.css b/docs/Stylesheet.css index 1872c7d..425604f 100644 --- a/docs/Stylesheet.css +++ b/docs/Stylesheet.css @@ -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; @@ -76,4 +76,8 @@ table tr td { text-align: left; border: 1px solid #ccc; } -/* @end */ \ No newline at end of file + +details { + margin-left: 2rem +} +/* @end */ diff --git a/docs/template.md b/docs/template.md index f284780..9054e8c 100644 --- a/docs/template.md +++ b/docs/template.md @@ -1,12 +1,9 @@ ## System Description -  {tm.description} -  - ## Dataflow Diagram - Level 0 DFD ![](sample.png) @@ -14,68 +11,99 @@   ## Dataflows -  Name|From|To |Data|Protocol|Port |:----:|:----:|:---:|:----:|:--------:|:----:| {dataflows:repeat:|{{item.name}}|{{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}}}}
}}|{{item.processedBy:repeat:{{{{item.name}}}}
}}| } -  - ## Actors -  -Name|Description|isAdmin -|:----:|:--------:|:----:| -{actors:repeat:|{{item.name}}|{{item.description}}|{{item.isAdmin}}| -} +{actors:repeat: +Name|{{item.name}} +|:----|:----| +Description|{{item.description}}| +Is Admin|{{item.isAdmin}} +Finding Count|{{item:call:getFindingCount}}| + +{{item.findings:if: -
-
+**Threats** + +{{item.findings:repeat: +
+ {{{{item.id}}}} -- {{{{item.threat_id}}}} -- {{{{item.description}}}} +
Targeted Element
+

{{{{item.target}}}}

+
Severity
+

{{{{item.severity}}}}

+
Example Instances
+

{{{{item.example}}}}

+
Mitigations
+

{{{{item.mitigations}}}}

+
References
+

{{{{item.references}}}}

+   +
+}} +}} +} ## Boundaries {boundaries:repeat: -Element|{{item.name}} +Name|{{item.name}} |:----|:----| Description|{{item.description}}| -InScope|{{item.inScope}}| -Parent|{{item:utils:getParentName}}| -Parents|{{item.parents:call:{{{{item.name}}}}, }}| +In Scope|{{item.inScope}}| +Immediate Parent|{{item:call:getParentName}}{{item.parents:not:Primary Boundary}}| +All Parents|{{item.parents:call:{{{{item.name}}}}, }}| Classification|{{item.maxClassification}}| +Finding Count|{{item:call:getFindingCount}}| + +{{item.findings:if: -
-
+**Threats** +{{item.findings:repeat: +
+ {{{{item.id}}}} -- {{{{item.threat_id}}}} -- {{{{item.description}}}} +
Targeted Element
+

{{{{item.target}}}}

+
Severity
+

{{{{item.severity}}}}

+
Example Instances
+

{{{{item.example}}}}

+
Mitigations
+

{{{{item.mitigations}}}}

+
References
+

{{{{item.references}}}}

+   +
+}} +}} } ## Assets {assets:repeat: - -
-
- -Element|{{item.name}} +|Name|{{item.name}}| |:----|:----| Description|{{item.description}}| -InScope|{{item.inScope}}| -Type|{{item:utils:getElementType}}| -Finding Count|{{item:utils:getFindingCount}}| +In Scope|{{item.inScope}}| +Type|{{item:call:getElementType}}| +Finding Count|{{item:call:getFindingCount}}| -###### Threats +{{item.findings:if: + +**Threats** {{item.findings:repeat:
@@ -91,29 +119,26 @@ Finding Count|{{item:utils:getFindingCount}}|
References

{{{{item.references}}}}

  -   -  
}} +}} } -
-
- ## Data Flows -{dataflows:repeat(e): - -
-
- -Dataflow|{{item.name}} +{dataflows:repeat: +Name|{{item.name}} |:----|:----| Description|{{item.description}}| -InScope|{{item.inScope}}| -Finding Count|{{item:utils:getFindingCount}}| +Sink|{{item.sink}}| +Source|{{item.source}}| +|Is Response|{{item.isResponse}} +In Scope|{{item.inScope}}| +Finding Count|{{item:call:getFindingCount}}| + +{{item.findings:if: -###### Threats +**Threats** {{item.findings:repeat:
@@ -128,12 +153,8 @@ Finding Count|{{item:utils:getFindingCount}}|

{{{{item.mitigations}}}}

References

{{{{item.references}}}}

-   -    
}} - +}} } - -  diff --git a/pytm/report_util.py b/pytm/report_util.py index 0a3d804..90df7de 100644 --- a/pytm/report_util.py +++ b/pytm/report_util.py @@ -17,8 +17,8 @@ def getParentName(element): def getNamesOfParents(element): from pytm import Boundary if (isinstance(element, Boundary)): - parents = map(lambda b: b.name, element.parents()) - return list(parents) + parents = [p.name for p in element.parents()] + return parents else: return "ERROR: getNamesOfParents method is not valid for " + element.__class__.__name__ @@ -37,6 +37,3 @@ def getElementType(element): return str(element.__class__.__name__) else: return "ERROR: getElementType method is not valid for " + element.__class__.__name__ - - - \ No newline at end of file diff --git a/pytm/template_engine.py b/pytm/template_engine.py index 1f2a712..6e5d7ce 100644 --- a/pytm/template_engine.py +++ b/pytm/template_engine.py @@ -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 @@ -7,39 +9,83 @@ 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.startswith("call"): - result = value() - if type(result) is list: - template = spec.partition(":")[-1] - return "".join([self.format(template, item=item) for item in result]) + # Example usage, format, count of spec_parts, exampple format + # methood:call 1 {item:call:getParentName} + # methood:call:template 2 {item.parents:call:{{item.name}}, } + # object:call:method_name 2 {item:call:getFindingCount} + # object:call:method_name:template 3 {item:call:getNamesOfParents: + # **{{item}}** + # } + + if (hasattr(value, "__call__")): + + result = value() + if type(result) is list: + template = spec.partition(":")[-1] + return "".join([self.format(template, item=item) for item in result]) + + return result - return result - elif spec.startswith("utils"): + else: - spec_parts = spec.split(":") + method_name = spec_parts[1] + template = spec_parts[-1] - method_name = spec_parts[1] - template = spec_parts[-1] + result = self.call_util_method(method_name, value) - module_name = "pytm.report_util" - klass_name = "ReportUtils" - module = __import__(module_name, fromlist=['ReportUtils']) - klass = getattr(module, klass_name) + if type(result) is list: + return "".join([self.format(template, item=item) for item in result]) - method = getattr(klass, method_name) - result = method(value) + return result - if type(result) is list: - return "".join([self.format(template, item=item) for item in result]) - - return result + return "ERROR using call operator" + + elif (spec.startswith("if") or spec.startswith("not")): + # Example usage, format, count of spec_parts, exampple format + # object.boolean:if:template 2 {item.isResponse:if:True} + # method:if:method_name:template 3 {item.parents:if:Has a parent} + # object:if:method_name:template 3 {item:if:getNamesOfParents: + # **{{item}}** + # } + # object.boolean:not:template 2 {item.isResponse:not:False} + # method:not:method_name:template 3 {item.parents:not:Does not have a parents} + # object:not:method_name:template 3 {item:not:getNamesOfParents: + # **{{item}}** + # } + + if (hasattr(value, "__call__")): + result = value() + elif(len(spec_parts) == 3): + method_name = spec_parts[1] + result = self.call_util_method(method_name, value) + else: + result = value + + if (spec.startswith("if")): + return (result and spec_parts[-1]) or "" + else: + return (not result and spec_parts[-1]) or "" - elif spec.startswith("if"): - return (value and spec.partition(":")[-1]) 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 diff --git a/tests/output.md b/tests/output.md index 5529787..4bf8c31 100644 --- a/tests/output.md +++ b/tests/output.md @@ -1,12 +1,9 @@ ## System Description -  aaa -  - ## Dataflow Diagram - Level 0 DFD ![](sample.png) @@ -14,7 +11,6 @@ aaa   ## Dataflows -  Name|From|To |Data|Protocol|Port |:----:|:----:|:---:|:----:|:--------:|:----:| @@ -26,217 +22,157 @@ Name|From|To |Data|Protocol|Port |Query for tasks|Task queue worker|SQL Database|[]||-1| -
-
- ## Data Dictionary -  Name|Description|Classification|Carried|Processed |:----:|:--------:|:----:|:----|:----| |auth cookie||PUBLIC|User enters comments (*)
|User
Web Server
| -  - ## Actors -  -Name|Description|isAdmin -|:----:|:--------:|:----:| -|User||False| + +Name|User +|:----|:----| +Description|| +Is Admin|False +Finding Count|0| + -
-
## Boundaries -Element|Internet +Name|Internet |:----|:----| Description|| -InScope|True| -Parent|| -Parents|| +In Scope|True| +Immediate Parent|Primary Boundary| +All Parents|| Classification|Classification.UNKNOWN| +Finding Count|0| -
-
-Element|Server/DB +Name|Server/DB |:----|:----| Description|| -InScope|True| -Parent|| -Parents|| +In Scope|True| +Immediate Parent|Primary Boundary| +All Parents|| Classification|Classification.UNKNOWN| +Finding Count|0| -
-
## Assets - -
-
- -Element|Web Server +|Name|Web Server| |:----|:----| Description|| -InScope|True| +In Scope|True| Type|Server| Finding Count|0| -###### Threats - - - -
-
-Element|Lambda func +|Name|Lambda func| |:----|:----| Description|| -InScope|True| +In Scope|True| Type|Lambda| Finding Count|0| -###### Threats - - -
-
- -Element|Task queue worker +|Name|Task queue worker| |:----|:----| Description|| -InScope|True| +In Scope|True| Type|Process| Finding Count|0| -###### Threats - - -
-
- -Element|SQL Database +|Name|SQL Database| |:----|:----| Description|| -InScope|True| +In Scope|True| Type|Datastore| Finding Count|0| -###### Threats - -
-
- ## Data Flows - -
-
- -Dataflow|User enters comments (*) +Name|User enters comments (*) |:----|:----| Description|| -InScope|True| +Sink|Server(Web Server)| +Source|Actor(User)| +|Is Response|False +In Scope|True| Finding Count|0| -###### Threats - - - - -
-
-Dataflow|Insert query with comments +Name|Insert query with comments |:----|:----| Description|| -InScope|True| +Sink|Datastore(SQL Database)| +Source|Server(Web Server)| +|Is Response|False +In Scope|True| Finding Count|0| -###### Threats - - - -
-
- -Dataflow|Call func +Name|Call func |:----|:----| Description|| -InScope|True| +Sink|Lambda(Lambda func)| +Source|Server(Web Server)| +|Is Response|False +In Scope|True| Finding Count|0| -###### Threats - - - -
-
- -Dataflow|Retrieve comments +Name|Retrieve comments |:----|:----| Description|| -InScope|True| +Sink|Server(Web Server)| +Source|Datastore(SQL Database)| +|Is Response|False +In Scope|True| Finding Count|0| -###### Threats - - - -
-
- -Dataflow|Show comments (*) +Name|Show comments (*) |:----|:----| Description|| -InScope|True| +Sink|Actor(User)| +Source|Server(Web Server)| +|Is Response|False +In Scope|True| Finding Count|0| -###### Threats - - - -
-
- -Dataflow|Query for tasks +Name|Query for tasks |:----|:----| Description|| -InScope|True| +Sink|Datastore(SQL Database)| +Source|Process(Task queue worker)| +|Is Response|False +In Scope|True| Finding Count|0| -###### Threats - - - -  From 26f8bd5c9b0eb69de974cc69dfb9e71a7b3acb86 Mon Sep 17 00:00:00 2001 From: Nick Ozmore Date: Thu, 15 Apr 2021 03:51:10 -0400 Subject: [PATCH 6/8] Bugfix+review comments. Created 2 templates, updated tests, README, sample.png. Reverted changes to tm.py. --- README.md | 6 +- docs/{template.md => advanced_template.md} | 3 +- docs/basic_template.md | 56 ++++++++ pytm/template_engine.py | 75 +++++----- sample.png | Bin 24347 -> 60312 bytes tests/output.md | 160 ++------------------- tests/test_pytmfunc.py | 2 +- tm.py | 9 +- 8 files changed, 109 insertions(+), 202 deletions(-) rename docs/{template.md => advanced_template.md} (96%) create mode 100644 docs/basic_template.md diff --git a/README.md b/README.md index e4a0efc..487ef6e 100644 --- a/README.md +++ b/README.md @@ -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 ``` @@ -64,7 +64,7 @@ optional arguments: -h, --help show this help message and exit --debug print debug messages --dfd output DFD (default) - --report REPORT output report using the named template file (sample template file is under docs/template.md) + --report REPORT output report using the named template file (sample template file is under docs/basic_template.md) --exclude EXCLUDE specify threat IDs to be ignored --seq output sequential diagram --list list all available threats @@ -196,7 +196,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: diff --git a/docs/template.md b/docs/advanced_template.md similarity index 96% rename from docs/template.md rename to docs/advanced_template.md index 9054e8c..9d1297d 100644 --- a/docs/template.md +++ b/docs/advanced_template.md @@ -63,8 +63,7 @@ Name|{{item.name}} |:----|:----| Description|{{item.description}}| In Scope|{{item.inScope}}| -Immediate Parent|{{item:call:getParentName}}{{item.parents:not:Primary Boundary}}| -All Parents|{{item.parents:call:{{{{item.name}}}}, }}| +Parent|{{item:call:getParentName}}{{item.parents:not:N/A, primary boundary}}| Classification|{{item.maxClassification}}| Finding Count|{{item:call:getFindingCount}}| diff --git a/docs/basic_template.md b/docs/basic_template.md new file mode 100644 index 0000000..b89635d --- /dev/null +++ b/docs/basic_template.md @@ -0,0 +1,56 @@ + + +## System Description +  + +{tm.description} + +  + +## Dataflow Diagram - Level 0 DFD + +![](sample.png) + +  + +## Dataflows +  + +Name|From|To |Data|Protocol|Port +|:----:|:----:|:---:|:----:|:--------:|:----:| +{dataflows:repeat:|{{item.name}}|{{item.source.name}}|{{item.sink.name}}|{{item.data}}|{{item.protocol}}|{{item.dstPort}}| +} + +## Data Dictionary +  + +Name|Description|Classification +|:----:|:--------:|:----:| +{data:repeat:|{{item.name}}|{{item.description}}|{{item.classification.name}}| +} + +  + +## Potential Threats + +  +  + +|{findings:repeat: +
+ {{item.threat_id}} -- {{item.description}} +
Targeted Element
+

{{item.target}}

+
Severity
+

{{item.severity}}

+
Example Instances
+

{{item.example}}

+
Mitigations
+

{{item.mitigations}}

+
References
+

{{item.references}}

+   +   +   +
+}| diff --git a/pytm/template_engine.py b/pytm/template_engine.py index 6e5d7ce..aea40cd 100644 --- a/pytm/template_engine.py +++ b/pytm/template_engine.py @@ -9,6 +9,7 @@ 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 @@ -19,63 +20,55 @@ def format_field(self, value, spec): value = value.items() return "".join([self.format(template, item=item) for item in value]) - elif spec.startswith("call"): - # Example usage, format, count of spec_parts, exampple format - # methood:call 1 {item:call:getParentName} - # methood:call:template 2 {item.parents:call:{{item.name}}, } - # object:call:method_name 2 {item:call:getFindingCount} - # object:call:method_name:template 3 {item:call:getNamesOfParents: - # **{{item}}** - # } - - if (hasattr(value, "__call__")): + elif spec.startswith("call:") and hasattr(value, "__call__"): + # Example usage, format, exampple format + # methood:call {item:call:getParentName} + # methood:call:template {item.parents:call:{{item.name}}, } + result = value() - result = value() - if type(result) is list: - template = spec.partition(":")[-1] - return "".join([self.format(template, item=item) for item in result]) + if type(result) is list: + template = spec.partition(":")[-1] + return "".join([self.format(template, item=item) for item in result]) - return result + return result - else: - - method_name = spec_parts[1] - template = spec_parts[-1] + 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}}** + # } - result = self.call_util_method(method_name, value) + method_name = spec_parts[1] + template = spec.partition(":")[-1] - if type(result) is list: - return "".join([self.format(template, item=item) for item in result]) + result = self.call_util_method(method_name, value) - return result + if type(result) is list: + return "".join([self.format(template, item=item) for item in result]) - return "ERROR using call operator" + return result elif (spec.startswith("if") or spec.startswith("not")): - # Example usage, format, count of spec_parts, exampple format - # object.boolean:if:template 2 {item.isResponse:if:True} - # method:if:method_name:template 3 {item.parents:if:Has a parent} - # object:if:method_name:template 3 {item:if:getNamesOfParents: - # **{{item}}** - # } - # object.boolean:not:template 2 {item.isResponse:not:False} - # method:not:method_name:template 3 {item.parents:not:Does not have a parents} - # object:not:method_name:template 3 {item:not:getNamesOfParents: - # **{{item}}** - # } - + # 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() - elif(len(spec_parts) == 3): - method_name = spec_parts[1] - result = self.call_util_method(method_name, value) else: result = value if (spec.startswith("if")): - return (result and spec_parts[-1]) or "" + return (result and template or "") else: - return (not result and spec_parts[-1]) or "" + return (not result and template or "") else: return super(SuperFormatter, self).format_field(value, spec) diff --git a/sample.png b/sample.png index de5fe502b23349ea1ca25b0543fcd92ea411fc5e..dc7d815f824b1c4d3312547179b791e8cd4bf38a 100644 GIT binary patch literal 60312 zcmbrm2RxSl+dh6Fd(Z5hm7PLllTlVkN=QZa44E0(GonI9vJxthy@|3SWDD7Q&;NP( ze!tInJ-_Gq|HkX{>h&r2?Y^$-{eGY4d7Q^_oOh_U=2c<>dIAJNh}Be;bP)uT3PG^M z@o?ZL4=#TD2LHpkr*TyYIYIwRtIB?kAgqX*lDwXK^5Te_q2A47+0`AUXQP}dVLUWv z&iUJRhX^#AV%=6MW}R30Bof+fDHhs%cu%aHw>j=h{)d_K%~A9kL<5u;Rr+(N7*yoL zFqFs=E(tsP_!c?4zQv1j;9cq8SUt0R_Qa&tV#eZ3eb!@l3JtGpz7Ya{3PyFflDE}z7J7d5w=}<`9 zQplvaDu&F`=a_Er>C>{GpSaii-#=~bXim9X#rpA+qi-{j<;fu>qFv-IwTy$!HZ;Z} z$>N2KL^6;zzZAyUGczmuS-{|QR!GqGPE}7c??RHk8YNvQ|Fgu);c&T}u#oT9X6;7f zJGH5luPQQ}#a)_iFETDAxzwJ>;k#v}`uK6ts|&bX1xD$0#pc3~`UqaNs!sXtFugx3 z;D1yXApEH3jK}$#GPK+>%;i(}2keIN67sEc|0slrw!|(jX)r!-r9EAMw>+0<;(~$> z%?ctTgUS_g4Lg!B`aX4HDj+j6uz?3%FNu)WgF|P-!#^zAU&|GyrfOZr<*1lth%CjL z{(OFfmH*Y*tmor7m;Mh4lFNUZMR*sSh;+_53XrmA@aa-fVo?eS)ix>}4|*N$;vNVY z`^Nbf>#SFdO%r zZF$(kL2-s7Iy&qXMY{+7@cY1|4LtNIm_lf-S%V$kkE3{aq3Jft8 zl*3IY=Wa=NC~B$4i`#kpk$!TG-Q2N6hdtw!9A1d$4(s3`on3p!v);6)YvzfUG#|CR zSGcoOVh53$@g#u7Qb|8JQTqQZOdXjalJjViao^hgq5D4!>f~TF0y+ZZ^jzJa{m0C4 zUb8G1R{ijTG8gr7kLH=kOSYOjAC(2|0zsO2CqvO(e zZIjZht_=U%S?u30lP4}g>i-Wh81qB&?bvk!=bTlzH7y38VlLQNgsFqarP&>oc$cX-(W=dE`ic}`tNs@#k zdqzeE(QmKSI$T;AFNx}dWvRg6;D-MGvjMoI&2vAq^B2zPFtxlE4GpnMQ+v*3cC^2R zAe5ApmL*1a%3bhdV`GIcUTn(I$wZK|f_LQJ$au%3rV8#{@jaHlWIr0Qwa{-q)f$WV znfUBsb89BomRR>Y?5#i8yBpOMe)i_Z`ONY#3rNXUNw-VwzL`xf6xK9XdG5r#c=0S< zF}S|1jbPzdfufEMIWqo3d`#)dlgr7;$z(J%&u(OE){Tv^U}Iw^$#`F0UtcF`j*5y> zfp@Qc<9=h>^Hyzj;-2wlJi3@V#2c&6S;u@&uJl@6+~1rFD7am!P+*{##dmBhEGS4) zbGS~3m`*l^;t>;LBVKDwXJ-eBIga-igcb*ivDnzyaQ@hjR?>A|*|~GEiat;1w1yAs(0n|ZVi9FyFR_Lvp8s8v~qlOQ06d!iPSbVDUaIO+D3|xx?}Yhnq0ke zhw=Dm#WxeS0`cqZ)m!{s(#~q*wb^~O%wvPgZnQEqKAwv6+_{OFAFt2Zm3!^l;CKIQ zM00V|{`S&&Us=y>8?TKY;$`k@5Vh>WcEb|G<*tN?u*0|O`|D! zb+bIzYSo^GT!-58c<-;slTAw-*cR9a_l1H|_0Pqnr7iDoeL@g8Kq}G9SBk9r1uM6I zTN{>GQ7BrK_ZvN#Kbgx@E|B!_L$bbdXEc2A^6?(*YzzexW0#LxGtBODl03y z;P6>_7wGw}p~vUqK#>_1jnG{pTwL6MB?AKkJUl!(7ZUf2`8Nwy92|r>)#`mu z4ony?yWoBO`ju9~fpvRjEZQQ~0|!}cX0SJfgX!Sxtmxw-b9^v%k^!NVB<&$;@Zrv_ zg2LE&aWy=)ctI%fTydQEx4+oPJ22a?o+1>p}4rXYMkD~12`A3 z3G(@U86F!mMIS%%eX@P{kW2K!h3-84{A`1jKeKQ62fx@3nGad9bHrY-ra6n3pXvTB zAS8!ASC6l(qT*&*RYC%_QK_AA`j<}WjR>z9|D5u*INX1sIW()M@n_9oq|ev=Ux2}X z{CWqLZt4F8c!Wx}!O9c9;n!>X@A>vWmw^8PqyA56T`zpN#@A$yB1ApQpd5jsx;XXi zxq2e&d4B#zNRECrCr2yliR>DhnkuKIDZmQ(54)l^M`0C2&Y@yD^u;#XHaj#zspI{v zN|7&7lji`{h~lwX_1@IhCPPfyUkHTtudm)zu0PY~jXpw2?MQ`NvHh5g;jLR{5QgfB zih7t(bov-dJeUKSq#n!V_uZSQ4;m=7XMs&j5X|lQS7z5EwE6uR9|HpeJuBC)T`O|_ ztC^+2E?SGmT*C;&Rxl|`bCQU;sf9(bpC58}JB>aVMT*acK0YA){^JKZEiE2Gd*LBb z6r-d+1aHuJjwgQqc*EyMl+Q?6NUW``5jj&+RvTMea%Sca4iA(_g`rCKnTCdjN_ebu zHb>BL*ncW2s%;ml`QFZ~m-&xZ87|q#H9RApSXr?z@`j=<;W|(C;ciu)_5-D75?hzZ zyA}T!umWU^j1j~ovs;K3y(Gpr^WtpeV0TP65wm@FO-N{A6=hbSOuU z4^|kkJXH(2q*x=B9+WAUT}1~TA8gw;HZ|3Cc9P1iS>?#f%k%K_>t!ghV^vPU8#Qc3 z%BdlzW1bAYdle%7fa3qXNBVD1ds}>JDhUGv!?o9!h!KaKWn=!^UxVFkQ#!J?y4sOAv!?Afzuw*M0?+w8egg^SFN zR!O}Qw~s~{JdW2I=~s>P8}Q20X*y$8TT{e_8nva|<<0-kEzTNhsa zFgU3p>Lnf<52>iASUEY*AZb76icIS;E04B^W`AarB55}Z?;?JX5cfW|z-J=}z>Mh| z`RBN}aGpMW>fquM_>V*vnWd5V!m`w6P}JSivte|U`O1?`xE)-g?V8nk!qu&Y*?+olIfVpSozE|v{QmL+;y2x%m}$|O zs?u-Ny_4}9AD}e#7n?!$yp1YZV zOd?YVxm)$*+Yo{WhbYr3GCVxL%QO8hOTj$0|wIdR_(d&&dFK}i(#d^2r{1H zHYzgkcYdBAMKidpOd@kZ!g+>B_r?vhWas4YBuhAAnwgm;i9L$*GT@Yyyj6Wj0Wbvs z1By;yg;=~dbwWj!pycjvJ^~OysHv%mbHFF{A}i}$&(EK+adG~F#^neS#e9VT8K`g* zDtB8UK@5vbF(@c0Bl9lu^3D~n1nHu{CFcy>gGQnt1`i2tbXaJp{K@fA?CaOzGT!?r z#=|+-^4>I;R`->CN>*=r&>*`oBxd`Y3USz)*B66!my%mE(5Rp}jQYH=*RBInn|mW$ zTU#4}I_|PKK#q@(?>?86n5LWSu)TQe{{8#3j282~xwFGxFW?Z<>t%dti@NxrjcQi zZ-Ud}=;kK0QF8O~`)9-qFWil%uT0##RzU3`6rW+RIjf|k$8piB7mqK20J1@sRf&^n znfJjZKyBf$ZM(a>tfHb6U%q@fw||y~1{0x&R%CvcgoFg~1I!t)l$)EozO%i(y}q+C z^P{0Ll(N|S;IWd5%H--qL+khNjg=l7WKe0Oq@)nP%uEi00weuN_vl~gC*wlbAGGwm zd5Rzjd5dE|w=7H|3bq_!Z%;c$^`g(817E*p2qeSF9yjq^y2#1NS^Mi3jrFhh@^*HD zadB}m@$sA|&xoZZrcHjm^n1MY8w2@Oa64Erm_$DN=LA5vBw1e>zsYv20;3X4Z7*2` zRaLy1j+Ag{T+GL+y(Jzm(jw!BdmCk*JA%JU?Mc(z*##lQiI^@^+1c3*Kn;Y0c%MuY zX&_t8X^YyD*pEpkz17@n6uYCqE3F7bSQfMv3JMCCy1KgKQ&WLcQ}@xH0C89uy|Ra+ zR8@1lPuS7Xf%u)j`2pdP(yrY4b$fTG=7cy>1jm+}!*8t6w%M*X!_~IZgOTVchoM(Ho2%#`N{nHZN0Hp^y;gE<3B`+`O;o;#l z0|uYr1aWSq2Q5z+kIuzXpEoewSssyaoNT1*M{igpOvYS6Nr~EEBs#KsXVeqZ&#?Gj z5QIOZDF$iGy;*f5xxnQ5ini@Y9Yumgg9Ha>3p{%#fhb8amk8_xoRa47E(jq&b* z8#>C$$|2FwSN}9en%UYKTfMHYSK#F4&ICxT(y6)kxw);g^QyW!0n{ry2dsRNKR2hU zax_ozn35|l0kZzXmzS59S|)`V5CtqZ2LSDvnHl{%Y<*Ofyw5{I+Qi(J?@Z0iu=Df3 zS6I5CtBZm!Kso_t8L1Q3UqVE2L4jKwtzv*wOKtV$&6{ZGZSO&=pnr$1Pwe#YOcM|h z#h=CUWOQ?L3w-wM#bt42;|=I=(6gx${runR`t{N6w)2Iiy*WDToh{KU!JKOFF#+8z z`Fy9SAOXDd=&N`Yr z-<#AN^Hzmrql{Jv--Ev$DXSYB0aaC3o@_6M`W|n*Y3=B^Wa&KL%X=+J7~}fuOUi(3 zMXY*x05E(zC@~W7^7fusSO|yoapugK+PXRnxU;L*t`P~|tqg(WjsE2L__)k#@1xGq zCJG7c?d=DKhTt4AOE`@C)gAxW>U#le|D~LX_m+OLq%#guzwv_SpS!yoh>qURu^v8lx&tfTIKh`;_A{A4k$_7YGi5m!N2csQj3o{pX#r@hMsK{1NmMzfzsBT)0eNMoR>3w1CUy z6baAm_m#G-F>DI?ePOUgZ9}4>qN!yjy1H@F=d*kswx82@frn%OC$nzdi%S4o=?M8T zz0XQHQPB7dvId76k1Y^Qga9Ec5LOy~Lsn>YnqZI5Ab={^2nY!wQ2`WJyY@;<&1S5+ z>if=T_&7|zkjTh{QsZ(LHGqJ4kt!S?Ols`9s^P%FOHoli1il$sXHj6U69jObO+bJ+ zj&q`cR5mau2>Y_jESi?bp-fl~emn=*!g1;MMWi;E#fL}e$I-9b_815WqlDk#p;v&T zqJjde8bxJQ)wC@o*EK)&ta?~lA^Qr*CzLx<5FP{s1mol5ey}!>3o(SMHYxP@sDbOT zE<)&$YcEByFI~C>8$3Hw0qhQ8>frC+ZG?|+-MR%i9E}fbg%t>>Y#k48{`+? zYa$?Gk*Vz6fi8cdCrblCZr;4<;raTKt?9#uH~{=UTXxr{MOUX(n_RC)yKri!Kh4C5 zA7tdZN-C&#ak8eF85szbZ+Yd-)7{2U8n;N2LNNO}xpd8~#M3f_ea7m(VQ7c9nS zXYl~8xIcMZ3d}MV&vE2Q(Am3k5YR;cTHv1Q0sbRJ5Z|*dm*G&g zMql3F-R1e%2rb_d29*~3^z?KPntXozAcoDTht6T@)p>Om(M^_BNKVTCJ;JaH6@e-@ zF*Ae1rWC3n_@LY5F|YM4_tQkI69n}YffJ3b?G-H#>2w0F0)2~^^)Djm@1Z{nrxCzJ zy!Pj^-68P1K0eTZ)QrXB^IaDoPQN_RSqK7cgZt4|KRFdupup`ftQRg&TDeYZ4lD?T zzBeq!gjX$xz6g*oONXGWD?}@Rqw2%EuD}<5CxfTLPID)Y4=Zz;CP2_WEpM7ez$g&5 ztA2Qx(V`<+9#~e`p5Qk=`)bw0t23|d@Ac*BuWoKOCY-#3m<403pe6@#h0z*8rz=ReCrF?4;%7;H|B#Sq_(p z0FBtr#PM(F0`mWWj@^*$&=)od30mkqn}G;=Q?{IkhMAs5?^`)}t>|Nwj`;X^rxhW5 z5|ZY%KP}`eEG8U@&;A12!_JQVBSq>g(0q7=gy%n92bA%6c{r5r!o%KHVT{#c9e9Vb z$CD@b0G&=Ts9G5vhY!Bxhi-?&@f0z=Agc8KrfC{YwW5;PhzG{PJ=X^qm90 zf781r1KrI8C;Ea_#K%+0h7Asw0&JIpq9O*;|G`*MPmk)uov-&+U7_3~a_GJGiM7AR zUx#+JZgKDvbZIC7@Y!~V23k6x25E&&5xcQ!V}Zix)ZyW;&fNvK#qR(BQE#?@)+dhx z@@uivv^qpshVx%Q#Iv)Oh#V9!l$Y4EhPsC!|JLodJ=aQ5sj=T%pGKMGd*00V@A@+dG%vb+2H_aJW9zW`H!(gKhR1WJxmq}=KOs6%fwJNSwFc(2o!T}rA# zw*=%7_yzQRa0Y=c_6rEWf*Xy3BnWFp?!CYH6n+8{jU$L5nfr4P&qSl6qeZ2qgNx7d z|K$aMYg};{uf+&Bf{rH$o)uO~1Uk~P^71x|luhe|%KiC#v^n>i)Xu4yqNbz_sI8R) z_<;&1AQ3b}UD5C+Wzk62*9;BGiDNGH^02hbK~QCX$`(!-B@&pQpDzrV1RBqK(AmJ< z52Y0r6cj)yW0#e!=`B7v-l=JCe=2U(s|rw0-e%~_b>LBJl!leQufy-)b#_5PO+2iQa;l9Cd`sfLi*SFWjKS$Vk6xmZ|OOiWF2+Su3tT$r3>XJljy%*@Oj zu5n%XrLsk-5VAAoYqFega`|qSZs--y?N-}OjSm$S6@;LsOe`+)@8A;>0$S_NzbWLw zfS8$^bA0^tY5EHYI?1!Y3SNVPMU6C$R(Wb1d1^DGI;7F0<5LvQ{1bo7$=aa(3qj4SB9c;;jswpfgw?8{C3`NQGl7}txhNK3=tZ3c?u z5i&R}ygF_^KGlcv(iRlNb?GjUz8G>qz-E2_V_jlY?73qn0%&~g`-RTdKS0K=0oT~J zwE1-Mr74IRW|4ca3?WcJT933+rFlzlS3EB8dci6rq(wzdjSoZ);B(dM*QufU zP$5dt4Pqm)uU_$ST}xs`LVyiR$dSB!*$5$v;|zsz>v7PRe=|rWGuK66|MC8XGxU)v zqwh}0I@;TxmnvVqD(hIYy6S)-Cm|sbV%`5C6uK8lA`CJ(*AxoUcvNFv8MhQfO`YjNtb-Yv-19i@+zp+8JAflCFwjoJc^2n!l=gOwDc!2+qn-8C;kDJ(TB!4YVPiq1g$OhQffovMgO==(-ou)I90!a@ z`iuVRSqk#BLx-A_h>aiK6-L>s@R+)}TeG6;4KSJ|$t{HT^H)eKrB!GPn;jc(o z;>vP=;Ttck*-U30#P8+HTe&TmVcgD`D9L-L#>aT{*rBz%U(~XT6{!Uk5z2BjaG~`e z1tY74zDI=O_M`DPFA6MqWB48HthAfqARWn)0U)%i_h)3IxYL93#uYW$Be7w?ox4DJ zYIt2S`q{vyTvPevQKRCG($3f7q>!@>tGy&~NEq>)ot^JPY8;*$S|SSR~;IexKCiCcyg%JzEDq~FBfzVB#is~#`=0a=$U{^ zpFtmjwi-@9sIy4v_E+6EXLX>UngSDris-;m27@U81L=4p8(rbHVqkZuUc$wmLEqlq zPAIolNrrU2zjc9#QGyH+hSR#aGG=mYD=Q|}J1)L|N`GC^hQJvIxf>fnO#~o`L%;Jh zG4ZV3a2Wae=^KU?4I_n>Y4|6y4n={3fq5u*mqJ5af;L&N6X{Q6v!b ztwT7Z%y;G;>BTJlfVag&-d%g?4|%Tv?gp@z<*)a2Am#GuYq-w$WQ8Bn9~~a9?k^N_ z@bKWF3MxpVz4L7asp&PGAEDRU-P=nC#3$jt%8KelY>LlJj}Q0OJ^O&PX$#KbF5dv6 zBX8tS>bm<%)y^3N?aw@hpoS`GYsYwPq(Hmu4$}5?#eNVoVBa1@6?9uRa=-CH zE7DXkkitRH(b0905em{}yc-i&mHu|V5t8j*Ez-|7K;;mRYtU;4wA|f7g&7Pv;5))I zGT5OWzVY6Ec}SZNZh}wS08-E@xfdD=Ol}xN37s_YjaVb*>aXh)*=CzVXnu#hll}AC z5Ny;*;}I}=Qc|}gXhjr0jC6PFTuYYd9$&kAbp7Q%w|v`^8d7Fy9zej;**@W zaB`>u2T9a?ed^ovFV;XgaFjqX?S_UWU}3viRZ9!ltMx0S`k+sBv-rk<7nwmUxdBmduun3JUEt&AF>(?t1PTgG^(t_GZ?LVpJw{PF- z?Z&$T*B-?p8wGSVeTCSiKCen}Amxy}lyVb%m2dJ&*rB&-~=V>om#UE}!HvmQFaQFNb$x)z)^)&RRm_){>%$h0r&^_$L>6t>&bGm|MhkqH5QQfNdiVfy*kkIbbocWfR-N< zD1SUs(iWh^9DrSeBcd3`2|+qO-?HlmfyPw-B(R`P2`7fk!fXfN3$)#bha@Zkfn zc%qY&ljDE>WUBrsVLGK_QZjzdIn4E(Lz?8}3nA~Ii7Gdj7idSbuB&63W6^x4cGLNI zVAQ9kr|WPVEe?|6R-3m(_O*C0zCXqI?d> zFMz+jZHB-Jg^4`uOf_!O?>jXm==~M5A7unz5HOX<@@UoV(`K@+sRCflC z{>MH8fz0n#19H!sMR#@7!|^|uAO0Tz?egf+gXNoU zi+Bl{90&dN#}hTjVKGhNI-{~f7r**!3)R$ciBd>@dRWKC_3r!eMLLk7bp(q4sRazn z$(P);C8a{c`yY?17e3eXGhNa_Zca2L#Qg@Bj;(>qGNaSOAO5!lt|%J~Q7bE_udbS* zM$0^9!ldna?2^*bo(CAndi62=L+^dLsj0Npf#S(XF0L4GEkUdI_~S7h}_iQ%-LvJ2XP)e?1g@YO~^!)LD`x|_K3je{tpw&WsW@3 zPH)TGvyDzIZ>e&1Ge3&(h=ZCI3fqUk^3ZBhLh$ftpYGDHIYghXrY50uMQ3^>Vy{cW zW6TRHTznMK1*F!XF!^wk(vMEVa7zlGBVR6qk{z{>AoU=djf(9UO* zlu}HL)sgOiRUzZK?1;H%{fpJD4s)28z&}roGT;J@UPBCiA6y}ubrNxE+?^ByjVBYtW_ zU!0_Vo;3;VtMnkf`|vLF$AeAz^fxTuafn5iuVg-X`qV#%=7ywXUGJC)xcE~lphE5E z7n;uhy;)iJooMEyB2oTzG7%NEyy%4hWB0N{hU)El?AkU0h5VrHeb<~VZ;Yg}3JzIG zI_19|@?f=oFMB`)Oc@7hwE0)p^TJkC?-K>ii)c<1)+l`Z=>NO_Ci6|O?f>MxL1cB- z!=v?fIn_*Eo%Tg5&i;qLvZLjtz71FI5(RNy84-`3=fAton%C6Jbh-B7plDWmjtM9J zd`C*`$q(_athWf_qNdh*yP6ReJg2HEeC1ZxTv})df>8Z0tlvDOk5Hfcqeo2^v=6`g z-8a*zJc@PPOjK5n;80q7&h+!$H}iQ>(KL>U3bd^LEmD1`Ktc2MJJ!HMy}mBc%m}YKN=Mwdt*M~p3k`z z?>|%k1NulYhfRN>P%2eh55+Hy7p#9zj%24mNz?!NuITP(Jg(H#R+Cba`KzhrF*Y`7 zP7a6|eG)O_uIT~CAIr~12#-kAOf5#vojXK=_K;REkR2|q7cIIM?T9|r$|XfJ;C}1r zDH>@EC5;+N!{|-9Tz83|C@y8>hR;sUhsjA(7!m0jDc5Jj>CHOy;X0%A_|jV(;s~kM zljgxMzP`EP0dsaE{`Vi@e`{_o`$Iw80t zlAJem1NE)R`5tYZMg1f{6!Txw(9i%mMGswwh~-d;^-btXZn}jk;h?qEiWS&Rv|oWL zeQqWN+BT8huh8`O-J;jYJ#|YyqnGuO2DPuvmP3sKlrZD#)H?dQU(0IW`}+PXA6x{@ zHWOS)ZNr7Sxn0mMP$bIbJk~9>#$7n45&zHZ)~5{?fmr~}FD-b%C%U`#C;uy7Mh}|! z`b+^bOfQoX5ahj64`~AznqGKra>hc5$tSBMqBz-EBr^fnj3rafL4 zK%Ezo!--N{^md>SfB_@k*C4;oeQ!F^tl?aHF3u_2I>FD+j~Zr@UcJIZJ{t*uC`|Ps zEcKGH6`vS6xq9MSz5a^by0e#I7sd|F>#xjiSEN_$$>$5&RO zK|4YaSV2^m2|CX%dg>V_gipT*f;tk`X+YK|c84+IbbB*-_y`_)AMhCy=*Jo4D+9R> zO$r&b`9Q{?ZsJn=u@@(vATQp3@BoA7?xRhxGb4UyO}xlazX}3Z0RB4z=yMzsfTotC zm6E8`4FZ94H9?*ejSm1s~cLn-qEY!iH@c8j1V8|mQB8Y~oy%~HV*1^Mq zN+%uyWFRG@Tsnv`ia^T)Nf-cjmb#7(&6naq^Ni?@Waofn0Ucut7`j)7>}w{V`+*(_ z13}Ry5CZ5Xb$_wFy+`C99t5Z3{ui)iUVNtfHCnMhZDRDvk65{wyfa#pRM``&t}qIg7G6VrRJ&8pwN`sHgwmlg0IGLRv>l8VI3O8Tf)Gk ztrrfI*ElG>wcr;bXJR6TUQLxoX$1D}!SnRc~5bRq@!mfaeD{ z3IW(XaW1$~V4$ak9!t3owFm>tgg)=$SPcvK>-qit{e{8q01kvtx{0h-`HPD-sM8Hp z|0eJiq6YJr7&4Tew*)b}XIh6?XXiwVmHi1O$oadg4WuX?14@DdGokNo95s)HD=K!nAt0(fJ1+E9Q-1`hGYxuU5qw8-HVK z?3faTeZvIrl=*>Pc@LO;mAhJhPnM*Fl4h^&q64El|*6Tnpi%p!2;^{`pDWKlNmw zQ^UT0zveRAg#jNtmHEz<2tu;&mIB^;qY?s$*|H~Fe5i;EoKYRnIdeVpS-^&k15>{X z;}ffpoH9X~*gYMa3QwiLA;>VgmY7QN<0%$QO>uzrH3K3n>9Q-|JFPcS0|SO=;HW3+ zujKYwU3(+L1PKP+f7E3L=gQCG{5+_^8Ak7Chb5=JC+E7*UF?8liH==>A#xm85p+S` z2_8Zn?ZT%f0|?FYf_wLNHoayMXg{>C7*0C7cn2qs+!;j>*ezP^VL*C792o1% zYk~L@|HhNZEOL@j=-12Ue*k zONY1YHxGFqJy@iJYHDPGp@d0Z43N^YG%~C{!aM>nhL{j*^*}S4T3O|}sBNM%czM)d zi424sP!HpVkbUbcWMOJC)UW3w= zzE2ifXqx2KN^Jzh0diF~d^Q>S+1==815!wbAlikKY-Vi@Pw_SlJdn)jC<8<+85Px; zNR><%HLXa&i`1_|@cs$4^vq#skQh8;;BIaK2Rh#Q^XK77=+i(Nm2jCQ(@d2P)yY&z z&&g4pi%Sj5$l;m#o?75SrxgKAlGR(FnRX8jP>S*Ny5{C^1-$??3W0!WhE#)^l>ADu zPH8EtWcVRFD=Pw?9w1Orh&0d}Gmw=wT*7s;*PH(n0#$?_BZ&dCwod99U%xK=z~ z>^#2s(f8kZjcXA-S<)AQd29f>5j9SHXqTrJg^?%F4(Q;=itq>3jIuQH5>rR7y_`0# z$!jeU*tYOJcW@&T&6%ii5|DrrB;mGSB6EElyc67DbTc}@Qs zsdgPFLXGff8RWlPN&CrqEoJ#FW28#J*}Lu-=ok`YVCxZ%=8~fm3$*DB(@)2jQS|Lg*Ed%mk^t<6o+RD4 zcmV0F*kw)^P43Vh7?d;SDUN#lWnz?cIs;-$6P$Rfkt3>-SRD%<;52lon2tVM)%EFpw_w7iYf3ekZ-xXdCdPZLvRj%^MR~9;6D0;pB z)~RSs!Is1HWK#^A;wl6Zk14R~?UwnT$UN+T!Xmj9AY6a+@n;f*2P{myaI#Ko3xZ1qE%Hv%cEC3hDKt@9RCKLZ5&a1K4l9=!qD$gOhK1; zbIYz<=Cs0`YQUeVf?FIP1N_@~6u3X8`rfsSV!AMJhm4%<UcVb7LVN)4b$5@e7aOK$=&w1sj+2G?$o(uZGYR`rU6K-`x7;b$!iVzPw;$gws9pj}`62l6O(7mD zGh;*-K{vd{Nm0ykHEK0VcSeUHOiOjFj6AM6GJPx)m-V?kIVHz?$`en*$gC_3L^QxZ z%^h;Ji4XpZxQxd{0W#jKA8t`1V5tl^TsCGM;`hKgI%sg;TOU>oQHmKDQgl2y_HCbX zdV8>6Z@GRyBqFkIv?^eI&sjAkx%aVta}!2{{QY~Fdp};sq)6fZNjT3Ydf~vH|{Ip_$+=4>}Q8v|XAdkgQ+oV3>9$u)CGw3+pb zgJg7c@^`+9#z(UdF6gtg@n2!m=8HYv=oF@vq?oko^YbMnXsYsTB_eq?5r|*!Lk(1I zSPoC+!XPDGg#Kn=qR_rEfye zNy!aVB)N2LQxJA5GvgoWX-{#CwBYWirp6pfO}-lVMH9A&9h66IZbW}STT`Bng|tB@ zlT}oNd|CUg{Qv^pF6);sG~1Lz1GS3-BD6#-QKV9j$Bp|xv{pK>jq&%WM6#Y7*dvIB zn*9E|>%y-@uwovTETybSEGFtD1Mz%nyK-{#WC1tR-<(8_qKD8zP$N?b)$`1 zmHwha+wK~IOh4z=HZ(dejZI}%!oe}k%|#EB3MH4^eVb%`?YG&A z&1qh1nCu!FAwA{%!9?=qg+jUyJD=@+btco#i??Q0Yj~!8yg_PcL@%>AfDsx= zyeZ{I^Q(PjG|13=3NjNQ)1$Wz z7UpmmP+gBwY>NdI(tKsK3Pq{)4Grmqg`HBmBZww)5&qn*w>9QjLj`GpbKMO;={nXhe}*;KJM(Cyw6_}(Z?&#y02#9^Rct1^=Du= z_T?+WS?{=q=~t%7)avi=v^k?X_7OXG%o{gkuH|VwV>2z}vCvN06GkwFom-K3%lI4S zf`K-nKvlXcM262`AEb1}(H@JTkVQ@{5XmqvyCcuP3La zWr!1m`BGk9m{EMW1}j`C=8NT1(uID-y-r#9c9?5KJ^cj%13jXhjywpW%ged7z_E0& z!shB0+}osVnB{du-?rSJ#zp%FC;I`zu9T5^d4c{rli%j$o3~Hmk#gxsk9{ zD{@_7=*jA#oiDZ{VSfwr67M06YXyWl&MpfnClsqQ&C7sRkDba zsM*=M_h*yF7n(G<>?~`Ivc+7oIs=W8BuqI#d$-|}^z4nptgy4W%+eZi(PG5V_7BJ!Wv07Jv{WCH-aoFvw)dI0Sc zLz#`7H=>;waGlUuIA_T|g3xosOgZ88OQ*bHm!bOmgX9E04b6naCH*r8GV z0~H}_Mlh&%kgoZ%^f&`GRbUJS7@NXCdqtII;ddB6Re5{C&;}C(f559f$&__1&EKC) z_jGQCltl*pK7-0kvbb#UrJBOjT*$XeYaG0i-vFZ5{X zk|<7~-n{HO$;5#hr0^6DIJS9nMEanTTJ9wv0)e;bo_$HL{obG5{IBNy#o^ps{=oR8 z-oa07q{P!qihth1e{=2z+Tz2*tb>3a3}EvfjF^D&Wt!<~cK1!@K(a`5 zJQ*}^of;qSQ60R!w}5e>+%TcN`wGwHNLaoQ75EAXMo>%}c6}}8&b|Gd548lHR|62W z9+d&LhBeT#7gz&r@Q%BzjHbZl2yB1~L)&N2WZMM-L@&{N^h50a3JIdSMxUw8gF3*` zS&%0u9n~5kg6OwAfUF)2ns(%E91lZU-M<~vkB=uaU>!3=VA!UyJy8h#ngO^+UaDBt zK$!nVNkH|iI5)-=C2VXqC3BQr16K^VyZz8RK!+sUx8|t@O{%|3g=zBrhuDJU&K%1k zZ0yThi%NHFE0bvLNS$WFLRb^qQG8ECFBSry5QZ`ifQQji3rmq}h#fT18Y|2wse9uV z993zByw&RGQ)wT8ie<*&ir7wQ}H;`jVeVEeGm>zEUesoo^B7YvZC*Ue|lW) zmPC(e7&;9G!xk#wO+zm+06XUhn7iQ3O&eR{qYuUN=B^7WnTS|v1pbHB`mbCPp8@JltL;I%4PxE&V2FaOM zLLQ_pey8IxshNRu*=S(Uwa^=C{S5fMO3xhkyy>OZ|N@u>NMGhlgr@bzPu==|A@tR;5g!Ewv$J(1Fj& z%vmBn9(8hk9hku{+*(!-q*4Z3Jap%QTn`)!ofTe*Jp%b0uOLN>j)Cx&oZqjGY(P2j zqHwIwAfQ?VI8!Ld2(Ytb5(Htwd4p7u;RsVoc>VWUu>KJWeZTj_AQS~WR>z-qT7FZ;2b=Cwkn)&yP$-d+1Om$aElTB_U#*fcXzj{rsfQjGPkyTojmx1 zfMCG!_CC7PU*o=piF6g4U$t#!f7`{*!O>mjXdcq!!prs``qQTiwC1}&^TDyhZz?BH zh2|=-TI%R+w5KZ^W)uTCePx7+g!W=@pfQ68U2JULfBEx5ZQr4o2+GnaDJxG8 z%%C$keh+`VdUWyjtp}A4iQhgb+D!-!WbJDYAKH5(?SToy+jf7s-*T#PQuOBO6M`Le z0-mwUTRnC}21mYyZFp+Jl0+vdO-3 zx5sFzSdN*@HLm#b>&b9vK|%UhNxngcK6?_0=jJRqWvCJnf>jHa;{f{h4!+1cFj7PoS>}*j9HBEZ%tIur7$dhEk`< z^<17WAp=+;;Ie}C_^{21!1mY#JYM*wv{hdoHUcEgw_-a$$cP^>nKW|)fgl3}o|&DS ztLyEJcZH(r0w08%3iusYc(PJEXsFW>BwHu22&mjS)Ky$V-I0hJ-OpujqL zM}gP=S!yaXtvNdJgMN=y2~IM~lL7RB-41>&Za)dbA*e$fWvkN5et)tcEq}Ze22OTU z;FjwbUc3g4#dNkS9llY497jt0vG<1(&%VY`}~y;2CqezXv(L8)#+J znMp@S*Cf*q(^Im0hZYGJXoE; z0%e;Cs}|I3Lno(?MaF<|=@5fV72;-Eb5Nwd*MfYEen*ix(6~eh{Dr|hq8Mh0CZyV6 zn2fOHR+&S3QKdoStyhuMJSahL#bp) zgUA#q4d37^{=%;@q- z@q;OeGv44@CtAArRpLAL_}MdsmZw!GPco<-naDVNwW0QgxA}|DMh-qIYHGz*PgCU_ zLMOgK-o^Ur(XcmCt`c*|%-E}4q90h7E?xil#LA8G^71OSOV6oYl97NzMpKSi;RLgN zYy~k2>uFAdSiY)!v$*(ariBX^|A~){bw=&sNTq_snD}V;AkJ3J@4NZAxfOW0?|%%u z0AaprqlvBhIthZ}i*rR;0bK8_HkV3n(0$U_cnXuo0wr~(yX33*=F2juyBKY>B6U2# zZ_BM7ySDGq3xuBCII!XSz;fhl%MWTSZVUMRGteJ%m;IiePbs-Vdh74s^PYHWL|eii z_9SgX1XLLnFo7xFD*+RZ_;{_i9+p!8~<>8~0U7v{! z+(dw-Rd4kJGqWvM8l+D?iWieIHG{(ZL?+$YrH@fDZ)oO&i4t$$Vv>)lw*>#ZSw05e zi+gx&MStGi(EI{Nr%T=0qP}BS5j{#W*`R7VFF*e}G+MSd`Huaxsy?{;PR-VJ67(&b zuDz8tF_lUlMf4xVIgFKWhUGwkB^|~pa?QV`B}z(4Qf<~Pw~ZNHE|?4*G|MBfz{7@L zzMX(UNy8L*_>XpBK7$OAY9+IuN`>0Vy07fc4};Q-2V*LT*$SSX;{s1LR~BTn*TRxd z4lM0HdDhaKJz1?i_f!s@dw^+dbbUKlSTJn3$QT;>|`rz1u{q|24aky8HMsJE|k1eGDmyvcaQ@ zM`=tFGiVAE6B8l&VvJ7g5JoNM-&iEx2S)QL==vdN#6U#sSXnPlnLykVb`B2t6Tgo= zzOq&5+xS~jEFT8`bn#cfy72Kk^Gi@1=tA?h^}7HjX^Wcqa?B+0Z}^lfkJ3REaSwGe zk$>Y$ca&ZVwOP08RUhchTu~?pn+FT*>hDZ^gH;OaiKnHddgyVx@-qGX{CI3{!;@eL z&_4=V>TI_0addMq0GTybj~M;%YfgppjY#6dsf+zr6{Wu@3D28+$HbulWPtD!JH)so zXwd=-gpZHZC0_}(4TwJX2>O+{l^ZShD0>a+KB zZfSJQS#%xB5tDRsLXR1KC;vdym141vHkw`-12{Yw!9WKgMYLxO%0PC=N35)@?sjyb z$XQ0lW2-g;IRE(7Q(Jk3?OOlcwC$`#z8~iGe-D`lgzglDloy8kJA`X&SoEXiy4rqEf%A2o zMb=eC6W@%VEdD+jR7hvV7e8=TKfn>Zm-^33cQ;Y!1D-iIJLi^)8m%43GG|@ z#O&Cf0V`8eQ@2Ki;=bi5ecV3$>^v&HF}&`J^PhSNm#eS&yKdFg{7Ww;b~&J7ApS8# z>4f5T@qSlouVSv1D(bOV$rQd>hP}||gPCTgY|(9Q%KO(7ThtnbbfQt*`s*t<6bghv zg4d@XXQ9FWWj^zGXCXJ4ogw!uex&}z9k8x1wOiC@+qP{*dsj>Cx+?Q?%Y4M4V0z*y zrHA_(ioj~on_GPus4s4laI%3Z`tP4$B~aeB8B-&-cBL*ILyOUmcf7#1PBo;YW4%S4 z{784HG&|1{jhhdOF8*?9epGw85wqKZ3<=THB>@&PZAzIS!!%q2LN^2#J)w}29&Oyg z0M_gCxD)HcYrZ&-w^_H_1oDItl`-VHFgA-s<^ET6`}yG8zrO1pE3|)g_3(W?f08c*@pu1^77oAw5;~*$%asj3PBC# zfFrdj;EO<3c6J3yyT7eib>JKMhfg4b+zJ9o;THHx6#DlxJbbvF@4h&sWmdnvX38X` zElWaR>;wlz5XEI>{@{k7C5lDPrzrpNK3VqrJYWQD{qxg&Y!jYcje5k^-*egbuPo?T zvS7i2(ttl*NrN`rNc`b%tHE$u_tU!^V_`lRHi5u_!O9b_wx4}_Y9vSf@+E5Md!4r` zAABK~nwq*As0n&UCJNdbTdT5%KFR(?SG>A*35f|cxrQX%@mag$ZO!#f5fVx81zQ|< zG#CsAn;U_@f7Y}gHtE_ljXUlZmx@G`?zGk7yu|8n>*Db9w6dSpuBs8-czZNXVLAxE zqFCvUqJn}WsVJbAv3zw1FHmg7AO3y8o9X3lw;>{DeFRi3Y#5>LLb*+sODi;?F2_=S z)nH6-{$dsa0xfk^>!AN-R5sI#jvLt7T>&%VeGL%$z2QP%Q4K0I^A+#8akYFD0H4b2 z3iV?*%Y&Pud1_W|#B_V+V~6&U=afBHR2axn6$n_t3MVvKS-Tchp`k##vn&NQU^!Gz za?mv~JNs>P+IgL+31qt$IM-IBKm9;(vy^if(2Ro0!}jax8ZMU4pdJB_O}Ey($L9n# zPwR|IL30H{PVZW~4d=FAUcP$om~xTXYxf2qE@^OjD92wMcryhsXyvMw9Q`LO(8PBI z3r!JKVtj>5N0!Zvj?Nl`4Ich0yJkYQ?x!v}v8**}r+6c*JaBD`Uc7wCGh&%KqGn<- z)H(|7`$^=v#ijL#($NeHm{FyXu3EZXUh$;-X$Y|Mv5_o#j<+O|(XQ9vgyCNc5pFdnya_1#RsWvs^ALe&+`xbHNx?%u8ID&T{J}fk83LCkt z-{ovCWX=&#oF29x{Te6;4)IF|0c27S+}JI+bVQaxgE@ ztiJUW{ep*9=~R(ft^w2U)v^yr_^A7WJLcs&ZPT0gV+C4{4;OR3W`}CU&`a;5#k%0M z7qX;~lG8gJE37_NCA?w}K(Wk3!Dg={CyQ%pJNmf1?;QGR`EG~P>vkD#U$$qDcD;KK z*H+{0nW0)c`szHl*A-sh5PSTX!OzWW!U4E=9X?E*RVuPM1T<%1fBlz( z;BzgYE@D{HGX`Xroq|nN%r+k`GZXwYG=6!91LDqJlK|BT9Ieimhst_&n8H&E@`U{Z z0-jbJg5-!=9jld}<>o#Kq4b9r`G|B>2rkl9ajh^@E`LNndWnmPoMzter1kSpGWL-E z-|(7D*QRe^m9+ZRx#>Iv{8}Unx+)_D?u2m9FZzP9f;-~Efr*z?77TyxsdaL>_@b3H zNQk2$M;7b}>e{yBVmT#2KfV@DTsNB9RdOi1@K5~g{@VR<&5X&q-~FcH0WJ@TA3^lR z#Kn2t-U5Dr8i>)XkbXc^ z7wq@D$_HVz^QEO8@vcQIlF~F*CJcm3@{I(6I5G0j)_lZZobooBDOG?QF7>A>^MT>) zOD3Or|03fn>K}iMd;BDj3z~k?wsv3LyW`Dw{itHQdUeb!fcfR<06L;6r0sO~FJ?;n z4;SE+KdFk?7Pq(77?3Y{0B)JampVnNAJ2EuStXJE!MONE6ra4K&ehNOqPVYLeF=`! z@a=e*%PDlTz}_-VB!K4X3<13p=8XwI^k9TBxmQO45fsaN~ z%<}b&8YiXRq6SR#Y>lPYmMy65)>q1?+s+*iJzR- zId1&6`#=OKkn_@PdIEmjD7LeWli%76J}R|aHl9UN7PO?*3HqEyZ(6pmp#8&7>XY}~ z2CNQ=@2sM0R-w#31JHA+4D{hD-I6rc0p1G8f=|W=z*mCW?PMflcgIFs^3l>?pgXKP z#hoNMG`L#rXv(Ya;NiAI)g~pOpGH0z4$1$1@_$jl6-Cuc0-rD!x#<+TX`7l9c}XE- zFD#@-qq}VN# zm@(R0ka$&<9*ga3MP|I%> zF^S?M5|O+gXVv=ih)(N9P|<2n{8@!sn83R70IT6s*xo9Wy^qQ8N}dBew0Q1-n##5H z47%rI$By-9H=4gWcMX4z0j)ZI&*_*Lw((72E;^80Zr;4PGRUZ+s>;ZCsYZZ=ZxT`q zfZ)KE)eC{4;wSRn(&Eng41Y+k&)u@Xv$y?&^;<=p*q#%rrFR@Wxj^L~Vmhw~r4AUM z=DPFK@ml&7#CsArXpe1>m?z!jV*z-xjzIYI4Gac>_QOYH5WqiDDl?f`yu#V*z@P=y z+H`!D`Mt_=DTC_f zr3}JS08C+O^G;Cv!~cf<*(4Xja|u<{Dg=?U51ng=l7jv}+<%;py4Ji)1^oqpUD_iI z{Gc9la;RNxcKB;@#F7gT_aTB-ocI%riIFBu-j>1K6e7Y&TyBCOnAVuS=1#7S4q;^# zB;X4Hb3p7?v``tiiq;Km{QIO)#^^-Xf1+O#Bca7+l5s^QR=7Pk2`2zUhCm~&ZIRXb zVqAm_NP1Mk@UtQ{08j8r6J%@Mhh5FhT6v9QXVKCUL?0dx+}$O+8}X@p2!@25%EQxy ze+qw(P=;*me*7m3&%e_z??Z+ep;_#yTaGG2XB>kLl?;thG^t@^ut2S_Lb*fUAeW19 z=0NQe=6$Z`WZGVA$xH~Ga5S*mV_-d>IJHA0p&zan4QT1(k&yd9X(&hS%ZpSO{d;WQ-G>kRkxp10e@3Vlvjgx)MoIE$ov$|O>_(7Y;kbYQX>@^(sHhYLHVznw zB#`m&i#x~Fa1sMsgvE@J>~-NFNx&vG*8APv?L7m+^@SM{PMCFsqleY zVoyzG#?sAvgo_7}G<%g>*P!4cvjYdf1tNSCBuRO6Q1>xHblG&MVK1N|IfsUY;O(#f zJK>EuD}cGd>H0svSHjKZpQ&^fJ4D~BkY~K0XeQ_B1I(`wzhE+W8Kb{Wxn{ywBnhuk$ik$sjip(5<6k@9DlB%$7$+hbT0$U`K@FHgc zVkW_7Tq5V9{?48E@iXLb+EHts1g|27HQHN4hS%v|w{(C!ch#13tp3k1T!IGm4;d01 zogF1qRr13_AOT0d#pO1s8KiB)_scs!;3VIVtl5t|L%7R?&qJ)wP}kx4uJE6ma3DNi zG+sJr2?PI9+a%VCak0F~U7g#CKMCAJ92q;<$W~WD5g!je5!Wm-=`$LD~@iI-so3D0scmEhL)~dEG!Bo>U z-no7UID5wtI5m+GqeCMFR-|I-y`mX7o3l8f6w zF3znp@{8G7Gba@Ou)x*f#;rK zIdn3HN7#b-E@3=`FpsZkS_W$=cxGh4?Rw%_#VV6q@pu&ae&<7*bK8$QN=twA8@aEx ztpY&uU*BD1$hZ-MXcfD>3D75#ZK&ibQ%Eqg09VR>#7$5ok}iU3La1kq4m51K&BYP95l0=-dFrCA*)XFqj4Zjfp0t13kNoFW5 z$O{MvC=RLba==B$|NAB7-tDh>~5q0%rlzrNoP;M4%xIDCW-#>lu98Vd)9 zF8bRJ2p>t46y%%42v!Agk3Ww^Rd`QH`A+}_*r<>?23n6r_z|_D31-9vp4h9wcmM>K z)yW2ddLm#KQNXD+NKl3=6hB?Rg`{GX+5xd#T?MA1L_dVYOMU?w>T&(BYz$blQP+@r z4y&`jG(WEI%Xw%Esd6~Q5myVOM-X7O&SK79B@oN}5jqCM1yNO1eqxD-w1tm31zn21(Tshi82vYKjYF2sj6;k~U07`Mf3d}sj=dM|iZU@VY@XAKJ7JHaRyY@u4& zal_M&WJBcAa10s8^~ILsR>Iqi1A7Eqz}Lb^7iUucDm<2meQsyzp5>^Fo56j;!(0<4 ze;UBCeQ%{J5z|y3dczu})1YP<^$(J;t4}^UmX$S&Kpo)ZHR1aLk?oC1DUL-WTh8{zG&HIKOc9;KSjez^_svVyvK`AhJINCHOm_ z`3?vv#8ewkljx;32Tp|r$e6K{yNmG8&dyH6j)2?a1H{ZU=q6_c060%VDM*a)Q2Ze$ z9*Zn3$>=HM9_M>K1NUB}>5!e`+Q_lQ0kAJYU-+T3!eh8Aq5*3LzV1bCW}$fmzGbnZ z3g1HujJeS=lD!AGdh)E5L(E}2fRmm@cA6K5?dN8PgZbXMAQEKxi23sb3#*tl?+)gb|~BH5v#?}~9?$w~w?+S=OQ+?PbTqDWl^lXf!IATtaM^J37k zPRCm!@c5ETnuEuu9HvP`tO}o=TBl_Ge>k|0{-z;K^-n}ViQiAGuL%e49Kr8UWxPO? zM0>!71P7H#dUI*}IfPpxu5`he8ACa;p$WccDPIhd}=feREG$+LKw zR2&6w-%g3)4Cu(QUe2QZU$=%YqZ+bE*l-09bEx+BT-o62|Hlt!4V)< zWEm0CM&D#)iixHs>rOj6A@q^N+zS5M50F&&bAR*>p<5)5utW!l30FUi>#}cq*VxAj zBW012XQZcWDX4-(>Vr`@5%qy!c0^CidK+$X66VzR!0o|xglT2=3r29pxq}#i;1x}t zbwe1-i)xRRXdf40WQtkZ-w61i3n9M(GFf8fjj|i8!Db{~!oYfbdCP?_`m|Fodij6s zy-gCCqM5j4C0@czAU)*B!?W+Mno*578@tZPMV0c%$bP}x$3!;=Jv5nyKvGK7es;$u znTQY#6QjQ*I;%k#;oi%aX#tzf3~-O4o$+Rn6*~xbD7)J5 zMY>Gk{!Q4xh5$?M97OI!wu{XwjCDaXTIl*(J9V5vSRsx&HvUcdY7*|Wo_a&U1FoM{{}Lf*TJl-8baEJQD{N(k6#7L8ODJqqLICU94Yh7Wi~ zpI7UeKo<%1(bp`ARqla$l0EeLCyN#?ByI*r=4WLwqFe~CFI(2O?D>y(e@x0I4wJFZJIz1U^D}MTMk)3Y2i*iLnEN)CG!1W<+{; zPf$uDz;%F!hF_Qs{!{@Dcg52kt!vRrS74aH1V^+LMi)1o#2F@0zL16j6mK3Tmy1uR zgPZhG-qFP+636;32Fhq0nsH&I&$hC*W(`onkhSXzFZKawXPRiNI}a0@0J1DVODl9D zGAimGN{n#LTT+%Kivbeg637WeDG>?|;!Nvmeh^|Ms{yFNj)D;djhvhuIsTVG=9)mU zkEEw_1hO+`5CBP+VT#)j+2mMze4dxWOB;`jvv~eOS{e?g2Hr~y89|x5){AJyUtU$k z^Cc8i#;6|iHNy*WbY1{0ZU+hSFsIrVmy^KQ=6O3$ z-npZWWPR%T*AqK@7xU0`vV(t4>X8)w^4dN$mTXgJIdg&nC*w60@1kaamn)4q>NBzz zgfHuT=f!d=k?dTOu+;}4@-*ok@PKNEIJ3CP;sSW-f_biO)eSE-%vZ27D9lMgi!~m_ zl^0_1Hgv4=a5ykoE25?T1RsxMi}C-d^2flaySuv;S!Fv`kn87;X(HE;QQm*5jKW#) zrAdIn1)8i3-h;^v@aWF?EP?;4hBFfZ(h(1(f;}>YzP>)r%~>;daL(iu6o?6jeb(;mzB()zPSZqYoJ6=Olm+84t5?kK#GhXqv22PnGrLhf zaSGxFv%$+-Pb@*oCOFLBJ|34jgrkC%UB}T;1dbgO?;dg!7d5B|G?A47oUJa|>OYz) zm#iY3y9^IrnjAbJ*kl_f@p2@;-T)Sm+PGT)uK&XIzzu0`+M1P)*vbRnQXW#m{EH_h z)3;=SobXtMML1lKiK&CN!YUSVgqQ{p#mC=ID+S>`BYzMNY)JnHP#5FKn@ORV&3c5l?=|2N z25c?3i%NMC%oe}~1M$5koO_YS2i%^dD;3ZSiGht_ow|OFo?T#)=l@04Wd?8s zT4&&2I^!c#lnD1Q5mFlSf^a45ySXJe% zz@9)IE=LF`XbKv!&xFKQ{9BE%)?;}2{uJ@92aE+J*@ba0m{bdMMU^8>IjVC%xsAaI zw&^sfF@Il(98w6)P6+9_CI_3K%>N*$w^sij?*2F*zRcQ$Gyotz5*UKAN6;WUz;Kcs zPyry#Ku{tG-WUS~+m{S{?8NMB_ru#K*mz|^&=L~{UZ3d$(AzZ)k^#$;W|0^Ps2`gH z9wls@2ouc;D|R2B>CJ>{p+Xo+C=ZCcBc3_&L0HWdMfRSdTFFKaY9H$WMkPf-VS-wH zV4yMLMcvUGDVO8kL!rSDD-T5YKVQd#V~$#pnXz$25s?w4fnX5+i!Atgf;{Eka_%2>E7GCC|L_Yh5j4Hg*aBc#c%+ImqLQC2Aa zQjt~5S5dHxS`Hd4!X?WiV1bX}fRTpof5BZ8Cc*;q7d-DQo2|Nb4V+ML3xVz}ua@7i z!HKdW_jm>ZkcMC?H7aWR)t4s_>Z&|&a*F{oEx3z2Cwg!fzDM9CLPH}}4zP_^Z5%gn z1=Wp-r2~FvA4}PGB#^7$Eof!76|v9rr{Cl$W~J6OI8~K9vcE;Ms{(Tq zY8(a%-S6XyFJNkr2>>82%QD+IfLTSQWv1E8K)Z26Sd$+;c(54$_;bL<$)X8L19%D5 zkFg2R2VGzqqS#n>!y*<{ zK@<8aCJY~`-P3t`t>VC@kr6}Z|KG! zLzUYEY7s?|N(ZxEGF$lcX?MYgLbHpt_=#j44n^VMF;fCtf@m6+73SS2w+&>mErS5g zU{AeTd%Jd7)@OC4(OKMG)RRlJwHs|#9`Z-ifbyjgKky#kfj3V&|HB0!w%a+~#f`9| z(&%1v*ui!Va2R-k9@2aTLeXAkq7eq$H%e5kLbe0=G^{ta4kqJV-B{(Q zZ*@5>jfDc?FeuZz@CzrkJ1%c)HNa8S>I*?;*9KS0-rhb0Nj@_nJ1uPmg|>@JFJ9o` z}hr05$tt-#r9usfQN~NEqg>;dqan5V6sHm(!)5mbuVlfssECz4yBye7` zk(l(`j!n^SEst&QQh#{lU&e%t;s}Iamc>ke(b*ZBrx#Tco%UGBXGm3oH62!46qdx;U?$q$mmxaJ#Iv4;dlFk_%_DCe8>G;O3mLX2+uCZrdKi)4QwZ&NJ z^gn&G1LkSyAyv07(F~j{RHofM5g!y<}q!UT1)Jce}bQ<~U(k9*P&Td>m~y zfTDXG3)Q(pf5U$+M|$O zF{kna@cmg!^?*2F{nC01Zu%Mq6X8Oaf#Q&?uVV4o}JwvE6;mk zPLBK6t2$Z86JM+o71j2r$8DFV9(Q!eKXt-h3W$v0#?`j55v-1vczjkkqiKjHK3-l9 zf3O}p>`SC~%|#{8zQ2EAF~N0o=kgdMz5nv14Rh3&i1oXH|ED3we8wIL>u=jEc5U9e zb!#_(;{8B3U|NR+FyE;FXsQ}c=12ou_Pe@1#67-G{EDb@*sqk_-Mxb?*X$w2V;Fm! zxbq6|;u!{e+^B|Y(2OA;Y|Oln4TcC==GXgF1IKm2U7!V1oBeIyZ2R@=SGY%t%<94Is=QdoIhdm>+K?tX@_BH zFR(#b8N_^)x1iBHP56bBb`Z23*kQ<1WYyi{M~|fSZgilT>Be3k=8)~VX0uJp?aGm? zhd(M?U#yMYSGS1%#t<3e(g*k76|o^yO6t?2m84Ppm;^T)1u!(xjcw>Ny*YP8AatU1 zPk786+Jv--h(*}SaR33ktztaCSy;kFlWa~H7=(aFaNs8jYWvuIdv{tq0`k`kP%~`l zO2p~?4i5LQ03c0A1mYSP106otrQ6%yes%>PpByTS0jrl=Ul93`LtwI$MJm{$r%#_& zVU0H4Co2n>=n++p*WummH;BQhbg6wn5g%2ola!3wa%3Xpq4PC?&#?Y_K$Qc{aFdxP zw9DOeI&H`H?GJrcBCvp$oMu)i(^&zP+)La_h##H?+(zGj4J2R;d*{Ip@S)M9%53W# z0eAj+7Z{Viam|`=i>o$i4E9*sEHM$B=HMW+Z+2AcQZY%bAF<_#)(wM0uyn2*pBgmd zMF|nhV2=t2Y753)rKoOj5h4ltV>j-DURW^p3J!b%0Seco^)=A|G$Z7-B3HKB-Z}P= zZ+`r06k1KEopl)me1-O96z8Jh8cxUtv;?7{p<(-q-l|$Kg@a&pbaIdhj%m*S=#)T8 zbCUUdL$7Q4B4{*3vc$&1vMg8>qbrY-*Hd^Q>fBGgz@HReqVrVrBaH#4RS!@N0$X!T7T)5$ZLcE}WudsYb2W1`;lK#QL zbJ+uX9Ij8%+oSGbj!|$P41#;8{^jA$4u%~~dhJ6y^zy`Y0{=TCgM{Vr zz3quvxBk{T7UA3&+%r<(VUC}H-j~SP$MvlfUTs>~b!~=~5LEDea=sr;NF$EP9$COA z_srh3MR?B2-kxmX3qe&tg~+`yITZICC!8v{5w^>3`qA%!SugF|P@J%y*fqRAR#9!?m)>;P>pwRjU=&6+2$%`U$+@2nr|bIpEmp0$#abpkV)Sujq@lBu zMw!^y$kf`tRb7~zi+yF_ZV*d;sNJ5mCXBez=)24{l27#i0mXUXs4k0BMwW7j1-hx}I3#-@Hu9guZG~>v{$ojnJN?MzI`Lma3LJQd1+G_9Vc?Q8V6H$@K zhbN4i?lp6AbBjKyAKEw4eZmUJ<9%Rs{%0h6FdG;sAW$dK7-P`3C}U71thIhkp>;$* zjQDT^il+QfEfOx9s;VlA>%S-k=3}g{9`6da&CVn!Be9!9?}q?IQGZu+N*!V&ygo_} zgs56=%t1}h~m&`#)VPRcXW+2GeBy}eJ4#3yQNEbBEV7(q-)WAMx znxLSp%y=zXXD)T7RoZ~of&pq&LDp>O;0u&)Q6H~ZtinR@ckkZmQ2M)eomc-kp`B~7 z1=}nX_dn*#{1S5IiqJ2V0v0K8-8}9)CdqXcn-^`sk|W$?v!X!qI^yrL09|h1#LLfb z06fJPqfdkk8ny^p*y*eI5?#uMBJkU+!dnbO_Q&RVb4(Ls$b4loC~ zB^zmQ*Y2w;1q61Ip8@zZ9hfs1XHkF*pe)c8OMD0q;o3a(Em;Um=FluWK(0ey^AQbI z*>q4f^F@<#OEL{|c5^cTWYXa)VOUhHx)AjX7B!fT{rJ&*RU25Q#Fj04$7a5dv_5!{ z0ofqG-P0#easet@x42xjT1i|ZX{?6ldo))M_+AP zWI?ZcI<{?9j=@J4&p6@g)pZOR$R6F_ziG_&GbFE4w>fY-C7PSHTZ*=PdE%KfOZ~?C z_Lk0k?~VM-S*eb5$IW$Wgn;=ALxR42G1So^t9m9h(X#H;(LrVtr4%7(5fRA^!?v|6 zKfa9{Q!W_kCnY9o>--U;0VBvlB02YH7>uRWj2fsKt|~)R!ZmPeF8hpn*5II{fq{WY zh1E{TwR|yq7ZGtd?K(Fx0OLzp%%sIIZeo;@a^>o(-@pC%{F@pdy@KFEiKJ>w4;VGb z{n}VePa|ajMNG7&NY4Yo~g zJ)S_hZx1{XYi#@8sh2No!!dDCcG9o9^Go=)Vi3PkwAd!g4$XE?fnCu*dY|`(}a*q}k zCU-x=x&m|?77-!+%c+}xd*V5?(}kbptQG(PdH3N%PnrpatXiNZ<7iV-!LPnePM*Yp zFgcZ7{gS0=QC8O7-N$Qg-O@5PUaAuY{?;muCeNR+``V0PI04YM0Vp(9gi}%@O_-}B zrKGTiH=#pcbL{5EeALdE{$Y_Cm}uZN5x^8440ple4^Oq2`Kq)9j5yRG#0!eJZvhG; zE4L{^<%6rO=ly`^Q-`(gS?7|k#Z>CZaPlq_Iz8vp;mU^Rer%N<$2?{b_+UPJV{=IK zE$L{55e;1}K)ztUt`{i&@|}fAx8WLUUM!uotntf`LO2Ady+EUB12c*vw6ogS?uzRm zuI_l#2np;j2g~8TGg>q%1ggI7&#yVUFm)aX`u^j`AV@pSpm0ZnX@+IvdJ?I5Mfqig z$nikIh%|EHqD4+f5DirrDPGxjuHgNBJcDFZX_LtM1Bk%z1&q#XbS2nk!>d;-L0bY2 zuZINE4(ra*Ut>38eGoELj?=dh8$v?mZqxjh-!|j)j zZ>HVY0#gz~FQcw-`+n;ZmXk1ciAbuaijjvZ#BjR3o4+i5I?d}HkNr;s$BQGcmWkHR9lt7zk&=So~_1g zb72Su?r^Rl>8)JgP;p&dyHT>zY;-RL!U3l(baQm;{rWl9f(#;AOl-W1%c?xzUPcH; zTT7QznE5(a)TuxW&lpytaP;=|rS%%8DdaYLN&f-37?hivti$u_inQAT?0;A47Ba27 zq<%b|Y6pue{ijV+QG8_yGtqKD4BMiVu6?4Qq?ZS<^T3GJ66DKPvEp0n^ zhNTi2PWBo)IaB;cb76v33JZ#o@r?v zDmHtBUBQuFmLeS4SMm*-pInVhI2f|WPt&LPpEWhyZn)lUSP zW=y%s_BW*}_wQL%(iMXMWo5+&iptsa^rBCP`Nd>p*g*sb58@u=2@KRO0|W9-&+^Zs zVY}PXqL;d@Y-g7$8AC}(!0L|-Q-A#U0gmNe_p&<6&+{g^DJS8}g9*xMAeRu6LodI# zpgv>MfxTq6IoWxj(KNuT;b#ce}R(~6<$@vJbcf^NnvIz#w83cC3>jE*f245 zugDNwqOfsdk}y90=5{`QORqAEc~yLfW1<2#H@8FKY6>jx?jP#(!hIBlA;HAd6tQ$i zl8y&En(|xkQ6d9{7ov};tu5hFE^tv{(cP;g!S|dO->7^K&QL(6PSK^B5I(G(IxeRG z-q=2OXKI88=t))2i=Nq)6-9mvrFz6rp7Pq>hBJGEoT-rvdI$h}AFP8wY0uynmUMRky{vEY@7FKqE%HsQ{~#Oj#pC0D!`@>oBkZ z>Mk%SG9yHDY~3=3V4Qr(b>{0=dk>E|bZLtpX5Ruj5dmmg^_8pBn}lvs$nD40%Vmhq zc#pe#ixTj6S|V^e@TW9TSYuV?Rot`I4GhNQrom03ywk|^hYHq_x~|dl!%ZU!k!#l! zz<(m)k(BS?g;Dg*Lng`S>);K|Le2G`T1r)3e94mGTD`8U4U{w!E2~Hli+2TjfZrt{ zBSTm1Mg;|nCP$1ft1KhvARm%~B{nV&QavK#6onxVa1+y`x(`7`>4%U9O+i7|8Q0W2 zp+WnS8o^D6Lr~rTA~krPS+H}`MEv)S+m28qg|b^Iimmk-o2C2(a|3P_%w{11J$G*F*>YE7EOv${QTFaIy#7Q4K)ZX=(d#_ zt{4oVCCkNzH1REj1GnAIuD*BdGv*+Ba`$c+e_bP5s|;QLO1o?JkFhxOw zOEi}7!A7_rUBXjzJNVYheUyh@D0=q7g&p_-ZfExHl3kFta~qr$ID*zQF)?8Yi!SvM zMSaP)O@;%25OXmI*!W2Wx~#IV-}D^rM=o0!0`i=U7!5N`3pXcpiI7=KN|ug(3Ik=^ z96KRAUS8AXyFL|bvbV|%D_08?ozRzS=@j)C7rGy7dVB%@;e3wH2iQR*)sD)Q&Yv&& zVAbY~XN?gT4*tw*M&)rGvdTJ08p)Bq6*NW%b| zh7q~<(HXFQ7hmgt2gk zRTp&{Jbd~zAAK`7Wqr+=1x`7f32rmy^c7oCCAnm$4)@;jD>gfq>1KGo*^+&Y#kQ2Rw5SumKHFi|OL`8pIdHDY{8XN#IHR2g z!#iT`U@}Rqr*NkoAmhWv=LS?sw(ci%g_`)3IxsP@Zu>kk5)D5TxFxZ*&dl+>?>#}g-Y4>a69{Iv}Fa^*wM)=!(`yk6>^jQG4~!#S+G+CUU2v-v*(i3nk@m zRgbN?>zm^{RvQ{pfrUCD(oK;wG<9 z0MH-NN_6{5NELn-zB-a(QtcZZK>;X|6hNP!i&oCzd%>XQmaO#ivS5UrZ)iM;g|#l# zb6s9QBpEfpJqt*@tmifM8*?$s`5=#_`&2h!12HhpqH0gWo~`QYwtPN#F&exq+;YRh z!&!=@2`UE3{P1vFQLM3!@2?+z!s={nF;W+<7pfXL4Mr%eaMvXaV)QlxAVoK3>BgX$ zzchPgOU!c5H=!7+5fC544y+k3>sAM)R}%;cSyK)60|xi>PcuN;hzC<}a4@j}g|dWz z^v|A|Wf}r-1_+&S?p!G3beSm2wJda-j_=RNqJP7QLg~Q)umi}eBMyaIy)gnd5g1^! zjBU&bCTl8{wNJA%n0P(94|HVAM>zuTQlw6n_z9Cr1W-z<)wOsCVe01bd1Z7+_&D#v zGTi^w)OHD-$)Qwa*`bo|`u z__wcz3bHng@IF z*G-=I;GI&02IL30Ozy`#tJcs^L5Wa`aHAiv^6=xl9QZ!Xnk~e&7s?Jii^AP)Gk93% zQ1J~94;Q{(3s-5(=-p-9nymI6dvvOTpAhjdpWX)8EqT~8Jq5Yg_koDf<$Lxr@4H&xbIT)AlzSosj z@ZxYWmYHF+YFYRTO<+0J&D5g!KsW3;_|eM!E(?=W)!!{(WM?}@ajJw3Y8WAEa*)Nt z(VjYGuqQA8L-FSPev7?(gqAN~w0`|xKSWgi$dnY7o2AQ|yp^5jTLzENEL6ip>I9M( zvH~H^A`ILE0)uc})H8o4V0;xtzaUpP)4gX?&{qin7ztpbiYY8#XG6AwefGPPHwzR` ze6NCa8anbNFi{7crJ(c9Khvhp-b;bIr-xK&v0Y@fkS;0~Xy_|&MIb!eJQqgjUP6V0 zp5tm~TfewvJ=BQkb5Q^9`jp+;`va#FP)T-42|MWB_o0Cy$*%V6vmFg)cUq;^uJ5W_ z#ly4a@x=Y6rUB#yMS;5v?gEo=bzWs6YNY*eqb$-9a>}n1zHG^$)_**SIx9wnZude zY9Y~S5d?Js=+xDhWxCJf`2ROR(&~@^kHRt*7HT@!){gB^5wJ-SMhAjbNsJVPO{N8g zCg;)hkWF+j=+eO!98C6Q=!zv#=mUW)hrdz+mCvoI&nA63Wzl@ge|r`e)g;UC0}_1Z zCow7mQ|;`PD`&txBOJ4*PmP@>Ve4j3q-=FTe@!(1{QR($aBxtaLbYu&=D8PL&`A`* zMpx81P~#Ck7rIuUlBeqGw%|k{&M!o!26Kq;lh8+T&pHA~wvcWH@k1DvIVhqDu#74& zf^@SX6S3MJ@7pfLpw5&MgpKUn*XPE?dztyc-NAl7sTM;+L!!U0a`asHB=FIxU!uao z3_A2NkImgwdazWIya$0ZAV3vE7ZB%p)YiskCt#q4Rt~r>@^3tJ@mc&iGg|5}d&x=E2*yS75kcR8v!9;DNSS&$D)tnRFPi@{C)xC7qCgdUDMqF{Da> zrW%vz7{E{u8X7{O5QQPpj;}aI&)C-2lCZ1@8M4F1w?Agh$%6xqk_cFLX8i3HR54j! zgFsft$8WYT~*792K!-q34uT0n}E-I2m%8(O6W0+szH$_H5uBL-WlMjXPS->G+ zR4aa8S6<4omKIyMT`Kt*Dmf!E{s7i)7@-Y2agb$lyMun+>h6BztYBbl>{(g$4S_T% zJ>38;OPqqOduDO*4sdTgtEUUiZi@kXFEA*~&d#PADkv!>MMTF$Vgu9zrc zKr4oKI^($@v`N$;h zQ$4mv<1o>NZ5e9|1_s;36FbP81RMM1pVsLh%SyCI<*u9FxFP%DfyE3Wgdr zyyiZN*o9PXs8#Z}Bc?a56e-bZZpCNJF69-ZJ>#8}TH4I7A#aZ7N4!bERXAOwpq{}~vNJqVOk3S&>z>l=~< zX4<-f(PG)PwE`MLT{~SrA2}Ubu?_7IS@B2coS^#a5~TWZTK1=ME4I7>%?_I?PI}>9 z5kOiXb`pRe-Nk)?+(d0L@I3-4UwKD~F`i7cD($)!5@;icaEJTvo@DOY>A>^%3%;T$ za|G?Ei+7)6$+h?EZgWsX3F+)C2CH;0_yb^CLd-3YJh={SBnu4ZW?y-d1#@VliM#@I zJT0(Nn3t}4T{3Cu4;L_G7P1DGs9OPAqh>gXuAyj4H(qRFf{4n5D6!z%fU*NPWCPLj zL3{BNYL~_A?Dr9DF7&^tG!iK~#vLW!EkRBW(I}&YyoWHAnVtPy?1N-XjP}6j%kmQ6 zGZ$R{<{!MZsn(uGXuI%|p_M6{8Q+futf*2ae(LTp0Xhj9bwfu-h30iN)N(gb2B0!c zzIu`_rHdvOPk*74(@^u`-TR*HIB#+*a6{~_Qn&7DW!^i(_pG?d3=k|}qBubfN`84) zO3Z$b^Wu*Veg60{1NXwO`g(Qsq0=s%*??$9ysuU`HY>=xxPB# zj%Ut{K!;mIry$A>4pt{4!vPEYd{N|3vc?nP>d1#n0f#YOCXB~g#+wKfYPd=Ymz?K-tTTrs zHvtUF0SauGATLutZ1owlNKrn1{-Vk!8=ZPXamm(1W`X);08>E7m54VBYoE4l zd)blwgj3jkXRUKm)i!b?L&?|(NM0BKI6za8#_Hd5y7#PpmLo>nwoTb7tVI5B^3HC4 zU?^5>&(UlXat*2TVAg_8zxjrS^@_`x_N62!R%u}VjCmz=h{U$H|I3$nG&Z}RJ5+3I z*mq;MJLYFjvM-340rDvEkSVL3Vg-344s>xhRhe!LT&jiML3g$~hek!gZFPUY^8gG) zV0h3EpT~xSO#oe$1bN*l9N;3+Pj6N>7hAfj#s&?Ah04py!!MM`?6BX055zF?!S&Nq zih{hwi}1&-MVEQ;2sT$5fqo)xd^z^qTI2H0`wPt#6cCTdA3~?G5FiTJ;|oy7RC>N$ zg|7G>%Gn7R|1?5^2}8Y8Q00L{mVwvd)7cfp%gG})17wi8!As;fmhDZLMi$l&0M>x-8E zp0}eyZrlLDWr*V^0xf@htc5Z-HJK@qc&{<@2QZLyjs@Phm<1b&r&U zNZt?8J{ToEFTW9?4siMnIxm$0+&JKDa>f+@`CzH1w+I1t$T`$?3+{otE~+a6gOR<` zgri5m1vCwVnEfive#iY=29-Ms`;AUH9y>r&k7Eh9S=Rz9ske`pRix-&PK&zs!pbnh z)RUY@R+yU;@E;AdE?8$6=MV=96yDl}dr@{{JkAD18p%_nr!~1>`URiZG;?HkFkv^~SD_hN1iPqcXbC9_l+#IM7mBA| zvVO98B{E^Qxb~rBU|Ep*?j61f(6avb%zDrvIdqz=hA^kAur!)H)U2I+Keh1Cy93aP){37#LLPDtAccp02#%rhJnMEB-yvWP7 zl1Pq`UnSmrDQNri>+K&BR-Vu-KJUj%0K|~uxuJ?A7>ho_brX)uQu2}$?WFl9O%0I- z>||=)t}7Y{JQW8Gp!sQir2{E^?w?2~k5hp<1`Z0vzFX^+qjLsNEme0Jd|+LVUxG0o zwhO>jM*sOhIq$arzVMUQYnppg(S+Fp?Ro%NJmw{YZ2@37v$B%zf^8Qli`XC#o0{@Q z5-0mF{b~czJju@w4r%sNV7H_C~S)2@n;^;z$$>3=9_WA3l5- z!pi~<3DYn?W`;j{oH3#N<67^p%)tz>C|Qu7hDLl!AZ%D2R5^Q0%zik7moPTM~Z#K;zWZKEnvJ^_#Wt z@wszB>|b;(QPL^dOE$4P9Ous}vESfqY?bWK6Pul^cV4~}cP(2_Ns!qo7AGTBIZ=E~ z=GlN!=h!Mj&;0xI@KnK@_F7svQv_8u3agBq|h@kdl1&Qk}i`wa@om*Z+U6 zbFR*1c>RXwSmsf zM`dTs?)DvBjh($RP$k2RIuJ_^;}H$}3M%5NwQEBdg`;57VFW@lc*pkrcqe9Ue4g;~ zZUYRHTD1dNg;o?$3r;gl!%kar>D5dPjmWC%>UVoa`1`lwWaC0)pg3cO@l9vc0V5bV zQgQpg2dar80|>OczjoPxH|i8ZJmAU*G@7icbmVwMtdNHEwZ?~pf1|_|NrEmC42Ci( z{KWWN{! zHlh)#sOM0}8c;&9h39cRb3~Idk{* zcwm>;DuZQu{l*PhxTWA(r&Z}jsNB1EFWahk7(YKhNYw(6N@)T#0GP_!o`33qwLwnG`Thb3*F3hWvNEOj+)3zKMMR9Ts3U5T0wlY7 z#CcqdMK4Qk-MVT;Czv5>$7ZD31jEE!n`!?MGx3gq95-BUM;A*kODH+{-}eMPh&radaPpS{v`oUP=jK zr@mXrJPaol6hX=3^8xe@-Wcoo55SlQ$CnSn!~6H680zT38<$w9 zh><SvBQxO^v|EwOz68kvK?8PEXYbW+{`0VGzEgSH&x# zd1qvf)5O8eAh*=*yONvhi}7F7eLouVesWdIHqj?G5k@Ct+h&X_wRleSBrDbn$W81N zqy2Xx+gSZ*im*sN%JiaagkG#7Ocq%K$bvfI-+*0~H#A^1TFgoUu0Nt#?e**s1S-I7 zn4xQDrw3Jy`JYY{=bFxWfo5KF=w3XUCjP53endDQd~x%@TK}1|XB%qVf~yjzbc);| zNdK{FR-<)?g{tfJgO@q_rT#!S7M0L4lCFt;D}U{JdUc}x*5=$b&qvlkh=He1B2xq~ zh6s^_4*-m!$tM`RI2%MfrK{3IGJFu9cQ)Qy!dN2FqEUVUCr@Sr6(!sT36k)xg9aBD z{{c#LYVQjO@-XIOTSOK($=ZoeJusA*JoIRWJdzX@SWH^^S|iSaSwe5~@h$YxwX!pj z0|=H)D9E2_E8-b&`W4VX`<^T~t8GW^0X9-8UWbwtEW<-9Y%b#CkYTMv<)4kP0Lg|8 zuu2P#eZ=F3;5aojG!$Jb0~03%f0LK_fm!5*AOO--b67C3tJJn0c)D7E0bd*uqd z`>qHj@Sv&zh&Jsgjtt1cfwMB@ky62TEvyGlFOMcr;_yqCl)BRvYa3Uejf&bBl>Nl) zkR;n;e@diX`ThH%4N*WpeZ0K|bHoF(&~y+HsRUr|ZG(PK%bPbz<{l-Ze6VzQVnEHA zvAOo_mP2|Sk&xedpPPjKy~NyY{*SL=5l}@8h8yaR2y0MaWzqg(cH$%m>YktI^tksA zA*$P~>wb(C7O4b|vlA4KB0pgx<1aWte8@K1z90Ttm$n@4P>Y4KyQ-#UDkR_S^*68W zPKV_aaq)Bv!;HYRIgO_MXpFT)vtnK1BdksxJkW`CXqi=mn1zvW`}sYAsx8;i0kQ#J z%G9fScJDSyE0BwYj(8kJPn471?=(Q1Ox&oN8hkD~+7SHdOcO6T1qFW$DogENicK{8 zg*~q(in4ahMy@W zi_!bg5NgWOi+6H+rhpW=R3Z=wE0Se3ahxkZ^J+d`RZ3O~E~RfsQ9?{f^zSScSuDQJ zVY==cgqM^eJsbiFO2r*`63WXJF3ek5ZN-(Gm}?!Vb^?dLg44+dPpd&}giLLYq2FzZ z5%@#d(@3U92yeC3WwG?GX*&)2DLEDk`wtqcCcPSGjyuvjAqM4x^jWg?#|KWsb=&X0As3@p1-}(Ip616!y)}@E9EEKetvB^ zs5430mg#yFbyx;N&=`XY7Pmo~kF?#*t?*8qSaisA=u_zh%(F8YCnPWbyIR0cX8RHZgWDMW^M;@B`a3zr&UcJVDrV1bxaUQm z$ulMYu=W-E2>KErBm5l;ub*!TwGaU~({i(q?b7s2D#rKR@dTWvefsEm!q0E$3%`ED zd|#)_Z!SRb-+!C_08T}PlR-7VYJQYfyacjo>|Dof|c;Zt3 z;sp>TZzW(nG~N!G5sgxOC;b7C81!lJ=997Z@zW%l=vk`d;7$o&q}T?KVgN0xppDrS ziDV;u#ya#-If@;?X2;^lCMNXUbLkI15xdjTaTFjvoZhbh@e#0y6QL$}-(Qk8Zs&8c z6Mww{x_1M8>JXxLhM;f^G9Kn7G7jQ(pb=#3G#Q@&(=`vccE z0GS_fjUyj#4|hW*%K9{I$`nt3+$OiZ)?Dw<7vE1m*X__!n@v0vwehEnhP)%gf_R)`?J^Ld{-RU@J05sMWv2$eB>o;$#QTb3!g^n7MU>a0~ zkK<(swga%mFr<*+;Ty*0W?ex!L^jemU|>soAR<98A{%OpKyY6R(O*E_y?fuj5?J`9 zU2p&ou=QEabRxIIOgldB(CVXi#H(3}+1cUMImuW8ig166$8kC=h~5IPCh<74VGdUX z;vjNSKr}y@9En*FvY#liMunKN|2-0L5an*TZjY!1tR~3h19vIPM)V)g%vDvLf>+0@ z$?OErUPnh2I^HI6hLkWVlSNM9_IPBFfmYSlPDd*>wjP*-S+FdcXh_#rG3>t>BxaeF zfUSLQf%?x+q}Kr)ZTkjem3fB3%^h|1$D;)w?-fr!M*tkdz=@{`^XT6}IL>Lf=(;h_ue4?~Vaie+T941E>x?~t6g)1OX zgJqk*jZMMe#HAk1sGCbW&3fF}v60_DOxAw#C(@}+FzeG?;TkiCD1Yr2B|SeHCI5>T zZgI-k$cSOkh4O()jVXl33*Nh8PueW<@?CVmH!*SQ(!Q5`l-&xg*4HDrecK_mVbpT5wuwS#+Ayw-!FiNYig__Wtf?i2P7${~ zP2rH)dMy=bApu;hZ-d~U;7c50@=PH*62LD>bY~E~Z~~}?091wlgU`cb)|U7+^R+eK^PrhxmuaBGTABVA%k*XuvVv zd+4h3A8bs{UkEH(uqS4s_*2KXj;0b@wik)pIBD6;k|dq&2%3?SSy$`8Qa=yPBy$(i z@zU%G_jD)xiw}W1m?+xQh)KHw&R9iV4?(~lCxZU`VvHjjslN!Q1>0m<$(72dPcbFc zRgOsjO<=NnpDa8lI5y~967QlZACnzT0Lm$^*|KGlkFW1(SQ_%kfFFSJob7VLX5Om1=iy8SyUGsQ2@;mAy#7#iION_Xm)ua~Bs zz2^c(IF?gwpR%o<-UvicDoR^hTiGwNu0VVegVZ0El@t9nhVj<4hGX0O(65g;a{6H z^-%MQWiZqdUxj?@d?nz1XkEPFD3ZqgfjB_Us||-gEP-xszRTY9+07tf+8CT9Q)AG^ zfFzry9L3G5?|R{nid@}gTY3gHZp5qS%IgLeqK9B+|E{IQ8*Rm6h{?cb1uVnMEI!g_ zq=8AREXSgz_V!fK`3^UR62ZC{{Nd(;!2u;GK&jpYOU-U!g8JIO(Db3~$Vn4A@)p+{ zA9u&cwR8i^POE~vOQn{s=Q^FgE^zJ{nfQSgzfJUke_*$}jCd^&nO10eoy z0yd3rT*XVorNWPrzx%%wY{I~|Xu_p6xxFd&7DHR6i=J;2=1QV&G_cEqUZN1UhqBm- zlU=)ashRZyP?G?;!fS>$3w1z|Vim24NyTbBjy4!*PqU?fw@8C?>eE%nkOgL-xC$@A z7?8W;Agpjsm>$hi;zKfT5M_5*_Cjt#Zjm{8=<>*biQe=SK=tXZb4_e>H7N)2pFJp220P(3jxwQj%oj?uO9Is&9%UH(^lO( z^ZrrEIK<$oX~9etRXo{%F_u7{6%-YZBj%BbKXN^xc@zG1oSOep4QgTmT6)lGZA~=z zf~oAp-C_!ri(j1beh@ zxVHla3V;jWH{?rVC*MKnp84Xm8wPU$3;y3F%jCBRLr+3T;Oq5y_*t~3o zH`|czVu(y07AINTlHw7qWngc73`%s)!ba`ap9j$CQY`rs{7;Z>-T){s1J*CZKy}7` zv_0K;$dwEdZ9h=GfH{7u(pR!VWY>yK8@dN7l`KY*n>bU5H~$Ivfz$$5eqc;m`}9k+ z!+|0B!ij{=E6B~Q=08N=P$4L}Ju@!C$Ip+Cfyrz6J%O1I&8|ZJ zL7Gqy4jC@&`K%pZUaHq^^H`)$V9WoaWL}jKnRd8fh!sy4h0ZVL8_FU-ArcF;gSh@m z9Q(|Kb?+Ilqc?Z(oetP9q!y8y`2flpICLBb$qI;%0C;X7RBD13|L*<9uSZhvoChoi z<{`Xgt(uB_#4&V{w3&pWJnh{i9Wc9!hoqREuGZs_9e_kliUt3ZCyNo2PNBw!+cP#^ z+=fhNhcc{QlviX5jOG|axFa7!*2lGQHQvBzf~A7d*f4zlzGf&nu1*Dj7Ziv>Sxj#6(gQdwSR^&@@+6D`Y8*5j$kh8h5ZdKS| zLH^;f0quE>%#Zh@MP!f5T@TnKs%E|-P}6w)_HlAMP4=qZ^*?K`W>%@|plP#%8){K4 zVUls+xz2cJg(USPW93q}6NXbHL^x_y;=(*W$h_}R8NG@L)VwRPIE{rWEj1w@Qz($v|v7Y~nNgeOj1?_e>2);iJa zES1GCLlP}MY8Xj4fk^KU+YU?3@Jtq#w;#GbhGljN4j}kZ2b_RskFWQ(QsD^dMj=u$ ztiiC%{dRYev(a!Ga<8YXgSH}+!c5=b*a7T28?+CJxhMIzn>-Y+YYJr{6+1UIYjy?B$)ioGXTT@c@fam@$LqS%79_S^G(%!am!A%*VkK zs_S9xT__EUO&p}(p6;I(HUjSfRJo1$`se1q?eEu^urjzXG*U1`|19hMf~aw84+<|( zKDlM=y8Y@Gwj43;RNx+0m7qx?=er{*N`qHO`yN;`=_vvG@1J}vHXWXkxQPAB)}o`8 zVV`WCR*BGEmTMRM<%?b+*rdnKUAbbp%D_9!%RN5!iFR@|ZAC_L*<rDoXj0_`)(2b|&4ZQ**1Nyma z&*A2v`3d`x%kX+)@b|09gMEJ7Bv}9RdbZZ2odr;z-&)5>prFW0&4E86ulRV9KL88od}uR7uv?)Juuo{^s)dm=D=(Xa(@rRuZ-{J?lZ zcOC?M|_}*2|Z- z6mrpL9cc9UsFPa(0zOJKMh3KQAWYgAoS5Nr8)rP^&SQXc)5tz>mr48|W2#6aI?%{S z)9qid=MCHoCt!gi-((ce4j)cKiR_Neo;YzL{7bkMX6vIsWVXZ@J$!e=a8FdPyqDc& z=h;u1rQq*>aW@n?3`1mqw(6C2nKkwfSJAuWGuj|Z1FC?Ni7$4$tg1vAh*}*W<_+KS z?N28@bIe`zfyCveVxqe7#;n$p0zP?e5yM3)$7c>z_ozZ!dk*2S6NDW|vP&U*sDJ6g{)#u} z-#Vfe{eQcIop8pGVyOCW6AD#?*C;Q8zeEEp(6{%=%2J|%keD=J_t1xv8Zaup@+n&# zI&?Z$hcFVRuC&#AMY8q^UEdcfj-|(kJ{dMl-`m$0@ERRl@D^$aQq^F^C+prsi;onA z3=yYjzzQa^@=|<3xu2q7cmFeQ#DNy_IoaEO=e#@O-2sl-K`;G?c%A;2f{K>g?^DGpd{*W%y1dT+K4fUm#%7rj-Mtg!d$6zKpK77nF zm0G~7Z7sK0YHyF_nu;l|mSXvnlHNON$**F%>y>|8g z9*Ez{^k>J&xLPKq&k`RuzNsaqai_n8{;ycea9Ba05){?fZ+>ISe>p}*PHy-(uxGGb zi>$L{rcBXc*+;jO;dS{-(Ued~bz6d$?|W%B2^4}1Pm?sCb*AC;LPZb&96jdDqG2K;E3qg1@4D00@V~oj z=pPo($u)iffunBNJPi~O0GnW@WyM2Z*%^9>&o*=>=x1}B^DBo5DGj5i$ zi1B9xC;T?Zz8~+;dxYi8txGmiP)3dbLdV_EK=whrAXm%P&dxjK2~$P0 zj1I*JGC2y9#b+1`%6h+$_4lun%il6K!+c|NmvKy&YbD4Fm|Jx(JdoYyf^E${hah1E zcBq=}3@Fy|jw}p}qKfgJ*?$-U_z4-q5BnmsBvqXUe#wjd{m)n8_#h7!Q&Urhowvy} ztG1Zd%r>AG5u<4!`t|}jsB?*%k=7r_Z=@%~k=LPUOhuc=Zv%=f2 zKB-h8?K7zuFe}*0!x<1$x4gk6dI<=e;+wa9`)y<_$@*tog0)7Y>!0;G^PG+o{eGO7 zaQUM1CX%#{*#hjE?;p<8sc(6WMZ^_S;IK4StQfkgb0=1bfgVvZzwwCtfl>ClKwe7V z=l43eNf~d?C^_No&ud>LW}EZJv@onhZs2{}oEIW&flp7K>x@Z%bytM8*zL5%{;Ow50LR`h&v8X+6;9HIvN&XLEcdWrz?tEeClf z$CfxuyCG;L1}^xh>}&O?jA`f=j9!sHFYAp&9ajdHga8qre3O%RgxR`>3&)i@6-@qe zev*^^?6SD(+k2aMFT};a4z`xW@>&g`)sS&}|0q($<__SasM*?^HeFz3uCh98p2J~z zH6skx_vlG`K>0EG0gO-8jJt5Vt!)5K)we?-E%)1&b(@`1UWf14=3m9b*Ai?s?_!sX zoF7581ah)6#9*&=%w)sY##)5H#E8WyNdRc%5i&vLG^{(ro`VH&SC;0!;25OBeoNL+ zZynMTUWUfNpjy6r;c`#;g9kqBX02ObOIXYj6BQFve|qLF3%@KCxgu+2NoBliSt!lC z!?;4en6BVJP`AT-Crq4ZgPsYci~y2Uz;ytnv34Pdbqf)_9)rP)-6R93nM%kND^`TP zo~58rNKpVC3A`dw(e$|Q-WYZm-C^-%?u#1^zLh5W0wgqrWJrcHi42#KZV!1X9qB5c zZc>06m*%rDmu(+Nkd+}wlkGOoGck66c=kB6k-N6v$2bx$tG{)`)Xy^}%3bk_zqH3` zRnG!Z)I^!tS?TEp@Ga9}goS*xL`6lhk1_q@3LV~RwQ)e+=;u5R)P}b4W{q6B&si4q<(|`?P1$B(b z>y{~0^7yJxl?|f|hbziB4j)DuAPt(T4wNO(vuA@Qv)Jt;qa{czHf$Fty2?N=Afz82 zsyqyoVZ706b{u#Eo5gt0{*b8ys3%eeS^^Fi?Ag|ao$WYvn?S!tw*bwRI250ZCsu5H zTpX{So*w+B1G(;CzFA=WZuEk84w926kA*Ul(PXjCU!~N#YDN-7+0XhqjiTfatkBlU z-P7%?I@+#PF$5^JiowzzJ!RIR#aF0H4l4o=P@(1ODTEte-s*HSLCNIh)82^Dh`__>gSMx7l zHWHN8F)=xPH6u1{ak~S6wwa6|#I06y&)K)s+HI1k_so852O>R4?%fn)h!H6OhDZbbqB>yH9e$+58ZbL(a z5lpocZU_*2B3lVd9T?EH83!UT+CndWu31Q$sfTp$dHC)EC>xuG=~_>I0=NnxKNTWRyU)qcfv;}THsp6pqF@T!HKK_}>O zbJQj|s7l=Ida?sqGs(8ViV8)CF09-ec^|X5zLNV_7)Er}8B?buymmbt%$%T9*0K;pk<*~ur>b> zr}S2Q$fk2PPMz%%QEOFSoqpWzWTqHls`0(U*@H&o@-RqzwTz66;^!JYryN#l4Mcx% z#pY#DW<)O7Gwr_ZJalCeo?Dm5D zm^!2_JM@!m7)IzT`2s9ZPP{f>_0%auN0^K z_y}>DRz~IppPLRnw_>44p0;pCIkO>|lVbRz}bZS3=#tF88FjU59 zC0$j$0mgKFXCPiE>35r6zZQ_?2fpF{>Eu>wIB@D%VK=j}j|gvJBK9ntG{3#d=y|HK zP>k!A6#yHYDs-IAYN@`tBO!8%Z^rD|P?p%`&pa&=q)Q`N;Oh0@8_x`H(%4fDgc?5k zcJJudyWa1?XzS(JkF~Y6Z*pO0Tj=cmS&}KlIi?JD(#;&>=S7q{w-b*K$JZ;IGH$4| zVM06#1vu?-P^etX;>~@}2B9?--Szo%;z30-47)0|jyw0y%xZzCj*jVch@Pn-jWZT| z@M+$`c|^>*VZ+&g0zv%rX*!I9k;EED19G(AWUb$=f#Z^GxB$ms&>}%$M2#iyeSS9)}! zL^H(cC2%kEMFgQEDyY35>cM8D3ezE)-(ZB45|=&Zet3icY&TrXc5(n42LNaV56X7Hc!;}xT^y@}@dSJzTkc!# zj2u3QHQuXLkDl;FK{y@(%JJGevW?5-O6-{p))oyhhJaBXN(ZcRGFnkoRAd;^Py*4H zz;AWOZ(A`}K3FZfNeHEmYH9N&KDv;RoGd!9AnQQ6>(Gb(?hm*UH zuohVB=4?|%yTd&+Hc-}E7!68JFW~cy^;-pHfosfR{85aL1PhhrM2u?=r#BLui-_E1 zb?%gy-2`Mb5_To5GW*)hXzUq>x@El2?nFkNXY8x5=TEMp87$p$XRWT}hjbB2-6ey&0+i#+0fXO;V?LTTF!oz9SF;4YjG#MTP z1Yinb)j`H%7Dw;XC^jT%{6kJKAd0U>B&UFoL2e!^-eQjvB23V^PR#_=gPn@QekRy#CIcz+aTXOg;hGH}Q20`)#Yo`ygpw zVvJKMp)pN&CdTdjA_UP4NpSnUZd-=dnH{~CwbrH|ZWe}$(-;)c{K)25I``{ru&Si- znklSRg+#pVU!yINIkZr1LB329JJVPhyg>U2HxH3z^t*DyN z(XEg!js-Vq^S69ZOBhdp!+BrdL@}N?-N~f^_83oL8p*6`?0#4M^hd~2^R$}BJ5}Lk z5E?MnA>=E>D9vjl=`iM-`10ywCxjkI|=S$ zCs#xoBqjYA|9E^r*xVEB2S|2BsY-y+FKA|?$=A4hwmZf!K|GO_ZC-+iA8Vc_>s^T5 zpV(p6*q|m6bTtF^9#B&RwQsHe;4yd@^#tM@Ezlrq(*g1Y6_mY$drx%2>x$cou`umh)wE)3>;a>q zpOvD-d;+a${=#acu>wpiw2nJ>vI&JuxCMfXFz_OPuxOuK$REZx0NC<+qB-Tzwro6S z`B1=5NsGZtFKTSuiju}3{lVJyJueG+pvIiR!KjD=IyPImDPfEZF|xgdcbv- zuv)w>ff86}-a)rk2P-?VevGsC_4P?9&ni5BTQ+ zdaZI`@)C0pZ$KPFqh)y1Jf4VTO=NF8InPExISlRq_jxWV1+=~(BfqLRof8-!zmnPq zC@fr8W{*Ij4+yHTa-A-2ZY*tHD4g2Ta^wilq3bNx-HKjWMJS-Arhk&3K(E_wd$DnL|I06dnq zpX2rdbtm6N*RmL~qK(euPtBqcIymU>Nqe0?g}eq7?rFdlWbu*+>Y_CEH?EDPKuTPyio!dLVjTkKhd+8 z|0MG+F3SHWR!6-ajh@0|A@$A{SNJEiwe@s<_*k~A$gX>5?+%Y%XP*J-5oeXA1D1>) zBa8=@+n|o*!y+J{y*_Kq7+a6t9fRg|ji-jdM7f(TWBvhn{Oy|lLP7pNzi2qxvVfp2 e|GU`(lee1F&JVF!#+KW!qOi8p=EPg$r5U+KUAG>>Tcf^936dV zbUJD%njd0nG8Yq5w8U_|BQ)lBaOcUB{02o#5lrv5$EUMH>l>X9ahvWvtW6-G5d7@Y z^LT%pe{tMp%BH(cl}aKMhK2C-?dNj9K>x2)#hVEIZ=47Z`f;Pg|9=mw7g*p5MqDkw ze|)rM(#_8NBoq@&Mt_$_E<2wXIY3M@<-XMt z8bbBq3O-XjPm#&K#fLac>3EHL*Q%E0*kD2D#o1sVLKvIS*&TsOvyJf>M%8fr1tPk3 z1%>Cn1gy6OYnf=$7uM8OExCJf2xVZf#M0UM`R$#%OJfx#t@uvwHXNLM>VE`1-Co8IJ;x)j$24_2*~7KTioI=HyLI@ z-PnoJ9U>+s6%CFQDwmYp5wh<*|5dc-vggn5V9^sxCtGlKvZL9bvFb~(f4ZwUrClT7 zykF00XVR4}DP4ZYllZ+AL}RJZ;PIZZz+*hU*2%-;sd@S%zq{FoD~yMmF+_A6ckjYO zHjgbIffm=@%#GiEK7TIhf()!x(f7(yzo|4OFdRSw})$ohnKKM^8RhfVqIa~)Ti)B772JaY^1E9Ka$V_`Pg=wh%jdl>~S8d)8vP%OZKfoIZ&KPcOsYMT^WSFD>PxYklZm zD;C94jd5|V{!*Y!#_H76$)GCIf5qzxib{0J7muBSpLU{3QBzILW#>yP!OPH|_6SZo znegu|6H?+2bgKtkx6@DzxY*c((b1tJOx~H34Z2IH>X%KqVS`%LN{0)vM%dd0-9Qf3Ps zOJT{Exz+qGYULQEVEtqv9nUcJ-pzDIMEZDd@&hq8rMC(rb=Go4z46v_vr6y$s;jW6 z2XF~rD5O|A6W zK-q0Opj?w%d#WBq+(|_(GFFzeokp+Tl6amw?uAiGjt)q;MEDZ;@VC zqKA&n{Cm-YVISX^o$c^rvRwiprkfX9R`oy@-gx$x8WpSEsJFuEyz zQ)hYkH><-9EVtRx*0>w`9v>tP+w_cRMZH{8TVCj@PFU8LrnqU~JR1n1l}gQ3ZaZEH zTVS+qKS5v($4HSwVYMLy3xVLtfc$#=27}`=dgA{r?c>^`3)wWa^WVJ76M)VnNz4ij zWQ10vzcJBorMm_>4}q|{{(t36s*|F0}L(yNvILi7juiaZtD34(IJGjvY_i>W8u;A8(W1H%TJaf=%gxF7$FG}e6MAt}z ze#O%zM$oN#+d=!uB~Q_rC&o}KPuq&TQ3kTw{bTQD{#ZfS#?;&}LM_F^XE%bArG<$o zp?W2)&vacxkp7*}Qfw~%2TNuDdX&UG^Bubq6Fr?q3^#AyTpz0#HsYXgKS*ZVifTAX z7ft20p7ZCMUrh915prFgIzOv$KKkl)g_E?4y+?q`BR4)uFuOWmymOR4p%$z*MQ~I2mluwOzV&iB2Z2Q1>o>Q*Qq`iIpZ-I$dpeNb=#kC2o^eXtg&Br-d)ms9ZC!kO z<*wsq-Tpw8HVkO(SCOEgpmvSD+g1w+BLl;L&Hm<;kjcOU-ke~;qaTJ68&!*U>vSyT zuP4nz+b684e1mw%HhOs8z8 zpFe-XotTu3^4cu?=pQc7W%d5i-X1EHAmkx-UtWi73^ z;{1;vaSzW34Za>6I98dVA~B7M{_dgt{Q{5ERhVrF2?^&q9jr&DLri%pIUgO0EAP1U zBs_-IHXY8_hWP*X?OXU5owc=fZC#xo-W^(x+VivHl9CcCDk?=q#reCzf^J6wJDu#h zu3OLQ=0l!dXv;Qz)tB<@>({R%BO~+k^V{Lg&CN}I1RkfT;qBJiS^=-GRwoBr5bfNP zj;ZD4_V55M$N1nurTw}F>;Q9fb5T*>xXQx9LU=bfcSUZl+2&*&B_-wU+qWq^PKpE^ zAQB8qoZ3QZV2m}chuhzLt~#ub8kA7O#4q+FRy@G&MRGa_Ttpzo<*coXWio0Uw^G<> z1O;_+1{3%lWX^7pl9JxJ!zl5yAC|n;?*_NyFNxy?=CdMNrf8@o8YaIXQSk zlisG!7A<99V6fe4v)GMI85$Ng-^>@k(HYJDd}eLUjIz1ZxSv`g)N);1N=m7K`_Uu$ zlV1L0mNvK#qU+ZOXfQOG(8a>@gR+v6*+_AV8P}C7S4`atO`(&;8+r8T(G`3$LLq_M z_MM*}ZQC9O=jP_#;kJn5vwL3k2ASuwzrk+UiH_y&gvYUoi9%+-3Jp3U>EjW0A-UDG z-h@{O?yf=5>Fduy1kBUJK#~5VqZ)^)eMYW-zFvvgb$54v?6SAJwIwf@P2}h8y}Q)+ zR6@Y)*F&$b_x#9UTO<85GasNv=XFR|Lz&cz^&z&2VbrwfL^b_ttlXTDp1z3JbfVgB zn@C&l)vM={lELKskVJ(2A`13PY;3Hire=;eQA(+0OdG2U*^E+&Oq!2n=>Pv+i-&yQYMUrz?eGu+4;uwlZJ{rPOH#g5iiTHed zk(ooy%gg(LnAzIuX`JHPudY>3H@hKAC>e`C9BwWyF7B@m%RWA(6FO|0taEYL7(XmY zJY|Aa%Ne}OV|5R5%yJAN($U#j^w~3VKHDGqY;dRaya&g}4y!}vaEnv!$L28N!IAc+ z^iqv6q0sgx@2nZikxooz3JO2@H)~T*n9pBw#m4{o!~VJ(^4jU7%Vq>RnMHRkK*GkM zoIBrN8`)Va>0wx&kJ`niw6?Kf)veEE2_Sqmn5`@mg;{C(&;GPs{+SL-AR;27)_fi5 zKR2hp)pV!$^z`(2f3x9blP~1HoF;5a5iPCp03y2L;$kLIzVx)T@TjQZTaRYq9VV-6 zmldR?yHKmcLI=}c5@EF1l-~pIEILvS7JUBE(<9_|WNT-KTJFzKR8W`;XpbMkd&Lz? zL`2kZcC-o=!{cO@qCZ2H1o?woe0RB@?*9E(kdHhLnke$Ms;nV#59@lk!M5FgH#s>; zOyjgS^18xoWX4v)vnHd;auVj;!T@do=^w$Us?h^g2L~5-z@!W|XOz36bFNBGgk`PD z1J*Psl=08-iRffXogPz7;hN`7UQO=pm5c2k&Q+&6{j;WO=L?zSsV}a)V7p#*XlUqM zGfY9SzrWlf6?Cc{u<@EM`?xlVLdYsBN1TRo_|UpZJsidVyMGwd)6em-oxVU8O zhEmSXM`M-^>srRLaXgP+e)Dc@6xP+9g52~sbw|7u$4lv7w%-`PO+=)}LF%%%Rt5Q} z;S5#KKA!y4&+zr@)|QsTqod1Lu4H`q!Z5++T;~o+iMB73<{R3P`$9T=Mk&i`>oqsU8z}-}anE zcTh$(;|Eo^R#nzqTx1uQy z=%&;&>~H_2nPobspk0S&7w8Z=W!S~@pi%JePWB}g5?^j25DGu<&D>obF6eu9aU2KT z;aGX>S$h8}Huz9DcNYnOT*cmHei?8)`MCg<&Us{fhd7-g9hKqQ5rZy#Fs zfA!?sLiV{;9}o`0|DS|Sn&}42o#%r7qI%Ju*m zp!Nu}_Zn3aULgJO_3r@xFVH~$2L#jk{K)G>H`Ji24i%arBeA_rC07lalH6EsORm@InuRknGti{(ca>FYz7GnD zWPIu5bojf;51QmxuU?&3X7lHf5X*v69(jeU>^EqHXk9o3+uq{Lx#6J2AP zD9(DJ+Q`cG;FI&6EO0l-TmRYL_W*Q3;j;FKf+7)~uHF49Ipw<31vSpi)zulbl72>h zG+t!`t)w{&0X-y_bxyklm{~38$wn(Ih4j9EEG}jf8!~-z*NDS;*NBz10D2(}++1hq zvs_Rs^UKSJ(_RE;>y{p2bmgYQ8;6UDoBbKV4Grgh6z&g{QX{f+a^PFbpzCey=vYN} zzl-q>Wb}D0FEWO8T=G;4bl|7X8RdL9A9bht8PXrX(piGbhWxqA`7gcT5wcH)Hrm8y z8<@w>w&8~0vL1USiDhspc++8*r91RK*=F^=8~RxLB{|K7saHW zBBQ0ImN{qw6KouBqo($mZLRqmG*!^8%Ea+d5{O}AVaXMvc6QiF1EDiwVrA8>w#_0; zQOuBmux4OjcrGFFC|YW}kP+R!sg;|l%ggr-nPxFjP0nLA4LGQ+ ztqsYE{NC-%d~kRO^#qzm#=`=&B4h%m$C+zbTX;f(j*3dTK?kwd*H4j=v?Ogt9Crm> z4-U3wp@X-k?TBO!qc&a{$a?bR3H0hTG&ImTil)l9b;%eH6 z{B|p)P`-~f^*KnV9B1)R6tL*mk!Dl%+A?w)8YT~KahZ?m7p>dnNw&3i%UI2IXjfQh ztE;Q4s6~GI^%!k5AnRRL_p))Kjw1P`R6&=!ThpsC;KGpEyEx?;|4TELfW?YE^ zPHcR9&g-LPTnz>!|CE++JDzLTu066@kUF$188PD^O-)IWiRV++&;SzdvZdu_mCa&y zKXgT1uioe8TcwniDrNMWk5|e^hkpK?S?`nH ziw_HMN=Sj5G;M#HL>LpBdb!yMwB_D}P?J;_7gkp_b#yE;N<$gqAcOo|w#`w=GwDqz z#iyf|SedBVpKA-7Zvb3r#1`G&(J|;WRK{W_Xm5|K)7^mW8lgVh8XWa{eqjNJ6855g zokCglw}9#C>E>pUL(A0!j?_LLSmtA1?z>)S{-It~POdjylH|~mfFKxJCFn=?R|azc zE6P4b4XllnsOD(^_{vS&9LgIz+*u^=X@J)T2M1wFE$dK2B|U=t)^k71$I6veR2ZK#1@@wLT6W3UUoJqyWvMRMn1lynU)W)dlGIUy}iBF7Q^D>&E-ccD=PtT0t6eQ z&l`Pv@k*y*2`bmuB82c2VDzyH%P27l^NDIsO3JSt9UUQdStBFZK^|2|e@{=(?QLsH z=s`s?Wa5FOINQw-a)WGOuVStUP4`hMumg~#!sujm%FUvkJ=GQOp$qPT4kY1<3I}Z) z2peeqU`HlP!ES4(srPa}9-9&s5y{nU(8=frgzNz5Z>5UZl=~Jg7dtjCuF@Gk78W5A zZd{!u0L29PEg6y^@hWUt1}3H(NL+xnPNc@KUZoJuz|JIIRZt_LfAByYOCI(`Ln)3^ z`ig$HIow)%I2zYji-d%PJaXFBZyh!W`H~<1qCtGcQZZ++J;XCOSk}T~!yB6j&9zWb z)i`W!91MC$OG`u8c4<-x2nbZ$ufw0wq@*Nf&2pa4$u_pqG5k>08D|z3U(*SE@-SA> zwIIHE(+>8*)(P+f4c7DR5U|~!A3aSdwwU0fYggCM$Ub4#{c-v7<@Tsq$m6TyRV4zF z^?P=&>bB(=?am-0Y*PTtn5c1ZD2#(9*7^~j?b1EjDrZzS;eFr&WQ@%htft)dvoiRL z=v;P}9y$J*-rcqJ`U)v?z>Riq*dYGs zc&xPf?(pz1A|irH^kXLDTR%Us3X6$>vUK`hbZhyijzzm#tJYEe?D$6r6=|U8X?}HQ zXD4jd>-({>mQ#e`QllqsGdfn+8GNt zN5HXACPUG(z($igR=~;H&~O3Pmrm+)((o#|R#Z%UeD49PL!&1Kqe?Cz@<&DcfGKKa zFrLp28ukLlConKb2>0*bCu8*@`djhqN$%WpD566`26`Z=bE-SPZDs@Z&fcPY_rPyNlCElZDM&H3!o{OGMj* zczKT8xU9lB_-2oES0mlLa5s+8O85tSz zKeH!XHtW!kvet2{-tBl}ylQ7qRoBPQPv~^Fzk9t6;9W#i)N6fxsThv7ExWA#UnAJv zH;n^I2ko7SyZ&h)z zf;*iak;#fn9U7U?u3k-!izCZZaoL)AcBm&I(FV6&ZM*!6gS6?}H!ibZNiwzWr_P5V zVne)RuA0xBovW!Opz5wo)Kowk0PY5`Gte~Fz#a%;sGQm=m4K)tE-f7))UM|}=l3&4 z>dU38#3Ik0-NyC(^ofuruyz2L5SXi__4UVqx#$(YJP5v6T%vOoHVB~7;J`o>3I*h2 z!BLgeKSU6)Lzlgh;aZHU`QaI{Qnd411%axRsnYAkAM*Q$2hhC{NX_RIH*|WXJ zqE?uco!x#$JmOj$#>B- zpE5~^YlBvmg?86+Khodc*f<7ikNM=<;V-?i^mGGu!zBu* zX3I^Tg2lv!)0(M4t;&1enRU*CsScZa(a|8bni;;bd1BYyRXdNKCCqQwu04!2&-B=q z{RoEb*G=41pQ7sBVmuME1@7rvdqcsul7j=Xjt=ykA{To+@=>TkbO|mhN$RLt!M8)t zoA~hfC1J_ry2ZPP1c5rNCZ@~_LpoIonsQ0?I>SOonZT7fKDavCnbv&?1HtThO7ZF> znB3;rU{Fy*GJ3PS*wYJ(Zko{iMUq9MC*03U0Ow*iZ^b=C((RjmevHE*fmKPieg>SR ztv06g2KAa97w}&d60QAQO;5SsW;S#_N5}cPdwir;Jv$RUuk4h+yBE6f4djO33C)d{ z&R>BURJrB%pG>f#8 zXvTwsGLTxLOZ##o=fK21!n4|{RLO`#3kxIg*(U5bVa?Y5Yri=xT3~&cHO$|4ue_mowfX=qLzv(|qn8&By_+*v(6*&z*+$j*FlTj1bW zoivd4!4I1g$Aa+i8ab|{`M-@3^XPh&P1%zt^%QPt##8l2Q|I-MEfc=Dxzt&cPw96$ zIek|znQIsW&gwGmr_P1Zoy86kRu-3yDtk~fPIGr{wG#zHN6ZABOU)Lff@=LO z?re7;v3A8I{#_6cfuep%t!v#qBD zgEwwulH5T4xSAc6-QIp3`8oJ5YH7H~Fr;*UJoOGoCLf*HV|lQ3cpRn^egoXFx__bf z9a<0dB<^}W-wwu#=d&{wwAA6O+9rZd3Rv=*8X8=BzGop~Cwm(x=d;7HhGXq6pR3kK zqdBUNo%bygqm54z&$GIJe4CMZnDx@#2{p_dx zQqnGR{J9hpJ^}0U@aE7!R#{7ng0ZpU25wTzUxm@!*v_r0`p=)e8vE!;m!5})QmC(9 z6|^$7CFYv{bvwxidv2c`_LSAvA7wts`tlw81%xSRHG1|tHs!!o9QfNV3H}iu!ol;S zD2pXI{`Al=Y|7E}jAIJ^1&2BRO^~W{n01TOjO{P>BLB{Uits<4fa8Yq2_84+`B=+; zHlZBZU6%8lql4L)oy7*(mEY)nO6e_{!zr+*25y0&$y#GU$KF3nsYoo;JET7a$;nH4 zN)dD8EZfycvBhq?n_u>R(wJw+tk9F@4W)_^%$6Ek?_MY+7Z`!OTB@ZS9X&4@RX$at zntFQ1Eq9tJ?;v<2e&<%^org)<8ZoCWk6Qz4z?*XSvGWpzyV~j|tM@^{B~H?+f-ei> zyeNf*9--mKCsx`{L&PMpJ3E20Lgsop(t+jLHNIvPXil?_VJrVOl$~Ucxod$LuMcNh zIA8ZXci0%XaYL#0wEX3Xg3Ur_d{=DxdmM;5ZldcOW|L-hm0|@t8Xo7+YU5#J zPrc66ki&Xu^L3?G;I#1NP?bbk8E1yan7Y<3>6NB#EcKI+XImS1K`s3dfK zE7c;EmF+)50SS#({v$0W2QFW_{XLLf@U&DkgT3z5zQwd4qT#Ii`|vRPqrROsI`f4W zx483X@+x*yMcXLK&d%E3A+eq!-z|p8YV6B6KHiUWZqLh7^dUrM^xLmxQm}tt_1l-n zqO_o)#er2x1;^4y^0DW-dc!LlnNK>B|GKg~FEip3WQ<%y^osvaHEqL-e*h}b1{<0& z{g;NHWS-1Q;(7J|baJz>;s-SnY~xYJN4D&`|wMY!?9;QKYGE(0R)ZGA74mG>Z$Fm7Mcm3h;+H z8SH9WPtkvOp!JIOabj@X{f#+(#UT?d+!_4a*9wmzby*PqkQ4vQvG$YG0b?3>lJg?M zNR%}23lO)G0_iwiKIma`;Hu_tpbZ%mjK9gy_w?VoE5@&Q3I30>JMF?+w;L*&dN>y* zoBO&8KwWAd8Q2CkqHqws)xa$_0M>fyhmYFd-{0O|=}T?>c$-}&QP4bo4UoVXaCx9y zt$}v{I{EX%#Ph?Got{BsQrkT=C72}Nw~9JDJA==k0*9)ltv!(>UHJxtl{?&I61+gi z9&gkXJc+)z;fKDRN>Ko&NF);EQ6S}@2?T5l&=I@?A|lPu+lA``MASAjTMMUG1aZ{n zaC^SJy&VukT6#L(9j>+ghVwef=b-RNK7S6%EA)RvrxvnHC8kV_`^D4x#F&Vqj$FS0hA0D}AiBPccM13EiVG_tv=X%^%-x0wJs zz+bD2ixuvtM*#FHUtFCaaTq{{vC(Isa~2a^^8evJft^SI78w+*@G8KwZYxa{`akmn!D5-9`t3AxKJ1pdrPk9w0nvZ```|z`N4*u()M+f67BQH0{qH zE6V00)T$35*d=zhwu;2}fEMkH;p{NuvK+7MQ+~Drs-%Jf6!^(9(UhU0mpBXn>urD=h52|HQx`T_pz( z4=<0=ueMfzvKc}-?OgD9z!dNelJl1YGY|k+e+k7@vK}@6j~~O&j>(3L^u&$>Xe5Bq z#|O0y7yxGrEiJ7&#!z0j=a)2YIMekaK&O_Kl?@IKZU6~?#0&yz8}vxo1c54tO&v-R zFeU&V1zo-;QAii)&#*9dquwM?yUCA}&v!wi2gU;^wXFR74fv)K3milvYNO%&3_|wD zj~@dA1F+>TUB0}x+`kz8I^l5U1M3`S6svB2Ufwzcs5bBJ+KA$>l_{f-z-C}m;#|Et z9}EIB4nBT&cQ*@g?%COVzzx27H3MPrucK!qQqVpsDoV2_l#Vp; zJ5c&PF`TBLXaf-&`Wm%4t)(0{xM?e8{e$jRMvUrM!#_&~z?o?>H_5D8)P5F9eR z-1KvDt;o6nAd+FulBFoP%szj8!Q-%@RoB6_9gb5Q?~_z0uVv@0FVEG$Dt9I}FkX)U{u zIY3#)rYyId+E`xZ)&KDcIMD1X>*EmiHJkNe=aV3f65arPts!g-NMK-C*q-njB9qANS3hGz8zK6a$<)gqWhkLBL*qU$THb`f2$g+y z2RR+8h26@)GTK#;@sXT=52RTp1_nO3-}g5ey$P3>m$!v~!P==$CGCLBH!;YJf#A5} z;c*UH#yXHjpk$YQra4!kA>c+k?g;Pm@*cusZI8o70K!}M{Af7Qb-N9CFCcdAdYrsb zjaUID1`-zNGfGd1-08~X6)e4?`l{JZs$B!oX3lSebHaY`_(TigInXR7dALZ2R;8ZDfU2gR zD1D=SR&M@Ei^f%a{BoUwlO(lRU}YH@8RzxE5S5&q%oUagBxGosbSzgJ1-JrNrOC+2 zCxCdZa?yQcKFT4cLd`h?TjT+35i#)330h4HgF*!UKOpcD6VXl~$V;G1MYKLbrvjLE zFsFnQHMh3De*Kz&j7JXZ5tP4+SMa64CGqfKuMr1q_#Gb-9k8{TREzOqAT(lt3bh<9 zr56;e2S)$07v;l;R$yO(n6+#;Ee~CQpoZPQ>5F#<6kR_1HL#=B*sXFoZO@gKmOk;i z%nWfe_ah{b$rlO@7MB0}_ZlUJ(op1qAr;Ur7)BcwzjkDn4n3>`ZQRh*F_ zCIP%=REI^k!S&(b?KB3$DylE)YNHPpT?}zN`ZX&(Zz2}aMQDx@qxd>{!)h_-hFdX> zv>Np2b{ioqDZDA-)prgrkK_K+(BQv0u8>B*U*=w1VIT{dNK4Z3CK5`yitgl%2)_kT zq+%ichw`COPEJHvk*En>ChrRBxWy60MJm&K4WK|jkKF~i(!47zqpoqLHY7NJ1KQ8} zzt0oRYct|{4&_Vl$~=q%lyS_Xdci7a%$ErF^#w%Zwe8zsHZ+LEN==?VQFpE1zIq5@ zs(=Vvcp#y!&Z z=O6C-Ue-Q|eRO}6?fbz6&siS5N%FYZ-pH~qR`2xg-xxO`KoP1JO1){0_w+|E zZP`1kcMmS?ovIr3eVj*lM!pqJxqVGO3D2bJH>NzMnSwiJae&FiBxb5lM%V|$y+fsy zR_{8V40O1QP-Y4sMnC%|@Hu$~cc>?RUiu2 zdom`&;oS>zmY0v`B%+b)7p7%+ipyN>MSqvSXGMdw(i(CZD`9WbEP^TPy3q62%?ybB z-W!Y3$J*n2tc>bITLwOaFfn!pKVNm=atut=%-#QFl?(>yW50_Ib)U`@z#>iTw2kGh>WPudSK8ESUyZT>57+IG}af8n!VJ& zF`2J0<6TTdluD7sAWH4?JvK3mFg0ioAFZ1&MqWoSQ&@g7y2^uUT(4q_lbg(&{U(4h zJX46DL^J(y;~UKDR%DALmA9knzT;c5+bSZg zFt6Ys%;QpjV}@{y-n&b6wGsaUHi8fz-+sm#$DE7=4>7<)n-o%6{Eq)+A(5O=H9g`1 z&kclUO!?#uGH#4$S8kt^9BR+<0Gs;=wamrBA8&{XMQaeFw4;Jr{wtUj@-U}}tL;x= zlCN3!P?LBdUUW^?5s~q|t^RXMJ!yT~3=3gDFv9<}kEl>Klp4OiqIHB2v8Go@)casO z5wG&mD{KyO_fPM}3LpOpxcFe22gX=QAo?{!8{API z>03+$&s!Hd1c@FGQ!i1A05acLk8Z{^^#_;G5`;S03*hD}l)e{1njM zY@sX9WZ77OWvR-sM$3p+)(ar>k0v(V=~N5<{P}~%coKRI@9Myw1MS6^FAFT;<;rUc z$Kfh^Ma&jcFTsYlR*&97kfZpAUT`9 ze@k<2E)!)lsHJ2~ZQ2jrPYytwUtoFmVRqa?emD*!EwN|Mq+>XsO1eWa1D9viN)CIT zf4uJka)?;q#10XZMXt7d=b^&%o8pNGx?#0B#( z?ZkVPyb!#vhK!%aGbu~ex;bU-KE#6KL|ps_fEGzfNfH(J3Xq+%b44I7L55;OUyuH7EJ}q^q@7{@l7)@eIxyy zS3@OBGqp#>iN|L476qeR9k`t7NO$?Wz|jGIp7rHrdhg7vtV56U&2u0oa?8ty?vFIS zy$DT-YQEO8NaMwdw0vGb0P2-izd>pSeD_5vIy5ehX*{$02XkIl4gJ8eqWz!guRyCD zA0B3fjZEc$B&Js2?+_{n9v3E0ZuT8qE(TsX|8Y_d+5S} z7hnO;GicF(hhzn|M8N!u_T1=`QS>UPiQVF&8-?yIXV8RIr}lwIXL~!C!6hS^)QevE zJlpi9mVmY&A_Q90p~HbO{*b6BlAAXT*rNIDR&G#Gcr5i&(u_bm@4UBW{O}g?=~IA% zNm3v`LPN6*ifUrwR3x)zuByiHI~Xq9`YmvNx9Q_xVSVPc`Es%&jQ0KagE&2$R5nK6 z;4|M51)k&nza5zk2-n-qV97u~378+ATpt7HeA#n(pghPaC^pyE$#2|%Nl5`f^ZomG zXBQXvunho!l|bu<2D1(ysP34`+fYlukp?}Wg#BWvZn5!Bk-NR9#sa z5HA-9n}vlB?KZvj9SZPCIRKPDvCC`e>9GXv366fi1Ylrblo)m?@I770PJxC$5dg`F zgM>tYN=`hNdCW;sVWC{k;KpcKhx0Q$lS`p@I5A`Mgsp3Qd#!mU?&fL6#lTDe@Q(<_ zd%RCBd-bj8fxb{d5C3tdiF~_&7M*C=?846!dOrsOQy1Q?~%z z%PGjja7aYYPmj*d&R)E50p>HjYHJIiU9c)i<5%{Oag*GNmhP&aqa5S)t|!)Oa|U_x zzng*T0^@zIYc2TuV8c>Nz=<;M+$QWp{;?{X3_HQSrhuP66@Ur@ds$RO1WY2JWlkO* zH5oX#^N-7nWgkI`>swmN;S>o+3`hgPjwtAU;`s7q^0Nhd;@!V?H&`p^{5#s)xA*o` zM9I~5ukcpxf%9%q+5IYn5O{3H%S=@v%R9ox%*T@Znfk4Rt-#-mai#{71s>J!{~Aw(;*gh{3x=$T%lxzeTj8^$iViD1+`kW}t)9CSW2N zR<<9?S-6}x`1Q+ZT5irO?VD1OpFZ`Lyq29$jmI&(Ci83jPJVTBG5ZTcuoFQlhXM!t zB%aOSy-bD;b)1#e`Zjqv+ClXiGA^tP;H#{w)6>!7#+*Z5@hg1$SyfML3Uw*gOqKq_ zP`A3w)r2h(3fv|aP!p@muO%uV4f zJ|^KOK<;`I0@ctiIvD$trl#d}V+Jf`z(&ABjbo=wjFvW8k*& zCgrAhkh0~fbP{E$L7d-Vgou zJ_^s!x;4_)Y{uoV_UjE;Wwv*L-vVkbrpxenRk>2k)UGg#i+**~Xz|BA3MDumkeZ;T zdZ`49&TIouRr}T8O&L(yWw4S@q`-a#$4rzgdHRyd_^ipn+;BG){d)l*i@Sj1ZF>s5 zDIC#K#6n8Ej;3Z%hESEHZgn{H7{^`@7 z458CyAheaf0G+4u_kfyc^mTG_x2>5LNEq!DKt7ons%^dl{MnxSkAIGiO@2s6^^lp2 z$EwkcOH52mQ>v%Cn;4ntk_x027(QVS=dxt~rJy)Cl+M7U=uHScL%Ra%i*1|58!J8X z0^aVyXkr-)7na&sWStPePdEqxhl*hP>!==8?`8Y>wE`7m4M-N`1DR;Hd2j&&wT*lA z>Mdk zxy>G)_Wd!@&;rRm$QHVlR>0AFz+6B!oo|x?M-0>n=p;TJQ*xUU2g zaP9kcuof}>h{}+cm){mU6nrEWY3;fWUyf$zo;~v+dk7+x>R@u{O|bS05~fkPboP$Ioa5X!CQ&48Z{Ltr?4>NwsG*O} zY<Rl0+suOk9OC>fi+IP9$`7VXh| zZT{cF%B3%M!e0x8K_3G)itU_&dOkR&0<9mNbj;kre=G}I?9DZ}5Sn`hN({b~$&lC4 zwXlj?%RRv0$r-)2HU*F}YMTmp8pj|qfx0y3f1ip<4>lBW73Z#$e%C@(PjzEn9k0J= zslTWX5i}=wd`(a$)Hh8jMF0`Z<7Ciol2nb_t&gJM)MYIkae_fWNsNn&bNw^@L2 zjF80SEJFo_g)HSZSNY-ty0*tUTO31O*4^{_O0m zLgg?sGlT9OR4$k^@K?%Pss!bJD>K5{X!l{wE;((`+|p$U1&93RPee{P|wrA_V9vL+3N{!S|}P2m28gfh5_B3{1Z zR#}?-J&3 zqCCG3P|~ojEIPGIj@RJem|4j=3j@OnbU`Z}t@fg?cHb`M`*Dx%bZwvyfXUq|UHnS1; zLst|oqa3@Y+6VXsb}_t1Wn(QUFtDqu3y#;;by0Z7)ZN4)eHUx}l8N%{_yahi{Y6tp zDl8M(xFIp!yC;0=#(RJ|JU&KKvvhQ?p^;0e&sI})E0MEwOW-j6313+bkjp`ur`W&q zR3j(;i*uAZ*=<;W8-KaYb1?0ywO;>b*Xs9O>(!t#C76-nY_x#8EQmy@C+qMFHR=ac z+^bENMq$&$MMqb`(qa?Q{u+OV$4A8bt8!qxfY<+WxxXk%1`cYsd-agLfny@jNsW-P zb3=mj_xA^jHi#;d0Lp+Rha=!78X7St9HqcxuA+|MoN#7J$_%i}0OR&isK9^#G$OIK z{(D>%iX3DGcqc1}p_8>v044S$XLjE~R}4*1EyNdqLGqro{B2F@8ps>q{i_0D2G(x^ zE(e=Ju@KhGz50UZPeedEZ zj{Qc%{768+6}U@KhfmPt{n4&*6%CNwH6W%!(I^c|K4owQYoMR6FZ6%%g8p!#5ScRw zrx-^B{JC8i2{|ww=0??!C;KEIsWrd?hlhv5;=_Je1xu-nw6u1yet@-fe^P-ZXh!H9 z1;c_BQ<+Z2Q^wnSi*$mo&8wd}P31Yf$6FgL{*NQI|W71!w&O3N>4L}LxA#|r& z_|nVI&(97&P(ZM9<$6v&juYtI&>b;pRa(Kp7&tQ8aJp_eUlV2k{r4R%vjea>K{hZI=DhyHzWJh`91I{R12`#Ku+! zt^CCC6Tk>Khz|g>#fZb#$0z;E7Y&j@Gp?(Y;p*0wG23%IR}`yZF^h}r2Fb4Ot$x%j zo?=*i_1Z&2Xr!%MhcmVd65m~rY@vq1?_Bv+Gz)1uTuet};4QR&dz(|bXEpf@3}L$6 zc>=*~3=HicZsjl9bC=!}=fE83s=BPq`hhjSv~(9(`%jGK;41(x!eGFdXU&v4LJfi?|em0)m2PEhM_R-1A^v zpPiW@lL#np42kJCT>VHS^yTMuO?pyf{~MyIGvQ54tBautQe1-m*Nh znr;MdIGlItDjWGGZ;!+~wlRPliieM%zi*cZ{r+}p85#DQUg7~wn^DL3tO_TMB${#s z4sY6@Nei=>8f022`R|X;$7&jGRb_u8n0`Hk=M#f4CnXM4GsAxS#UkK(7#E{DR~ey~ z+rzZ$AJh6~p3*%3NGam>3{SRR9L{twM1H?T!};}lICf~d%H0DJaT84ms$Xfu5{hXo z=8OAQr-7^88*82M(Jnnz`#Vl1uU~K0xojTp*Da1mq2zJJ7zLBG6Dl$f;ru@ligXv&yd=bQQYi)WJM{U#w0et`^?4wY>B;+WO@^$7S9zqu-b}737Pg{k;#KL{M=T=@ z7n>y}(r9nkDWr`nymZ2;V8D38CjDf7*;72kTC!}OX^P>ph$Jd4JKP-o4yVHpl`9y1 z@D9|HE&3hgbLUBF6!aJSwjUKMeSCCVxb(7l66Y2nd;bk3&mdnB`s#sZLY(T+3nB0u z6rz4grrgk8(c>|gAeZxx3ktkJUqSu6FI~k~eO&pjfAlX|ja_>56BfQ&Gs=|yanGM$ zzoB$JeM#YI6YDO+b&+n4OCkNipYBUDmPMRLUpYRE}jol~n?ZWJ(HL4uzY>MUrZB1omPnj68B_-QD zLWX}4CM6*qXod-? zZi>H~3{O_f^iPz>*}eaMNGs#QR)1dP1O7v|fRpFOdjWC81kS3>bo|Ufi)B?1@=_cr zd8WU58e}8bER81~P04(jy0@TG!^!T}Ohy8J-@7LsFLg1MPVO#liZ|ZVz@eKX)GK?W z_V!@e_DbK~{wQ~RF*oY?;8QWN@8DpzAYgr>H zLX@$~SVmN~vS(K)dad(nk|9m9B>NI+p&}J!DO<8c$dV$ZY@smre82mC@B4k<`~5d_ z@45G!d+s^sp5OC(p2s25M{_6~W3YM80nWvh@`;z?3JZt%7M96^)cU+rEsPveevk}vjA(a`eSRor*+fCF#2~K z8AXIYimYxMSNc9oYTx(S-Xc~`MeS-VS0nv0J6qn7Ki)6%FA=$}W=h&bjf^km%Ja_% zpYy~s&T6clWjyCI6J)del4H-$Ao>n$i_r^MTuRg!7V-#?jlRaNR0QJLQmpgU4jd^( zh*s7#wzK;(X|(52aW9YdxP+*;mt?fW&$}kE-KH_7#lM9l@>5DYF0rw{(B=DnR7(9d zZ~fuxTcZTHjQQX99en)xHoJSW_E@3#se#3fy*3y(y6ZiBvy)Y_p7v%h$$In8)wl|* zvBmV6F2^TGbo9KDArEI~cu>Cjg{Wke!6eDpFFsutTREFM3m5fDs1p2|gUXc(S%K}? z6&s`L>{VI=8Zrh>!7}mQEhDXoO2U}iU1{HT9w%W84vZ+r$Y$zUxK~;f@t7{>TsCsN zX<*gBD_)#@%6*S$J%4^_bmDPsJ{ICrP7KKf%npxI~XuvDO94}nAvzwDe#$x==GYnOB)tHZ#4rD|vh7w(g8$&rH%Bg(!?JK7%4p z|EeQS7DRklsb$3htu1mM*K(B zz$=e!z95jt%PMVm?{1O89n{^*@0vT(bQQlcwBrpezj39IYgp!AmJyq0bJ*rzF1ptZ zTS?>u`>owHeMBvI`tZc25|;!MLqy%9@UE^Z;@g;Wbj0gQ+BAz0ZGUByi47&tAiL*t z+Lb6i49`V(X*rLGCNW!NPd5C$zIsqa!j{6}=W;xGmxh=uR)|m>Xi68bx<+s>uc%fx zBGeYyOj%h9=wYl<`7nG28Kg%@5l$8>lSOw)QP#3$%_Me~@fL2jAJsohAkiPS%)hQ@ zQ`-0X-j?1<(^$mW@gcFw*2~nYBN4;bkSQ#-fBMHB)=Jgaqe16BbkD}7#~^t}p9$Qs zAS%dMZ!%1wh1dtT$zElR%M{w{orkbHNnnlQ)L!5e#<)LtvREg5B^VuFkdC7#(z&Da zHt#xVZOm3uO~>V6Tg@m}b`u5Tsxdj+O!N@`#I<7KJI1w+HvCW3+o-&9EK(BGJ9{;> zZypG>pUkH|N{vA#NEu>V-x*>K?;&8ijrVrdKalSdEWg$vCuks-bxlkkS#S-Cn%EPL z;cE?$Lj0p%+cL-F^oLj;$P)>O@55GtuIxRAv6beeU5vKVsCFze2IXzb4sngO2S`z; z>p7Pcv(30&p*8cbqA+6@#^Z#n$MtUICUL>_>ZzI3L#fr&Sb{jhzSFFhq#2HBhTqYz z(l9~_U4nIP1w=AXr_OK3I`Xxe)q32hrt;p1L4;KNv0S4q3y$4-II4urex!4pN9aY7 z49&5C*wQ?4MF&gSSw_TX>T~GsWuMn6Kx9(YFrH%vUE~)|A=F%=w3rjQf!z{#+e-DE zMPe-l2-W#~3BgMt_wpxopOD-Pgs*jqwcD15;ku+Of*4&_!^n5{?$~p8`O`n&bUiG2I^?GvX#NY@_u zV+)v_fNK(yv3y?(AOvw*vE7r+KM+8sBj5Wz`4IT#AV3#kASDEoolvlg68$xM2~cQM z1B?$&dUWhAzwQ!i(H4JfjM-#la=6+?LIWKfk(L!flNky4wCLI}hsNi6w2EDIrNA$R z9LVXu2dGN|G+q@pbD=!$;I zZ*};^1qdW>WMv%!fhe%1KK$wgjw56%&ZC>XA4uuWIiQ;k2DB&uC(rhwa|2LO(b#F| z_ivd1&V-I8ACG>jh>RtuVUlEj;vUw50ureW9+S}St;tzmufq{W-r}l-e z19=a{@9v*P>1Xa*tNoDGA@8nm1(Ox6l}?6$4-;h9*(=?*KCXUTgkYWd+t5-Ys3Ob8 zJ_2}4)Hx5?4}s^uz=G#qk5@Pt%lOs2bz`6@1g#%*^EKG8ro+xWHTCIeiXFUUvSO8T z92Cj4ER(vI^8O{kPU&FW@3hgDBZ;V;yW5AUucw znie39m*eA65oP}*xc`{z;DD>v<5jixv*o%&#Bx;hA{q!Y+W~n|@qQOJRe})s8&Q6WH>=q2x*gEBBc8mk!voUPtFZ}2GX42p`QtWAzi{ZHZ`FL9}SO^OPTkx zfgbgitk`xXceU`q6DB~YTLMBZNnU%4r4R?~#V`pxc*wUBbe6ym;>LO~0IRAW&RQ~O zfYSvC?x_u60QUkK2Pm^`@jC!fOMk4uwjSiBFcRMkLf5&txF}XHIp6I`%gWj;?gPZb zpdcpf*a6qF1}^~IC)Jwewmgp6pDl{!rl#eFObBtlgX}aL)I`fO=e+Z9z0xVheSD?8!bcCBHRh9= z@BWPICqqzP`+K`*lO@mKJ(r!c2RIfaf6|btfQb})a(_>S3Nm4kfsEC`tN}HzL&$gm zOlJrG27b}?F~hQ6@cubgV*fh`JYfj{kj2%11P~*#?>FE$^lO#gFHW6-|Cx%wRNnx* zh3w^4cY){Co?V3yc`4Io>xt&`Pn!T=Xpansm>DJ_#Hgs99i%igz#$F#RYdi_jes0n zUtb^KE08-0^^%}HfJUbKRgPDD%ktize%#)_0sH}VaK|0O&*7Zl7MYfHsNYkWgC_9V z0VaO`-V-uFpxOPJ<}EN43_M;a3Tmg-Un^jfxn%KJ&J)7+rh`EP07XHBi9T18Na)mn z_x%sge-E5C#ZXj7SV^L=(44E=q`J2DO)C;+``0Eq{$=r#k1L=9S59wZ8rFlcPXyp$ zpI|r)|G@!nGYb%K4gruhalHCxGGtq8S+=;&sDsM~s#Tr<)N8I?14S6o#r_iBuV$Uu zeqsogVj!;806!yQ_JZhhU}3{rsRhJA#mMCrjp?U5Xb)gP5LxU)E%gAuh#1R7jhV`B z(+AVBIYH4IYV5TS^n0wszk54yi^D?Uw&moL@*1P*1Va(e8Z`~b&u;gC6}#V1+|=BB z=uF)vSm);=2S(MWfWRt0a1EXLQ~nI_!$XY^zomc-Idk0t(seWbmM0z`D=9MOCXL?6 zM8@8RM|+y%C_{dtqK(P_~^#ild=kk{$5lc^98=m2%=60?^2T42q(=RH96_!oqV7olSNwkud(QR{`h=%@K75(8yI@`UoSQ+>}{ZHkj zkbA*-ZN}2nfPc1cXLE>jRI(XD!2hFeqvQoM754%ni3dLUPQ3cQ&Adp${}oAxBCb$G z^Y4ZkWpT#ZDyd-b{QlS?EIm)km6O^R^H(=f2vRa~Z@O7Yw7-l0`}ywFh1}vQd&?;X zT}hGU)t7{tI(en3cM@de3{sGo8-G6rziU+9UK3M9&nyGEb2zUEvB8Q(z%@3!`fs74 zo&TxGO-hE>%?m5W4=4S3^#kA}Erv*fK$0^-5_v1}*V`j0q~;h!-R7izfs-eU_!1H& z|J-(|NLc8RQxKyhHB5J$9zh(cIRSUl;MKQ+JKcotOEM^7G0F_pA(^_d*!ViqTgiwO zwQzfp%8r3jwK=2*_Tm)ptbCb^@)*iixHOno{{>ts*ICFybG&Sadvh1DVoVcec0jm47LAUWRoa^>tSVi+r=kd3?JF zN!Vi&Q>i4?{$g-5DSS){lj3w{P!PkDb4+;!IjCejeX)#{qB;;xJy~9(Y1dLg-N!<) zgH>i|@WSabUY?w<^D)TpLIxZUxe$M>BfebNoNS4>dJPb;Jmg}L-->!fxk-xkoYYT6 zYnxUF%o>o6i`5v%)H}f&)gh4tL>GqtWxa{~*=~j?)fsv{Kl$BL&EkBvoU>4gwzJ6X zrZ69*-XsH&vDI{0Jb#6ist27`;K}&9k4^bi!wiAyMU+OIA3EL}O3BX^87V|^xc#wW zL1ecJyB~c=_;mlA3)^RleL#$LpPe^T=U+GrbZW$Ebe+>C+f& diff --git a/tests/output.md b/tests/output.md index 4bf8c31..23805a0 100644 --- a/tests/output.md +++ b/tests/output.md @@ -1,9 +1,12 @@ ## System Description +  aaa +  + ## Dataflow Diagram - Level 0 DFD ![](sample.png) @@ -11,6 +14,7 @@ aaa   ## Dataflows +  Name|From|To |Data|Protocol|Port |:----:|:----:|:---:|:----:|:--------:|:----:| @@ -23,156 +27,18 @@ Name|From|To |Data|Protocol|Port ## Data Dictionary +  -Name|Description|Classification|Carried|Processed -|:----:|:--------:|:----:|:----|:----| -|auth cookie||PUBLIC|User enters comments (*)
|User
Web Server
| - - -## Actors - - -Name|User -|:----|:----| -Description|| -Is Admin|False -Finding Count|0| - - - - -## Boundaries - - -Name|Internet -|:----|:----| -Description|| -In Scope|True| -Immediate Parent|Primary Boundary| -All Parents|| -Classification|Classification.UNKNOWN| -Finding Count|0| - - - -Name|Server/DB -|:----|:----| -Description|| -In Scope|True| -Immediate Parent|Primary Boundary| -All Parents|| -Classification|Classification.UNKNOWN| -Finding Count|0| - - - - -## Assets - - -|Name|Web Server| -|:----|:----| -Description|| -In Scope|True| -Type|Server| -Finding Count|0| - - - -|Name|Lambda func| -|:----|:----| -Description|| -In Scope|True| -Type|Lambda| -Finding Count|0| - - - -|Name|Task queue worker| -|:----|:----| -Description|| -In Scope|True| -Type|Process| -Finding Count|0| - - - -|Name|SQL Database| -|:----|:----| -Description|| -In Scope|True| -Type|Datastore| -Finding Count|0| - - - - -## Data Flows - - -Name|User enters comments (*) -|:----|:----| -Description|| -Sink|Server(Web Server)| -Source|Actor(User)| -|Is Response|False -In Scope|True| -Finding Count|0| - - - -Name|Insert query with comments -|:----|:----| -Description|| -Sink|Datastore(SQL Database)| -Source|Server(Web Server)| -|Is Response|False -In Scope|True| -Finding Count|0| - - - -Name|Call func -|:----|:----| -Description|| -Sink|Lambda(Lambda func)| -Source|Server(Web Server)| -|Is Response|False -In Scope|True| -Finding Count|0| - - - -Name|Retrieve comments -|:----|:----| -Description|| -Sink|Server(Web Server)| -Source|Datastore(SQL Database)| -|Is Response|False -In Scope|True| -Finding Count|0| - - - -Name|Show comments (*) -|:----|:----| -Description|| -Sink|Actor(User)| -Source|Server(Web Server)| -|Is Response|False -In Scope|True| -Finding Count|0| - +Name|Description|Classification +|:----:|:--------:|:----:| +|auth cookie||PUBLIC| -Name|Query for tasks -|:----|:----| -Description|| -Sink|Datastore(SQL Database)| -Source|Process(Task queue worker)| -|Is Response|False -In Scope|True| -Finding Count|0| +  +## Potential Threats +  +  +|| diff --git a/tests/test_pytmfunc.py b/tests/test_pytmfunc.py index e43b058..7cfc89e 100644 --- a/tests/test_pytmfunc.py +++ b/tests/test_pytmfunc.py @@ -352,7 +352,7 @@ 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) diff --git a/tm.py b/tm.py index b756d7e..1734ef1 100755 --- a/tm.py +++ b/tm.py @@ -17,20 +17,12 @@ tm.isOrdered = True tm.mergeResponses = True -all = Boundary("TM Boundary") internet = Boundary("Internet") -internet.inBoundary = all - -company = Boundary("Company") -company.inBoundary = all server_db = Boundary("Server/DB") server_db.levels = [2] -server_db.inBoundary = company vpc = Boundary("AWS VPC") -vpc.inBoundary = all - user = Actor("User") user.inBoundary = internet @@ -42,6 +34,7 @@ web.sanitizesInput = False web.encodesOutput = True web.authorizesSource = False +web.inBoundary = server_db db = Datastore("SQL Database") db.OS = "CentOS" From 063861946850c3b00d1643c8e70f5fd8bcd8dccb Mon Sep 17 00:00:00 2001 From: Nick Ozmore Date: Mon, 27 Sep 2021 22:21:46 -0400 Subject: [PATCH 7/8] clean up & bugfixes --- docs/advanced_template.md | 11 +++++++---- pytm/template_engine.py | 19 ++++++++++--------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/docs/advanced_template.md b/docs/advanced_template.md index 9d1297d..2fd184b 100644 --- a/docs/advanced_template.md +++ b/docs/advanced_template.md @@ -14,9 +14,11 @@ Name|From|To |Data|Protocol|Port |:----:|:----:|:---:|:----:|:--------:|:----:| -{dataflows:repeat:|{{item.name}}|{{item.source.name}}|{{item.sink.name}}|{{item.data}}|{{item.protocol}}|{{item.dstPort}}| +{dataflows:repeat:|{{item.display_name:call:}}|{{item.source.name}}|{{item.sink.name}}|{{item.data}}|{{item.protocol}}|{{item.dstPort}}| } +{dataflows:repeat:{{item:call:getElementType}} +} ## Data Dictionary Name|Description|Classification|Carried|Processed @@ -63,7 +65,8 @@ Name|{{item.name}} |:----|:----| Description|{{item.description}}| In Scope|{{item.inScope}}| -Parent|{{item:call:getParentName}}{{item.parents:not:N/A, primary boundary}}| +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}}| @@ -93,7 +96,7 @@ Finding Count|{{item:call:getFindingCount}}| ## Assets {assets:repeat: -|Name|{{item.name}}| +Name|{{item.name}}| |:----|:----| Description|{{item.description}}| In Scope|{{item.inScope}}| @@ -131,7 +134,7 @@ Name|{{item.name}} Description|{{item.description}}| Sink|{{item.sink}}| Source|{{item.source}}| -|Is Response|{{item.isResponse}} +Is Response|{{item.isResponse}}| In Scope|{{item.inScope}}| Finding Count|{{item:call:getFindingCount}}| diff --git a/pytm/template_engine.py b/pytm/template_engine.py index aea40cd..9118a5d 100644 --- a/pytm/template_engine.py +++ b/pytm/template_engine.py @@ -22,7 +22,7 @@ def format_field(self, value, spec): elif spec.startswith("call:") and hasattr(value, "__call__"): # Example usage, format, exampple format - # methood:call {item:call:getParentName} + # methood:call {item.display_name:call:} # methood:call:template {item.parents:call:{{item.name}}, } result = value() @@ -36,28 +36,29 @@ def format_field(self, value, spec): # Example usage, format, exampple format # object:call:method_name {item:call:getFindingCount} # object:call:method_name:template {item:call:getNamesOfParents: - # **{{item}}** + # {{item}} # } method_name = spec_parts[1] - template = spec.partition(":")[-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: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

} + # 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__")): From d9253958ca20e92a79c6630fdb5cfba1eb0f6e09 Mon Sep 17 00:00:00 2001 From: Nick Ozmore Date: Wed, 29 Sep 2021 19:03:08 -0400 Subject: [PATCH 8/8] cleanup, removed extra report content from testing. --- docs/advanced_template.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/advanced_template.md b/docs/advanced_template.md index 2fd184b..5a8892d 100644 --- a/docs/advanced_template.md +++ b/docs/advanced_template.md @@ -17,8 +17,6 @@ Name|From|To |Data|Protocol|Port {dataflows:repeat:|{{item.display_name:call:}}|{{item.source.name}}|{{item.sink.name}}|{{item.data}}|{{item.protocol}}|{{item.dstPort}}| } -{dataflows:repeat:{{item:call:getElementType}} -} ## Data Dictionary Name|Description|Classification|Carried|Processed