├── .github └── dependabot.yml ├── .gitignore ├── LICENSE ├── Readme.md ├── Resources ├── Maltego-Local-Transform-1.png ├── Maltego-Local-Transform-2.png └── Maltego-Local-Transform-Result.png ├── TransNet.sln ├── build.cake ├── src └── TransNet │ ├── Entity.cs │ ├── ExtensionMethods.cs │ ├── ITransformable.cs │ ├── MatchingRule.cs │ ├── TransNet.csproj │ ├── Transformation.cs │ └── logo │ └── logo.png └── test └── TransNet_Test ├── EntityTest.cs ├── ExtensionMethodsTest.cs ├── TransNet_Test.csproj └── TransformationTest.cs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: nuget 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | open-pull-requests-limit: 10 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # Cake Build System 7 | [Tt]ools/ 8 | [Aa]rtifacts/ 9 | [Tt]estResults/ 10 | 11 | ############### 12 | # DocFX # 13 | ############### 14 | /**/DROP/ 15 | /**/TEMP/ 16 | /**/packages/ 17 | /**/bin/ 18 | /**/obj/ 19 | _site 20 | 21 | # VS Code 22 | .vscode/ 23 | 24 | # User-specific files 25 | *.suo 26 | *.user 27 | *.userosscache 28 | *.sln.docstates 29 | 30 | # User-specific files (MonoDevelop/Xamarin Studio) 31 | *.userprefs 32 | 33 | # Build results 34 | [Dd]ebug/ 35 | [Dd]ebugPublic/ 36 | [Rr]elease/ 37 | [Rr]eleases/ 38 | x64/ 39 | x86/ 40 | bld/ 41 | [Bb]in/ 42 | [Oo]bj/ 43 | [Ll]og/ 44 | 45 | # Visual Studio 2015 cache/options directory 46 | .vs/ 47 | # Uncomment if you have tasks that create the project's static files in wwwroot 48 | #wwwroot/ 49 | 50 | # MSTest test Results 51 | [Tt]est[Rr]esult*/ 52 | [Bb]uild[Ll]og.* 53 | 54 | # NUNIT 55 | *.VisualState.xml 56 | TestResult.xml 57 | 58 | # Build Results of an ATL Project 59 | [Dd]ebugPS/ 60 | [Rr]eleasePS/ 61 | dlldata.c 62 | 63 | # Benchmark Results 64 | BenchmarkDotNet.Artifacts/ 65 | 66 | # .NET Core 67 | project.lock.json 68 | project.fragment.lock.json 69 | artifacts/ 70 | **/Properties/launchSettings.json 71 | 72 | *_i.c 73 | *_p.c 74 | *_i.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.pch 79 | *.pdb 80 | *.pgc 81 | *.pgd 82 | *.rsp 83 | *.sbr 84 | *.tlb 85 | *.tli 86 | *.tlh 87 | *.tmp 88 | *.tmp_proj 89 | *.log 90 | *.vspscc 91 | *.vssscc 92 | .builds 93 | *.pidb 94 | *.svclog 95 | *.scc 96 | 97 | # Chutzpah Test files 98 | _Chutzpah* 99 | 100 | # Visual C++ cache files 101 | ipch/ 102 | *.aps 103 | *.ncb 104 | *.opendb 105 | *.opensdf 106 | *.sdf 107 | *.cachefile 108 | *.VC.db 109 | *.VC.VC.opendb 110 | 111 | # Visual Studio profiler 112 | *.psess 113 | *.vsp 114 | *.vspx 115 | *.sap 116 | 117 | # TFS 2012 Local Workspace 118 | $tf/ 119 | 120 | # Guidance Automation Toolkit 121 | *.gpState 122 | 123 | # ReSharper is a .NET coding add-in 124 | _ReSharper*/ 125 | *.[Rr]e[Ss]harper 126 | *.DotSettings.user 127 | 128 | # JustCode is a .NET coding add-in 129 | .JustCode 130 | 131 | # TeamCity is a build add-in 132 | _TeamCity* 133 | 134 | # DotCover is a Code Coverage Tool 135 | *.dotCover 136 | 137 | # Visual Studio code coverage results 138 | *.coverage 139 | *.coveragexml 140 | 141 | # NCrunch 142 | _NCrunch_* 143 | .*crunch*.local.xml 144 | nCrunchTemp_* 145 | 146 | # MightyMoose 147 | *.mm.* 148 | AutoTest.Net/ 149 | 150 | # Web workbench (sass) 151 | .sass-cache/ 152 | 153 | # Installshield output folder 154 | [Ee]xpress/ 155 | 156 | # DocProject is a documentation generator add-in 157 | DocProject/buildhelp/ 158 | DocProject/Help/*.HxT 159 | DocProject/Help/*.HxC 160 | DocProject/Help/*.hhc 161 | DocProject/Help/*.hhk 162 | DocProject/Help/*.hhp 163 | DocProject/Help/Html2 164 | DocProject/Help/html 165 | 166 | # Click-Once directory 167 | publish/ 168 | 169 | # Publish Web Output 170 | *.[Pp]ublish.xml 171 | *.azurePubxml 172 | # TODO: Comment the next line if you want to checkin your web deploy settings 173 | # but database connection strings (with potential passwords) will be unencrypted 174 | *.pubxml 175 | *.publishproj 176 | 177 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 178 | # checkin your Azure Web App publish settings, but sensitive information contained 179 | # in these scripts will be unencrypted 180 | PublishScripts/ 181 | 182 | # NuGet Packages 183 | *.nupkg 184 | # The packages folder can be ignored because of Package Restore 185 | **/packages/* 186 | # except build/, which is used as an MSBuild target. 187 | !**/packages/build/ 188 | # Uncomment if necessary however generally it will be regenerated when needed 189 | #!**/packages/repositories.config 190 | # NuGet v3's project.json files produces more ignorable files 191 | *.nuget.props 192 | *.nuget.targets 193 | 194 | # Microsoft Azure Build Output 195 | csx/ 196 | *.build.csdef 197 | 198 | # Microsoft Azure Emulator 199 | ecf/ 200 | rcf/ 201 | 202 | # Windows Store app package directories and files 203 | AppPackages/ 204 | BundleArtifacts/ 205 | Package.StoreAssociation.xml 206 | _pkginfo.txt 207 | *.appx 208 | 209 | # Visual Studio cache files 210 | # files ending in .cache can be ignored 211 | *.[Cc]ache 212 | # but keep track of directories ending in .cache 213 | !*.[Cc]ache/ 214 | 215 | # Others 216 | ClientBin/ 217 | ~$* 218 | *~ 219 | *.dbmdl 220 | *.dbproj.schemaview 221 | *.jfm 222 | *.pfx 223 | *.publishsettings 224 | orleans.codegen.cs 225 | 226 | # Since there are multiple workflows, uncomment next line to ignore bower_components 227 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 228 | #bower_components/ 229 | 230 | # RIA/Silverlight projects 231 | Generated_Code/ 232 | 233 | # Backup & report files from converting an old project file 234 | # to a newer Visual Studio version. Backup files are not needed, 235 | # because we have git ;-) 236 | _UpgradeReport_Files/ 237 | Backup*/ 238 | UpgradeLog*.XML 239 | UpgradeLog*.htm 240 | 241 | # SQL Server files 242 | *.mdf 243 | *.ldf 244 | *.ndf 245 | 246 | # Business Intelligence projects 247 | *.rdl.data 248 | *.bim.layout 249 | *.bim_*.settings 250 | 251 | # Microsoft Fakes 252 | FakesAssemblies/ 253 | 254 | # GhostDoc plugin setting file 255 | *.GhostDoc.xml 256 | 257 | # Node.js Tools for Visual Studio 258 | .ntvs_analysis.dat 259 | node_modules/ 260 | 261 | # Typescript v1 declaration files 262 | typings/ 263 | 264 | # Visual Studio 6 build log 265 | *.plg 266 | 267 | # Visual Studio 6 workspace options file 268 | *.opt 269 | 270 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 271 | *.vbw 272 | 273 | # Visual Studio LightSwitch build output 274 | **/*.HTMLClient/GeneratedArtifacts 275 | **/*.DesktopClient/GeneratedArtifacts 276 | **/*.DesktopClient/ModelManifest.xml 277 | **/*.Server/GeneratedArtifacts 278 | **/*.Server/ModelManifest.xml 279 | _Pvt_Extensions 280 | 281 | # Paket dependency manager 282 | .paket/paket.exe 283 | paket-files/ 284 | 285 | # FAKE - F# Make 286 | .fake/ 287 | 288 | # JetBrains Rider 289 | .idea/ 290 | *.sln.iml 291 | 292 | # CodeRush 293 | .cr/ 294 | 295 | # Python Tools for Visual Studio (PTVS) 296 | __pycache__/ 297 | *.pyc 298 | 299 | # Cake - Uncomment if you are using it 300 | # tools/** 301 | # !tools/packages.config 302 | 303 | # Tabs Studio 304 | *.tss 305 | 306 | # Telerik's JustMock configuration file 307 | *.jmconfig 308 | 309 | # BizTalk build output 310 | *.btp.cs 311 | *.btm.cs 312 | *.odx.cs 313 | *.xsd.cs 314 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # TransNet 2 | TransNet is a .Net Standard library to create [Maltego](https://www.paterva.com/web7/buy/maltego-clients.php) transformations. 3 | 4 | [![license](https://img.shields.io/github/license/secana/transnet.svg)](https://raw.githubusercontent.com/secana/TransNet/master/LICENSE) 5 | [![Build status](https://ci.appveyor.com/api/projects/status/jffcahmtd6u73p6n/branch/master?svg=true)](https://ci.appveyor.com/project/secana/transnet/branch/master) 6 | [![NuGet](https://img.shields.io/nuget/v/TransNet.svg)](https://www.nuget.org/packages/TransNet/) 7 | [![NuGet](https://img.shields.io/nuget/dt/TransNet.svg)](https://www.nuget.org/packages/TransNet/) 8 | 9 | ## Writing a transformation with *TransNet* 10 | The following example shows how to write a Maltego transformation with *TransNet*. 11 | 12 | ### Install TransNet into your project 13 | *TransNet* is a .Net Standard 2.0 library which means it runs with the .Net Framework and with .Net Core under Windows, Linux and Mac. 14 | To use *TransNet* in your project just install the [TransNet Nuget package](https://www.nuget.org/packages/TransNet/) into your project. 15 | 16 | ### The Code 17 | Writing a transform is pretty straight forward. Maltego will call your transformation with command line arguments, which are parsed by *TransNet*. 18 | The resulting entities which you want to return have to be printed to *stdout*. 19 | 20 | A minimal transformation which returns a "Person" entity looks like this: 21 | 22 | ```csharp 23 | using System; 24 | using TransNet; 25 | 26 | namespace MyTransform 27 | { 28 | class Program 29 | { 30 | static void Main(string[] args) 31 | { 32 | // Create a new transform based on the command line input from Maltego. 33 | var transform = new Transformation(args); 34 | 35 | // Add an entity to return. 36 | var person1 = new Entity("Person", "Alice Yu"); 37 | 38 | // Add the entity to return to the transformation. 39 | transform.Entities.Add(person1); 40 | 41 | Console.WriteLine(transform.TransformToXML()); 42 | } 43 | } 44 | } 45 | 46 | ``` 47 | 48 | You can reach the same result with the floating API. The code above looks like this: 49 | 50 | 51 | ```csharp 52 | using System; 53 | using TransNet; 54 | 55 | namespace MyTransform 56 | { 57 | class Program 58 | { 59 | static void Main(string[] args) 60 | { 61 | var transform = new Transformation(args) 62 | .AddEntity("Person", "Alice Yu"); 63 | 64 | Console.WriteLine(transform.TransformToXML()); 65 | } 66 | } 67 | } 68 | 69 | ``` 70 | 71 | The example below shows how you can add additional fields to your returned entity and how to add an edge label with some properties. 72 | 73 | ```csharp 74 | using System; 75 | using System.Collections.Generic; 76 | using TransNet; 77 | 78 | namespace MyTransform 79 | { 80 | class Program 81 | { 82 | static void Main(string[] args) 83 | { 84 | // Create a new transform based on the command line input from Maltego. 85 | var transform = new Transformation(args); 86 | 87 | // Add an entity to return. 88 | var person1 = new Entity("Person", "Alice Yu"); 89 | 90 | // Add additional fields to the entity. 91 | var person1Age = new Entity.AdditionalField("Age", "Age", "45"); 92 | var person1HairColor = new Entity.AdditionalField("HairColor", "Hair Color", "Blond"); 93 | person1.AdditionalFields.Add(person1Age); 94 | person1.AdditionalFields.Add(person1HairColor); 95 | 96 | // Adding an edge label. 97 | person1.AddEdgeLabel("My own transformation.", new List> 98 | { 99 | // Adding edge properties. 100 | new Tuple("My first edge property", "My first edge property value"), 101 | new Tuple("My second edge property", "My second edge property value") 102 | }); 103 | 104 | // Add the entity to return to the transformation. 105 | transform.Entities.Add(person1); 106 | 107 | Console.WriteLine(transform.TransformToXML()); 108 | } 109 | } 110 | } 111 | ``` 112 | 113 | You can reach the same result with the floating API. The code above looks like this: 114 | 115 | ```csharp 116 | using System; 117 | using System.Collections.Generic; 118 | using TransNet; 119 | 120 | namespace MyTransform 121 | { 122 | class Program 123 | { 124 | static void Main(string[] args) 125 | { 126 | var transform = new Transformation(args) 127 | .AddEntity("Person", "Alice Yu") 128 | .AddAdditionalField("Age", "Age", "45") 129 | .AddAdditionalField("HairColor", "Hair Color", "Blond") 130 | .AddEdgeLabel("My own transformation.", new List> 131 | { 132 | new Tuple("My first edge property", "My first edge property value"), 133 | new Tuple("My second edge property", "My second edge property value") 134 | }); 135 | 136 | Console.WriteLine(transform.TransformToXML()); 137 | } 138 | } 139 | } 140 | ``` 141 | 142 | #### Access field values 143 | If your transformation needs some field values from the input entity provided by Maltego to your transformation via command line arguments use this: 144 | ```csharp 145 | var fieldValue = transform.InputArguments["fieldName"]; 146 | ``` 147 | This lets you access the field "fieldName" from the input entity. 148 | 149 | 150 | #### Debug prints 151 | If you need to output any debug information to Maltego which gets shown in the output panel use: 152 | 153 | ```csharp 154 | transform.PrintDebug("My debug message"); 155 | ``` 156 | 157 | To set the Maltego progress bar to a specific percentage while your transformation runs use: 158 | 159 | #### Progress bar 160 | ```csharp 161 | transform.PrintProgress(50); 162 | ``` 163 | This will set the progress bar to 50%. 164 | 165 | ## Add a transformation to Maltego 166 | To add your own transformation to Maltego follow the next steps: 167 | 168 | 1. Go to the "Transforms" tab. 169 | 2. Click on "Local Transform". 170 | 3. Configure the details. 171 | 1. Add a name and description for your transformation. 172 | 2. Add an *unique* transformation ID. 173 | 3. Select the input entity type. Your transformation will be available for this input type. 174 | 4. Configure the command. 175 | 1. Add your transformation executable here. 176 | 5. Click "Finish". 177 | 178 | ![Image](./Resources/Maltego-Local-Transform-1.png) 179 | ![Image](./Resources/Maltego-Local-Transform-2.png) 180 | 181 | ## Using the transformation in Maltego 182 | After you've added your transformation to Maltego for one input entity you can use your transformation the following way: 183 | 184 | 1. Add one item of the entity type you selected as input to the graph. 185 | 2. Right click the entity and select your transformation under "Local Transforms". 186 | 187 | ![Image](./Resources/Maltego-Local-Transform-Result.png) 188 | 189 | If you click on the edge, you will find your added edge properties there, too. 190 | -------------------------------------------------------------------------------- /Resources/Maltego-Local-Transform-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/secana/TransNet/a5242b8cbe47030ede7a4df240d7e7e3954fa289/Resources/Maltego-Local-Transform-1.png -------------------------------------------------------------------------------- /Resources/Maltego-Local-Transform-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/secana/TransNet/a5242b8cbe47030ede7a4df240d7e7e3954fa289/Resources/Maltego-Local-Transform-2.png -------------------------------------------------------------------------------- /Resources/Maltego-Local-Transform-Result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/secana/TransNet/a5242b8cbe47030ede7a4df240d7e7e3954fa289/Resources/Maltego-Local-Transform-Result.png -------------------------------------------------------------------------------- /TransNet.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29521.150 5 | MinimumVisualStudioVersion = 15.0.26124.0 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5C2B2227-D4DA-425B-8EC6-37D9C220EDA1}" 7 | ProjectSection(SolutionItems) = preProject 8 | .gitignore = .gitignore 9 | build.cake = build.cake 10 | Readme.md = Readme.md 11 | EndProjectSection 12 | EndProject 13 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{ED5D2DC6-08F0-4FF6-A168-24F58C3706C2}" 14 | EndProject 15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{F63B09BA-E971-4DEA-AD9D-02EC0A0CEAEC}" 16 | EndProject 17 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TransNet", "src\TransNet\TransNet.csproj", "{47771707-2DF7-4C31-BB56-B21132646490}" 18 | EndProject 19 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TransNet_Test", "test\TransNet_Test\TransNet_Test.csproj", "{9F224D19-F44E-46FD-87BE-FDBB85CBF860}" 20 | EndProject 21 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Resources", "Resources", "{8567E27C-7FFD-4B25-AA98-A4A42E434891}" 22 | ProjectSection(SolutionItems) = preProject 23 | Resources\Maltego-Local-Transform-1.png = Resources\Maltego-Local-Transform-1.png 24 | Resources\Maltego-Local-Transform-2.png = Resources\Maltego-Local-Transform-2.png 25 | Resources\Maltego-Local-Transform-Result.png = Resources\Maltego-Local-Transform-Result.png 26 | EndProjectSection 27 | EndProject 28 | Global 29 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 30 | Debug|Any CPU = Debug|Any CPU 31 | Release|Any CPU = Release|Any CPU 32 | EndGlobalSection 33 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 34 | {47771707-2DF7-4C31-BB56-B21132646490}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {47771707-2DF7-4C31-BB56-B21132646490}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {47771707-2DF7-4C31-BB56-B21132646490}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {47771707-2DF7-4C31-BB56-B21132646490}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {9F224D19-F44E-46FD-87BE-FDBB85CBF860}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {9F224D19-F44E-46FD-87BE-FDBB85CBF860}.Debug|Any CPU.Build.0 = Debug|Any CPU 40 | {9F224D19-F44E-46FD-87BE-FDBB85CBF860}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {9F224D19-F44E-46FD-87BE-FDBB85CBF860}.Release|Any CPU.Build.0 = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(SolutionProperties) = preSolution 44 | HideSolutionNode = FALSE 45 | EndGlobalSection 46 | GlobalSection(NestedProjects) = preSolution 47 | {47771707-2DF7-4C31-BB56-B21132646490} = {ED5D2DC6-08F0-4FF6-A168-24F58C3706C2} 48 | {9F224D19-F44E-46FD-87BE-FDBB85CBF860} = {F63B09BA-E971-4DEA-AD9D-02EC0A0CEAEC} 49 | {8567E27C-7FFD-4B25-AA98-A4A42E434891} = {5C2B2227-D4DA-425B-8EC6-37D9C220EDA1} 50 | EndGlobalSection 51 | GlobalSection(ExtensibilityGlobals) = postSolution 52 | SolutionGuid = {6D4EE871-DD02-436A-BFCF-661426BFD545} 53 | EndGlobalSection 54 | EndGlobal 55 | -------------------------------------------------------------------------------- /build.cake: -------------------------------------------------------------------------------- 1 | var target = Argument("target", "Default"); 2 | var apiKey = Argument("apiKey", null); 3 | var testFailed = false; 4 | var solutionDir = System.IO.Directory.GetCurrentDirectory(); 5 | var testResultDir = System.IO.Path.Combine(solutionDir, "testResults"); 6 | var artifactDir = "./artifacts"; 7 | 8 | Information("Solution Directory: {0}", solutionDir); 9 | Information("Test Results Directory: {0}", testResultDir); 10 | 11 | Task("PrepareDirectories") 12 | .Does(() => 13 | { 14 | EnsureDirectoryExists(testResultDir); 15 | EnsureDirectoryExists(artifactDir); 16 | }); 17 | 18 | Task("Clean") 19 | .IsDependentOn("PrepareDirectories") 20 | .Does(() => 21 | { 22 | var delSettings = new DeleteDirectorySettings { Recursive = true, Force = true }; 23 | CleanDirectory(testResultDir); 24 | CleanDirectory(artifactDir); 25 | 26 | var binDirs = GetDirectories("./**/bin"); 27 | var objDirs = GetDirectories("./**/obj"); 28 | var testResDirs = GetDirectories("./**/test-results"); 29 | 30 | DeleteDirectories(binDirs, delSettings); 31 | DeleteDirectories(objDirs, delSettings); 32 | DeleteDirectories(testResDirs, delSettings); 33 | }); 34 | 35 | Task("Restore") 36 | .Does(() => 37 | { 38 | DotNetCoreRestore(); 39 | }); 40 | 41 | Task("Build") 42 | .IsDependentOn("Restore") 43 | .Does(() => 44 | { 45 | var solution = GetFiles("./*.sln").ElementAt(0); 46 | Information("Build solution: {0}", solution); 47 | 48 | var settings = new DotNetCoreBuildSettings 49 | { 50 | Configuration = "Release" 51 | }; 52 | 53 | DotNetCoreBuild(solution.FullPath, settings); 54 | }); 55 | 56 | Task("Test") 57 | .IsDependentOn("Clean") 58 | .IsDependentOn("Build") 59 | .ContinueOnError() 60 | .Does(() => 61 | { 62 | var tests = GetFiles("./test/**/*Test/*.csproj"); 63 | 64 | if(tests.Count == 0) 65 | { 66 | Information("Found no test projects"); 67 | return; 68 | } 69 | 70 | 71 | foreach(var test in tests) 72 | { 73 | var projectFolder = System.IO.Path.GetDirectoryName(test.FullPath); 74 | try 75 | { 76 | DotNetCoreTest(test.FullPath, new DotNetCoreTestSettings 77 | { 78 | ArgumentCustomization = args => args.Append("-l trx"), 79 | WorkingDirectory = projectFolder 80 | }); 81 | } 82 | catch(Exception e) 83 | { 84 | testFailed = true; 85 | Error(e.Message.ToString()); 86 | } 87 | } 88 | 89 | // Copy test result files. 90 | var tmpTestResultFiles = GetFiles("./**/*.trx"); 91 | CopyFiles(tmpTestResultFiles, testResultDir); 92 | }); 93 | 94 | Task("Pack") 95 | .IsDependentOn("Clean") 96 | .IsDependentOn("Test") 97 | .Does(() => 98 | { 99 | if(testFailed) 100 | { 101 | Information("Do not pack because tests failed"); 102 | return; 103 | } 104 | 105 | var projects = GetFiles("./src/**/*.csproj"); 106 | var settings = new DotNetCorePackSettings 107 | { 108 | Configuration = "Release", 109 | OutputDirectory = artifactDir 110 | }; 111 | 112 | foreach(var project in projects) 113 | { 114 | Information("Pack {0}", project.FullPath); 115 | DotNetCorePack(project.FullPath, settings); 116 | } 117 | }); 118 | 119 | Task("Publish") 120 | .IsDependentOn("Clean") 121 | .IsDependentOn("Test") 122 | .Does(() => 123 | { 124 | if(testFailed) 125 | { 126 | Information("Do not publish because tests failed"); 127 | return; 128 | } 129 | var projects = GetFiles("./src/**/*.csproj"); 130 | 131 | foreach(var project in projects) 132 | { 133 | var projectDir = System.IO.Path.GetDirectoryName(project.FullPath); 134 | var projectName = new System.IO.DirectoryInfo(projectDir).Name; 135 | var outputDir = System.IO.Path.Combine(artifactDir, projectName); 136 | EnsureDirectoryExists(outputDir); 137 | 138 | Information("Publish {0} to {1}", projectName, outputDir); 139 | 140 | var settings = new DotNetCorePublishSettings 141 | { 142 | OutputDirectory = outputDir, 143 | Configuration = "Release" 144 | }; 145 | DotNetCorePublish(project.FullPath, settings); 146 | } 147 | }); 148 | 149 | Task("Push") 150 | .IsDependentOn("Pack") 151 | .Does(() => 152 | { 153 | var package = GetFiles($"{artifactDir}/TransNet.*.nupkg").ElementAt(0).FullPath; 154 | var source = "https://www.nuget.org/api/v2/package"; 155 | 156 | if(apiKey==null) 157 | throw new ArgumentNullException(nameof(apiKey), "The \"apiKey\" argument must be set for this task."); 158 | 159 | Information($"Push {package} to {source}"); 160 | 161 | DotNetCoreNuGetPush(package, new DotNetCoreNuGetPushSettings { 162 | Source = source, 163 | ApiKey = apiKey 164 | }); 165 | }); 166 | 167 | Task("Default") 168 | .IsDependentOn("Test") 169 | .Does(() => 170 | { 171 | Information("Build and test the whole solution."); 172 | Information("To pack (nuget) the application use the cake build argument: --target=Pack"); 173 | Information("To publish (to run it somewhere else) the application use the cake build argument: --Target=Publish"); 174 | Information("To push the package to nuget.org use the cake build argument: --target=Push --apiKey=\"your nuget.org API key\""); 175 | }); 176 | 177 | RunTarget(target); -------------------------------------------------------------------------------- /src/TransNet/Entity.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Xml; 5 | 6 | namespace TransNet 7 | { 8 | /// 9 | /// Represents an entity in Maltego. 10 | /// 11 | public class Entity : ITransformable 12 | { 13 | /// 14 | /// Type of the entity. Has to be one available in Maltego. 15 | /// 16 | public string EntityType { get; } 17 | 18 | /// 19 | /// Value of the entity. This will be shown on the entity in Maltego. 20 | /// 21 | public string Value { get; } 22 | 23 | /// 24 | /// Weight of the entity. 25 | /// 26 | public int Weight { get; } 27 | 28 | /// 29 | /// Additional fields for the entity. Will be shown in the properties panel 30 | /// in Maltego. 31 | /// 32 | public List AdditionalFields { get; } 33 | 34 | /// 35 | /// Is true if the entity has an edge label, false if not. 36 | /// 37 | public bool HasEdgeLabel { get; private set; } 38 | 39 | /// 40 | /// Create an new Maltego entity. 41 | /// 42 | /// Type of the Maltego entity. 43 | /// Value of the Maltego entity. 44 | /// Weight of the Maltego entity. 45 | public Entity(string entityType, string value, int weight = 0) 46 | { 47 | EntityType = entityType ?? throw new ArgumentNullException(nameof(entityType), "EntityType cannot be null."); 48 | Value = value ?? throw new ArgumentNullException(nameof(value), "Value cannot be null."); 49 | Weight = weight; 50 | AdditionalFields = new List(); 51 | } 52 | 53 | /// 54 | /// Adds an edge label and optional edge properties to the edge. 55 | /// 56 | /// Label of the edge 57 | /// 58 | /// Properties of the edge as a tuple where Item1 is the property name 59 | /// and Item2 is the property value. 60 | /// 61 | /// This entity object. 62 | public Entity AddEdgeLabel(string label, List> properties = null) 63 | { 64 | if (HasEdgeLabel) 65 | throw new InvalidOperationException("Only one edge label per edge is allowed!"); 66 | 67 | AdditionalFields.Add(new AdditionalField("link#maltego.link.label", null, label)); 68 | AdditionalFields.Add(new AdditionalField("link#maltego.link.show-label", null, "1")); 69 | 70 | if (properties != null) 71 | { 72 | for (var i = 0; i < properties.Count; i++) 73 | { 74 | AdditionalFields.Add(new AdditionalField("link#" + i, properties[i].Item1, properties[i].Item2)); 75 | } 76 | } 77 | 78 | HasEdgeLabel = true; 79 | 80 | return this; 81 | } 82 | 83 | /// 84 | /// Convert the entity object to an XML readable by Maltego. 85 | /// 86 | /// The transformation result readable by Maltego. 87 | public string TransformToXML() 88 | { 89 | var settings = new XmlWriterSettings {OmitXmlDeclaration = true, CheckCharacters = false}; 90 | 91 | using (var sw = new StringWriter()) 92 | { 93 | using (var xw = XmlWriter.Create(sw, settings)) 94 | { 95 | xw.WriteStartDocument(); 96 | xw.WriteStartElement("Entity"); 97 | xw.WriteAttributeString("Type", EntityType); 98 | 99 | xw.WriteStartElement("Value"); 100 | xw.WriteString(Value); 101 | xw.WriteEndElement(); 102 | 103 | xw.WriteStartElement("Weight"); 104 | xw.WriteString(Weight.ToString()); 105 | xw.WriteEndElement(); 106 | 107 | if (AdditionalFields != null) 108 | { 109 | xw.WriteStartElement("AdditionalFields"); 110 | foreach (var af in AdditionalFields) 111 | { 112 | xw.WriteRaw(af.TransformToXML()); 113 | } 114 | xw.WriteEndElement(); 115 | } 116 | 117 | xw.WriteEndElement(); 118 | xw.WriteEndDocument(); 119 | } 120 | return sw.ToString(); 121 | } 122 | } 123 | 124 | 125 | /// 126 | /// Additional fields are extra information for an entity which 127 | /// are shown in the properties panel of the entity in Maltego. 128 | /// 129 | public class AdditionalField : ITransformable 130 | { 131 | /// 132 | /// Creates an additional field to be used by an entity for additional information. 133 | /// These fields are passed to the next transformation as STDIN parameters. 134 | /// 135 | /// Name of the additional field (mandatory). 136 | /// The name which will be displayed in the UI field. 137 | /// The value of the field. 138 | /// 139 | /// "strict" or "loose". If "strict" is chosen, the attribute will be used to distinguish two entities with the same 140 | /// attribute value. If "loose" is used, entities with the same name but different attribute values will be considered 141 | /// as equal entities. 142 | /// 143 | public AdditionalField( 144 | string name, 145 | string displayName = null, 146 | string value = null, 147 | MatchingRule matchingRule = TransNet.MatchingRule.Loose 148 | ) 149 | { 150 | Name = name; 151 | DisplayName = displayName; 152 | Value = value; 153 | MatchingRule = matchingRule.ToString().ToLower(); 154 | 155 | if (name == null) 156 | { 157 | throw new ArgumentNullException(nameof(name), "Name is mandatory and cannot be null."); 158 | } 159 | } 160 | 161 | /// 162 | /// Name of the additional field 163 | /// 164 | public string Name { get; } 165 | 166 | /// 167 | /// Display name of the additional field. 168 | /// 169 | public string DisplayName { get; } 170 | 171 | /// 172 | /// MatchingRule (loose, strict) for the additional field. 173 | /// 174 | public string MatchingRule { get; } 175 | 176 | /// 177 | /// Value of the additional field. 178 | /// 179 | public string Value { get; } 180 | 181 | /// 182 | /// Transform the entity object into an XML element usable 183 | /// by Maltego. 184 | /// 185 | /// Maltego entity XML. 186 | public string TransformToXML() 187 | { 188 | var settings = new XmlWriterSettings {OmitXmlDeclaration = true}; 189 | 190 | using (var sw = new StringWriter()) 191 | { 192 | using (var xw = XmlWriter.Create(sw, settings)) 193 | { 194 | xw.WriteStartDocument(); 195 | xw.WriteStartElement("Field"); 196 | xw.WriteAttributeString("Name", Name); 197 | if (DisplayName != null) 198 | xw.WriteAttributeString("DisplayName", DisplayName); 199 | xw.WriteAttributeString("MatchingRule", MatchingRule); 200 | xw.WriteString(Value); 201 | xw.WriteEndElement(); 202 | xw.WriteEndDocument(); 203 | } 204 | return sw.ToString(); 205 | } 206 | } 207 | } 208 | } 209 | } -------------------------------------------------------------------------------- /src/TransNet/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | namespace TransNet 2 | { 3 | /// 4 | /// Extension methods to simplify the usage of the library. 5 | /// 6 | public static class ExtensionMethods 7 | { 8 | /// 9 | /// Add an entity to the current transformation to be 10 | /// returned to Maltego, 11 | /// 12 | /// Transformation object. 13 | /// The entity type to add to the transformation. 14 | /// Value of the entity. 15 | /// Weight of the entity. 16 | /// The added entity. 17 | public static Entity AddEntity( 18 | this Transformation transform, 19 | string entityType, 20 | string entityValue, 21 | int weight = 0 22 | ) 23 | { 24 | var entity = new Entity(entityType, entityValue, weight); 25 | transform.Entities.Add(entity); 26 | return entity; 27 | } 28 | 29 | /// 30 | /// Add an additional field to the entity. 31 | /// 32 | /// Entity where the field is added. 33 | /// Name of the additional field. 34 | /// Display name of the field. 35 | /// Value of the field. 36 | /// Matching rule of the field. 37 | /// The entity where the field was added. 38 | public static Entity AddAdditionalField( 39 | this Entity entity, 40 | string name, 41 | string displayName = null, 42 | string value = null, 43 | MatchingRule matchingRule = MatchingRule.Loose 44 | ) 45 | { 46 | var additionalField = new Entity.AdditionalField(name, displayName, value, matchingRule); 47 | entity.AdditionalFields.Add(additionalField); 48 | return entity; 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /src/TransNet/ITransformable.cs: -------------------------------------------------------------------------------- 1 | namespace TransNet 2 | { 3 | internal interface ITransformable 4 | { 5 | string TransformToXML(); 6 | } 7 | } -------------------------------------------------------------------------------- /src/TransNet/MatchingRule.cs: -------------------------------------------------------------------------------- 1 | namespace TransNet 2 | { 3 | /// 4 | /// Matching rule for additional properties. 5 | /// 6 | public enum MatchingRule 7 | { 8 | /// 9 | /// The attribute will be used to distinguish two entities of the same type 10 | /// in Maltego. 11 | /// 12 | Strict, 13 | 14 | /// 15 | /// The attribute will *not* be used to distinguish two entities of the same type 16 | /// in Maltego. 17 | /// 18 | Loose 19 | } 20 | } -------------------------------------------------------------------------------- /src/TransNet/TransNet.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0;netstandard2.1 5 | Stefan Hausotte 6 | 7 | Library to write Maltego transformations. 8 | Stefan Hausotte 9 | https://github.com/secana/TransNet 10 | https://github.com/secana/TransNet 11 | Apache-2.0 12 | Maltego Transformations 13 | https://raw.githubusercontent.com/secana/TransNet/master/src/TransNet/logo/logo.png 14 | logo.png 15 | 1.2.0 16 | Added extension methods for a better API usage 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | bin\Release\netstandard2.0\TransNet.xml 25 | true 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /src/TransNet/Transformation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Xml; 6 | using System.Xml.Linq; 7 | 8 | namespace TransNet 9 | { 10 | /// 11 | /// This class provides the capabilities to read and write 12 | /// Maltego transformations. 13 | /// 14 | public class Transformation : ITransformable 15 | { 16 | /// 17 | /// List with input arguments from the command line. 18 | /// 19 | public Dictionary InputArguments { get; } 20 | 21 | /// 22 | /// The entity value of the input to the transformation. 23 | /// This is equal to the first STDIN argument and the main 24 | /// property of the input entity. 25 | /// 26 | public string EntityValue => InputArguments["EntityValue"]; 27 | 28 | /// 29 | /// List with entities to return to Maltego based on 30 | /// the input of the transformation. 31 | /// 32 | public readonly List Entities = new List(); 33 | 34 | private string _optionalParameter = null; 35 | 36 | /// 37 | /// Create a representation of a Maltego transformation. 38 | /// 39 | /// Command line arguments given to a transformation application by Maltego. 40 | public Transformation(string[] args) 41 | { 42 | InputArguments = ParseSTDIN(args); 43 | } 44 | 45 | /// 46 | /// Create a representation of a Maltego transformation. 47 | /// 48 | /// Maltego transformation XML 49 | public Transformation(string xml) 50 | { 51 | ParseTransformOutput(xml); 52 | } 53 | 54 | /// 55 | /// Convert the whole transformation into an XML readable by Maltego. 56 | /// 57 | /// Maltego readable XML. 58 | public string TransformToXML() 59 | { 60 | var settings = new XmlWriterSettings {OmitXmlDeclaration = true}; 61 | 62 | using (var sw = new StringWriter()) 63 | { 64 | using (var xw = XmlWriter.Create(sw, settings)) 65 | { 66 | xw.WriteStartDocument(); 67 | xw.WriteStartElement("MaltegoMessage"); 68 | xw.WriteStartElement("MaltegoTransformResponseMessage"); 69 | xw.WriteStartElement("Entities"); 70 | 71 | foreach (var e in Entities) 72 | { 73 | xw.WriteRaw(e.TransformToXML()); 74 | } 75 | 76 | xw.WriteEndElement(); 77 | xw.WriteEndElement(); 78 | xw.WriteEndElement(); 79 | xw.WriteEndDocument(); 80 | } 81 | return sw.ToString(); 82 | } 83 | } 84 | 85 | private void ParseTransformOutput(string xml) 86 | { 87 | var doc = XDocument.Parse(xml); 88 | 89 | var entities = doc.Descendants("Entity"); 90 | foreach (var entity in entities) 91 | { 92 | var entityType = entity.Attribute("Type").Value; 93 | var value = entity.Element("Value").Value; 94 | var weight = Convert.ToInt32(entity.Element("Weight").Value); 95 | 96 | var newEntity = new Entity(entityType, value, weight); 97 | 98 | var addFields = entity.Element("AdditionalFields"); 99 | foreach (var field in addFields.Elements()) 100 | { 101 | var attributes = field.Attributes().ToList(); 102 | var name = attributes.FirstOrDefault(x => x.Name == "Name")?.Value; 103 | var dname = attributes.FirstOrDefault(x => x.Name == "DisplayName")?.Value; 104 | var mr = (MatchingRule) Enum.Parse(typeof(MatchingRule), attributes.FirstOrDefault(x => x.Name == "MatchingRule")?.Value, true); 105 | 106 | 107 | newEntity.AdditionalFields.Add(new Entity.AdditionalField( 108 | name, 109 | dname, 110 | field.Value, 111 | mr 112 | )); 113 | } 114 | 115 | Entities.Add(newEntity); 116 | } 117 | } 118 | 119 | /// 120 | /// Prints a debug message to Maltego. 121 | /// 122 | /// Message to display as debug in Maltego. 123 | public void PrintDebug(string message) 124 | { 125 | Console.Error.WriteLine("D:{0}", message); 126 | } 127 | 128 | /// 129 | /// Sets the progress bar in Maltego. 130 | /// 131 | /// Number of percent to set the progress bar to. 132 | public void PrintProgress(int percentage) 133 | { 134 | if (percentage < 0 || percentage > 100) 135 | throw new ArgumentOutOfRangeException(nameof(percentage), "Percentage has to be in range 0-100."); 136 | 137 | Console.Error.WriteLine("% {0}", percentage); 138 | } 139 | 140 | 141 | /// 142 | /// Split the STDIN arguments and return them as a dictionary. 143 | /// The first argument is mandatory and represents the "EntityValue". 144 | /// The optional second argument is a string with additional fields of the 145 | /// format "field1=value1#field2=value2 ...". 146 | /// 147 | /// Command line arguments of an transformation application. 148 | /// Dictionary with all key/value pairs of the command line arguments. 149 | private Dictionary ParseSTDIN(string[] args) 150 | { 151 | var dictionary = new Dictionary(); 152 | 153 | if (args.Length == 0 || args.Length > 3) 154 | throw new ArgumentOutOfRangeException(nameof(args), 155 | "Wrong number of arguments. Only 1-3 arguments are allowed."); 156 | 157 | // Add the first optional argument if it exists 158 | _optionalParameter = args.Length == 3 ? args[0] : null; 159 | 160 | // Add the mandatory argument which is the entity value. 161 | var evPos = args.Length == 2 ? 0 : 1; 162 | dictionary.Add("EntityValue", args[evPos]); 163 | 164 | // If a second/third argument exists, it is of the form "field1=value1#field2=value2 ..." 165 | if (args.Length >= 2) 166 | { 167 | var pair = args[args.Length - 1].Split('#'); 168 | foreach (var p in pair) 169 | { 170 | var keyValue = p.Split('='); 171 | if (keyValue.Length != 2) 172 | throw new ArgumentException("Secondary argument cannot be split at \"=\". Format error."); 173 | dictionary.Add(keyValue[0], keyValue[1]); 174 | } 175 | } 176 | 177 | return dictionary; 178 | } 179 | } 180 | } -------------------------------------------------------------------------------- /src/TransNet/logo/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/secana/TransNet/a5242b8cbe47030ede7a4df240d7e7e3954fa289/src/TransNet/logo/logo.png -------------------------------------------------------------------------------- /test/TransNet_Test/EntityTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using TransNet; 3 | using Xunit; 4 | 5 | namespace TransNet_Test 6 | { 7 | public class EntityTest 8 | { 9 | [Fact] 10 | public void Entity_ArgumentEntityTypeIsNull_ThrowsArgumentNullException() 11 | { 12 | var exception = Assert.Throws(() => new Entity(null, "value", 0)); 13 | } 14 | 15 | [Fact] 16 | public void Entity_ArgumentValueIsNull_ThrowsArgumentNullException() 17 | { 18 | var exception = Assert.Throws(() => new Entity("entityType" , null, 0)); 19 | } 20 | 21 | [Fact] 22 | public void Entity_CorrectArguments_SetsAllProperties() 23 | { 24 | var entity = new Entity("entityType", "value", 10); 25 | 26 | Assert.Equal("entityType", entity.EntityType); 27 | Assert.Equal("value", entity.Value); 28 | Assert.Equal(10, entity.Weight); 29 | Assert.NotNull(entity.AdditionalFields); 30 | } 31 | 32 | [Fact] 33 | public void AddEdgeLabel_CorrectArguments_AddsEdgeToAdditionalFields() 34 | { 35 | var entity = new Entity("entityType", "value", 10); 36 | 37 | entity.AddEdgeLabel("label", new System.Collections.Generic.List> { new Tuple("key", "value") }); 38 | 39 | Assert.True(entity.HasEdgeLabel); 40 | Assert.Equal(3, entity.AdditionalFields.Count); 41 | Assert.Equal("link#maltego.link.label", entity.AdditionalFields[0].Name); 42 | Assert.Equal("loose", entity.AdditionalFields[0].MatchingRule); 43 | Assert.Equal("label", entity.AdditionalFields[0].Value); 44 | Assert.Equal("link#maltego.link.show-label", entity.AdditionalFields[1].Name); 45 | Assert.Equal("loose", entity.AdditionalFields[1].MatchingRule); 46 | Assert.Equal("1", entity.AdditionalFields[1].Value); 47 | Assert.Equal("link#0", entity.AdditionalFields[2].Name); 48 | Assert.Equal("loose", entity.AdditionalFields[2].MatchingRule); 49 | Assert.Equal("value", entity.AdditionalFields[2].Value); 50 | } 51 | 52 | [Fact] 53 | public void AddEdgeLabel_EdgeLabelAlreadySet_ThrowsInvalidOperationException() 54 | { 55 | var entity = new Entity("entityType", "value", 10); 56 | entity.AddEdgeLabel("label", new System.Collections.Generic.List> { new Tuple("key", "value") }); 57 | 58 | Assert.Throws(() => entity.AddEdgeLabel("label2", new System.Collections.Generic.List> { new Tuple("key", "value") })); 59 | } 60 | 61 | [Fact] 62 | public void ToXML_FullEntity_CreatesCorrectXMLOutput() 63 | { 64 | var expected = "value10valuelabel1value"; 65 | var entity = new Entity("entityType", "value", 10); 66 | entity.AdditionalFields.Add(new Entity.AdditionalField("name", "displayName", "value", MatchingRule.Strict)); 67 | entity.AddEdgeLabel("label", new System.Collections.Generic.List> { new Tuple("key", "value") }); 68 | 69 | var xml = entity.TransformToXML(); 70 | 71 | Assert.Equal(expected, xml); 72 | } 73 | 74 | [Fact] 75 | public void AdditionalField_NameIsNull_ThrowsArgumentNullException() 76 | { 77 | Assert.Throws(() => new Entity.AdditionalField(null, "displayName", "value")); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /test/TransNet_Test/ExtensionMethodsTest.cs: -------------------------------------------------------------------------------- 1 | using TransNet; 2 | using Xunit; 3 | 4 | namespace TransNet_Test 5 | { 6 | public class ExtensionMethodsTest 7 | { 8 | [Fact] 9 | public void AddEntity_ValidEntity_AddsEntityToTransform() 10 | { 11 | var transform = new Transformation(new[] {"EntityValue", "field1=value1#field2=value2"}); 12 | 13 | transform.AddEntity("EntityType", "EntityValue", 2); 14 | 15 | Assert.Equal("EntityType", transform.Entities[0].EntityType); 16 | Assert.Equal("EntityValue", transform.Entities[0].Value); 17 | Assert.Equal(2, transform.Entities[0].Weight); 18 | } 19 | 20 | [Fact] 21 | public void AddAdditionalField_ValidField_AddsFieldToEntity() 22 | { 23 | var entity = new Entity("EntityType", "EntityValue"); 24 | 25 | entity.AddAdditionalField("Name", "DisplayName", "Value", MatchingRule.Strict); 26 | 27 | Assert.Equal("Name", entity.AdditionalFields[0].Name); 28 | Assert.Equal("DisplayName", entity.AdditionalFields[0].DisplayName); 29 | Assert.Equal("Value", entity.AdditionalFields[0].Value); 30 | Assert.Equal("strict", entity.AdditionalFields[0].MatchingRule); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/TransNet_Test/TransNet_Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | runtime; build; native; contentfiles; analyzers 12 | all 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /test/TransNet_Test/TransformationTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using TransNet; 4 | using Xunit; 5 | 6 | namespace TransNet_Test 7 | { 8 | public class TransformationTest 9 | { 10 | [Fact] 11 | public void Transformation_CorrectCommandLineArgs_SetsAllProperties() 12 | { 13 | var transform = new Transformation(new []{"EntityValue", "field1=value1#field2=value2"}); 14 | 15 | Assert.Equal("EntityValue", transform.EntityValue); 16 | Assert.Equal(3, transform.InputArguments.Count); 17 | Assert.Equal("EntityValue", transform.InputArguments["EntityValue"]); 18 | Assert.Equal("value1", transform.InputArguments["field1"]); 19 | Assert.Equal("value2", transform.InputArguments["field2"]); 20 | } 21 | 22 | [Fact] 23 | public void Transformation_NullArguments_ThrowsArgumentOutOfRangeException() 24 | { 25 | Assert.Throws(() => new Transformation(new string []{})); 26 | } 27 | 28 | [Fact] 29 | public void Transformation_MoreThanTheeArguments_ThrowsArgumentOutOfRangeException() 30 | { 31 | Assert.Throws(() => new Transformation(new[] { "1", "2", "3", "4" })); 32 | } 33 | 34 | [Fact] 35 | public void Transformation_WrongArgumentFormat_ThrowsArgumentException() 36 | { 37 | Assert.Throws(() => new Transformation(new[] {"1", "2", "wrongFormant"})); 38 | } 39 | 40 | [Fact] 41 | public void TransformToXML_ValidInput_ReturnsXMLTransform() 42 | { 43 | var transform = new Transformation(new [] {"EntityValue", "field1=value1#field2=value2"}); 44 | transform.Entities.Add(GetExampleEntity()); 45 | 46 | var actual = transform.TransformToXML(); 47 | 48 | Assert.Equal(GetExampleXML(), actual); 49 | } 50 | 51 | [Fact] 52 | public void Transformation_ValidInputXML_ParsesXML() 53 | { 54 | var transform = new Transformation(GetExampleXML()); 55 | 56 | Assert.Equal("EntityType", transform.Entities[0].EntityType); 57 | Assert.Equal("Value", transform.Entities[0].Value); 58 | Assert.Equal(1, transform.Entities[0].Weight); 59 | Assert.Equal("AdditionalField", transform.Entities[0].AdditionalFields[0].Name); 60 | Assert.Equal("DisplayName", transform.Entities[0].AdditionalFields[0].DisplayName); 61 | Assert.Equal("Value", transform.Entities[0].AdditionalFields[0].Value); 62 | Assert.Equal("strict", transform.Entities[0].AdditionalFields[0].MatchingRule); 63 | } 64 | 65 | [Fact] 66 | public void PrintDebug_Message_LogsDebugMessage() 67 | { 68 | var transform = new Transformation(new[] { "EntityValue", "field1=value1#field2=value2" }); 69 | var sw = new StringWriter(); 70 | Console.SetError(sw); 71 | 72 | transform.PrintDebug("Debug message."); 73 | var actual = sw.ToString().TrimEnd(); 74 | 75 | Assert.Equal("D:Debug message.", actual); 76 | } 77 | 78 | [Fact] 79 | public void PrintProgess_Percentage_LogsPercentage() 80 | { 81 | var transform = new Transformation(new[] { "EntityValue", "field1=value1#field2=value2" }); 82 | var sw = new StringWriter(); 83 | Console.SetError(sw); 84 | 85 | transform.PrintProgress(50); 86 | var actual = sw.ToString().TrimEnd(); 87 | 88 | Assert.Equal("% 50", actual); 89 | } 90 | 91 | [Theory] 92 | [InlineData(-1)] 93 | [InlineData(101)] 94 | public void PrintProgress_InvalidPercentage_ThrowsArgumentOutOfRangeException(int percentage) 95 | { 96 | var transform = new Transformation(new[] { "EntityValue", "field1=value1#field2=value2" }); 97 | 98 | Assert.Throws(() => transform.PrintProgress(percentage)); 99 | } 100 | 101 | private Entity GetExampleEntity() 102 | { 103 | var entity = new Entity("EntityType", "Value", 1); 104 | entity.AdditionalFields.Add( 105 | new Entity.AdditionalField("AdditionalField", "DisplayName", "Value", MatchingRule.Strict)); 106 | 107 | return entity; 108 | } 109 | 110 | private string GetExampleXML() 111 | { 112 | return 113 | "Value1Value"; 114 | } 115 | } 116 | } 117 | --------------------------------------------------------------------------------