diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2597e76
--- /dev/null
+++ b/README.md
@@ -0,0 +1,107 @@
+
+
+
+
+
+
+# PESD Exporter - Burp Suite Extension
+
+Generate security-oriented sequence diagrams and fine-grained parsed traffic from Burp Suite Proxy history.
+
+This extension is based on Proxy Enriched Sequence Diagrams format.
+
+Discover the format [here](mds/Format.md) and read the introduction post in [doyensec blog](https://blog.doyensec.com/2023/02/14/pesd-extension-public-release.html).
+
+
+
+The exporter handles Burp Suite Proxy's traffic conversion to PESD format and offers the possibility to execute templates that will enrich the resulting exports.
+
+## Extension UI
+
+
+
+
+- Select and send specific traffic entries from Burp Suite Tabs to the Exporter tab
+
+- Specify a mode of operation for the export. Supported modes :
+ - ***Domains as Actors*** - Each domain involved in the traffic is represented as an actor in the diagram. Suitable for multi-domain flows analysis
+ - ***Endpoints as Actors*** - Each endpoint (path) involved in the traffic is represented as an actor in the diagram. Suitable for single-domain flows analysis
+
+- Configure the flags set that will be matched in the generation of the sequence diagram. Read about flags in the format definition page, section ["Base Diagram Syntaxes" ](mds/Format.md#flags)
+
+- Select the templates that will be executed on the resulting export. Read more about templates in the following section
+
+- Auto-Export. Checked by default, sending items to the extension will directly result in a export.
+ User is redirected to the Browser view and the items are cleaned after the export within the extension
+
+## Export Capabilities
+
+
+
+
+
+
+- **Expandable Metadata**. Underlined flags can be clicked to show the underlying metadata from the traffic in a scrollable popover
+
+- **Masked Randoms in URL Paths**. UUIDs and pseudorandom strings recognized inside path segments are mapped to variable names `` / ``. The re-renderization will reshape the diagram to improve flow readability. Every occurrency with the same value maintains the same name
+
+- **Notes**. Comments from Burp Suite are converted to notes in the resulting diagram. Use ` ` in Burp Suite comments to obtain multi-line notes in PESD exports
+
+- **Save as** :
+ - Sequence Diagram in `SVG` format
+ - `Markdown` file (MermaidJS syntax),
+ - Traffic `metadata` in `JSON` format. Read about the metadata structure in the format definition page, ["exports section"](https://github.com/doyensec/PESD-Exporter-Extension/blob/main/mds/Format.md#exports)
+
+## Extending the diagram, syntax and metadata with Templates
+
+By default, a generic diagram follows the basic [PESD syntax ](mds/Format.md#base-diagram-syntaxes).
+PESD Exporter supports syntax and metadata extension via templates execution.
+
+#### What is a Template?
+Templates are iterations that occur on the basic PESD object in order to enrich its content by:
+- Adding new Flags or modifying existing ones
+- Framing sections of the resulting diagram. Read about [MermaidJS Alt Syntax](https://mermaid-js.github.io/mermaid/#/sequenceDiagram?id=alt).
+- Enriching the metadata with new findings
+
+***Approach Idea :*** Users can leverage this extensibility to parse metadata and markdown in order to add new value in both of them by adding new logic.
+
+#### Currently Implemented Templates
+
+The Extension currently supports the following templates :
+- **OAuth2 / OpenID Connect.** The template matches standard OAuth2/OpenID Connect flows and adds related flags + flow frame.
+ Oauth2 supported flows : Implicit Grant and Code Grant. OpenID supported flows : Code Grant, Implicit Grant and Hybrid flow.
+ Respectively based on [rfc6749](https://datatracker.ietf.org/doc/html/rfc6749) and [openid-connect-core-1_0](https://openid.net/specs/openid-connect-core-1_0.html)
+
+- **SAML SSO**. The template matches Single-Sign-On flows with SAML V2.0 and adds related flags + flow frame.
+ Based on [SAML V2.0 ](http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0-cd-02.html#5.1.Web%20Browser%20SSO%20Profile|outline), supported flows:
+ - SP-initiated SSO using a Redirect Binding for the SP-to-IdP message and a POST Binding for the IdP-to-SP message
+ - SP-initiated SSO using a POST Binding for the message and an Artifact Binding for the message
+ - IDP-initiated SSO using a POST Binding for the IdP-to-SP message; no SP-to-IdP message is involved.
+
+Template matching example for *SAML SP-initiated SSO with redirect POST*:
+
+
+- ***pesd.json*** , a JSON file containing parsed and raw data extracted from the traffic. This file represents the metadata of the format that enable further analysis automation and detailed flows saving
+
+```
+{
+ "item1":{
+ "req":{
+ "path":"/favicon.ico",
+ "headers":["[...] Array of Headers [...]"],
+ "protocol":"http",
+ "url_params":{},
+ "port":80,
+ "destination":"example.com",
+ "raw":[...] BASE64ENCODED RAW REQUEST [...],
+ "body_params":{},
+ "id":1,
+ "http-verb":"GET",
+ "body_json":{},
+ "cookies":{}
+ },
+ "templateMatches":{ ... },
+ "res":{
+ "Stated-MimeType":"HTML",
+ "headers":[...] Array of Headers [...],
+ "raw":"[...] BASE64ENCODED RAW RESPONSE [...]",
+ "Inferred-MimeType":"HTML",
+ "id":2,
+ "cookies":{},
+ "statusCode":404,
+ "CSP": [...]CSP[...]
+ }
+ },
+ .
+ .
+ .
+ "itemN":{
+ .
+ .
+ .
+ }
+}
+
+```
+
+### Base Diagram Syntaxes
+
+The traffic conversion syntax was defined on top of MermaidJS markdown syntax. The definition was designed to represent HTTP traffic with Sequence Diagrams.
+
+Requests/responses arrows are filled with information that helps to visualize useful details of the traffic while maintaining the whole diagram readability.
+
+Two main modes of representation for Application Flows are supported:
+- ***Domains as Actors*** - Each domain involved in the traffic is represented as an actor in the diagram. Suitable for multi-domain flows analysis
+Current Syntax :
+```
+request message syntax : [HTTP.METHOD] /path/of/the/request
+ $Flag1$ $Flag2$ ... $FlagN$
+
+response message syntax : [Status.Code] Content-Type
+ $Flag1$ $Flag2$ ... $FlagN$
+```
+
+- ***Endpoints as Actors*** - Each endpoint (path) involved in the traffic is represented as an actor in the diagram. Suitable for single-domain flows analysis
+Current Syntax :
+```
+request message syntax : [HTTP.METHOD] $Flag1$ $Flag2$ ... $FlagN$
+
+response message syntax : [Status.Code] Content-Type $Flag1$ $Flag2$ ... $FlagN$
+```
+
+###### Flags
+Flags are basically Strings that represent the presence of something within req/res.
+List of currently supported basic flags :
+```
+In Requests :
+ - HasCookies
+ - HasUrlParams
+ - HasBodyParams
+ - HasJsonParam
+ - HasXMLParam
+ - HasMultiPartAttr
+ - HasBearerToken
+ - HasAuthz
+In Responses :
+ - Content-Type
+ - CookiesSet
+ - HasCORS
+ - HasXFrameOption
+ - HasCSP
+
+```
+
+### How to generate PESD exports?
+
+PESD can be generated directly from Burp Suite with the [PESD Exporter Extension](../README.md)
+
+Alternatively, it is possible to use the [PESD Wrapper Java library](PESDWrapper.md).
+The library exposes an object with all the methods needed to fill and modify a PESD Object.
diff --git a/mds/PESDWrapper.md b/mds/PESDWrapper.md
new file mode 100644
index 0000000..3887758
--- /dev/null
+++ b/mds/PESDWrapper.md
@@ -0,0 +1,97 @@
+
+# PESD Wrapper - Methods Detail
+**Note**: If a method's signature contains the *Integer index* parameter, it is optional. If specified in the call, its value is used as index within the data array when performing the method's action. Otherwise the current iterator index value is used.
+
+| Return Type | Method's Signature and Description |
+|--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| - | **PESDWrapper(PrintWriter stdout)** main constructor |
+| - | **PESDWrapper(PrintWriter stdout, JSONArray data)** Constructor usable to create copies of a PESD Object, simply use *getStucturedData()* to extract JSONArray data and pass it to the constructor |
+| *String* | **getSeqDiagramdMD(Integer op_mode)** This method serializes the JSONArray data to MermaidJS Markdown syntax. The *op_mode* parameter indicates the export mode: - 0 = Domains as Actors - 1 = Endpoints as Actors Returns a string containing a valid MermaidJS Markdown Sequence Diagram |
+| *JSONObject* | **getMetadata()** Generates the metadata export with PESD format |
+| *JSONObject* | **getLine(*Integer index*)** Returns line at *index*, if no parameter is passed, returns line according to internal iterator |
+| *Integer* | **getIterator()** Returns the current index value of the internal iterator. Return -1 if the end of the object is reached |
+| *void* | **setIterator()** Set index value of the internal iterator to 0 |
+| *void* | **startIterator(Integer num)** Set index value of the internal iterator to *num* parameter |
+| *void* | **nextLine()** Increase internal iterator index of 1 |
+| *void* | **prevLine()** Decrease internal iterator index of 1 |
+| *void* | **addReq(String source, String destination, String method, String path, List flags, JSONObject metadata, *Integer index*)** Method used to add a Request line to the obj that will have the format: Markdown : S ->> D : [METHOD] /path \n Flag1 Flag2 ... FlagN Metadata: JSONObject generated by PESDMetadata obj |
+| *void* | **addRes(String source, String destination, String status_code, String mimeType,List flags, JSONObject metadata, *Integer index*)** Method used to add a Response line to the obj that will have the format: Markdown : D ->> S : [STATUS_CODE] MIME_TYPE \n Flag1 Flag2 ... FlagN Metadata: JSONObject generated by PESDMetadata obj |
+| *void* | **addAlt(String text, *Integer index*)** Method used to add a Alt line (rectangle start in MermaidJS MD) to the obj that will have the format : Markdown : Alt NAME |
+| *void* | **endAlt(*Integer index*)** Method used to add a end line (rectangle close in MermaidJS MD) to the obj that will have the format : Markdown : end |
+| *void* | **addNote(String note, *Integer index*)** Method used to add a Note line to the obj that will have the format: Markdown : Note right of Browser : TEXT |
+| *void* | **modifyFlags(List flagsArr, *Integer index*)** Substituting flags in a transaction during an iteration. |
+| *void* | **addFlag(String flag, *Integer index*)** Adding a flag to a transaction while iterating. |
+| *void* | **modifyMetadata(JSONObject metadataObj, *Integer index*)** Replacing a metadata object in a line with new metadata while iterating. |
+| *void* | **removeLine(*Integer index*)** Remove line while iterating |
+| *Boolean* | **altEndConsistencyNormalizer()** This method is used to resolve local consistency problems within the resulting MermaidJS Markdown before the serialization. Currently it simply removes non-closed and non-opened Alt rectangles (Alt without end or end without Alt) Moreover, in case of 2 consecutive Alt, the first occurrence is removed |
+| *void* | **addToPos(int pos, JSONObject jsonObj)** Function needed to perform JSONArray.put(pos, json) without losing an entry due to replacement. Basically it performs right-shift of all the entries after the position needed for the put |
+| *Integer* | **getSize()** Return the number of lines within the Sequence Diagram |
+| *JSONArray* | **getStucturedData()** Return a copy by value of the data contained in the wrapper |
+
+
+
+
+### Internal Data Structure
+The internal data structure :
+```
+[
+ {
+ "type": "req",
+ "source": *String*,
+ "destination": *String*,
+ "maskedPath": *String*,
+ "method": *String*,
+ "path": *String*,
+ "flags": *List*,
+ "metadata": *JSONObject*
+ },
+ {
+ "type": "res",
+ "source": String,
+ "destination": String,
+ "status_code": String,
+ "mime_type": String,
+ "flags": List,
+ "metadata": JSONObject
+ },
+ {
+ "type": "alt",
+ "alt": String,
+ },
+ {
+ "type": "end"
+ },
+ {
+ "type": "Note",
+ "note": String,
+ },
+.
+.
+.
+
+]
+```
+
+### Usage Examples
+```
+PESDWrapper pesdObj = new PESDWrapper(out_printWriter);
+// Generic function that fills the object
+fill_PESD(pesdObj);
+// Setting the internal iterator to 0
+pesdObj.startIterator();
+// Iterating until we get end of PESD from *getIterator* (-1)
+while (pesdObj.getIterator() != -1) {
+ // Getting current line according to internal iterator
+ JSONObject line = pesdObj.getLine();
+ // Switching actions depending on the type of line
+ switch((String) line.get("type")){
+ case "req":
+ // do stuff
+ case "res":
+ // do stuff
+ default: break
+ }
+ // Classic i++
+ pesdObj.nextLine();
+}
+```
\ No newline at end of file
diff --git a/mds/WritingTemplates.md b/mds/WritingTemplates.md
new file mode 100644
index 0000000..5569217
--- /dev/null
+++ b/mds/WritingTemplates.md
@@ -0,0 +1,111 @@
+
+## How to write templates
+
+Required Steps :
+
+#### 1) Develop your template
+In order to manipulate the pesd object you should read the [PESD Wrapper Java library](PESDWrapper.md) documentation and learn about all the exposed iteration / manipulation methods.
+
+Templates must inherit from **TemplateParent** and override the *Run* method. The engine will construct all the templates and save them in a TemplateParent var, then it will call the *Run* method to manipulate the *PESDWrapper* object.
+
+The following code is an Empty Template structure to start with:
+```
+/*
+ * Empty Template Structure
+ */
+
+public class EmptyTemplate extends TemplateParent{
+ // As you can see, the creation will set pesdObj and stdout to be accessible directly within your code
+ public EmptyTemplate( PESDWrapper pesdObj, PrintWriter stdout) {
+ super(pesdObj, stdout);
+ }
+
+ // put all your logic inside the run method
+ @Override
+ public PESDWrapper run(){
+
+ // your Logic
+
+ // All the templates must return a PESDWrapper obj
+ return this.pesdObj;
+ }
+}
+
+```
+
+#### 2) Adding the new Template Class to the Burp extension
+1. Modify PESDPanel.java by adding the a checkbox for your template
+
+2. Modify PESDPanel.java:45 and add your template name to the String array of templates
+ ```
+ this.Templates = new String[]{"SAML_SSO", "OAuth2/OIDC","MY_NEW_TEMPLATE_NAME"};
+ ```
+
+3. Add the following lines of code to the PESDPanel constructor:
+```
+YOUR_TEMPLATE_checkbox.setVisible(false);
+YOUR_TEMPLATE_checkbox.doClick();
+```
+
+4. In PESDPanel.java, modify *EditTemplates_ButtonActionPerformed* method by adding *your_template_checkbox.setVisible()* with false for the **if** and true for the **else** case.
+
+5. In PESDPanel.java, fill *YOUR_TEMPLATE_checkboxActionPerformed()* method as follows
+```
+private void YOUR_TEMPLATE_checkboxActionPerformed(java.awt.event.ActionEvent evt) {
+ if (SAML_SSO_TEMPLATE_checkbox.isSelected()){
+ this.Templates[$INDEX]= "MY_NEW_TEMPLATE_NAME";
+ } else {
+ this.Templates[$INDEX]="";
+ }
+ }
+```
+Note that *$INDEX* is the position of your template inside *this.Templates* array (see step 2).
+
+6. Add the a case for the new template in TemplateRunner.java. Add it inside the switch-case in the constructor as follows:
+```
+case "MY_NEW_TEMPLATE_NAME":
+ this.template = new NewTemplateClass(diagram, metadataObj, stdout);
+ break;
+```
+
+7. When you are done with coding, name and save your template inside the templates package directory.
+
+8. Re-build the Extension and you are ready to go. What a long journey!
+
+
+## Example Template : Flow-wide Frame Add
+This simple example shows how to iterate through a PESDWrapper object and add a frame (Mermaid Alt syntax) around the entire flow being exported. This is just a demonstration of the basic iteration.
+
+```
+/*
+ * Example Template that wraps the Entire flow in a Flow Frame (Alt/end named rectangle)
+ */
+
+public class EmptyTemplate extends TemplateParent{
+
+ public EmptyTemplate( PESDWrapper pesdObj, PrintWriter stdout) {
+ super(pesdObj, stdout);
+ }
+
+ // put all your logic inside the run method
+ @Override
+ public PESDWrapper run(){
+
+ pesdObj.startIterator();
+ while (pesdObj.getIterator() != -1) {
+ JSONObject line = pesdObj.getLine();
+
+ // adding and Alt/end flow frame to wrap the entire flow
+ if(pesdObj.getIterator()==0){
+ pesdObj.addFlag("HELLO PESD FLOW FRAME");
+ } else if (pesdObj.getIterator() == pesdObj.getSize()-1) {
+ pesdObj.endAlt(pesdObj.getIterator()+1);
+ }
+
+ pesdObj.nextLine();
+ }
+ return this.pesdObj;
+ }
+}
+
+```
diff --git a/resources/export.html b/resources/export.html
new file mode 100644
index 0000000..162ce91
--- /dev/null
+++ b/resources/export.html
@@ -0,0 +1,500 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PESD Exporter
+
+
+
+
+
+
+
+
+
+
+
PESD Exports
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..45951de
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'pesd-exporter'
diff --git a/src/.DS_Store b/src/.DS_Store
new file mode 100644
index 0000000..c6412b0
Binary files /dev/null and b/src/.DS_Store differ
diff --git a/src/burp/BurpExtender.java b/src/burp/BurpExtender.java
new file mode 100644
index 0000000..5429f9d
--- /dev/null
+++ b/src/burp/BurpExtender.java
@@ -0,0 +1,80 @@
+
+package burp;
+
+import java.util.List;
+import pesd.PESDExportTab;
+
+import java.util.*;
+import java.awt.event.*;
+import javax.swing.JMenuItem;
+import pesd.PESDPanel;
+
+public class BurpExtender implements IBurpExtender,IContextMenuFactory
+{
+
+ private IBurpExtenderCallbacks callbacks;
+ private IExtensionHelpers helpers;
+
+ private final static String DOMAIN_ACTORS = "Domains as Actors";
+ private final static String ENDPOINTS_ACTORS = "Endpoints as Actors";
+ private final static String CLEAN_FLOW = "Clean Flow";
+ private PESDExportTab Tab;
+
+ @Override
+ public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks)
+ {
+ this.callbacks = callbacks;
+ helpers = callbacks.getHelpers();
+ callbacks.setExtensionName("PESD Exporter");
+ PESDExportTab tab = new PESDExportTab(callbacks, helpers);
+ this.Tab=tab;
+ callbacks.addSuiteTab(tab);
+ callbacks.registerContextMenuFactory(this);
+
+ }
+
+ @Override
+ public List createMenuItems(IContextMenuInvocation invocation) {
+ final IHttpRequestResponse[] messages = invocation.getSelectedMessages();
+ if (messages == null || messages.length == 0) return null;
+ JMenuItem i1 = new JMenuItem(DOMAIN_ACTORS);
+ JMenuItem i2 = new JMenuItem(ENDPOINTS_ACTORS);
+ JMenuItem i3 = new JMenuItem(CLEAN_FLOW);
+ i1.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ copyMessages(messages,0);
+ }
+ });
+ i2.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ copyMessages(messages,1);
+ }
+ });
+ i3.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ cleanFlow();
+ }
+ });
+ return Arrays.asList(i1,i2,i3);
+ }
+
+ private void copyMessages(IHttpRequestResponse[] messages, Integer opMode) {
+ PESDPanel panel = (PESDPanel) this.Tab.getPanel();
+ panel.setItems(messages,opMode);
+ panel.setCountLabel();
+ if (panel.getAutoExport()){
+ panel.clickPESDExport_button();
+ }
+
+ }
+
+ private void cleanFlow() {
+ PESDPanel panel = (PESDPanel) this.Tab.getPanel();
+ IHttpRequestResponse iHttpRequestResponse[] = new IHttpRequestResponse[0];
+ panel.items=iHttpRequestResponse;
+ panel.setCountLabel();
+ }
+}
diff --git a/src/pesd/Oauth2_OpenID_Template.java b/src/pesd/Oauth2_OpenID_Template.java
new file mode 100644
index 0000000..aa98541
--- /dev/null
+++ b/src/pesd/Oauth2_OpenID_Template.java
@@ -0,0 +1,104 @@
+/*
+ * This template is based on the following specification documents:
+ * https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.1
+ * https://openid.net/specs/openid-connect-core-1_0.html#Authentication
+ */
+package pesd;
+
+import java.io.PrintWriter;
+import java.util.Base64;
+import org.json.JSONArray;
+import org.json.JSONObject;
+
+public class Oauth2_OpenID_Template extends TemplateParent{
+
+ public Oauth2_OpenID_Template( PESDWrapper pesdObj, PrintWriter stdout) {
+ super(pesdObj, stdout);
+ }
+
+ @Override
+ public PESDWrapper run(){
+ pesdObj.startIterator();
+ while (pesdObj.getIterator() != -1) {
+ JSONObject line = pesdObj.getLine();
+ JSONObject metadata;
+ JSONObject urlParams;
+
+ switch((String) line.get("type")){
+ case "req":
+ try {
+ metadata = (JSONObject) line.get("metadata");
+ urlParams = (JSONObject) metadata.get("url_params");
+
+ if (urlParams.has("client_id") && urlParams.has("response_type")){
+ // identifying first reqs of OpenID flow: code grant, token grant and hybrid
+ if (urlParams.has("scope") && urlParams.getString("scope").contains("openid")){
+
+ if (urlParams.getString("response_type").contains("code") && urlParams.getString("response_type").contains("token")){
+ // hybrid flow
+ pesdObj.addFlag("OIDC_HybridGrant");
+ } else if (urlParams.getString("response_type").contains("code")) {
+ // hybrid Code Grant flow
+ pesdObj.addFlag("OIDC_CodeGrant");
+ } else if (urlParams.getString("response_type").contains("token")) {
+ // implicit grant flow
+ pesdObj.addFlag("OIDC_ImplicitGrant");
+ }
+ pesdObj.addAlt("OIDC", pesdObj.getIterator());
+ } else {
+ // identifying first reqs of OAuth2 flow: code grant and implicit grant
+ switch(urlParams.getString("response_type")){
+ case "code":
+ pesdObj.addFlag("CodeGrant");
+ break;
+ case "token":
+ pesdObj.addFlag("ImplicitGrant");
+ break;
+ default:
+ break;
+ }
+ pesdObj.addAlt("OAuth2", pesdObj.getIterator());
+ }
+ }
+ //identifying last request of Code Grant flow
+ if (urlParams.has("code")){
+ pesdObj.addFlag("Code");
+ pesdObj.endAlt(pesdObj.getIterator()+2);
+ }
+ } catch(Exception e) {
+ stdout.println(e);
+ }
+ break;
+
+ case "res":
+ try {
+ //identifying last request of Implicit Grant flow
+ metadata = (JSONObject) line.get("metadata");
+ JSONArray headers = (JSONArray) metadata.get("headers");
+ for (int i = 0; i < headers.length(); i++) {
+ String header = headers.getString(i);
+ if (header.contains("Location") && header.contains("#") && header.contains("access_token") && header.contains("token_type")){
+ pesdObj.addFlag("AccessToken");
+ pesdObj.endAlt(pesdObj.getIterator()+1);
+ }
+ }
+ // looking for implicit grant tokens inside the response body
+ String raw_res_base64Encoded = (String) metadata.get("raw");
+ byte[] decodedBytes = Base64.getDecoder().decode(raw_res_base64Encoded);
+ String decoded_raw_response = new String(decodedBytes);
+ if(decoded_raw_response.contains("access_token") && decoded_raw_response.contains("token_type")){
+ pesdObj.addFlag("AccessToken");
+ pesdObj.endAlt(pesdObj.getIterator()+1);
+ }
+ } catch(Exception e) {
+ stdout.println(e);
+ }
+ break;
+
+ default: break;
+ }
+ pesdObj.nextLine();
+ }
+ return this.pesdObj;
+ }
+}
diff --git a/src/pesd/PESDExportTab.java b/src/pesd/PESDExportTab.java
new file mode 100644
index 0000000..31a8b7b
--- /dev/null
+++ b/src/pesd/PESDExportTab.java
@@ -0,0 +1,41 @@
+
+package pesd;
+import burp.IBurpExtenderCallbacks;
+import burp.IExtensionHelpers;
+import burp.ITab;
+import java.awt.Component;
+import javax.swing.JPanel;
+/**
+ *
+ * @author francesco lacerenza https://twitter.com/lacerenza_fra
+ */
+
+public class PESDExportTab implements ITab {
+
+ private final IBurpExtenderCallbacks callbacks;
+ private final IExtensionHelpers helpers;
+ private final JPanel panel;
+
+ public PESDExportTab(IBurpExtenderCallbacks callbacks, IExtensionHelpers helpers) {
+ this.callbacks = callbacks;
+ this.helpers = helpers;
+ JPanel panelCreate = new PESDPanel(callbacks, helpers);
+ this.panel=panelCreate;
+ this.panel.setMaximumSize(this.panel.getMinimumSize());
+ }
+
+ @Override
+ public String getTabCaption() {
+ return "PESD Exporter";
+ }
+
+ @Override
+ public Component getUiComponent() {
+ callbacks.customizeUiComponent(this.panel);
+ return this.panel;
+ }
+
+ public JPanel getPanel() {
+ return panel;
+ }
+}
diff --git a/src/pesd/PESDExporter.java b/src/pesd/PESDExporter.java
new file mode 100644
index 0000000..fc2c277
--- /dev/null
+++ b/src/pesd/PESDExporter.java
@@ -0,0 +1,330 @@
+package pesd;
+
+import burp.IBurpExtenderCallbacks;
+import burp.ICookie;
+import burp.IExtensionHelpers;
+import burp.IHttpRequestResponse;
+import java.io.PrintWriter;
+import burp.IHttpService;
+import burp.IParameter;
+import burp.IRequestInfo;
+import burp.IResponseInfo;
+import java.util.ArrayList;
+import java.util.List;
+import org.json.JSONObject;
+import org.apache.commons.lang3.StringEscapeUtils;
+
+/**
+ *
+ * @author francesco
+ */
+public class PESDExporter {
+
+ private IExtensionHelpers helpers;
+ private IHttpRequestResponse[] allReqRes;
+ private PrintWriter stdout;
+ private Integer operationMode;
+ private Boolean[] wantedBools;
+ private String metadata;
+ private JSONObject metadataObj;
+ private String[] templates;
+
+ public PESDExporter(IBurpExtenderCallbacks callbacks, IExtensionHelpers helpers, IHttpRequestResponse[] allReqRes, PrintWriter stdout, PrintWriter stderr, Integer mode, Boolean[] syntaxBools, String[] templates) {
+ this.helpers = helpers;
+ this.allReqRes = allReqRes;
+ this.stdout = stdout;
+ // op. mode = 0 -> Domains as Actors , op. mode = 1 -> Endpoints as Actors
+ this.operationMode = mode;
+ // Selected template
+ this.templates = templates;
+ // wantedBools index: 0=HasUrlParams, 1=HasBodyParam, 2=HasJsonParam, 3=HasXmlParam, 4=HasMultipartAttr, 5=HasAuthzToken, 6=Content-type, 7=CookiesSet, 8=HasCORS, 9=HasXFrameOp, 10=HasCSP, 11=HasCookies
+ this.wantedBools = syntaxBools;
+ }
+
+ public String[] generatePESD() {
+ JSONObject csp_res;
+ String CSP = "";
+ PESDWrapper pesdObj = new PESDWrapper(this.stdout);
+ // PESDMetaData will handle data addition to the metadata
+ PESDMetaData metadataObj = new PESDMetaData(this.helpers);
+ //Looping over all req/res couples (items) and converting them to PESD markdown and metadata
+ int num = 1;
+ String browser = "Browser";
+ for (int rc = 0; rc < this.allReqRes.length; rc++) {
+ try {
+ //needed items parsing vars
+ Boolean noRes = false;
+ IResponseInfo iResponseInfo = new IResponseInfo() {
+ @Override
+ public List getHeaders() {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ public int getBodyOffset() {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ public short getStatusCode() {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ public List getCookies() {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ public String getStatedMimeType() {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+
+ @Override
+ public String getInferredMimeType() {
+ throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
+ }
+ };
+
+ IHttpService httpService = allReqRes[rc].getHttpService();
+ String actor = httpService.getHost();
+ IRequestInfo iRequestInfo2 = helpers.analyzeRequest(allReqRes[rc]);
+ String path = iRequestInfo2.getUrl().getPath();
+ IRequestInfo iRequestInfo = helpers.analyzeRequest(allReqRes[rc].getRequest());
+ try {
+ iResponseInfo = helpers.analyzeResponse(allReqRes[rc].getResponse());
+ } catch (Exception e) {
+ noRes = true;
+ }
+
+ JSONObject params_res = this.getParamsSetFlag(iRequestInfo);
+ // getting params lists: url, body, json, cookies, csp
+ JSONObject url_params = params_res.getJSONObject("url_params");
+ JSONObject body_params = params_res.getJSONObject("body_params");
+ JSONObject body_json = params_res.getJSONObject("body_json");
+ JSONObject req_cookies = params_res.getJSONObject("req_cookies");
+ if (!noRes) {
+ csp_res = this.getHasCSPFlag(iResponseInfo);
+ csp_res.getBoolean("HasCSP");
+ CSP = csp_res.getString("CSP");
+ }
+ // adding metadata about the item
+ JSONObject metadata_item = metadataObj.addItemMetadata(num, path, iRequestInfo, iResponseInfo, allReqRes[rc], url_params, body_params, body_json, CSP, req_cookies, noRes);
+ num = num + 2;
+ List flags_req = aggregateReqFlags(iRequestInfo);
+ pesdObj.addReq(browser, actor, iRequestInfo.getMethod(), path, flags_req, (JSONObject) metadata_item.get("req"));
+ try {
+ if (!metadata_item.getJSONObject("req").get("comment").toString().isEmpty()) {
+ pesdObj.addNote(metadata_item.getJSONObject("req").get("comment").toString());
+ }
+ } catch (Exception e) {
+ }
+
+ //Handling items with response
+ if (!noRes) {
+
+ List flags_res = aggregateResFlags(iResponseInfo);
+ String mime_type;
+ if (this.wantedBools[6]) {
+ mime_type = iResponseInfo.getStatedMimeType();
+ } else {
+ mime_type = "NoNe";
+ }
+ pesdObj.addRes(actor, browser, String.valueOf(iResponseInfo.getStatusCode()), mime_type, flags_res, (JSONObject) metadata_item.get("res"));
+ } else {
+ num = num - 1;
+ }
+
+ } catch (Exception e) {
+ stdout.println("Error:" + e.toString());
+ }
+ }
+ //template engine run
+ TemplateRunner engine = new TemplateRunner(this.templates, pesdObj, this.stdout);
+ this.metadata = engine.getMetadata().toString();
+ this.metadataObj = engine.getMetadata();
+ return engine.getDiagram(this.operationMode);
+ }
+
+ public String getMetadata() {
+ return this.metadata;
+ }
+
+ private Boolean getCookiesSetFlag(IResponseInfo iResponseInfo) {
+ // CookieSet Boolean
+ Boolean CookiesSet = false;
+ List iCookie = iResponseInfo.getCookies();
+ if (iCookie.size() != 0 && this.wantedBools[7]) {
+ CookiesSet = true;
+ }
+ return CookiesSet;
+ }
+
+ private Boolean getHasAuthzFlag(IRequestInfo iRequestInfo) {
+ Boolean HasAuthz = false;
+ if (this.wantedBools[5]) {
+ List headers = iRequestInfo.getHeaders();
+ for (String header : headers) {
+ if (header.toLowerCase().replaceAll("\\s", "").startsWith("authorization")) {
+ HasAuthz = true;
+ break;
+ }
+ }
+ }
+ return HasAuthz;
+ }
+
+ private JSONObject getParamsSetFlag(IRequestInfo iRequestInfo) {
+ JSONObject results = new JSONObject();
+ Boolean PramBools[] = new Boolean[]{false, false, false, false, false, false};
+ // Params Flags parsing and arranging for metadata
+ // Bools index: 0=HasUrlParams, 1=HasBodyParam, 2=HasJsonParam, 3=HasXmlParam, 4=HasMultipartAttr, 5=HasAuthzToken, 6=Content-type, 7=CookiesSet
+ // array of output bools ordered with true val if one of the types matches in one param, IParameter inteface types value : PARAM_URL=0, PARAM_BODY=1, PARAM_JSON=6, PARAM_XML=3, PARAM_MULTIPART_ATTR=5, PARAM_COOKIE=2
+ List iParameter = iRequestInfo.getParameters();
+ JSONObject url_params = new JSONObject();
+ JSONObject body_params = new JSONObject();
+ JSONObject body_json = new JSONObject();
+ JSONObject req_cookies = new JSONObject();
+
+ for (int i = 0; i < iParameter.size(); i++) {
+ switch (iParameter.get(i).getType()) {
+ case 0:
+ if (this.wantedBools[0]) {
+ PramBools[0] = true;
+ //URL . use getname and getvalue then add it to a list
+ String name = iParameter.get(i).getName();
+ String value = iParameter.get(i).getValue();
+ url_params.put(StringEscapeUtils.escapeJava(name), StringEscapeUtils.escapeJava(value));
+ }
+ break;
+ case 2:
+ if (this.wantedBools[11]) {
+ PramBools[5] = true;
+ //Cookie . use getname and getvalue then add it to a list
+ String name = iParameter.get(i).getName();
+ String value = iParameter.get(i).getValue();
+ req_cookies.put(StringEscapeUtils.escapeJava(name), StringEscapeUtils.escapeJava(value));
+ }
+ break;
+ case 1:
+ if (this.wantedBools[1]) {
+ PramBools[1] = true;
+ //BodyParam . use getname and getvalue then add it to a list
+ String name = iParameter.get(i).getName();
+ String value = iParameter.get(i).getValue();
+ body_params.put(StringEscapeUtils.escapeJava(name), StringEscapeUtils.escapeJava(value));
+ }
+ break;
+ case 6:
+ if (this.wantedBools[2]) {
+ PramBools[2] = true;
+ //bodyJSON . use getname and getvalue then add it to a list
+ String name = iParameter.get(i).getName();
+ String value = iParameter.get(i).getValue();
+ body_json.put(StringEscapeUtils.escapeJava(name), StringEscapeUtils.escapeJava(value));
+ }
+ break;
+ case 3:
+ if (this.wantedBools[3]) {
+ PramBools[3] = true;
+ }
+ break;
+ case 5:
+ if (this.wantedBools[4]) {
+ PramBools[4] = true;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ results.put("ParamBools", PramBools);
+ results.put("url_params", url_params);
+ results.put("body_params", body_params);
+ results.put("body_json", body_json);
+ results.put("req_cookies", req_cookies);
+ return results;
+ }
+
+ private Boolean getHasXFrameOpFlag(IResponseInfo iResponseInfo) {
+ Boolean HasXFrameOp = false;
+ List headers = iResponseInfo.getHeaders();
+ if (this.wantedBools[9]) {
+ for (String header : headers) {
+ if (header.startsWith("X-Frame-Options")) {
+ HasXFrameOp = true;
+ break;
+ }
+ }
+ }
+ return HasXFrameOp;
+ }
+
+ private JSONObject getHasCSPFlag(IResponseInfo iResponseInfo) {
+ JSONObject result = new JSONObject();
+ String CSP_string = "";
+ Boolean HasCSP = false;
+ List headers = iResponseInfo.getHeaders();
+ if (this.wantedBools[10]) {
+ for (String header : headers) {
+ if (header.startsWith("Content-Security-Policy")) {
+ HasCSP = true;
+ CSP_string = header;
+ break;
+ }
+ }
+ }
+ result.put("HasCSP", HasCSP);
+ result.put("CSP", CSP_string);
+ return result;
+ }
+
+ private Boolean getHasCORSFlag(IResponseInfo iResponseInfo) {
+ Boolean HasCORS = false;
+ List headers = iResponseInfo.getHeaders();
+ if (this.wantedBools[8]) {
+ for (String header : headers) {
+ if (header.startsWith("Access-Control-Allow-Origin")) {
+ HasCORS = true;
+ break;
+ }
+ }
+ }
+ return HasCORS;
+ }
+
+ private List aggregateReqFlags(IRequestInfo iRequestInfo) {
+ // ParamBools is an array of bools ordered with true val if one of the types matches in one param: PARAM_URL=0, PARAM_BODY=1, PARAM_JSON=6, PARAM_XML=3, PARAM_MULTIPART_ATTR=5 , PARAM_COOKIE=2
+ String PramFlags[] = new String[]{"UrlParams", "BodyParams ", "JSONParam", "XMLParam", "Multipart", "Cookies"};
+ List paramBools = new ArrayList();
+ Boolean[] params = (Boolean[]) this.getParamsSetFlag(iRequestInfo).get("ParamBools");
+ for (int i = 0; i < 6; i++) {
+ if (params[i] == true) {
+ paramBools.add(PramFlags[i]);
+ }
+ }
+ if (this.getHasAuthzFlag(iRequestInfo)) {
+ paramBools.add("HasAuthz");
+ }
+ return paramBools;
+ }
+
+ private List aggregateResFlags(IResponseInfo iResponseInfo) {
+ List resBools = new ArrayList();
+ if (this.getCookiesSetFlag(iResponseInfo)) {
+ resBools.add("SetCookies");
+ }
+ if (this.getHasCORSFlag(iResponseInfo)) {
+ resBools.add("CORS");
+ }
+ if (this.getHasXFrameOpFlag(iResponseInfo)) {
+ resBools.add("XFrameOp");
+ }
+ JSONObject csp_res = this.getHasCSPFlag(iResponseInfo);
+ if (csp_res.getBoolean("HasCSP")) {
+ resBools.add("CSP");
+ }
+ return resBools;
+ }
+
+}
diff --git a/src/pesd/PESDMetaData.java b/src/pesd/PESDMetaData.java
new file mode 100644
index 0000000..0b48f4e
--- /dev/null
+++ b/src/pesd/PESDMetaData.java
@@ -0,0 +1,87 @@
+
+package pesd;
+
+import burp.ICookie;
+import burp.IExtensionHelpers;
+import burp.IHttpRequestResponse;
+import burp.IRequestInfo;
+import burp.IResponseInfo;
+import java.util.ArrayList;
+import java.util.List;
+import org.json.JSONObject;
+import org.apache.commons.lang3.StringEscapeUtils;
+
+/**
+ *
+ * @author francesco
+ */
+public class PESDMetaData {
+
+ private final IExtensionHelpers helpers;
+
+ public PESDMetaData(IExtensionHelpers helpers) {
+ this.helpers=helpers;
+ }
+
+ public JSONObject addItemMetadata(Integer num, String path, IRequestInfo iRequestInfo, IResponseInfo iResponseInfo, IHttpRequestResponse reqResItem, JSONObject url_params, JSONObject body_params, JSONObject body_json, String CSP, JSONObject req_cookies, Boolean noRes) {
+ JSONObject item = new JSONObject();
+ //adding req json key
+ JSONObject req = new JSONObject();
+ req.put("id",num);
+ req.put("destination",StringEscapeUtils.escapeJava(reqResItem.getHttpService().getHost()));
+ req.put("protocol",StringEscapeUtils.escapeJava(reqResItem.getHttpService().getProtocol()));
+ req.put("port",reqResItem.getHttpService().getPort());
+ req.put("path",StringEscapeUtils.escapeJava(path));
+ req.put("raw", helpers.base64Encode(reqResItem.getRequest()));
+ req.put("http-verb",iRequestInfo.getMethod());
+ List req_headers=iRequestInfo.getHeaders();
+ List req_headers_escaped= new ArrayList();
+ for (String header : req_headers){
+ req_headers_escaped.add(StringEscapeUtils.escapeJava(header));
+ }
+ req.put("headers",req_headers_escaped);
+ //Escaped in PESDExporter
+ req.put("url_params",url_params);
+ //Escaped in PESDExporter
+ req.put("body_params",body_params);
+ //Escaped in PESDExporter
+ req.put("body_json",body_json);
+ //Escaped in PESDExporter
+ req.put("cookies",req_cookies);
+ //add highlight, comment keys
+ req.put("highlight",reqResItem.getHighlight());
+ req.put("comment", StringEscapeUtils.escapeJava(reqResItem.getComment()));
+
+ item.put("req",req);
+
+ if (!noRes){
+ //adding res json key
+ JSONObject res = new JSONObject();
+
+ res.put("id",num+1);
+ res.put("statusCode",iResponseInfo.getStatusCode());
+ res.put("raw",helpers.base64Encode(reqResItem.getResponse()));
+ res.put("Inferred-MimeType",StringEscapeUtils.escapeJava(iResponseInfo.getInferredMimeType()));
+ res.put("Stated-MimeType",StringEscapeUtils.escapeJava(iResponseInfo.getStatedMimeType()));
+ if (!CSP.isEmpty()){res.put("CSP",StringEscapeUtils.escapeJava(CSP));}
+ List res_headers=iResponseInfo.getHeaders();
+ List res_headers_escaped= new ArrayList();
+ for (String header : res_headers){
+ res_headers_escaped.add(StringEscapeUtils.escapeJava(header));
+ }
+ res.put("headers",res_headers_escaped);
+
+
+ JSONObject res_cookies_escaped = new JSONObject();
+ List iCookie = iResponseInfo.getCookies();
+ for (ICookie cookie : iCookie){
+ res_cookies_escaped.put(StringEscapeUtils.escapeJava(cookie.getName()),StringEscapeUtils.escapeJava(cookie.getValue()));
+ }
+ res.put("cookies",res_cookies_escaped);
+ item.put("res",res);
+ }
+
+ return item;
+ }
+
+}
diff --git a/src/pesd/PESDPanel.form b/src/pesd/PESDPanel.form
new file mode 100644
index 0000000..2636ab2
--- /dev/null
+++ b/src/pesd/PESDPanel.form
@@ -0,0 +1,418 @@
+
+
+
diff --git a/src/pesd/PESDPanel.java b/src/pesd/PESDPanel.java
new file mode 100644
index 0000000..4ce8c5b
--- /dev/null
+++ b/src/pesd/PESDPanel.java
@@ -0,0 +1,806 @@
+package pesd;
+
+import burp.IExtensionHelpers;
+import java.io.PrintWriter;
+import burp.IBurpExtenderCallbacks;
+import burp.IHttpRequestResponse;
+import java.awt.Desktop;
+import java.net.URI;
+import java.util.Arrays;
+
+import java.io.File;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+
+/**
+ *
+ * @author francesco
+ */
+public class PESDPanel extends javax.swing.JPanel {
+
+ private final IBurpExtenderCallbacks callbacks;
+ private final IExtensionHelpers helpers;
+ public IHttpRequestResponse[] items;
+ private int operationMode;
+ private PESDExporter pesdexporter;
+ private Boolean[] Bools;
+ private String[] Templates;
+ private String metadata;
+ private PrintWriter stdout;
+ private PrintWriter stderr;
+ private String[] diagrams;
+
+ public PESDPanel(IBurpExtenderCallbacks callbacks, IExtensionHelpers helpers) {
+ initComponents();
+ this.callbacks = callbacks;
+ this.helpers = helpers;
+ this.stdout = new PrintWriter(callbacks.getStdout(), true);
+ this.stderr = new PrintWriter(callbacks.getStderr(), true);
+ IHttpRequestResponse iHttpRequestResponse[] = new IHttpRequestResponse[0];
+ this.items = iHttpRequestResponse;
+ this.operationMode = 0;
+ // Bools index: 0=HasUrlParams, 1=HasBodyParam, 2=HasJsonParam, 3=HasXmlParam, 4=HasMultipartAttr, 5=HasAuthz, 6=Content-type, 7=CookiesSet, 8=HasCORS, 9=HasXFrameOp, 10=HasCSP, 11=HasCookies
+ Boolean PramBools[] = new Boolean[]{false, false, false, false, false, false, false, false, false, false, false, false};
+ this.Bools = PramBools;
+ this.Templates = new String[]{"SAML_SSO", "OAuth2/OIDC"};
+ // hiding checkboxes
+ HasJsonParam_checkbox.setVisible(false);
+ HasJsonParam_checkbox.doClick();
+ HasAuthz_checkbox.setVisible(false);
+ HasAuthz_checkbox.doClick();
+ HasBodyParam_checkbox.setVisible(false);
+ HasBodyParam_checkbox.doClick();
+ HasCORS_checkbox.setVisible(false);
+ HasCORS_checkbox.doClick();
+ HasCSP_checkbox.setVisible(false);
+ HasCSP_checkbox.doClick();
+ HasCookies_checkbox.setVisible(false);
+ HasCookies_checkbox.doClick();
+ HasMultipartAttr_checkbox.setVisible(false);
+ HasMultipartAttr_checkbox.doClick();
+ HasUrlParams_checkbox.setVisible(false);
+ HasUrlParams_checkbox.doClick();
+ HasXFrameOp_checkbox.setVisible(false);
+ HasXFrameOp_checkbox.doClick();
+ HasXMLParam_checkbox.setVisible(false);
+ HasXMLParam_checkbox.doClick();
+ ContentType_checkbox.setVisible(false);
+ ContentType_checkbox.doClick();
+ CookiesSet_checkbox.setVisible(false);
+ CookiesSet_checkbox.doClick();
+ jSeparator1.setVisible(false);
+ InRequests_label.setVisible(false);
+ InResponses_label.setVisible(false);
+ OAuth2_OIDC_TEMPLATE_checkbox.setVisible(false);
+ OAuth2_OIDC_TEMPLATE_checkbox.doClick();
+ SAML_SSO_TEMPLATE_checkbox.setVisible(false);
+ SAML_SSO_TEMPLATE_checkbox.doClick();
+ autoExport_check.doClick();
+ //setting automatically by default all booleans to true via Analysis preset
+ booleansPresets_ComboBox.setSelectedIndex(0);
+ }
+
+ /**
+ * This method is called from within the constructor to initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is always
+ * regenerated by the Form Editor.
+ */
+ @SuppressWarnings("unchecked")
+ // //GEN-BEGIN:initComponents
+ private void initComponents() {
+
+ ModeOfOperation = new javax.swing.ButtonGroup();
+ PESDExport_button = new javax.swing.JButton();
+ jSeparator1 = new javax.swing.JSeparator();
+ CleanFlow_button = new javax.swing.JButton();
+ itemsCount_label = new javax.swing.JLabel();
+ ContentType_checkbox = new javax.swing.JCheckBox();
+ CookiesSet_checkbox = new javax.swing.JCheckBox();
+ HasJsonParam_checkbox = new javax.swing.JCheckBox();
+ HasUrlParams_checkbox = new javax.swing.JCheckBox();
+ HasBodyParam_checkbox = new javax.swing.JCheckBox();
+ HasXMLParam_checkbox = new javax.swing.JCheckBox();
+ HasMultipartAttr_checkbox = new javax.swing.JCheckBox();
+ InRequests_label = new javax.swing.JLabel();
+ InResponses_label = new javax.swing.JLabel();
+ HasAuthz_checkbox = new javax.swing.JCheckBox();
+ BooleansPresets_label = new javax.swing.JLabel();
+ booleansPresets_ComboBox = new javax.swing.JComboBox<>();
+ HasCORS_checkbox = new javax.swing.JCheckBox();
+ HasXFrameOp_checkbox = new javax.swing.JCheckBox();
+ HasCSP_checkbox = new javax.swing.JCheckBox();
+ HasCookies_checkbox = new javax.swing.JCheckBox();
+ EditBools_Button = new javax.swing.JButton();
+ modeOfOperation_ComboBox = new javax.swing.JComboBox<>();
+ ModeOfOperation_label = new javax.swing.JLabel();
+ template_label = new javax.swing.JLabel();
+ jLabel2 = new javax.swing.JLabel();
+ jSeparator2 = new javax.swing.JSeparator();
+ autoExport_check = new javax.swing.JCheckBox();
+ EditTemplates_Button = new javax.swing.JButton();
+ OAuth2_OIDC_TEMPLATE_checkbox = new javax.swing.JCheckBox();
+ SAML_SSO_TEMPLATE_checkbox = new javax.swing.JCheckBox();
+
+ PESDExport_button.setText("PESD Export");
+ PESDExport_button.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ PESDExport_buttonActionPerformed(evt);
+ }
+ });
+
+ CleanFlow_button.setText("Clean Flow");
+ CleanFlow_button.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ CleanFlow_buttonActionPerformed(evt);
+ }
+ });
+
+ itemsCount_label.setText("Items count : 0");
+
+ ContentType_checkbox.setText("Content-Type");
+ ContentType_checkbox.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ ContentType_checkboxActionPerformed(evt);
+ }
+ });
+
+ CookiesSet_checkbox.setText("CookiesSet");
+ CookiesSet_checkbox.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ CookiesSet_checkboxActionPerformed(evt);
+ }
+ });
+
+ HasJsonParam_checkbox.setText("HasJsonParam");
+ HasJsonParam_checkbox.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ HasJsonParam_checkboxActionPerformed(evt);
+ }
+ });
+
+ HasUrlParams_checkbox.setText("HasUrlParams");
+ HasUrlParams_checkbox.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ HasUrlParams_checkboxActionPerformed(evt);
+ }
+ });
+
+ HasBodyParam_checkbox.setText("HasBodyParam");
+ HasBodyParam_checkbox.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ HasBodyParam_checkboxActionPerformed(evt);
+ }
+ });
+
+ HasXMLParam_checkbox.setText("HasXMLParam");
+ HasXMLParam_checkbox.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ HasXMLParam_checkboxActionPerformed(evt);
+ }
+ });
+
+ HasMultipartAttr_checkbox.setText("HasMultipartAttr");
+ HasMultipartAttr_checkbox.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ HasMultipartAttr_checkboxActionPerformed(evt);
+ }
+ });
+
+ InRequests_label.setText("In Requests :");
+
+ InResponses_label.setText("In Responses :");
+
+ HasAuthz_checkbox.setText("HasAuthz");
+ HasAuthz_checkbox.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ HasAuthz_checkboxActionPerformed(evt);
+ }
+ });
+
+ BooleansPresets_label.setText("Flags Set : ");
+
+ booleansPresets_ComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Analysis", "PoC" }));
+ booleansPresets_ComboBox.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ booleansPresets_ComboBoxActionPerformed(evt);
+ }
+ });
+
+ HasCORS_checkbox.setText("HasCORS");
+ HasCORS_checkbox.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ HasCORS_checkboxActionPerformed(evt);
+ }
+ });
+
+ HasXFrameOp_checkbox.setText("HasXFrameOp");
+ HasXFrameOp_checkbox.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ HasXFrameOp_checkboxActionPerformed(evt);
+ }
+ });
+
+ HasCSP_checkbox.setText("HasCSP");
+ HasCSP_checkbox.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ HasCSP_checkboxActionPerformed(evt);
+ }
+ });
+
+ HasCookies_checkbox.setText("HasCookies");
+ HasCookies_checkbox.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ HasCookies_checkboxActionPerformed(evt);
+ }
+ });
+
+ EditBools_Button.setText("Edit set");
+ EditBools_Button.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ EditBools_ButtonActionPerformed(evt);
+ }
+ });
+
+ modeOfOperation_ComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Domains as Actors", "Endpoints as Actors" }));
+ modeOfOperation_ComboBox.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ modeOfOperation_ComboBoxActionPerformed(evt);
+ }
+ });
+
+ ModeOfOperation_label.setText("Mode of Operation : ");
+
+ template_label.setText("Templates :");
+
+ jLabel2.setText("Proxy Enriched Sequence Diagrams Exporter");
+
+ autoExport_check.setText(" Auto-Export");
+ autoExport_check.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ autoExport_checkActionPerformed(evt);
+ }
+ });
+
+ EditTemplates_Button.setText("Edit set");
+ EditTemplates_Button.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ EditTemplates_ButtonActionPerformed(evt);
+ }
+ });
+
+ OAuth2_OIDC_TEMPLATE_checkbox.setText(" OAuth2 / OIDC");
+ OAuth2_OIDC_TEMPLATE_checkbox.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ OAuth2_OIDC_TEMPLATE_checkboxActionPerformed(evt);
+ }
+ });
+
+ SAML_SSO_TEMPLATE_checkbox.setText(" SAML SSO");
+ SAML_SSO_TEMPLATE_checkbox.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ SAML_SSO_TEMPLATE_checkboxActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
+ this.setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGap(41, 41, 41)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(itemsCount_label)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(ModeOfOperation_label)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(modeOfOperation_ComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(template_label)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(OAuth2_OIDC_TEMPLATE_checkbox)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(EditTemplates_Button)
+ .addGap(55, 55, 55)
+ .addComponent(BooleansPresets_label)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(booleansPresets_ComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(18, 18, 18)
+ .addComponent(EditBools_Button))
+ .addComponent(SAML_SSO_TEMPLATE_checkbox))))
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGap(10, 10, 10)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(HasUrlParams_checkbox)
+ .addComponent(HasBodyParam_checkbox)
+ .addComponent(HasJsonParam_checkbox)
+ .addComponent(HasCookies_checkbox)
+ .addComponent(InRequests_label))
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(HasXMLParam_checkbox)
+ .addComponent(HasMultipartAttr_checkbox)
+ .addComponent(HasAuthz_checkbox)))
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(InResponses_label)
+ .addGap(29, 29, 29))
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(CookiesSet_checkbox)
+ .addComponent(ContentType_checkbox)
+ .addComponent(HasCORS_checkbox))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)))
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(HasCSP_checkbox)
+ .addComponent(HasXFrameOp_checkbox)))))
+ .addGroup(layout.createSequentialGroup()
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 220, javax.swing.GroupLayout.PREFERRED_SIZE))))
+ .addComponent(jLabel2)
+ .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 233, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(PESDExport_button)
+ .addGap(18, 18, 18)
+ .addComponent(CleanFlow_button)
+ .addGap(18, 18, 18)
+ .addComponent(autoExport_check)))
+ .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGap(24, 24, 24)
+ .addComponent(jLabel2)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(itemsCount_label)
+ .addGap(9, 9, 9)
+ .addComponent(InRequests_label)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addComponent(HasXMLParam_checkbox)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(HasMultipartAttr_checkbox, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(6, 6, 6)
+ .addComponent(HasAuthz_checkbox, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(77, 77, 77))
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
+ .addComponent(HasCookies_checkbox, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(6, 6, 6)
+ .addComponent(HasUrlParams_checkbox, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(6, 6, 6)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(layout.createSequentialGroup()
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(modeOfOperation_ComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(ModeOfOperation_label))
+ .addGap(18, 18, 18)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(template_label)
+ .addComponent(EditTemplates_Button, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(BooleansPresets_label)
+ .addComponent(booleansPresets_ComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(EditBools_Button, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGap(6, 6, 6)
+ .addComponent(OAuth2_OIDC_TEMPLATE_checkbox)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addComponent(SAML_SSO_TEMPLATE_checkbox))
+ .addGroup(layout.createSequentialGroup()
+ .addComponent(HasBodyParam_checkbox)
+ .addGap(6, 6, 6)
+ .addComponent(HasJsonParam_checkbox, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(7, 7, 7)
+ .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 8, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGap(12, 12, 12)
+ .addComponent(InResponses_label, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(ContentType_checkbox)
+ .addComponent(HasXFrameOp_checkbox))))))
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(HasCSP_checkbox)
+ .addComponent(CookiesSet_checkbox))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(HasCORS_checkbox)
+ .addGap(92, 92, 92)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addComponent(CleanFlow_button, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(PESDExport_button))
+ .addComponent(autoExport_check))
+ .addGap(153, 153, 153))
+ );
+ }// //GEN-END:initComponents
+
+ private void PESDExport_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_PESDExport_buttonActionPerformed
+
+ this.pesdexporter = new PESDExporter(callbacks, helpers, this.items, stdout, stderr, this.operationMode, this.Bools, this.Templates);
+ this.diagrams = pesdexporter.generatePESD();
+ this.metadata = pesdexporter.getMetadata();
+
+ try {
+ InputStream page = getClass().getResourceAsStream("/export.html");
+ String response = new String(page.readAllBytes(), StandardCharsets.UTF_8);
+ String[] lines = this.diagrams[0].split("\r\n|\r|\n");
+ if (lines.length > 100) {
+ response = response.replace("TEMPLATEINSERT1", "%% This file contains a Sequence Diagram defined with MermaidJS Markdown syntax (see https://mermaid-js.github.io/)%%\n"
+ + "%%{init: {'theme': 'base', 'themeVariables': {'actorBorder':'#808486','actorTextColor':'#34343b','primaryColor': '#fa9b35'}}}%%\n"
+ + "sequenceDiagram\n"
+ + "PESD->>User: Hey!\n"
+ + "PESD->>User: If it has more than 50 entries \n"
+ + "PESD->>User: It is not a flow\n"
+ + "PESD->>User: It is traffic history!\n"
+ + "Note right of Browser : Seriously\n"
+ + "PESD->>User: Try again with less than 50 items");
+ response = response.replace("TEMPLATEINSERT2", "{}");
+ response = response.replace("TEMPLATEINSERT3", "%% This file contains a Sequence Diagram defined with MermaidJS Markdown syntax (see https://mermaid-js.github.io/)%%\n"
+ + "%%{init: {'theme': 'base', 'themeVariables': {'actorBorder':'#808486','actorTextColor':'#34343b','primaryColor': '#fa9b35'}}}%%\n"
+ + "sequenceDiagram\n"
+ + "PESD->>User: Nothing to mask here!\n"
+ + "PESD->>User: Flows longer than 50 items are not supported for your own good\n"
+ + "PESD->>User: Nothing personal\n"
+ + "PESD->>User: That's it \n"
+ + "Note right of Browser : Seriously");
+ } else {
+ response = response.replace("TEMPLATEINSERT1", this.diagrams[0].replace("`", "`"));
+ response = response.replace("TEMPLATEINSERT2", this.metadata.replace("`", "`"));
+ response = response.replace("TEMPLATEINSERT3", this.diagrams[1].replace("`", "`"));
+ }
+
+ File temp = File.createTempFile("exports", ".html");
+ String path = temp.getAbsolutePath();
+ try {
+ Files.writeString(temp.toPath(), response, StandardCharsets.UTF_8);
+ // directing the user to the Browser Export Page File
+ Desktop.getDesktop().open(new File(path));
+ } catch (Exception e) {
+ stdout.println(e);
+ }
+ } catch (Exception e) {
+ stdout.println(e);
+ }
+
+ // delete current items sent to the extension and set counter to 0 in the UI
+ IHttpRequestResponse iHttpRequestResponse[] = new IHttpRequestResponse[0];
+ this.items = iHttpRequestResponse;
+ setCountLabel();
+ }//GEN-LAST:event_PESDExport_buttonActionPerformed
+
+ private void CleanFlow_buttonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_CleanFlow_buttonActionPerformed
+ // delete current items sent to the extension and set counter to 0 in the UI
+
+ IHttpRequestResponse iHttpRequestResponse[] = new IHttpRequestResponse[0];
+ this.items = iHttpRequestResponse;
+ setCountLabel();
+ }//GEN-LAST:event_CleanFlow_buttonActionPerformed
+
+ private void HasUrlParams_checkboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_HasUrlParams_checkboxActionPerformed
+ if (HasUrlParams_checkbox.isSelected()) {
+ this.Bools[0] = true;
+ } else {
+ this.Bools[0] = false;
+ }
+ }//GEN-LAST:event_HasUrlParams_checkboxActionPerformed
+
+ private void HasBodyParam_checkboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_HasBodyParam_checkboxActionPerformed
+ if (HasBodyParam_checkbox.isSelected()) {
+ this.Bools[1] = true;
+ } else {
+ this.Bools[1] = false;
+ }
+ }//GEN-LAST:event_HasBodyParam_checkboxActionPerformed
+
+ private void HasJsonParam_checkboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_HasJsonParam_checkboxActionPerformed
+ if (HasJsonParam_checkbox.isSelected()) {
+ this.Bools[2] = true;
+ } else {
+ this.Bools[2] = false;
+ }
+ }//GEN-LAST:event_HasJsonParam_checkboxActionPerformed
+
+ private void HasXMLParam_checkboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_HasXMLParam_checkboxActionPerformed
+ if (HasXMLParam_checkbox.isSelected()) {
+ this.Bools[3] = true;
+ } else {
+ this.Bools[3] = false;
+ }
+ }//GEN-LAST:event_HasXMLParam_checkboxActionPerformed
+
+ private void HasMultipartAttr_checkboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_HasMultipartAttr_checkboxActionPerformed
+ if (HasMultipartAttr_checkbox.isSelected()) {
+ this.Bools[4] = true;
+ } else {
+ this.Bools[4] = false;
+ }
+ }//GEN-LAST:event_HasMultipartAttr_checkboxActionPerformed
+
+ private void HasAuthz_checkboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_HasAuthz_checkboxActionPerformed
+ if (HasAuthz_checkbox.isSelected()) {
+ this.Bools[5] = true;
+ } else {
+ this.Bools[5] = false;
+ }
+ }//GEN-LAST:event_HasAuthz_checkboxActionPerformed
+
+ private void ContentType_checkboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ContentType_checkboxActionPerformed
+ if (ContentType_checkbox.isSelected()) {
+ this.Bools[6] = true;
+ } else {
+ this.Bools[6] = false;
+ }
+ }//GEN-LAST:event_ContentType_checkboxActionPerformed
+
+ private void CookiesSet_checkboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_CookiesSet_checkboxActionPerformed
+ if (CookiesSet_checkbox.isSelected()) {
+ this.Bools[7] = true;
+ } else {
+ this.Bools[7] = false;
+ }
+ }//GEN-LAST:event_CookiesSet_checkboxActionPerformed
+
+
+ private void booleansPresets_ComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_booleansPresets_ComboBoxActionPerformed
+ // select all checkboxes for Booleans if Analysis mode and de-select all if PoC mode
+
+ if (this.booleansPresets_ComboBox.getSelectedItem().toString() == "Analysis") {
+ if (!HasAuthz_checkbox.isSelected()) {
+ HasAuthz_checkbox.doClick();
+ }
+ if (!HasMultipartAttr_checkbox.isSelected()) {
+ HasMultipartAttr_checkbox.doClick();
+ }
+ if (!HasXMLParam_checkbox.isSelected()) {
+ HasXMLParam_checkbox.doClick();
+ }
+ if (!HasBodyParam_checkbox.isSelected()) {
+ HasBodyParam_checkbox.doClick();
+ }
+ if (!HasUrlParams_checkbox.isSelected()) {
+ HasUrlParams_checkbox.doClick();
+ }
+ if (!HasJsonParam_checkbox.isSelected()) {
+ HasJsonParam_checkbox.doClick();
+ }
+ if (!CookiesSet_checkbox.isSelected()) {
+ CookiesSet_checkbox.doClick();
+ }
+ if (!ContentType_checkbox.isSelected()) {
+ ContentType_checkbox.doClick();
+ }
+ if (!HasCORS_checkbox.isSelected()) {
+ HasCORS_checkbox.doClick();
+ }
+ if (!HasXFrameOp_checkbox.isSelected()) {
+ HasXFrameOp_checkbox.doClick();
+ }
+ if (!HasCSP_checkbox.isSelected()) {
+ HasCSP_checkbox.doClick();
+ }
+ if (!HasCookies_checkbox.isSelected()) {
+ HasCookies_checkbox.doClick();
+ }
+ } else {
+ if (HasAuthz_checkbox.isSelected()) {
+ HasAuthz_checkbox.doClick();
+ }
+ if (HasMultipartAttr_checkbox.isSelected()) {
+ HasMultipartAttr_checkbox.doClick();
+ }
+ if (HasXMLParam_checkbox.isSelected()) {
+ HasXMLParam_checkbox.doClick();
+ }
+ if (HasBodyParam_checkbox.isSelected()) {
+ HasBodyParam_checkbox.doClick();
+ }
+ if (HasUrlParams_checkbox.isSelected()) {
+ HasUrlParams_checkbox.doClick();
+ }
+ if (HasJsonParam_checkbox.isSelected()) {
+ HasJsonParam_checkbox.doClick();
+ }
+ if (CookiesSet_checkbox.isSelected()) {
+ CookiesSet_checkbox.doClick();
+ }
+ if (ContentType_checkbox.isSelected()) {
+ ContentType_checkbox.doClick();
+ }
+ if (HasCORS_checkbox.isSelected()) {
+ HasCORS_checkbox.doClick();
+ }
+ if (HasXFrameOp_checkbox.isSelected()) {
+ HasXFrameOp_checkbox.doClick();
+ }
+ if (HasCSP_checkbox.isSelected()) {
+ HasCSP_checkbox.doClick();
+ }
+ if (HasCookies_checkbox.isSelected()) {
+ HasCookies_checkbox.doClick();
+ }
+ }
+ }//GEN-LAST:event_booleansPresets_ComboBoxActionPerformed
+
+ private void HasCORS_checkboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_HasCORS_checkboxActionPerformed
+ if (HasCORS_checkbox.isSelected()) {
+ this.Bools[8] = true;
+ } else {
+ this.Bools[8] = false;
+ }
+ }//GEN-LAST:event_HasCORS_checkboxActionPerformed
+
+ private void HasXFrameOp_checkboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_HasXFrameOp_checkboxActionPerformed
+ if (HasXFrameOp_checkbox.isSelected()) {
+ this.Bools[9] = true;
+ } else {
+ this.Bools[9] = false;
+ }
+ }//GEN-LAST:event_HasXFrameOp_checkboxActionPerformed
+
+ private void HasCSP_checkboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_HasCSP_checkboxActionPerformed
+ if (HasCSP_checkbox.isSelected()) {
+ this.Bools[10] = true;
+ } else {
+ this.Bools[10] = false;
+ }
+ }//GEN-LAST:event_HasCSP_checkboxActionPerformed
+
+ private void HasCookies_checkboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_HasCookies_checkboxActionPerformed
+ if (HasCookies_checkbox.isSelected()) {
+ this.Bools[11] = true;
+ } else {
+ this.Bools[11] = false;
+ }
+ }//GEN-LAST:event_HasCookies_checkboxActionPerformed
+
+ private void EditBools_ButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_EditBools_ButtonActionPerformed
+ //Hide/Show Booleans checkboxes
+
+ if (HasJsonParam_checkbox.isVisible()) {
+ HasJsonParam_checkbox.setVisible(false);
+ HasAuthz_checkbox.setVisible(false);
+ HasBodyParam_checkbox.setVisible(false);
+ HasCORS_checkbox.setVisible(false);
+ HasCSP_checkbox.setVisible(false);
+ HasCookies_checkbox.setVisible(false);
+ HasMultipartAttr_checkbox.setVisible(false);
+ HasUrlParams_checkbox.setVisible(false);
+ HasXFrameOp_checkbox.setVisible(false);
+ HasXMLParam_checkbox.setVisible(false);
+ ContentType_checkbox.setVisible(false);
+ CookiesSet_checkbox.setVisible(false);
+ jSeparator1.setVisible(false);
+ InRequests_label.setVisible(false);
+ InResponses_label.setVisible(false);
+ } else {
+ HasJsonParam_checkbox.setVisible(true);
+ HasAuthz_checkbox.setVisible(true);
+ HasBodyParam_checkbox.setVisible(true);
+ HasCORS_checkbox.setVisible(true);
+ HasCSP_checkbox.setVisible(true);
+ HasCookies_checkbox.setVisible(true);
+ HasMultipartAttr_checkbox.setVisible(true);
+ HasUrlParams_checkbox.setVisible(true);
+ HasXFrameOp_checkbox.setVisible(true);
+ HasXMLParam_checkbox.setVisible(true);
+ ContentType_checkbox.setVisible(true);
+ CookiesSet_checkbox.setVisible(true);
+ jSeparator1.setVisible(true);
+ InRequests_label.setVisible(true);
+ InResponses_label.setVisible(true);
+ }
+ }//GEN-LAST:event_EditBools_ButtonActionPerformed
+
+ private void modeOfOperation_ComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_modeOfOperation_ComboBoxActionPerformed
+
+ if (this.modeOfOperation_ComboBox.getSelectedItem().toString() == "Domains as Actors") {
+ this.operationMode = 0;
+ } else {
+ this.operationMode = 1;
+ }
+ }//GEN-LAST:event_modeOfOperation_ComboBoxActionPerformed
+
+ private void autoExport_checkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_autoExport_checkActionPerformed
+ // TODO add your handling code here:
+ }//GEN-LAST:event_autoExport_checkActionPerformed
+
+ private void EditTemplates_ButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_EditTemplates_ButtonActionPerformed
+ //Hide/Show Booleans checkboxes
+
+ if (SAML_SSO_TEMPLATE_checkbox.isVisible()) {
+ SAML_SSO_TEMPLATE_checkbox.setVisible(false);
+ OAuth2_OIDC_TEMPLATE_checkbox.setVisible(false);
+ } else {
+ SAML_SSO_TEMPLATE_checkbox.setVisible(true);
+ OAuth2_OIDC_TEMPLATE_checkbox.setVisible(true);
+ }
+ }//GEN-LAST:event_EditTemplates_ButtonActionPerformed
+
+ private void SAML_SSO_TEMPLATE_checkboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_SAML_SSO_TEMPLATE_checkboxActionPerformed
+ if (SAML_SSO_TEMPLATE_checkbox.isSelected()) {
+ this.Templates[0] = "SAML_SSO";
+ } else {
+ this.Templates[0] = "";
+ }
+ }//GEN-LAST:event_SAML_SSO_TEMPLATE_checkboxActionPerformed
+
+ private void OAuth2_OIDC_TEMPLATE_checkboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_OAuth2_OIDC_TEMPLATE_checkboxActionPerformed
+ if (OAuth2_OIDC_TEMPLATE_checkbox.isSelected()) {
+ this.Templates[1] = "OAuth2/OIDC";
+ } else {
+ this.Templates[1] = "";
+ }
+ }//GEN-LAST:event_OAuth2_OIDC_TEMPLATE_checkboxActionPerformed
+
+ public int setItems(IHttpRequestResponse[] newItems, Integer opMode) {
+ // called from BurpExtender.java to set selected items and operation mode in the panel
+ try {
+ int aLen = this.items.length;
+ int bLen = newItems.length;
+
+ // merging new selected items with previously selected items
+ IHttpRequestResponse[] c = Arrays.copyOf(this.items, aLen + bLen);
+ System.arraycopy(this.items, 0, c, 0, aLen);
+ System.arraycopy(newItems, 0, c, aLen, bLen);
+ this.items = c;
+ //updating Mode Of Operation combobox and op. mode attribute
+ this.modeOfOperation_ComboBox.setSelectedIndex(opMode);
+ } catch (Exception e) {
+ return 0;
+ }
+ return 1;
+ }
+
+ public void setCountLabel() {
+ itemsCount_label.setText("Items count : " + this.items.length);
+ }
+
+ public Boolean getAutoExport() {
+ return this.autoExport_check.isSelected();
+ }
+
+ public void clickPESDExport_button() {
+ this.PESDExport_button.doClick();
+ }
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JLabel BooleansPresets_label;
+ private javax.swing.JButton CleanFlow_button;
+ private javax.swing.JCheckBox ContentType_checkbox;
+ private javax.swing.JCheckBox CookiesSet_checkbox;
+ private javax.swing.JButton EditBools_Button;
+ private javax.swing.JButton EditTemplates_Button;
+ private javax.swing.JCheckBox HasAuthz_checkbox;
+ private javax.swing.JCheckBox HasBodyParam_checkbox;
+ private javax.swing.JCheckBox HasCORS_checkbox;
+ private javax.swing.JCheckBox HasCSP_checkbox;
+ private javax.swing.JCheckBox HasCookies_checkbox;
+ private javax.swing.JCheckBox HasJsonParam_checkbox;
+ private javax.swing.JCheckBox HasMultipartAttr_checkbox;
+ private javax.swing.JCheckBox HasUrlParams_checkbox;
+ private javax.swing.JCheckBox HasXFrameOp_checkbox;
+ private javax.swing.JCheckBox HasXMLParam_checkbox;
+ private javax.swing.JLabel InRequests_label;
+ private javax.swing.JLabel InResponses_label;
+ private javax.swing.ButtonGroup ModeOfOperation;
+ private javax.swing.JLabel ModeOfOperation_label;
+ private javax.swing.JCheckBox OAuth2_OIDC_TEMPLATE_checkbox;
+ private javax.swing.JButton PESDExport_button;
+ private javax.swing.JCheckBox SAML_SSO_TEMPLATE_checkbox;
+ private javax.swing.JCheckBox autoExport_check;
+ private javax.swing.JComboBox booleansPresets_ComboBox;
+ private javax.swing.JLabel itemsCount_label;
+ private javax.swing.JLabel jLabel2;
+ private javax.swing.JSeparator jSeparator1;
+ private javax.swing.JSeparator jSeparator2;
+ private javax.swing.JComboBox modeOfOperation_ComboBox;
+ private javax.swing.JLabel template_label;
+ // End of variables declaration//GEN-END:variables
+}
diff --git a/src/pesd/PESDWrapper.java b/src/pesd/PESDWrapper.java
new file mode 100644
index 0000000..82254e1
--- /dev/null
+++ b/src/pesd/PESDWrapper.java
@@ -0,0 +1,647 @@
+/*
+ * Proxy Enriched Sequence Diagrams Wrapper
+ * This Object exposes all the needed methods to create / access / manipulate PESD: MD + Metadata
+ *
+ * @author Francesco Lacerenza from Doyensec
+ */
+package pesd;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.UUID;
+import org.json.JSONObject;
+import org.json.JSONArray;
+
+public class PESDWrapper {
+
+ private JSONArray data;
+ private int iterator;
+ private boolean iterating;
+ private HashMap mappings;
+ private final PrintWriter stdout;
+
+ public PESDWrapper(PrintWriter stdout) {
+ // main constructor
+ this.stdout = stdout;
+ try {
+ this.data = new JSONArray();
+ this.iterator = 0;
+ this.mappings = new HashMap<>();
+ this.iterating = false;
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public PESDWrapper(PrintWriter stdout, JSONArray data) {
+ // Constructor usable to create copies of a PESD Object,
+ // simply use getStucturedData() to extract JSONArray data and pass it to the constructor to create a clone
+ this.data = data;
+ this.stdout = stdout;
+ this.iterator = 0;
+ this.mappings = new HashMap<>();
+ this.iterating = false;
+ }
+
+ public String[] getSeqDiagramdMD(Integer op_mode) {
+ // This method serializes the data JSONArray to MermaidJS Markdown syntax
+
+ // the Wrapper automatically checks and fixes the consistency of alt/end markdown symbols before exporting the MD
+ // This is done to avoid garbage alt / end symbols from templates edge-cases that could break the MermaidJS Rendering of the MD
+ this.altEndConsistencyNormalizer();
+ //Note about op_mode:
+ // 0 <- Domains as Actors
+ // 1 <- Endpoints as Actors
+ String seq_diagram_md = "%% This file contains a Sequence Diagram defined with MermaidJS Markdown syntax (see https://mermaid-js.github.io/)%%\n%%{init: {'theme': 'base', 'themeVariables': {'actorBorder':'#808486','actorTextColor':'#34343b','primaryColor': '#fa9b35'}}}%%\nsequenceDiagram\n";
+ String seq_diagram_md_masked = "%% This file contains a Sequence Diagram defined with MermaidJS Markdown syntax (see https://mermaid-js.github.io/)%%\n%%{init: {'theme': 'base', 'themeVariables': {'actorBorder':'#808486','actorTextColor':'#34343b','primaryColor': '#fa9b35'}}}%%\nsequenceDiagram\n";
+ this.startIterator();
+ try {
+ while (this.iterator != -1) {
+ JSONObject line = this.getLine();
+ String flags;
+ switch ((String) line.get("type")) {
+ case "req":
+ flags = this.flagsToStr((JSONArray) line.get("flags"));
+ if (op_mode == 0) {
+ seq_diagram_md += String.format("%s->>%s: [%s] %s %s\n", line.get("source"), line.get("destination"), line.get("method"), escapeMermaid(line.get("path").toString()), flags);
+ seq_diagram_md_masked += String.format("%s->>%s: [%s] %s %s\n", line.get("source"), line.get("destination"), line.get("method"), escapeMermaid(line.get("maskedPath").toString()), flags);
+ } else {
+ seq_diagram_md += String.format("%s->>%s: [%s] %s\n", line.get("source"), escapeMermaid(line.get("path").toString()), line.get("method"), flags);
+ seq_diagram_md_masked += String.format("%s->>%s: [%s] %s\n", line.get("source"), escapeMermaid(line.get("maskedPath").toString()).replaceAll("<", "_").replaceAll(">", "_"), line.get("method"), flags);
+ }
+ break;
+ case "res":
+ flags = this.flagsToStr((JSONArray) line.get("flags"));
+ if (line.get("mime_type") == "NoNe") {
+ if (op_mode == 0) {
+ seq_diagram_md += String.format("%s->>%s: [%s] %s\n", line.get("source"), line.get("destination"), line.get("status_code"), flags);
+ seq_diagram_md_masked += String.format("%s->>%s: [%s] %s\n", line.get("source"), line.get("destination"), line.get("status_code"), flags);
+ } else if (this.getLine(this.iterator - 1).get("type") != "req") {
+ if (this.getLine(this.iterator - 2).get("type") != "req"){
+ seq_diagram_md += String.format("%s->>%s: [%s] %s\n", escapeMermaid(this.getLine(this.iterator - 3).get("path").toString()), line.get("destination"), line.get("status_code"), flags);
+ seq_diagram_md_masked += String.format("%s->>%s: [%s] %s\n", escapeMermaid(this.getLine(this.iterator - 3).get("maskedPath").toString()).replaceAll("<", "_").replaceAll(">", "_"), line.get("destination"), line.get("status_code"), flags);
+ } else {
+ seq_diagram_md += String.format("%s->>%s: [%s] %s\n", escapeMermaid(this.getLine(this.iterator - 2).get("path").toString()), line.get("destination"), line.get("status_code"), flags);
+ seq_diagram_md_masked += String.format("%s->>%s: [%s] %s\n", escapeMermaid(this.getLine(this.iterator - 2).get("maskedPath").toString()).replaceAll("<", "_").replaceAll(">", "_"), line.get("destination"), line.get("status_code"), flags);
+ }
+ } else {
+ seq_diagram_md += String.format("%s->>%s: [%s] %s\n", escapeMermaid(this.getLine(this.iterator - 1).get("path").toString()), line.get("destination"), line.get("status_code"), flags);
+ seq_diagram_md_masked += String.format("%s->>%s: [%s] %s\n", escapeMermaid(this.getLine(this.iterator - 1).get("maskedPath").toString()).replaceAll("<", "_").replaceAll(">", "_"), line.get("destination"), line.get("status_code"), flags);
+ }
+ } else {
+ if (op_mode == 0) {
+ seq_diagram_md += String.format("%s->>%s: [%s] %s %s\n", line.get("source"), line.get("destination"), line.get("status_code"), line.get("mime_type"), flags);
+ seq_diagram_md_masked += String.format("%s->>%s: [%s] %s %s\n", line.get("source"), line.get("destination"), line.get("status_code"), line.get("mime_type"), flags);
+ } else if (this.getLine(this.iterator - 1).get("type") != "req") {
+ if (this.getLine(this.iterator - 2).get("type") != "req"){
+ seq_diagram_md += String.format("%s->>%s: [%s] %s %s\n", escapeMermaid(this.getLine(this.iterator - 3).get("path").toString()), line.get("destination"), line.get("status_code"), line.get("mime_type"), flags);
+ seq_diagram_md_masked += String.format("%s->>%s: [%s] %s %s\n", escapeMermaid(this.getLine(this.iterator - 3).get("maskedPath").toString()).replaceAll("<", "_").replaceAll(">", "_"), line.get("destination"), line.get("status_code"), line.get("mime_type"), flags);
+ } else {
+ seq_diagram_md += String.format("%s->>%s: [%s] %s %s\n", escapeMermaid(this.getLine(this.iterator - 2).get("path").toString()), line.get("destination"), line.get("status_code"), line.get("mime_type"), flags);
+ seq_diagram_md_masked += String.format("%s->>%s: [%s] %s %s\n", escapeMermaid(this.getLine(this.iterator - 2).get("maskedPath").toString()).replaceAll("<", "_").replaceAll(">", "_"), line.get("destination"), line.get("status_code"), line.get("mime_type"), flags);
+ }
+ } else {
+ seq_diagram_md += String.format("%s->>%s: [%s] %s %s\n", escapeMermaid(this.getLine(this.iterator - 1).get("path").toString()), line.get("destination"), line.get("status_code"), line.get("mime_type"), flags);
+ seq_diagram_md_masked += String.format("%s->>%s: [%s] %s %s\n", escapeMermaid(this.getLine(this.iterator - 1).get("maskedPath").toString()).replaceAll("<", "_").replaceAll(">", "_"), line.get("destination"), line.get("status_code"), line.get("mime_type"), flags);
+ }
+
+ }
+ break;
+ case "alt":
+ seq_diagram_md += String.format("Alt %s\n", line.get("alt"));
+ seq_diagram_md_masked += String.format("Alt %s\n", line.get("alt"));
+ break;
+ case "end":
+ seq_diagram_md += "end\n";
+ seq_diagram_md_masked += "end\n";
+ break;
+ case "note":
+ seq_diagram_md += String.format("Note right of Browser : %s\n", line.get("note"));
+ seq_diagram_md_masked += String.format("Note right of Browser : %s\n", line.get("note"));
+ break;
+ default:
+ break;
+ }
+ this.nextLine();
+ }
+ } catch (Exception e) {
+ this.stdout.println("error : "+e.toString());
+ }
+ return new String[]{seq_diagram_md, seq_diagram_md_masked};
+ }
+
+ private String flagsToStr(JSONArray flagsArr) {
+ // serialize flags array to a string that will be added to the transaction in the MD
+ String flags = "";
+ for (int i = 0; i < flagsArr.length(); i++) {
+ flags += String.format(" %s ", flagsArr.getString(i));
+ }
+ return flags;
+ }
+
+ public JSONObject getMetadata() {
+ // Creating metadata export with PESD format
+
+ JSONObject metadata = new JSONObject();
+ Integer item_num = 1;
+ JSONObject item;
+ this.startIterator();
+ while (this.iterator != -1) {
+ try {
+ item = new JSONObject();
+ if ((String) this.getLine().get("type") == "req") {
+ JSONObject line_metadata = (JSONObject) this.getLine().get("metadata");
+ item.put("req", line_metadata);
+ if ((String) this.getLine(this.iterator + 1).get("type") == "res") {
+ item.put("res", (JSONObject) this.getLine(this.iterator + 1).get("metadata"));
+ this.setIterator(this.iterator + 2);
+ } else if ((String) this.getLine(this.iterator + 2).get("type") == "res" && (String) this.getLine(this.iterator + 1).get("type") != "req"){
+ item.put("res", (JSONObject) this.getLine(this.iterator + 2).get("metadata"));
+ this.setIterator(this.iterator + 3);
+ } else if((String) this.getLine(this.iterator + 3).get("type") == "res" && (String) this.getLine(this.iterator + 2).get("type") != "req" && (String) this.getLine(this.iterator + 1).get("type") != "req"){
+ item.put("res", (JSONObject) this.getLine(this.iterator + 3).get("metadata"));
+ this.setIterator(this.iterator + 4);
+ } else {
+ this.nextLine();
+ }
+ metadata.put("item" + item_num, item);
+ item_num++;
+ } else {
+ this.nextLine();
+ }
+ } catch (Exception e) {
+ this.nextLine();
+ }
+ }
+ return metadata;
+ }
+
+ public JSONObject getLine() {
+ return (JSONObject) this.data.get(this.iterator);
+ }
+
+ public JSONObject getLine(Integer index) {
+ if (!(index > (this.data.length() - 1))) {
+ return (JSONObject) this.data.get(index);
+ } else {
+ JSONObject errorLine = new JSONObject();
+ errorLine.put("type", "error");
+ return errorLine;
+ }
+ }
+
+ public Integer getIterator() {
+ return this.iterator;
+ }
+
+ public void setIterator(Integer num) {
+ if (!(num > (this.data.length() - 1))) {
+ this.iterator = num;
+ } else {
+ this.iterator = -1;
+ }
+ }
+
+ public void startIterator() {
+ this.iterator = 0;
+ this.iterating = true;
+ }
+
+ public void startIterator(Integer num) {
+ this.iterator = num;
+ this.iterating = true;
+ }
+
+ public void nextLine() {
+ if ((this.iterator + 1) > (this.data.length() - 1)) {
+ this.iterator = -1;
+ this.iterating = false;
+ } else {
+ this.iterator++;
+ }
+ }
+
+ public void prevLine() {
+ if (this.iterator > 0) {
+ this.iterator--;
+ }
+ }
+
+ public void addReq(String source, String destination, String method, String path, List flags, JSONObject metadata) {
+ // Method used to add a Request line to the obj that will have the format:
+ // Markdown : S ->> D : [METHOD] /path \n Flag1 Flag2 ... FlagN
+ // Metadata: JSONObject generated by PESDMetadata obj
+
+ JSONObject line = new JSONObject();
+ line.put("type", "req");
+ line.put("source", source);
+ line.put("destination", destination);
+ line.put("method", method);
+ line.put("path", path);
+ line.put("flags", flags);
+ String maskedPath = pathMapper(path);
+ metadata.put("maskedPath", maskedPath);
+ line.put("maskedPath", maskedPath);
+ line.put("metadata", metadata);
+ this.data.put(line);
+ }
+
+ public void addReq(String source, String destination, String method, String path, List flags, JSONObject metadata, Integer index) {
+ // Same as normal addReq, but to a specific index
+
+ JSONObject line = new JSONObject();
+ line.put("type", "req");
+ line.put("source", source);
+ line.put("destination", destination);
+ line.put("method", method);
+ line.put("path", path);
+ line.put("flags", flags);
+ String maskedPath = pathMapper(path);
+ metadata.put("maskedPath", maskedPath);
+ line.put("metadata", metadata);
+ addToPos(index, line);
+
+ if (this.iterating & index <= this.iterator) {
+ this.iterator++;
+ }
+ }
+
+ public void addRes(String source, String destination, String status_code, String mimeType, List flags, JSONObject metadata) {
+ // Method used to add a Response line to the obj that will have the format:
+ // Markdown : D ->> S : [STATUS_CODE] MIME_TYPE \n Flag1 Flag2 ... FlagN
+ // Metadata: JSONObject generated by PESDMetadata obj
+
+ JSONObject line = new JSONObject();
+ line.put("type", "res");
+ line.put("source", source);
+ line.put("destination", destination);
+ line.put("status_code", status_code);
+ line.put("mime_type", mimeType);
+ line.put("flags", flags);
+ line.put("metadata", metadata);
+ this.data.put(line);
+ }
+
+ public void addRes(String source, String destination, String status_code, String mimeType, List flags, JSONObject metadata, Integer index) {
+ // Same as normal addRes, but to a specific index
+
+ JSONObject line = new JSONObject();
+ line.put("type", "res");
+ line.put("source", source);
+ line.put("destination", destination);
+ line.put("status_code", status_code);
+ line.put("mime_type", mimeType);
+ line.put("flags", flags);
+ line.put("metadata", metadata);
+ addToPos(index, line);
+
+ if (this.iterating & index <= this.iterator) {
+ this.iterator++;
+ }
+ }
+
+ public void addAlt(String text) {
+ // Method used to add a Alt line (rectangle start in MermaidJS MD) to the obj that will have the format :
+ // Markdown : Alt NAME
+
+ JSONObject line = new JSONObject();
+ line.put("type", "alt");
+ line.put("alt", text);
+ this.data.put(line);
+ }
+
+ public void addAlt(String text, Integer index) {
+ // Same as normal addAlt, but to a specific index
+
+ JSONObject line = new JSONObject();
+ line.put("type", "alt");
+ line.put("alt", text);
+ addToPos(index, line);
+
+ if (this.iterating & index <= this.iterator) {
+ this.iterator++;
+ }
+ }
+
+ public void endAlt() {
+ // Method used to add a end line (rectangle close in MermaidJS MD) to the obj that will have the format :
+ // Markdown : end
+
+ JSONObject line = new JSONObject();
+ line.put("type", "end");
+ this.data.put(line);
+ }
+
+ public void endAlt(Integer index) {
+ // Same as normal endAlt, but to a specific index
+ JSONObject line = new JSONObject();
+ line.put("type", "end");
+ addToPos(index, line);
+
+ if (this.iterating & index <= this.iterator) {
+ this.iterator++;
+ }
+ }
+
+ public void addNote(String note) {
+ // Method used to add a Note line to the obj that will have the format:
+ // Markdown : Note right of Browser : TEXT
+
+ JSONObject line = new JSONObject();
+ line.put("type", "note");
+ line.put("note", note);
+ this.data.put(line);
+ }
+
+ public void addNote(String note, Integer index) {
+ // Same as normal addNote, but to a specific index
+
+ JSONObject line = new JSONObject();
+ line.put("type", "note");
+ line.put("note", note);
+ addToPos(index, line);
+
+ if (this.iterating & index <= this.iterator) {
+ this.iterator++;
+ }
+ }
+
+ public void modifyFlags(List flagsArr) {
+ // substituting flags in a transaction during an iteration
+
+ if (this.iterating) {
+ JSONObject line = (JSONObject) this.data.get(this.iterator);
+ line.remove("flags");
+ line.put("flags", flagsArr);
+ }
+ }
+
+ public void modifyFlags(List flagsArr, Integer index) {
+ // Same as normal modifyFlags, but to a specific index
+
+ JSONObject line = (JSONObject) this.data.get(index);
+ line.remove("flags");
+ line.put("flags", flagsArr);
+ }
+
+ public void addFlag(String flag) {
+ // adding a flag to a transaction while iterating
+
+ JSONArray flags = (JSONArray) this.getLine().get("flags");
+ List list = new ArrayList();
+ for (int i = 0; i < flags.length(); i++) {
+ list.add(flags.getString(i));
+ }
+ list.add(String.format(" %s ", flag));
+ this.modifyFlags(list);
+ }
+
+ public void addFlag(String flag, Integer index) {
+ // Same as normal addFlag, but to a specific index
+
+ JSONArray flags = (JSONArray) this.getLine(index).get("flags");
+ List list = new ArrayList();
+ for (int i = 0; i < flags.length(); i++) {
+ list.add(flags.getString(i));
+ }
+ list.add(String.format(" %s ", flag));
+ this.modifyFlags(list);
+ }
+
+ public void modifyMetadata(JSONObject metadataObj) {
+ // Replacing a metadata object in a line with new metadata while iterating
+
+ if (this.iterating) {
+ JSONObject line = (JSONObject) this.data.get(this.iterator);
+ line.remove("metadata");
+ line.put("metadata", metadataObj);
+ }
+ }
+
+ public void modifyMetadata(JSONObject metadataObj, Integer index) {
+ // Same as normal modifyMetadata, but to a specific index
+
+ JSONObject line = (JSONObject) this.data.get(index);
+ line.remove("metadata");
+ line.put("metadata", metadataObj);
+ }
+
+ public void removeLine() {
+ // remove line while iterating
+
+ if (this.iterating) {
+ this.data.remove(this.iterator);
+ this.iterator--;
+ }
+ }
+
+ public void removeLine(Integer index) {
+ //remove line on at a specific index
+
+ JSONObject a = (JSONObject) this.data.remove(index);
+ if (this.iterating & index <= this.iterator) {
+ this.iterator--;
+ }
+ }
+
+ public Boolean altEndConsistencyNormalizer() {
+ // This method is used to resolve local consistency problems within the resulting MermaidJS Markdown before the serialization
+ // Currently it simply remove non-closed and non-opened Alt rectangles (Alt without end or end without Alt)
+ // Moreover, in case of 2 consecutive Alt, the first occurrence is removed
+
+ try {
+ this.startIterator();
+ Integer lastAlt = -1;
+ while (this.getIterator() != -1) {
+ switch (this.getLine().getString("type")) {
+ case "alt":
+ if (lastAlt != -1) {
+ this.removeLine(lastAlt);
+ lastAlt = this.getIterator();
+ } else {
+ lastAlt = this.getIterator();
+ }
+ break;
+ case "end":
+ if (lastAlt != -1) {
+ lastAlt = -1;
+ } else {
+ this.removeLine();
+ }
+ break;
+ default:
+ break;
+ }
+ this.nextLine();
+ }
+ if (lastAlt != -1) {
+ this.removeLine(lastAlt);
+ }
+ } catch (Exception e) {
+ stdout.println("Error:" + e.toString());
+ return false;
+ }
+ return true;
+ }
+
+ public void addToPos(int pos, JSONObject jsonObj) {
+ // Function needed to perform JSONArray.put(pos, json) without losing an entry due to replacement.
+ // Basically it performs right-shift of all the entries after the position needed for the put
+
+ for (int i = this.data.length(); i > pos; i--) {
+ this.data.put(i, this.data.get(i - 1));
+ }
+ this.data.put(pos, jsonObj);
+ }
+
+ public Integer getSize() {
+ return this.data.length();
+ }
+
+ public JSONArray getStucturedData() {
+ // returns a copy by value of the data contained in the wrapper
+
+ return new JSONArray(this.data);
+ }
+
+ public String escapeMermaid(String input) {
+
+ String[] segments = input.split("/");
+ if (segments.length == 0) {
+ return "/";
+ }
+ for (int i = 0; i < segments.length; i++) {
+ if (!segments[i].matches("^") && !segments[i].matches("^")) {
+ segments[i] = java.net.URLEncoder.encode(segments[i]);
+ }
+ }
+ return String.join("/", segments);
+ }
+
+ public String pathMapper(String path) {
+
+ String[] segments = path.split("/");
+ if (segments.length == 0) {
+ return "/";
+ }
+
+ for (int i = 0; i < segments.length; i++) {
+ if (uuidTest(segments[i]) || maskTest(segments[i])) {
+ if (!this.mappings.containsKey(segments[i])) {
+ this.mappings.put(segments[i], this.mappings.size() + 1);
+ segments[i] = substituteMask(segments[i]);
+ } else {
+ segments[i] = substituteMask(segments[i]);
+ }
+ }
+
+ }
+ return String.join("/", segments);
+ }
+
+ public String substituteMask(String segment) {
+ if (uuidTest(segment)) {
+ return String.format("", this.mappings.get(segment));
+ } else {
+ return String.format("", this.mappings.get(segment));
+ }
+ }
+
+ public Boolean uuidTest(String input) {
+ try {
+ UUID uuid = UUID.fromString(input);
+ return true;
+ } catch (IllegalArgumentException e) {
+ return false;
+ }
+ }
+
+ public Boolean maskTest(String input) {
+ // A path segment is a part in a path separated by /
+ // So it’s basically A–Z, a–z, 0–9, -, ., _, ~, !, $, &, ', (, ), *, +, ,, ;, =, :, @, as well as % that must be followed by two hexadecimal digits. Any other character/byte needs to be encoded using the percent-encoding.
+ //Although these are 79 characters in total that can be used in a path segment literally, some user agents do encode some of these characters as well (e.g. %7E instead of ~).
+ // That’s why many use just the 62 alphanumeric characters (i.e. A–Z, a–z, 0–9) or the Base 64 Encoding with URL and Filename Safe Alphabet (i.e. A–Z, a–z, 0–9, -, _).
+ int numChars = 0;
+ int upper = 0, lower = 0, number = 0, special = 0;
+
+ for (int i = 0; i < input.length(); i++) {
+ char ch = input.charAt(i);
+ if (ch >= 'A' && ch <= 'Z') {
+ upper++;
+ } else if (ch >= 'a' && ch <= 'z') {
+ lower++;
+ } else if (ch >= '0' && ch <= '9') {
+ number++;
+ } else {
+ special++;
+ }
+ }
+ if (upper > 0) {
+ numChars = numChars + 26;
+ }
+ if (lower > 0) {
+ numChars = numChars + 26;
+ }
+ if (special > 0) {
+ numChars = numChars + 5;
+ }
+ // Calculate the Shannon entropy of the input string
+
+ HashMap frequencyMap = new HashMap<>();
+
+ // Iterate through each character in the input string
+ for (int i = 0; i < input.length(); i++) {
+ char c = input.charAt(i);
+ if (!frequencyMap.containsKey(c)) {
+ // If the character is not in the map, add it with a frequency of 1
+ frequencyMap.put(c, 1);
+ } else {
+ // If the character is already in the map, increment its frequency
+ frequencyMap.put(c, frequencyMap.get(c) + 1);
+ }
+ }
+
+ // Initialize the entropy value to 0
+ double entropy = 0;
+
+ // Iterate through each character in the frequency map
+ for (char c : frequencyMap.keySet()) {
+ // Calculate the probability of the character in the input string
+ double probability = (double) frequencyMap.get(c) / input.length();
+
+ // Add the contribution of the character to the entropy
+ entropy -= probability * (Math.log(probability) / Math.log(2));
+ }
+
+ // Calculate the maximum entropy for a string of the same length and character set
+ double maxEntropy = Math.log(numChars) / Math.log(2);
+
+ // Compare the calculated entropy to the maximum entropy
+ double entropyRatio = entropy / maxEntropy;
+
+ // If the entropy ratio is close to 1, the input string is considered random
+ if (entropyRatio >= 0.90) {
+ return true;
+ }
+
+ if (input.length() > 18 && entropyRatio > 0.80) {
+ if (number > 0 && special > 0) {
+ return true;
+ }
+ }
+
+ if (input.length() > 25 && number > 0 && (upper > 0 || special > 0) && entropyRatio > 0.70) {
+ return true;
+ }
+
+ if (input.length() > 35) {
+ return true;
+ }
+
+ return false;
+
+ }
+
+}
diff --git a/src/pesd/SAML_SSO_Template.java b/src/pesd/SAML_SSO_Template.java
new file mode 100644
index 0000000..feaf9e5
--- /dev/null
+++ b/src/pesd/SAML_SSO_Template.java
@@ -0,0 +1,95 @@
+/*
+ * This template is based on the following specification documents:
+ * http://docs.oasis-open.org/security/saml/Post2.0/sstc-saml-tech-overview-2.0-cd-02.html#5.1.Web%20Browser%20SSO%20Profile|outline
+ * iin particular on section "5.1 Web Browser SSO Profile"
+ * The section describes the typical flows likely to be used with the web browser SSO profile of SAML V2.0.
+ * message flows involved in web SSO exchanges for the following use case scenarios:
+ * - SP-initiated SSO using a Redirect Binding for the SP-to-IdP message and a POST Binding for the IdP-to-SP message
+ * - SP-initiated SSO using a POST Binding for the message and an Artifact Binding for the message
+ * - IDP-initiated SSO using a POST Binding for the IdP-to-SP message; no SP-to-IdP message is involved.
+ */
+package pesd;
+
+import java.io.PrintWriter;
+import org.json.JSONObject;
+
+public class SAML_SSO_Template extends TemplateParent{
+
+ public SAML_SSO_Template( PESDWrapper pesdObj, PrintWriter stdout) {
+ super(pesdObj, stdout);
+ }
+
+ @Override
+ public PESDWrapper run(){
+ pesdObj.startIterator();
+ // this boolean var will be set to false if a SAML request is found
+ // that because IdP initiated SAML SSO are based on unsolicited SAML response directly sent to the SP from the IdP via HTML form Post
+ Boolean is_IdP_Initiated = true;
+
+ while (pesdObj.getIterator() != -1) {
+ JSONObject line = pesdObj.getLine();
+ JSONObject metadata;
+ JSONObject urlParams;
+
+ switch((String) line.get("type")){
+ case "req":
+ try {
+ if (line.get("method") == "GET"){
+ metadata = (JSONObject) line.get("metadata");
+ urlParams = (JSONObject) metadata.get("url_params");
+
+ // SP-Initiated SSO with Redirect and POST Bindings
+ // // Identifying the SAML Request for SP-Initiated flow with redirect-Post bindings (flow's start)
+ if (urlParams.has("SAMLRequest")){
+ is_IdP_Initiated = false;
+ pesdObj.addFlag("SAMLRequest");
+ pesdObj.addAlt("SAML_SSO_SPI_Redirect_Post", pesdObj.getIterator()-2);
+
+ } else if (urlParams.has("SAMLResponse")) {
+ // Identifying the SAML Response for SP-Initiated flow with Post-Artifact bindings (flow's end)
+ pesdObj.addFlag("SAMLResponse");
+ pesdObj.endAlt(pesdObj.getIterator()+2);
+ }
+
+ if (urlParams.has("RelayState")){
+ pesdObj.addFlag("RelayState");
+ }
+ }
+ } catch(Exception e) {
+ stdout.println(e);
+ }
+
+ try {
+ if (line.get("method") == "POST"){
+ metadata = (JSONObject) line.get("metadata");
+ JSONObject bodyParams = (JSONObject) metadata.get("body_params");
+ if (bodyParams.has("RelayState")){
+ pesdObj.addFlag("RelayState");
+ }
+ if (bodyParams.has("SAMLResponse")){
+ if (is_IdP_Initiated) {
+ // if is_IdP_Initiated is true and we find a SAML Response, it means that we have an IdP initiated flow
+ pesdObj.addAlt("SAML_SSO_IdP_initiated", pesdObj.getIterator()-2);
+ }
+ // Identifying the SAML Response for SP-Initiated flow with redirect-Post and Post-Artifact bindings (flow's end)
+ pesdObj.addFlag("SAMLResponse");
+ pesdObj.endAlt(pesdObj.getIterator()+2);
+ } else if (bodyParams.has("SAMLRequest")) {
+ // Identifying the SAML Request for SP-Initiated flow with POST-Artifact Binding (flow's start)
+ is_IdP_Initiated = false;
+ pesdObj.addFlag("SAMLRequest");
+ pesdObj.addAlt("SAML_SSO_SPI__Post_Artifact", pesdObj.getIterator()-2);
+ }
+ }
+ } catch(Exception e) {
+ stdout.println(e);
+ }
+ break;
+
+ default: break;
+ }
+ pesdObj.nextLine();
+ }
+ return this.pesdObj;
+ }
+}
diff --git a/src/pesd/TemplateParent.java b/src/pesd/TemplateParent.java
new file mode 100644
index 0000000..26d1dee
--- /dev/null
+++ b/src/pesd/TemplateParent.java
@@ -0,0 +1,27 @@
+package pesd;
+
+import java.io.PrintWriter;
+
+// This is the PESD template parent class
+//
+// In order to write new templates to match your needs you have to create a template class which extends this one
+// and overrides the run method.
+//
+
+
+public class TemplateParent {
+ public PrintWriter stdout;
+ public PESDWrapper pesdObj;
+
+ public TemplateParent(PESDWrapper pesdObj, PrintWriter stdout) {
+ // PESD Template required vars for the constructor
+ this.stdout = stdout;
+ this.pesdObj = pesdObj;
+ }
+
+ public PESDWrapper run(){
+ //Override This
+ return this.pesdObj;
+ }
+
+}
diff --git a/src/pesd/TemplateRunner.java b/src/pesd/TemplateRunner.java
new file mode 100644
index 0000000..c942446
--- /dev/null
+++ b/src/pesd/TemplateRunner.java
@@ -0,0 +1,139 @@
+/*
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
+ * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
+ */
+package pesd;
+
+import java.io.PrintWriter;
+import org.json.JSONObject;
+
+/**
+ *
+ * @author francesco lacerenza
+ */
+public class TemplateRunner {
+
+ private PESDWrapper pesdObj;
+ private final PrintWriter stdout;
+ private TemplateParent template;
+ private PESDWrapper[] templates_results;
+
+ public TemplateRunner(String[] templates, PESDWrapper pesdObj, PrintWriter stdout) {
+ this.stdout = stdout;
+ this.pesdObj = pesdObj;
+ //initializing copies (by value) of the exported pesdObj for the templates
+ this.templates_results = new PESDWrapper[templates.length];
+ for (int i = 0; i < templates.length; i++) {
+ this.templates_results[i] = new PESDWrapper(this.stdout, pesdObj.getStucturedData());
+ }
+
+ Integer i = 0;
+ for (String template : templates) {
+ switch (template) {
+ case "OAuth2/OIDC":
+ this.template = new Oauth2_OpenID_Template(this.templates_results[i], stdout);
+ break;
+ case "SAML_SSO":
+ this.template = new SAML_SSO_Template(this.templates_results[i], stdout);
+ break;
+ default:
+ this.template = null;
+ break;
+ }
+ if (this.template != null) {
+ this.run(template, i);
+ i++;
+ }
+ }
+ // At this stage we have templates_result containing the normalized pesd objs for each template
+ // we need to merge them in a final obj
+ try {
+ if (this.templates_results.length > 0) {
+ for (PESDWrapper result : this.templates_results) {
+ this.pesdObj = mergeResults(this.pesdObj, result);
+ }
+ }
+ } catch (Exception e) {
+ stdout.println(e);
+ }
+ }
+
+ public void run(String template, Integer i) {
+ try {
+ // executing the template i and saving its output
+ this.templates_results[i] = this.template.run();
+ // Before exiting we need to run altEndConsistencyNormalizer to solve local inconsistencies in the template resulting pesd obj
+ // This is done to prevent MermaidJS rendering failure. Sometimes edge-cases within the templates execution may result
+ // in Alt symbols without end symbols, viceversa or even mixed inconsistencies like two alt in a row.
+ // This may happen due to non-standard flow implementation or other causes.
+ // altEndConsistencyNormalizer removes such possible inconsistencies in the local result of the template.
+ this.templates_results[i].altEndConsistencyNormalizer();
+ } catch (Exception e) {
+ stdout.println(e);
+ }
+
+ }
+
+ public PESDWrapper mergeResults(PESDWrapper obj1, PESDWrapper obj2) {
+ // Ordered merge of two pesd obj that are template results of the same starting-obj. As each template result has local consistency (thanks to altEndConsistencyNormalizer) and
+ // it is originated from the same base obj, we can simply move Alt/end symbols from the shortest to the longest in an ordered way.
+ // By doing so we generate the final result that will contain the matches of all the templates
+
+ // we equalize the results in terms of length, simply by adding the Alt/end between the results
+ PESDWrapper first;
+ PESDWrapper second;
+ if (obj1.getSize() < obj2.getSize()) {
+ first = obj2;
+ second = obj1;
+ } else {
+ first = obj1;
+ second = obj2;
+ }
+ equalizeAltEnd(first, second);
+ return first;
+ }
+
+ public void equalizeAltEnd(PESDWrapper first, PESDWrapper second) {
+
+ try {
+ first.startIterator();
+ second.startIterator();
+
+ while (first.getIterator() != -1) {
+ JSONObject line1 = first.getLine();
+ JSONObject line2 = null;
+ if (second.getIterator() != -1) {
+ line2 = second.getLine();
+ Boolean diff_type = (String) line1.get("type") != (String) line2.get("type");
+ Boolean second_is_alt = ((String) line2.get("type")).contains("alt");
+ Boolean second_is_end = ((String) line2.get("type")).contains("end");
+ Boolean first_is_alt = ((String) line1.get("type")).contains("alt");
+ Boolean first_is_end = ((String) line1.get("type")).contains("end");
+ // If types differ and second is alt or end, the line is inserted as first
+ if (diff_type) {
+ if (second_is_alt || second_is_end) {
+ first.addToPos(first.getIterator(), line2);
+ } else if (first_is_alt || first_is_end) {
+ second.addToPos(first.getIterator(), line1);
+ }
+ }
+ } else {
+ second.addToPos(first.getIterator(), line1);
+ }
+
+ first.nextLine();
+ second.nextLine();
+ }
+ } catch (Exception e) {
+ stdout.println(e);
+ }
+ }
+
+ public String[] getDiagram(Integer mode_op) {
+ return this.pesdObj.getSeqDiagramdMD(mode_op);
+ }
+
+ public JSONObject getMetadata() {
+ return this.pesdObj.getMetadata();
+ }
+}