From ffdf8a240bd8359bb1037de66bcb7f7359fa1385 Mon Sep 17 00:00:00 2001 From: NickLech Date: Wed, 2 Oct 2024 15:38:54 +0200 Subject: [PATCH] Sostituzione con codice ZAP --- tool/.gitignore | 80 - tool/.idea/misc.xml | 11 - tool/.idea/vcs.xml | 6 - tool/CHANGELOG.md | 5 + tool/gradle.properties | 6 + tool/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43453 bytes tool/gradle/wrapper/gradle-wrapper.properties | 6 + tool/gradlew | 249 ++ tool/gradlew.bat | 92 + tool/migt.gradle.kts | 67 + tool/pom.xml | 140 - tool/readme.md | 5 - .../burp/IBurpCollaboratorClientContext.java | 97 - .../burp/IBurpCollaboratorInteraction.java | 41 - tool/src/main/java/burp/IBurpExtender.java | 31 - .../java/burp/IBurpExtenderCallbacks.java | 1173 -------- .../main/java/burp/IContextMenuFactory.java | 39 - .../java/burp/IContextMenuInvocation.java | 156 - tool/src/main/java/burp/ICookie.java | 61 - .../src/main/java/burp/IExtensionHelpers.java | 367 --- .../java/burp/IExtensionStateListener.java | 27 - tool/src/main/java/burp/IHttpHeader.java | 27 - tool/src/main/java/burp/IHttpListener.java | 38 - .../main/java/burp/IHttpRequestResponse.java | 102 - .../burp/IHttpRequestResponsePersisted.java | 25 - .../burp/IHttpRequestResponseWithMarkers.java | 44 - tool/src/main/java/burp/IHttpService.java | 39 - .../java/burp/IInterceptedProxyMessage.java | 116 - tool/src/main/java/burp/IIntruderAttack.java | 31 - .../java/burp/IIntruderPayloadGenerator.java | 50 - .../IIntruderPayloadGeneratorFactory.java | 40 - .../java/burp/IIntruderPayloadProcessor.java | 45 - tool/src/main/java/burp/IMenuItemHandler.java | 36 - tool/src/main/java/burp/IMessageEditor.java | 77 - .../java/burp/IMessageEditorController.java | 49 - .../src/main/java/burp/IMessageEditorTab.java | 103 - .../java/burp/IMessageEditorTabFactory.java | 39 - tool/src/main/java/burp/IParameter.java | 104 - tool/src/main/java/burp/IProxyListener.java | 37 - tool/src/main/java/burp/IRequestInfo.java | 95 - tool/src/main/java/burp/IResponseInfo.java | 73 - .../src/main/java/burp/IResponseKeywords.java | 58 - .../main/java/burp/IResponseVariations.java | 62 - tool/src/main/java/burp/IScanIssue.java | 123 - tool/src/main/java/burp/IScanQueueItem.java | 81 - tool/src/main/java/burp/IScannerCheck.java | 83 - .../java/burp/IScannerInsertionPoint.java | 174 -- .../burp/IScannerInsertionPointProvider.java | 38 - tool/src/main/java/burp/IScannerListener.java | 30 - .../main/java/burp/IScopeChangeListener.java | 25 - .../java/burp/ISessionHandlingAction.java | 51 - tool/src/main/java/burp/ITab.java | 38 - tool/src/main/java/burp/ITempFile.java | 33 - tool/src/main/java/burp/ITextEditor.java | 90 - tool/src/main/java/migt/API.java | 7 - tool/src/main/java/migt/BurpExtender.java | 191 -- tool/src/main/java/migt/ExecuteActives.java | 289 -- .../java/migt/ExecutePassiveListener.java | 58 - tool/src/main/java/migt/ExecutePassives.java | 211 -- tool/src/main/java/migt/ExecuteTrack.java | 463 --- tool/src/main/java/migt/Main.java | 1711 ----------- tool/src/main/java/migt/Marker.java | 29 - tool/src/main/java/migt/MessageOperation.java | 468 --- tool/src/main/java/migt/Operation_API.java | 23 - tool/src/main/java/migt/ParsingException.java | 15 - .../main/java/org/zaproxy/addon/migt/API.java | 23 + .../zaproxy/addon}/migt/At_Hash_check.java | 39 +- .../zaproxy/addon}/migt/At_Hash_update.java | 53 +- .../{ => org/zaproxy/addon}/migt/Check.java | 333 ++- .../zaproxy/addon}/migt/DecodeOperation.java | 257 +- .../addon}/migt/DecodeOperation_API.java | 33 +- .../zaproxy/addon}/migt/EditOperation.java | 417 +-- .../addon}/migt/ExecuteActiveListener.java | 40 +- .../zaproxy/addon/migt/ExecuteActives.java | 332 +++ .../addon/migt/ExecutePassiveListener.java | 73 + .../zaproxy/addon/migt/ExecutePassives.java | 228 ++ .../org/zaproxy/addon/migt/ExecuteTrack.java | 516 ++++ .../addon}/migt/ExecuteTrackListener.java | 66 +- .../java/org/zaproxy/addon/migt/GUIclass.java | 1871 ++++++++++++ .../zaproxy/addon}/migt/HTTPReqRes.java | 451 +-- .../{ => org/zaproxy/addon}/migt/JWT.java | 96 +- .../java/org/zaproxy/addon/migt/Marker.java | 50 + .../zaproxy/addon/migt/MessageOperation.java | 569 ++++ .../zaproxy/addon}/migt/MessageType.java | 40 +- .../{ => org/zaproxy/addon}/migt/Module.java | 77 +- .../zaproxy/addon}/migt/Operation.java | 180 +- .../org/zaproxy/addon/migt/Operation_API.java | 40 + .../zaproxy/addon/migt/ParsingException.java | 34 + .../org/zaproxy/addon/migt/ReqResPanel.java | 60 + .../{ => org/zaproxy/addon}/migt/Session.java | 78 +- .../zaproxy/addon}/migt/SessionOperation.java | 80 +- .../addon}/migt/SessionTrackAction.java | 44 +- .../{ => org/zaproxy/addon}/migt/Test.java | 149 +- .../zaproxy/addon}/migt/TestSuite.java | 69 +- .../{ => org/zaproxy/addon}/migt/Tools.java | 411 +-- .../{ => org/zaproxy/addon}/migt/Track.java | 97 +- .../{ => org/zaproxy/addon}/migt/Var.java | 42 +- .../{ => org/zaproxy/addon}/migt/XML.java | 158 +- .../org/zaproxy/addon/migt/ZAPPrincipale.java | 432 +++ .../application/BurpCertificateBuilder.java | 192 +- .../application/CertificateTabController.java | 500 ++-- .../application/SamlTabController.java | 806 ++++++ .../migt}/samlraider/gui/CertificateTab.java | 547 ++-- .../addon/migt/samlraider/gui/ImagePanel.java | 79 + .../addon/migt/samlraider/gui/SamlMain.java | 111 + .../migt/samlraider/gui/SamlPanelAction.java | 474 ++++ .../migt}/samlraider/gui/SamlPanelInfo.java | 68 +- .../samlraider/gui/SignatureHelpWindow.java | 70 +- .../migt/samlraider/gui/XSWHelpWindow.java | 75 + .../samlraider/helpers/CertificateHelper.java | 74 +- .../migt}/samlraider/helpers/FileHelper.java | 50 +- .../addon/migt/samlraider/helpers/Flags.java | 26 + .../migt/samlraider/helpers/HTTPHelpers.java | 68 + .../samlraider/helpers/X509KeySelector.java | 69 + .../migt/samlraider/helpers/XMLHelpers.java | 770 +++++ .../migt/samlraider/helpers/XSWHelpers.java | 316 +++ .../samlraider/helpers/diff_match_patch.java | 2474 ++++++++++++++++ .../migt/samlraider/helpers/xsw9Test.java | 49 + .../samlraider/model/BurpCertificate.java | 86 +- .../model/BurpCertificateExtension.java | 52 + .../model/BurpCertificateStore.java | 47 +- .../samlraider/model/ObjectIdentifier.java | 42 +- .../application/SamlTabController.java | 790 ------ .../main/java/samlraider/gui/ImagePanel.java | 72 - .../main/java/samlraider/gui/SamlMain.java | 119 - .../java/samlraider/gui/SamlPanelAction.java | 469 --- .../java/samlraider/gui/XSWHelpWindow.java | 72 - .../main/java/samlraider/helpers/Flags.java | 33 - .../java/samlraider/helpers/HTTPHelpers.java | 79 - .../samlraider/helpers/X509KeySelector.java | 84 - .../java/samlraider/helpers/XMLHelpers.java | 703 ----- .../java/samlraider/helpers/XSWHelpers.java | 297 -- .../samlraider/helpers/diff_match_patch.java | 2506 ----------------- .../java/samlraider/helpers/xsw9Test.java | 48 - .../model/BurpCertificateExtension.java | 56 - tool/src/test/java/At_Hash_Test.java | 59 - tool/src/test/java/Checks_Test.java | 440 --- tool/src/test/java/DecodeOperation_Test.java | 149 - tool/src/test/java/EditOperation_test.java | 205 -- tool/src/test/java/HTTPReqRes_Test.java | 363 --- tool/src/test/java/JWT_Test.java | 449 --- tool/src/test/java/MessageOeration_Test.java | 130 - tool/src/test/java/SessionOperation_Test.java | 29 - .../test/java/SessionTrackAction_Test.java | 74 - tool/src/test/java/Track_Test.java | 157 -- tool/src/test/java/Utils_Test.java | 193 -- tool/src/test/java/Var_Test.java | 44 - tool/src/test/java/main_Test.java | 42 - 148 files changed, 12900 insertions(+), 17425 deletions(-) delete mode 100644 tool/.gitignore delete mode 100644 tool/.idea/misc.xml delete mode 100644 tool/.idea/vcs.xml create mode 100644 tool/CHANGELOG.md create mode 100644 tool/gradle.properties create mode 100644 tool/gradle/wrapper/gradle-wrapper.jar create mode 100644 tool/gradle/wrapper/gradle-wrapper.properties create mode 100644 tool/gradlew create mode 100644 tool/gradlew.bat create mode 100644 tool/migt.gradle.kts delete mode 100644 tool/pom.xml delete mode 100644 tool/readme.md delete mode 100644 tool/src/main/java/burp/IBurpCollaboratorClientContext.java delete mode 100644 tool/src/main/java/burp/IBurpCollaboratorInteraction.java delete mode 100644 tool/src/main/java/burp/IBurpExtender.java delete mode 100644 tool/src/main/java/burp/IBurpExtenderCallbacks.java delete mode 100644 tool/src/main/java/burp/IContextMenuFactory.java delete mode 100644 tool/src/main/java/burp/IContextMenuInvocation.java delete mode 100644 tool/src/main/java/burp/ICookie.java delete mode 100644 tool/src/main/java/burp/IExtensionHelpers.java delete mode 100644 tool/src/main/java/burp/IExtensionStateListener.java delete mode 100644 tool/src/main/java/burp/IHttpHeader.java delete mode 100644 tool/src/main/java/burp/IHttpListener.java delete mode 100644 tool/src/main/java/burp/IHttpRequestResponse.java delete mode 100644 tool/src/main/java/burp/IHttpRequestResponsePersisted.java delete mode 100644 tool/src/main/java/burp/IHttpRequestResponseWithMarkers.java delete mode 100644 tool/src/main/java/burp/IHttpService.java delete mode 100644 tool/src/main/java/burp/IInterceptedProxyMessage.java delete mode 100644 tool/src/main/java/burp/IIntruderAttack.java delete mode 100644 tool/src/main/java/burp/IIntruderPayloadGenerator.java delete mode 100644 tool/src/main/java/burp/IIntruderPayloadGeneratorFactory.java delete mode 100644 tool/src/main/java/burp/IIntruderPayloadProcessor.java delete mode 100644 tool/src/main/java/burp/IMenuItemHandler.java delete mode 100644 tool/src/main/java/burp/IMessageEditor.java delete mode 100644 tool/src/main/java/burp/IMessageEditorController.java delete mode 100644 tool/src/main/java/burp/IMessageEditorTab.java delete mode 100644 tool/src/main/java/burp/IMessageEditorTabFactory.java delete mode 100644 tool/src/main/java/burp/IParameter.java delete mode 100644 tool/src/main/java/burp/IProxyListener.java delete mode 100644 tool/src/main/java/burp/IRequestInfo.java delete mode 100644 tool/src/main/java/burp/IResponseInfo.java delete mode 100644 tool/src/main/java/burp/IResponseKeywords.java delete mode 100644 tool/src/main/java/burp/IResponseVariations.java delete mode 100644 tool/src/main/java/burp/IScanIssue.java delete mode 100644 tool/src/main/java/burp/IScanQueueItem.java delete mode 100644 tool/src/main/java/burp/IScannerCheck.java delete mode 100644 tool/src/main/java/burp/IScannerInsertionPoint.java delete mode 100644 tool/src/main/java/burp/IScannerInsertionPointProvider.java delete mode 100644 tool/src/main/java/burp/IScannerListener.java delete mode 100644 tool/src/main/java/burp/IScopeChangeListener.java delete mode 100644 tool/src/main/java/burp/ISessionHandlingAction.java delete mode 100644 tool/src/main/java/burp/ITab.java delete mode 100644 tool/src/main/java/burp/ITempFile.java delete mode 100644 tool/src/main/java/burp/ITextEditor.java delete mode 100644 tool/src/main/java/migt/API.java delete mode 100644 tool/src/main/java/migt/BurpExtender.java delete mode 100644 tool/src/main/java/migt/ExecuteActives.java delete mode 100644 tool/src/main/java/migt/ExecutePassiveListener.java delete mode 100644 tool/src/main/java/migt/ExecutePassives.java delete mode 100644 tool/src/main/java/migt/ExecuteTrack.java delete mode 100644 tool/src/main/java/migt/Main.java delete mode 100644 tool/src/main/java/migt/Marker.java delete mode 100644 tool/src/main/java/migt/MessageOperation.java delete mode 100644 tool/src/main/java/migt/Operation_API.java delete mode 100644 tool/src/main/java/migt/ParsingException.java create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/API.java rename tool/src/main/java/{ => org/zaproxy/addon}/migt/At_Hash_check.java (76%) rename tool/src/main/java/{ => org/zaproxy/addon}/migt/At_Hash_update.java (73%) rename tool/src/main/java/{ => org/zaproxy/addon}/migt/Check.java (73%) rename tool/src/main/java/{ => org/zaproxy/addon}/migt/DecodeOperation.java (73%) rename tool/src/main/java/{ => org/zaproxy/addon}/migt/DecodeOperation_API.java (56%) rename tool/src/main/java/{ => org/zaproxy/addon}/migt/EditOperation.java (60%) rename tool/src/main/java/{ => org/zaproxy/addon}/migt/ExecuteActiveListener.java (53%) create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/ExecuteActives.java create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/ExecutePassiveListener.java create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/ExecutePassives.java create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/ExecuteTrack.java rename tool/src/main/java/{ => org/zaproxy/addon}/migt/ExecuteTrackListener.java (56%) create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/GUIclass.java rename tool/src/main/java/{ => org/zaproxy/addon}/migt/HTTPReqRes.java (67%) rename tool/src/main/java/{ => org/zaproxy/addon}/migt/JWT.java (77%) create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/Marker.java create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/MessageOperation.java rename tool/src/main/java/{ => org/zaproxy/addon}/migt/MessageType.java (66%) rename tool/src/main/java/{ => org/zaproxy/addon}/migt/Module.java (57%) rename tool/src/main/java/{ => org/zaproxy/addon}/migt/Operation.java (77%) create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/Operation_API.java create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/ParsingException.java create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/ReqResPanel.java rename tool/src/main/java/{ => org/zaproxy/addon}/migt/Session.java (54%) rename tool/src/main/java/{ => org/zaproxy/addon}/migt/SessionOperation.java (83%) rename tool/src/main/java/{ => org/zaproxy/addon}/migt/SessionTrackAction.java (80%) rename tool/src/main/java/{ => org/zaproxy/addon}/migt/Test.java (77%) rename tool/src/main/java/{ => org/zaproxy/addon}/migt/TestSuite.java (60%) rename tool/src/main/java/{ => org/zaproxy/addon}/migt/Tools.java (65%) rename tool/src/main/java/{ => org/zaproxy/addon}/migt/Track.java (69%) rename tool/src/main/java/{ => org/zaproxy/addon}/migt/Var.java (71%) rename tool/src/main/java/{ => org/zaproxy/addon}/migt/XML.java (71%) create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/ZAPPrincipale.java rename tool/src/main/java/{ => org/zaproxy/addon/migt}/samlraider/application/BurpCertificateBuilder.java (71%) rename tool/src/main/java/{ => org/zaproxy/addon/migt}/samlraider/application/CertificateTabController.java (70%) create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/samlraider/application/SamlTabController.java rename tool/src/main/java/{ => org/zaproxy/addon/migt}/samlraider/gui/CertificateTab.java (75%) create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/ImagePanel.java create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/SamlMain.java create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/SamlPanelAction.java rename tool/src/main/java/{ => org/zaproxy/addon/migt}/samlraider/gui/SamlPanelInfo.java (86%) rename tool/src/main/java/{ => org/zaproxy/addon/migt}/samlraider/gui/SignatureHelpWindow.java (56%) create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/XSWHelpWindow.java rename tool/src/main/java/{ => org/zaproxy/addon/migt}/samlraider/helpers/CertificateHelper.java (65%) rename tool/src/main/java/{ => org/zaproxy/addon/migt}/samlraider/helpers/FileHelper.java (52%) create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/Flags.java create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/HTTPHelpers.java create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/X509KeySelector.java create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/XMLHelpers.java create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/XSWHelpers.java create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/diff_match_patch.java create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/xsw9Test.java rename tool/src/main/java/{ => org/zaproxy/addon/migt}/samlraider/model/BurpCertificate.java (80%) create mode 100644 tool/src/main/java/org/zaproxy/addon/migt/samlraider/model/BurpCertificateExtension.java rename tool/src/main/java/{ => org/zaproxy/addon/migt}/samlraider/model/BurpCertificateStore.java (72%) rename tool/src/main/java/{ => org/zaproxy/addon/migt}/samlraider/model/ObjectIdentifier.java (86%) delete mode 100644 tool/src/main/java/samlraider/application/SamlTabController.java delete mode 100644 tool/src/main/java/samlraider/gui/ImagePanel.java delete mode 100644 tool/src/main/java/samlraider/gui/SamlMain.java delete mode 100644 tool/src/main/java/samlraider/gui/SamlPanelAction.java delete mode 100644 tool/src/main/java/samlraider/gui/XSWHelpWindow.java delete mode 100644 tool/src/main/java/samlraider/helpers/Flags.java delete mode 100644 tool/src/main/java/samlraider/helpers/HTTPHelpers.java delete mode 100644 tool/src/main/java/samlraider/helpers/X509KeySelector.java delete mode 100644 tool/src/main/java/samlraider/helpers/XMLHelpers.java delete mode 100644 tool/src/main/java/samlraider/helpers/XSWHelpers.java delete mode 100644 tool/src/main/java/samlraider/helpers/diff_match_patch.java delete mode 100644 tool/src/main/java/samlraider/helpers/xsw9Test.java delete mode 100644 tool/src/main/java/samlraider/model/BurpCertificateExtension.java delete mode 100644 tool/src/test/java/At_Hash_Test.java delete mode 100644 tool/src/test/java/Checks_Test.java delete mode 100644 tool/src/test/java/DecodeOperation_Test.java delete mode 100644 tool/src/test/java/EditOperation_test.java delete mode 100644 tool/src/test/java/HTTPReqRes_Test.java delete mode 100644 tool/src/test/java/JWT_Test.java delete mode 100644 tool/src/test/java/MessageOeration_Test.java delete mode 100644 tool/src/test/java/SessionOperation_Test.java delete mode 100644 tool/src/test/java/SessionTrackAction_Test.java delete mode 100644 tool/src/test/java/Track_Test.java delete mode 100644 tool/src/test/java/Utils_Test.java delete mode 100644 tool/src/test/java/Var_Test.java delete mode 100644 tool/src/test/java/main_Test.java diff --git a/tool/.gitignore b/tool/.gitignore deleted file mode 100644 index 4ac0fc7..0000000 --- a/tool/.gitignore +++ /dev/null @@ -1,80 +0,0 @@ -# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider -# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 - -# User-specific stuff -.idea/**/workspace.xml -.idea/**/tasks.xml -.idea/**/usage.statistics.xml -.idea/**/dictionaries -.idea/**/shelf - -# AWS User-specific -.idea/**/aws.xml - -# Generated files -.idea/**/contentModel.xml - -# Sensitive or high-churn files -.idea/**/dataSources/ -.idea/**/dataSources.ids -.idea/**/dataSources.local.xml -.idea/**/sqlDataSources.xml -.idea/**/dynamic.xml -.idea/**/uiDesigner.xml -.idea/**/dbnavigator.xml - -# Gradle -.idea/**/gradle.xml -.idea/**/libraries - -# Gradle and Maven with auto-import -# When using Gradle or Maven with auto-import, you should exclude module files, -# since they will be recreated, and may cause churn. Uncomment if using -# auto-import. -.idea/artifacts -.idea/compiler.xml -.idea/jarRepositories.xml -.idea/modules.xml -.idea/*.iml -.idea/modules -*.iml -*.ipr - -# CMake -cmake-build-*/ - -# Mongo Explorer plugin -.idea/**/mongoSettings.xml - -# File-based project format -*.iws - -# IntelliJ -out/ - -# Build files -target/ - -# mpeltonen/sbt-idea plugin -.idea_modules/ - -# JIRA plugin -atlassian-ide-plugin.xml - -# Cursive Clojure plugin -.idea/replstate.xml - -# SonarLint plugin -.idea/sonarlint/ - -# Crashlytics plugin (for Android Studio and IntelliJ) -com_crashlytics_export_strings.xml -crashlytics.properties -crashlytics-build.properties -fabric.properties - -# Editor-based Rest Client -.idea/httpRequests - -# Android studio 3.1+ serialized cache file -.idea/caches/build_file_checksums.ser \ No newline at end of file diff --git a/tool/.idea/misc.xml b/tool/.idea/misc.xml deleted file mode 100644 index b6a965c..0000000 --- a/tool/.idea/misc.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/tool/.idea/vcs.xml b/tool/.idea/vcs.xml deleted file mode 100644 index 6c0b863..0000000 --- a/tool/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/tool/CHANGELOG.md b/tool/CHANGELOG.md new file mode 100644 index 0000000..1b970ac --- /dev/null +++ b/tool/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## [0.0.1] - 2024-10-01 +### Added +- Addon creato nella cartela zap-extension \ No newline at end of file diff --git a/tool/gradle.properties b/tool/gradle.properties new file mode 100644 index 0000000..bf17fa0 --- /dev/null +++ b/tool/gradle.properties @@ -0,0 +1,6 @@ +version=1 +release=false + + +# Imposta la memoria heap e il metaspace della JVM +#org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m \ No newline at end of file diff --git a/tool/gradle/wrapper/gradle-wrapper.jar b/tool/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e6441136f3d4ba8a0da8d277868979cfbc8ad796 GIT binary patch literal 43453 zcma&N1CXTcmMvW9vTb(Rwr$&4wr$(C?dmSu>@vG-+vuvg^_??!{yS%8zW-#zn-LkA z5&1^$^{lnmUON?}LBF8_K|(?T0Ra(xUH{($5eN!MR#ZihR#HxkUPe+_R8Cn`RRs(P z_^*#_XlXmGv7!4;*Y%p4nw?{bNp@UZHv1?Um8r6)Fei3p@ClJn0ECfg1hkeuUU@Or zDaPa;U3fE=3L}DooL;8f;P0ipPt0Z~9P0)lbStMS)ag54=uL9ia-Lm3nh|@(Y?B`; zx_#arJIpXH!U{fbCbI^17}6Ri*H<>OLR%c|^mh8+)*h~K8Z!9)DPf zR2h?lbDZQ`p9P;&DQ4F0sur@TMa!Y}S8irn(%d-gi0*WxxCSk*A?3lGh=gcYN?FGl z7D=Js!i~0=u3rox^eO3i@$0=n{K1lPNU zwmfjRVmLOCRfe=seV&P*1Iq=^i`502keY8Uy-WNPwVNNtJFx?IwAyRPZo2Wo1+S(xF37LJZ~%i)kpFQ3Fw=mXfd@>%+)RpYQLnr}B~~zoof(JVm^^&f zxKV^+3D3$A1G;qh4gPVjhrC8e(VYUHv#dy^)(RoUFM?o%W-EHxufuWf(l*@-l+7vt z=l`qmR56K~F|v<^Pd*p~1_y^P0P^aPC##d8+HqX4IR1gu+7w#~TBFphJxF)T$2WEa zxa?H&6=Qe7d(#tha?_1uQys2KtHQ{)Qco)qwGjrdNL7thd^G5i8Os)CHqc>iOidS} z%nFEDdm=GXBw=yXe1W-ShHHFb?Cc70+$W~z_+}nAoHFYI1MV1wZegw*0y^tC*s%3h zhD3tN8b=Gv&rj}!SUM6|ajSPp*58KR7MPpI{oAJCtY~JECm)*m_x>AZEu>DFgUcby z1Qaw8lU4jZpQ_$;*7RME+gq1KySGG#Wql>aL~k9tLrSO()LWn*q&YxHEuzmwd1?aAtI zBJ>P=&$=l1efe1CDU;`Fd+_;&wI07?V0aAIgc(!{a z0Jg6Y=inXc3^n!U0Atk`iCFIQooHqcWhO(qrieUOW8X(x?(RD}iYDLMjSwffH2~tB z)oDgNBLB^AJBM1M^c5HdRx6fBfka`(LD-qrlh5jqH~);#nw|iyp)()xVYak3;Ybik z0j`(+69aK*B>)e_p%=wu8XC&9e{AO4c~O1U`5X9}?0mrd*m$_EUek{R?DNSh(=br# z#Q61gBzEpmy`$pA*6!87 zSDD+=@fTY7<4A?GLqpA?Pb2z$pbCc4B4zL{BeZ?F-8`s$?>*lXXtn*NC61>|*w7J* z$?!iB{6R-0=KFmyp1nnEmLsA-H0a6l+1uaH^g%c(p{iT&YFrbQ$&PRb8Up#X3@Zsk zD^^&LK~111%cqlP%!_gFNa^dTYT?rhkGl}5=fL{a`UViaXWI$k-UcHJwmaH1s=S$4 z%4)PdWJX;hh5UoK?6aWoyLxX&NhNRqKam7tcOkLh{%j3K^4Mgx1@i|Pi&}<^5>hs5 zm8?uOS>%)NzT(%PjVPGa?X%`N2TQCKbeH2l;cTnHiHppPSJ<7y-yEIiC!P*ikl&!B z%+?>VttCOQM@ShFguHVjxX^?mHX^hSaO_;pnyh^v9EumqSZTi+#f&_Vaija0Q-e*| z7ulQj6Fs*bbmsWp{`auM04gGwsYYdNNZcg|ph0OgD>7O}Asn7^Z=eI>`$2*v78;sj-}oMoEj&@)9+ycEOo92xSyY344^ z11Hb8^kdOvbf^GNAK++bYioknrpdN>+u8R?JxG=!2Kd9r=YWCOJYXYuM0cOq^FhEd zBg2puKy__7VT3-r*dG4c62Wgxi52EMCQ`bKgf*#*ou(D4-ZN$+mg&7$u!! z-^+Z%;-3IDwqZ|K=ah85OLwkO zKxNBh+4QHh)u9D?MFtpbl)us}9+V!D%w9jfAMYEb>%$A;u)rrI zuBudh;5PN}_6J_}l55P3l_)&RMlH{m!)ai-i$g)&*M`eN$XQMw{v^r@-125^RRCF0 z^2>|DxhQw(mtNEI2Kj(;KblC7x=JlK$@78`O~>V!`|1Lm-^JR$-5pUANAnb(5}B}JGjBsliK4& zk6y(;$e&h)lh2)L=bvZKbvh@>vLlreBdH8No2>$#%_Wp1U0N7Ank!6$dFSi#xzh|( zRi{Uw%-4W!{IXZ)fWx@XX6;&(m_F%c6~X8hx=BN1&q}*( zoaNjWabE{oUPb!Bt$eyd#$5j9rItB-h*5JiNi(v^e|XKAj*8(k<5-2$&ZBR5fF|JA z9&m4fbzNQnAU}r8ab>fFV%J0z5awe#UZ|bz?Ur)U9bCIKWEzi2%A+5CLqh?}K4JHi z4vtM;+uPsVz{Lfr;78W78gC;z*yTch~4YkLr&m-7%-xc ztw6Mh2d>_iO*$Rd8(-Cr1_V8EO1f*^@wRoSozS) zy1UoC@pruAaC8Z_7~_w4Q6n*&B0AjOmMWa;sIav&gu z|J5&|{=a@vR!~k-OjKEgPFCzcJ>#A1uL&7xTDn;{XBdeM}V=l3B8fE1--DHjSaxoSjNKEM9|U9#m2<3>n{Iuo`r3UZp;>GkT2YBNAh|b z^jTq-hJp(ebZh#Lk8hVBP%qXwv-@vbvoREX$TqRGTgEi$%_F9tZES@z8Bx}$#5eeG zk^UsLBH{bc2VBW)*EdS({yw=?qmevwi?BL6*=12k9zM5gJv1>y#ML4!)iiPzVaH9% zgSImetD@dam~e>{LvVh!phhzpW+iFvWpGT#CVE5TQ40n%F|p(sP5mXxna+Ev7PDwA zamaV4m*^~*xV+&p;W749xhb_X=$|LD;FHuB&JL5?*Y2-oIT(wYY2;73<^#46S~Gx| z^cez%V7x$81}UWqS13Gz80379Rj;6~WdiXWOSsdmzY39L;Hg3MH43o*y8ibNBBH`(av4|u;YPq%{R;IuYow<+GEsf@R?=@tT@!}?#>zIIn0CoyV!hq3mw zHj>OOjfJM3F{RG#6ujzo?y32m^tgSXf@v=J$ELdJ+=5j|=F-~hP$G&}tDZsZE?5rX ztGj`!S>)CFmdkccxM9eGIcGnS2AfK#gXwj%esuIBNJQP1WV~b~+D7PJTmWGTSDrR` zEAu4B8l>NPuhsk5a`rReSya2nfV1EK01+G!x8aBdTs3Io$u5!6n6KX%uv@DxAp3F@{4UYg4SWJtQ-W~0MDb|j-$lwVn znAm*Pl!?Ps&3wO=R115RWKb*JKoexo*)uhhHBncEDMSVa_PyA>k{Zm2(wMQ(5NM3# z)jkza|GoWEQo4^s*wE(gHz?Xsg4`}HUAcs42cM1-qq_=+=!Gk^y710j=66(cSWqUe zklbm8+zB_syQv5A2rj!Vbw8;|$@C!vfNmNV!yJIWDQ>{+2x zKjuFX`~~HKG~^6h5FntRpnnHt=D&rq0>IJ9#F0eM)Y-)GpRjiN7gkA8wvnG#K=q{q z9dBn8_~wm4J<3J_vl|9H{7q6u2A!cW{bp#r*-f{gOV^e=8S{nc1DxMHFwuM$;aVI^ zz6A*}m8N-&x8;aunp1w7_vtB*pa+OYBw=TMc6QK=mbA-|Cf* zvyh8D4LRJImooUaSb7t*fVfih<97Gf@VE0|z>NcBwBQze);Rh!k3K_sfunToZY;f2 z^HmC4KjHRVg+eKYj;PRN^|E0>Gj_zagfRbrki68I^#~6-HaHg3BUW%+clM1xQEdPYt_g<2K+z!$>*$9nQ>; zf9Bei{?zY^-e{q_*|W#2rJG`2fy@{%6u0i_VEWTq$*(ZN37|8lFFFt)nCG({r!q#9 z5VK_kkSJ3?zOH)OezMT{!YkCuSSn!K#-Rhl$uUM(bq*jY? zi1xbMVthJ`E>d>(f3)~fozjg^@eheMF6<)I`oeJYx4*+M&%c9VArn(OM-wp%M<-`x z7sLP1&3^%Nld9Dhm@$3f2}87!quhI@nwd@3~fZl_3LYW-B?Ia>ui`ELg z&Qfe!7m6ze=mZ`Ia9$z|ARSw|IdMpooY4YiPN8K z4B(ts3p%2i(Td=tgEHX z0UQ_>URBtG+-?0E;E7Ld^dyZ;jjw0}XZ(}-QzC6+NN=40oDb2^v!L1g9xRvE#@IBR zO!b-2N7wVfLV;mhEaXQ9XAU+>=XVA6f&T4Z-@AX!leJ8obP^P^wP0aICND?~w&NykJ#54x3_@r7IDMdRNy4Hh;h*!u(Ol(#0bJdwEo$5437-UBjQ+j=Ic>Q2z` zJNDf0yO6@mr6y1#n3)s(W|$iE_i8r@Gd@!DWDqZ7J&~gAm1#~maIGJ1sls^gxL9LLG_NhU!pTGty!TbhzQnu)I*S^54U6Yu%ZeCg`R>Q zhBv$n5j0v%O_j{QYWG!R9W?5_b&67KB$t}&e2LdMvd(PxN6Ir!H4>PNlerpBL>Zvyy!yw z-SOo8caEpDt(}|gKPBd$qND5#a5nju^O>V&;f890?yEOfkSG^HQVmEbM3Ugzu+UtH zC(INPDdraBN?P%kE;*Ae%Wto&sgw(crfZ#Qy(<4nk;S|hD3j{IQRI6Yq|f^basLY; z-HB&Je%Gg}Jt@={_C{L$!RM;$$|iD6vu#3w?v?*;&()uB|I-XqEKqZPS!reW9JkLewLb!70T7n`i!gNtb1%vN- zySZj{8-1>6E%H&=V}LM#xmt`J3XQoaD|@XygXjdZ1+P77-=;=eYpoEQ01B@L*a(uW zrZeZz?HJsw_4g0vhUgkg@VF8<-X$B8pOqCuWAl28uB|@r`19DTUQQsb^pfqB6QtiT z*`_UZ`fT}vtUY#%sq2{rchyfu*pCg;uec2$-$N_xgjZcoumE5vSI{+s@iLWoz^Mf; zuI8kDP{!XY6OP~q5}%1&L}CtfH^N<3o4L@J@zg1-mt{9L`s^z$Vgb|mr{@WiwAqKg zp#t-lhrU>F8o0s1q_9y`gQNf~Vb!F%70f}$>i7o4ho$`uciNf=xgJ>&!gSt0g;M>*x4-`U)ysFW&Vs^Vk6m%?iuWU+o&m(2Jm26Y(3%TL; zA7T)BP{WS!&xmxNw%J=$MPfn(9*^*TV;$JwRy8Zl*yUZi8jWYF>==j~&S|Xinsb%c z2?B+kpet*muEW7@AzjBA^wAJBY8i|#C{WtO_or&Nj2{=6JTTX05}|H>N2B|Wf!*3_ z7hW*j6p3TvpghEc6-wufFiY!%-GvOx*bZrhZu+7?iSrZL5q9}igiF^*R3%DE4aCHZ zqu>xS8LkW+Auv%z-<1Xs92u23R$nk@Pk}MU5!gT|c7vGlEA%G^2th&Q*zfg%-D^=f z&J_}jskj|Q;73NP4<4k*Y%pXPU2Thoqr+5uH1yEYM|VtBPW6lXaetokD0u z9qVek6Q&wk)tFbQ8(^HGf3Wp16gKmr>G;#G(HRBx?F`9AIRboK+;OfHaLJ(P>IP0w zyTbTkx_THEOs%Q&aPrxbZrJlio+hCC_HK<4%f3ZoSAyG7Dn`=X=&h@m*|UYO-4Hq0 z-Bq&+Ie!S##4A6OGoC~>ZW`Y5J)*ouaFl_e9GA*VSL!O_@xGiBw!AF}1{tB)z(w%c zS1Hmrb9OC8>0a_$BzeiN?rkPLc9%&;1CZW*4}CDDNr2gcl_3z+WC15&H1Zc2{o~i) z)LLW=WQ{?ricmC`G1GfJ0Yp4Dy~Ba;j6ZV4r{8xRs`13{dD!xXmr^Aga|C=iSmor% z8hi|pTXH)5Yf&v~exp3o+sY4B^^b*eYkkCYl*T{*=-0HniSA_1F53eCb{x~1k3*`W zr~};p1A`k{1DV9=UPnLDgz{aJH=-LQo<5%+Em!DNN252xwIf*wF_zS^!(XSm(9eoj z=*dXG&n0>)_)N5oc6v!>-bd(2ragD8O=M|wGW z!xJQS<)u70m&6OmrF0WSsr@I%T*c#Qo#Ha4d3COcX+9}hM5!7JIGF>7<~C(Ear^Sn zm^ZFkV6~Ula6+8S?oOROOA6$C&q&dp`>oR-2Ym3(HT@O7Sd5c~+kjrmM)YmgPH*tL zX+znN>`tv;5eOfX?h{AuX^LK~V#gPCu=)Tigtq9&?7Xh$qN|%A$?V*v=&-2F$zTUv z`C#WyIrChS5|Kgm_GeudCFf;)!WH7FI60j^0o#65o6`w*S7R@)88n$1nrgU(oU0M9 zx+EuMkC>(4j1;m6NoGqEkpJYJ?vc|B zOlwT3t&UgL!pX_P*6g36`ZXQ; z9~Cv}ANFnJGp(;ZhS(@FT;3e)0)Kp;h^x;$*xZn*k0U6-&FwI=uOGaODdrsp-!K$Ac32^c{+FhI-HkYd5v=`PGsg%6I`4d9Jy)uW0y%) zm&j^9WBAp*P8#kGJUhB!L?a%h$hJgQrx!6KCB_TRo%9{t0J7KW8!o1B!NC)VGLM5! zpZy5Jc{`r{1e(jd%jsG7k%I+m#CGS*BPA65ZVW~fLYw0dA-H_}O zrkGFL&P1PG9p2(%QiEWm6x;U-U&I#;Em$nx-_I^wtgw3xUPVVu zqSuKnx&dIT-XT+T10p;yjo1Y)z(x1fb8Dzfn8e yu?e%!_ptzGB|8GrCfu%p?(_ zQccdaaVK$5bz;*rnyK{_SQYM>;aES6Qs^lj9lEs6_J+%nIiuQC*fN;z8md>r_~Mfl zU%p5Dt_YT>gQqfr@`cR!$NWr~+`CZb%dn;WtzrAOI>P_JtsB76PYe*<%H(y>qx-`Kq!X_; z<{RpAqYhE=L1r*M)gNF3B8r(<%8mo*SR2hu zccLRZwGARt)Hlo1euqTyM>^!HK*!Q2P;4UYrysje@;(<|$&%vQekbn|0Ruu_Io(w4#%p6ld2Yp7tlA`Y$cciThP zKzNGIMPXX%&Ud0uQh!uQZz|FB`4KGD?3!ND?wQt6!n*f4EmCoJUh&b?;B{|lxs#F- z31~HQ`SF4x$&v00@(P+j1pAaj5!s`)b2RDBp*PB=2IB>oBF!*6vwr7Dp%zpAx*dPr zb@Zjq^XjN?O4QcZ*O+8>)|HlrR>oD*?WQl5ri3R#2?*W6iJ>>kH%KnnME&TT@ZzrHS$Q%LC?n|e>V+D+8D zYc4)QddFz7I8#}y#Wj6>4P%34dZH~OUDb?uP%-E zwjXM(?Sg~1!|wI(RVuxbu)-rH+O=igSho_pDCw(c6b=P zKk4ATlB?bj9+HHlh<_!&z0rx13K3ZrAR8W)!@Y}o`?a*JJsD+twZIv`W)@Y?Amu_u zz``@-e2X}27$i(2=9rvIu5uTUOVhzwu%mNazS|lZb&PT;XE2|B&W1>=B58#*!~D&) zfVmJGg8UdP*fx(>Cj^?yS^zH#o-$Q-*$SnK(ZVFkw+er=>N^7!)FtP3y~Xxnu^nzY zikgB>Nj0%;WOltWIob|}%lo?_C7<``a5hEkx&1ku$|)i>Rh6@3h*`slY=9U}(Ql_< zaNG*J8vb&@zpdhAvv`?{=zDedJ23TD&Zg__snRAH4eh~^oawdYi6A3w8<Ozh@Kw)#bdktM^GVb zrG08?0bG?|NG+w^&JvD*7LAbjED{_Zkc`3H!My>0u5Q}m!+6VokMLXxl`Mkd=g&Xx z-a>m*#G3SLlhbKB!)tnzfWOBV;u;ftU}S!NdD5+YtOjLg?X}dl>7m^gOpihrf1;PY zvll&>dIuUGs{Qnd- zwIR3oIrct8Va^Tm0t#(bJD7c$Z7DO9*7NnRZorrSm`b`cxz>OIC;jSE3DO8`hX955ui`s%||YQtt2 z5DNA&pG-V+4oI2s*x^>-$6J?p=I>C|9wZF8z;VjR??Icg?1w2v5Me+FgAeGGa8(3S z4vg*$>zC-WIVZtJ7}o9{D-7d>zCe|z#<9>CFve-OPAYsneTb^JH!Enaza#j}^mXy1 z+ULn^10+rWLF6j2>Ya@@Kq?26>AqK{A_| zQKb*~F1>sE*=d?A?W7N2j?L09_7n+HGi{VY;MoTGr_)G9)ot$p!-UY5zZ2Xtbm=t z@dpPSGwgH=QtIcEulQNI>S-#ifbnO5EWkI;$A|pxJd885oM+ zGZ0_0gDvG8q2xebj+fbCHYfAXuZStH2j~|d^sBAzo46(K8n59+T6rzBwK)^rfPT+B zyIFw)9YC-V^rhtK`!3jrhmW-sTmM+tPH+;nwjL#-SjQPUZ53L@A>y*rt(#M(qsiB2 zx6B)dI}6Wlsw%bJ8h|(lhkJVogQZA&n{?Vgs6gNSXzuZpEyu*xySy8ro07QZ7Vk1!3tJphN_5V7qOiyK8p z#@jcDD8nmtYi1^l8ml;AF<#IPK?!pqf9D4moYk>d99Im}Jtwj6c#+A;f)CQ*f-hZ< z=p_T86jog%!p)D&5g9taSwYi&eP z#JuEK%+NULWus;0w32-SYFku#i}d~+{Pkho&^{;RxzP&0!RCm3-9K6`>KZpnzS6?L z^H^V*s!8<>x8bomvD%rh>Zp3>Db%kyin;qtl+jAv8Oo~1g~mqGAC&Qi_wy|xEt2iz zWAJEfTV%cl2Cs<1L&DLRVVH05EDq`pH7Oh7sR`NNkL%wi}8n>IXcO40hp+J+sC!W?!krJf!GJNE8uj zg-y~Ns-<~D?yqbzVRB}G>0A^f0!^N7l=$m0OdZuqAOQqLc zX?AEGr1Ht+inZ-Qiwnl@Z0qukd__a!C*CKuGdy5#nD7VUBM^6OCpxCa2A(X;e0&V4 zM&WR8+wErQ7UIc6LY~Q9x%Sn*Tn>>P`^t&idaOEnOd(Ufw#>NoR^1QdhJ8s`h^|R_ zXX`c5*O~Xdvh%q;7L!_!ohf$NfEBmCde|#uVZvEo>OfEq%+Ns7&_f$OR9xsihRpBb z+cjk8LyDm@U{YN>+r46?nn{7Gh(;WhFw6GAxtcKD+YWV?uge>;+q#Xx4!GpRkVZYu zzsF}1)7$?%s9g9CH=Zs+B%M_)+~*j3L0&Q9u7!|+T`^O{xE6qvAP?XWv9_MrZKdo& z%IyU)$Q95AB4!#hT!_dA>4e@zjOBD*Y=XjtMm)V|+IXzjuM;(l+8aA5#Kaz_$rR6! zj>#&^DidYD$nUY(D$mH`9eb|dtV0b{S>H6FBfq>t5`;OxA4Nn{J(+XihF(stSche7$es&~N$epi&PDM_N`As;*9D^L==2Q7Z2zD+CiU(|+-kL*VG+&9!Yb3LgPy?A zm7Z&^qRG_JIxK7-FBzZI3Q<;{`DIxtc48k> zc|0dmX;Z=W$+)qE)~`yn6MdoJ4co;%!`ddy+FV538Y)j(vg}5*k(WK)KWZ3WaOG!8 z!syGn=s{H$odtpqFrT#JGM*utN7B((abXnpDM6w56nhw}OY}0TiTG1#f*VFZr+^-g zbP10`$LPq_;PvrA1XXlyx2uM^mrjTzX}w{yuLo-cOClE8MMk47T25G8M!9Z5ypOSV zAJUBGEg5L2fY)ZGJb^E34R2zJ?}Vf>{~gB!8=5Z) z9y$>5c)=;o0HeHHSuE4U)#vG&KF|I%-cF6f$~pdYJWk_dD}iOA>iA$O$+4%@>JU08 zS`ep)$XLPJ+n0_i@PkF#ri6T8?ZeAot$6JIYHm&P6EB=BiaNY|aA$W0I+nz*zkz_z zkEru!tj!QUffq%)8y0y`T&`fuus-1p>=^hnBiBqD^hXrPs`PY9tU3m0np~rISY09> z`P3s=-kt_cYcxWd{de@}TwSqg*xVhp;E9zCsnXo6z z?f&Sv^U7n4`xr=mXle94HzOdN!2kB~4=%)u&N!+2;z6UYKUDqi-s6AZ!haB;@&B`? z_TRX0%@suz^TRdCb?!vNJYPY8L_}&07uySH9%W^Tc&1pia6y1q#?*Drf}GjGbPjBS zbOPcUY#*$3sL2x4v_i*Y=N7E$mR}J%|GUI(>WEr+28+V z%v5{#e!UF*6~G&%;l*q*$V?&r$Pp^sE^i-0$+RH3ERUUdQ0>rAq2(2QAbG}$y{de( z>{qD~GGuOk559Y@%$?N^1ApVL_a704>8OD%8Y%8B;FCt%AoPu8*D1 zLB5X>b}Syz81pn;xnB}%0FnwazlWfUV)Z-~rZg6~b z6!9J$EcE&sEbzcy?CI~=boWA&eeIa%z(7SE^qgVLz??1Vbc1*aRvc%Mri)AJaAG!p z$X!_9Ds;Zz)f+;%s&dRcJt2==P{^j3bf0M=nJd&xwUGlUFn?H=2W(*2I2Gdu zv!gYCwM10aeus)`RIZSrCK=&oKaO_Ry~D1B5!y0R=%!i2*KfXGYX&gNv_u+n9wiR5 z*e$Zjju&ODRW3phN925%S(jL+bCHv6rZtc?!*`1TyYXT6%Ju=|X;6D@lq$8T zW{Y|e39ioPez(pBH%k)HzFITXHvnD6hw^lIoUMA;qAJ^CU?top1fo@s7xT13Fvn1H z6JWa-6+FJF#x>~+A;D~;VDs26>^oH0EI`IYT2iagy23?nyJ==i{g4%HrAf1-*v zK1)~@&(KkwR7TL}L(A@C_S0G;-GMDy=MJn2$FP5s<%wC)4jC5PXoxrQBFZ_k0P{{s@sz+gX`-!=T8rcB(=7vW}^K6oLWMmp(rwDh}b zwaGGd>yEy6fHv%jM$yJXo5oMAQ>c9j`**}F?MCry;T@47@r?&sKHgVe$MCqk#Z_3S z1GZI~nOEN*P~+UaFGnj{{Jo@16`(qVNtbU>O0Hf57-P>x8Jikp=`s8xWs^dAJ9lCQ z)GFm+=OV%AMVqVATtN@|vp61VVAHRn87}%PC^RAzJ%JngmZTasWBAWsoAqBU+8L8u z4A&Pe?fmTm0?mK-BL9t+{y7o(7jm+RpOhL9KnY#E&qu^}B6=K_dB}*VlSEiC9fn)+V=J;OnN)Ta5v66ic1rG+dGAJ1 z1%Zb_+!$=tQ~lxQrzv3x#CPb?CekEkA}0MYSgx$Jdd}q8+R=ma$|&1a#)TQ=l$1tQ z=tL9&_^vJ)Pk}EDO-va`UCT1m#Uty1{v^A3P~83_#v^ozH}6*9mIjIr;t3Uv%@VeW zGL6(CwCUp)Jq%G0bIG%?{_*Y#5IHf*5M@wPo6A{$Um++Co$wLC=J1aoG93&T7Ho}P z=mGEPP7GbvoG!uD$k(H3A$Z))+i{Hy?QHdk>3xSBXR0j!11O^mEe9RHmw!pvzv?Ua~2_l2Yh~_!s1qS`|0~0)YsbHSz8!mG)WiJE| z2f($6TQtt6L_f~ApQYQKSb=`053LgrQq7G@98#igV>y#i==-nEjQ!XNu9 z~;mE+gtj4IDDNQJ~JVk5Ux6&LCSFL!y=>79kE9=V}J7tD==Ga+IW zX)r7>VZ9dY=V&}DR))xUoV!u(Z|%3ciQi_2jl}3=$Agc(`RPb z8kEBpvY>1FGQ9W$n>Cq=DIpski};nE)`p3IUw1Oz0|wxll^)4dq3;CCY@RyJgFgc# zKouFh!`?Xuo{IMz^xi-h=StCis_M7yq$u) z?XHvw*HP0VgR+KR6wI)jEMX|ssqYvSf*_3W8zVTQzD?3>H!#>InzpSO)@SC8q*ii- z%%h}_#0{4JG;Jm`4zg};BPTGkYamx$Xo#O~lBirRY)q=5M45n{GCfV7h9qwyu1NxOMoP4)jjZMxmT|IQQh0U7C$EbnMN<3)Kk?fFHYq$d|ICu>KbY_hO zTZM+uKHe(cIZfEqyzyYSUBZa8;Fcut-GN!HSA9ius`ltNebF46ZX_BbZNU}}ZOm{M2&nANL9@0qvih15(|`S~z}m&h!u4x~(%MAO$jHRWNfuxWF#B)E&g3ghSQ9|> z(MFaLQj)NE0lowyjvg8z0#m6FIuKE9lDO~Glg}nSb7`~^&#(Lw{}GVOS>U)m8bF}x zVjbXljBm34Cs-yM6TVusr+3kYFjr28STT3g056y3cH5Tmge~ASxBj z%|yb>$eF;WgrcOZf569sDZOVwoo%8>XO>XQOX1OyN9I-SQgrm;U;+#3OI(zrWyow3 zk==|{lt2xrQ%FIXOTejR>;wv(Pb8u8}BUpx?yd(Abh6? zsoO3VYWkeLnF43&@*#MQ9-i-d0t*xN-UEyNKeyNMHw|A(k(_6QKO=nKMCxD(W(Yop zsRQ)QeL4X3Lxp^L%wzi2-WVSsf61dqliPUM7srDB?Wm6Lzn0&{*}|IsKQW;02(Y&| zaTKv|`U(pSzuvR6Rduu$wzK_W-Y-7>7s?G$)U}&uK;<>vU}^^ns@Z!p+9?St1s)dG zK%y6xkPyyS1$~&6v{kl?Md6gwM|>mt6Upm>oa8RLD^8T{0?HC!Z>;(Bob7el(DV6x zi`I)$&E&ngwFS@bi4^xFLAn`=fzTC;aimE^!cMI2n@Vo%Ae-ne`RF((&5y6xsjjAZ zVguVoQ?Z9uk$2ON;ersE%PU*xGO@T*;j1BO5#TuZKEf(mB7|g7pcEA=nYJ{s3vlbg zd4-DUlD{*6o%Gc^N!Nptgay>j6E5;3psI+C3Q!1ZIbeCubW%w4pq9)MSDyB{HLm|k zxv-{$$A*pS@csolri$Ge<4VZ}e~78JOL-EVyrbxKra^d{?|NnPp86!q>t<&IP07?Z z^>~IK^k#OEKgRH+LjllZXk7iA>2cfH6+(e&9ku5poo~6y{GC5>(bRK7hwjiurqAiZ zg*DmtgY}v83IjE&AbiWgMyFbaRUPZ{lYiz$U^&Zt2YjG<%m((&_JUbZcfJ22(>bi5 z!J?<7AySj0JZ&<-qXX;mcV!f~>G=sB0KnjWca4}vrtunD^1TrpfeS^4dvFr!65knK zZh`d;*VOkPs4*-9kL>$GP0`(M!j~B;#x?Ba~&s6CopvO86oM?-? zOw#dIRc;6A6T?B`Qp%^<U5 z19x(ywSH$_N+Io!6;e?`tWaM$`=Db!gzx|lQ${DG!zb1Zl&|{kX0y6xvO1o z220r<-oaS^^R2pEyY;=Qllqpmue|5yI~D|iI!IGt@iod{Opz@*ml^w2bNs)p`M(Io z|E;;m*Xpjd9l)4G#KaWfV(t8YUn@A;nK^#xgv=LtnArX|vWQVuw3}B${h+frU2>9^ z!l6)!Uo4`5k`<<;E(ido7M6lKTgWezNLq>U*=uz&s=cc$1%>VrAeOoUtA|T6gO4>UNqsdK=NF*8|~*sl&wI=x9-EGiq*aqV!(VVXA57 zw9*o6Ir8Lj1npUXvlevtn(_+^X5rzdR>#(}4YcB9O50q97%rW2me5_L=%ffYPUSRc z!vv?Kv>dH994Qi>U(a<0KF6NH5b16enCp+mw^Hb3Xs1^tThFpz!3QuN#}KBbww`(h z7GO)1olDqy6?T$()R7y%NYx*B0k_2IBiZ14&8|JPFxeMF{vSTxF-Vi3+ZOI=Thq2} zyQgjYY1_7^ZQHh{?P))4+qUiQJLi1&{yE>h?~jU%tjdV0h|FENbM3X(KnJdPKc?~k zh=^Ixv*+smUll!DTWH!jrV*wSh*(mx0o6}1@JExzF(#9FXgmTXVoU+>kDe68N)dkQ zH#_98Zv$}lQwjKL@yBd;U(UD0UCl322=pav<=6g>03{O_3oKTq;9bLFX1ia*lw;#K zOiYDcBJf)82->83N_Y(J7Kr_3lE)hAu;)Q(nUVydv+l+nQ$?|%MWTy`t>{havFSQloHwiIkGK9YZ79^9?AZo0ZyQlVR#}lF%dn5n%xYksXf8gnBm=wO7g_^! zauQ-bH1Dc@3ItZ-9D_*pH}p!IG7j8A_o94#~>$LR|TFq zZ-b00*nuw|-5C2lJDCw&8p5N~Z1J&TrcyErds&!l3$eSz%`(*izc;-?HAFD9AHb-| z>)id`QCrzRws^9(#&=pIx9OEf2rmlob8sK&xPCWS+nD~qzU|qG6KwA{zbikcfQrdH z+ zQg>O<`K4L8rN7`GJB0*3<3`z({lWe#K!4AZLsI{%z#ja^OpfjU{!{)x0ZH~RB0W5X zTwN^w=|nA!4PEU2=LR05x~}|B&ZP?#pNgDMwD*ajI6oJqv!L81gu=KpqH22avXf0w zX3HjbCI!n9>l046)5rr5&v5ja!xkKK42zmqHzPx$9Nn_MZk`gLeSLgC=LFf;H1O#B zn=8|^1iRrujHfbgA+8i<9jaXc;CQBAmQvMGQPhFec2H1knCK2x!T`e6soyrqCamX% zTQ4dX_E*8so)E*TB$*io{$c6X)~{aWfaqdTh=xEeGvOAN9H&-t5tEE-qso<+C!2>+ zskX51H-H}#X{A75wqFe-J{?o8Bx|>fTBtl&tcbdR|132Ztqu5X0i-pisB-z8n71%q%>EF}yy5?z=Ve`}hVh{Drv1YWL zW=%ug_&chF11gDv3D6B)Tz5g54H0mDHNjuKZ+)CKFk4Z|$RD zfRuKLW`1B>B?*RUfVd0+u8h3r-{@fZ{k)c!93t1b0+Q9vOaRnEn1*IL>5Z4E4dZ!7 ztp4GP-^1d>8~LMeb}bW!(aAnB1tM_*la=Xx)q(I0Y@__Zd$!KYb8T2VBRw%e$iSdZ zkwdMwd}eV9q*;YvrBFTv1>1+}{H!JK2M*C|TNe$ZSA>UHKk);wz$(F$rXVc|sI^lD zV^?_J!3cLM;GJuBMbftbaRUs$;F}HDEDtIeHQ)^EJJ1F9FKJTGH<(Jj`phE6OuvE) zqK^K`;3S{Y#1M@8yRQwH`?kHMq4tHX#rJ>5lY3DM#o@or4&^_xtBC(|JpGTfrbGkA z2Tu+AyT^pHannww!4^!$5?@5v`LYy~T`qs7SYt$JgrY(w%C+IWA;ZkwEF)u5sDvOK zGk;G>Mh&elvXDcV69J_h02l&O;!{$({fng9Rlc3ID#tmB^FIG^w{HLUpF+iB`|
NnX)EH+Nua)3Y(c z&{(nX_ht=QbJ%DzAya}!&uNu!4V0xI)QE$SY__m)SAKcN0P(&JcoK*Lxr@P zY&P=}&B3*UWNlc|&$Oh{BEqwK2+N2U$4WB7Fd|aIal`FGANUa9E-O)!gV`((ZGCc$ zBJA|FFrlg~9OBp#f7aHodCe{6= zay$6vN~zj1ddMZ9gQ4p32(7wD?(dE>KA2;SOzXRmPBiBc6g`eOsy+pVcHu=;Yd8@{ zSGgXf@%sKKQz~;!J;|2fC@emm#^_rnO0esEn^QxXgJYd`#FPWOUU5b;9eMAF zZhfiZb|gk8aJIw*YLp4!*(=3l8Cp{(%p?ho22*vN9+5NLV0TTazNY$B5L6UKUrd$n zjbX%#m7&F#U?QNOBXkiiWB*_tk+H?N3`vg;1F-I+83{M2!8<^nydGr5XX}tC!10&e z7D36bLaB56WrjL&HiiMVtpff|K%|*{t*ltt^5ood{FOG0<>k&1h95qPio)2`eL${YAGIx(b4VN*~nKn6E~SIQUuRH zQ+5zP6jfnP$S0iJ@~t!Ai3o`X7biohli;E zT#yXyl{bojG@-TGZzpdVDXhbmF%F9+-^YSIv|MT1l3j zrxOFq>gd2%U}?6}8mIj?M zc077Zc9fq(-)4+gXv?Az26IO6eV`RAJz8e3)SC7~>%rlzDwySVx*q$ygTR5kW2ds- z!HBgcq0KON9*8Ff$X0wOq$`T7ml(@TF)VeoF}x1OttjuVHn3~sHrMB++}f7f9H%@f z=|kP_?#+fve@{0MlbkC9tyvQ_R?lRdRJ@$qcB(8*jyMyeME5ns6ypVI1Xm*Zr{DuS zZ!1)rQfa89c~;l~VkCiHI|PCBd`S*2RLNQM8!g9L6?n`^evQNEwfO@&JJRme+uopQX0%Jo zgd5G&#&{nX{o?TQwQvF1<^Cg3?2co;_06=~Hcb6~4XWpNFL!WU{+CK;>gH%|BLOh7@!hsa(>pNDAmpcuVO-?;Bic17R}^|6@8DahH)G z!EmhsfunLL|3b=M0MeK2vqZ|OqUqS8npxwge$w-4pFVXFq$_EKrZY?BuP@Az@(k`L z`ViQBSk`y+YwRT;&W| z2e3UfkCo^uTA4}Qmmtqs+nk#gNr2W4 zTH%hhErhB)pkXR{B!q5P3-OM+M;qu~f>}IjtF%>w{~K-0*jPVLl?Chz&zIdxp}bjx zStp&Iufr58FTQ36AHU)0+CmvaOpKF;W@sMTFpJ`j;3d)J_$tNQI^c<^1o<49Z(~K> z;EZTBaVT%14(bFw2ob@?JLQ2@(1pCdg3S%E4*dJ}dA*v}_a4_P(a`cHnBFJxNobAv zf&Zl-Yt*lhn-wjZsq<9v-IsXxAxMZ58C@e0!rzhJ+D@9^3~?~yllY^s$?&oNwyH!#~6x4gUrfxplCvK#!f z$viuszW>MFEcFL?>ux*((!L$;R?xc*myjRIjgnQX79@UPD$6Dz0jutM@7h_pq z0Zr)#O<^y_K6jfY^X%A-ip>P%3saX{!v;fxT-*0C_j4=UMH+Xth(XVkVGiiKE#f)q z%Jp=JT)uy{&}Iq2E*xr4YsJ5>w^=#-mRZ4vPXpI6q~1aFwi+lQcimO45V-JXP;>(Q zo={U`{=_JF`EQj87Wf}{Qy35s8r1*9Mxg({CvOt}?Vh9d&(}iI-quvs-rm~P;eRA@ zG5?1HO}puruc@S{YNAF3vmUc2B4!k*yi))<5BQmvd3tr}cIs#9)*AX>t`=~{f#Uz0 z0&Nk!7sSZwJe}=)-R^$0{yeS!V`Dh7w{w5rZ9ir!Z7Cd7dwZcK;BT#V0bzTt>;@Cl z#|#A!-IL6CZ@eHH!CG>OO8!%G8&8t4)Ro@}USB*k>oEUo0LsljsJ-%5Mo^MJF2I8- z#v7a5VdJ-Cd%(a+y6QwTmi+?f8Nxtm{g-+WGL>t;s#epv7ug>inqimZCVm!uT5Pf6 ziEgQt7^%xJf#!aPWbuC_3Nxfb&CFbQy!(8ANpkWLI4oSnH?Q3f?0k1t$3d+lkQs{~(>06l&v|MpcFsyAv zin6N!-;pggosR*vV=DO(#+}4ps|5$`udE%Kdmp?G7B#y%H`R|i8skKOd9Xzx8xgR$>Zo2R2Ytktq^w#ul4uicxW#{ zFjG_RNlBroV_n;a7U(KIpcp*{M~e~@>Q#Av90Jc5v%0c>egEdY4v3%|K1XvB{O_8G zkTWLC>OZKf;XguMH2-Pw{BKbFzaY;4v2seZV0>^7Q~d4O=AwaPhP3h|!hw5aqOtT@ z!SNz}$of**Bl3TK209@F=Tn1+mgZa8yh(Png%Zd6Mt}^NSjy)etQrF zme*llAW=N_8R*O~d2!apJnF%(JcN??=`$qs3Y+~xs>L9x`0^NIn!8mMRFA_tg`etw z3k{9JAjnl@ygIiJcNHTy02GMAvBVqEss&t2<2mnw!; zU`J)0>lWiqVqo|ex7!+@0i>B~BSU1A_0w#Ee+2pJx0BFiZ7RDHEvE*ptc9md(B{&+ zKE>TM)+Pd>HEmdJao7U@S>nL(qq*A)#eLOuIfAS@j`_sK0UEY6OAJJ-kOrHG zjHx`g!9j*_jRcJ%>CE9K2MVf?BUZKFHY?EpV6ai7sET-tqk=nDFh-(65rhjtlKEY% z@G&cQ<5BKatfdA1FKuB=i>CCC5(|9TMW%K~GbA4}80I5%B}(gck#Wlq@$nO3%@QP_ z8nvPkJFa|znk>V92cA!K1rKtr)skHEJD;k8P|R8RkCq1Rh^&}Evwa4BUJz2f!2=MH zo4j8Y$YL2313}H~F7@J7mh>u%556Hw0VUOz-Un@ZASCL)y8}4XXS`t1AC*^>PLwIc zUQok5PFS=*#)Z!3JZN&eZ6ZDP^-c@StY*t20JhCnbMxXf=LK#;`4KHEqMZ-Ly9KsS zI2VUJGY&PmdbM+iT)zek)#Qc#_i4uH43 z@T5SZBrhNCiK~~esjsO9!qBpaWK<`>!-`b71Y5ReXQ4AJU~T2Njri1CEp5oKw;Lnm)-Y@Z3sEY}XIgSy%xo=uek(kAAH5MsV$V3uTUsoTzxp_rF=tx zV07vlJNKtJhCu`b}*#m&5LV4TAE&%KtHViDAdv#c^x`J7bg z&N;#I2GkF@SIGht6p-V}`!F_~lCXjl1BdTLIjD2hH$J^YFN`7f{Q?OHPFEM$65^!u zNwkelo*5+$ZT|oQ%o%;rBX$+?xhvjb)SHgNHE_yP%wYkkvXHS{Bf$OiKJ5d1gI0j< zF6N}Aq=(WDo(J{e-uOecxPD>XZ@|u-tgTR<972`q8;&ZD!cep^@B5CaqFz|oU!iFj zU0;6fQX&~15E53EW&w1s9gQQ~Zk16X%6 zjG`j0yq}4deX2?Tr(03kg>C(!7a|b9qFI?jcE^Y>-VhudI@&LI6Qa}WQ>4H_!UVyF z((cm&!3gmq@;BD#5P~0;_2qgZhtJS|>WdtjY=q zLnHH~Fm!cxw|Z?Vw8*~?I$g#9j&uvgm7vPr#&iZgPP~v~BI4jOv;*OQ?jYJtzO<^y z7-#C={r7CO810!^s(MT!@@Vz_SVU)7VBi(e1%1rvS!?PTa}Uv`J!EP3s6Y!xUgM^8 z4f!fq<3Wer_#;u!5ECZ|^c1{|q_lh3m^9|nsMR1#Qm|?4Yp5~|er2?W^7~cl;_r4WSme_o68J9p03~Hc%X#VcX!xAu%1`R!dfGJCp zV*&m47>s^%Ib0~-2f$6oSgn3jg8m%UA;ArcdcRyM5;}|r;)?a^D*lel5C`V5G=c~k zy*w_&BfySOxE!(~PI$*dwG><+-%KT5p?whOUMA*k<9*gi#T{h3DAxzAPxN&Xws8o9Cp*`PA5>d9*Z-ynV# z9yY*1WR^D8|C%I@vo+d8r^pjJ$>eo|j>XiLWvTWLl(^;JHCsoPgem6PvegHb-OTf| zvTgsHSa;BkbG=(NgPO|CZu9gUCGr$8*EoH2_Z#^BnxF0yM~t`|9ws_xZ8X8iZYqh! zAh;HXJ)3P&)Q0(&F>!LN0g#bdbis-cQxyGn9Qgh`q+~49Fqd2epikEUw9caM%V6WgP)532RMRW}8gNS%V%Hx7apSz}tn@bQy!<=lbhmAH=FsMD?leawbnP5BWM0 z5{)@EEIYMu5;u)!+HQWhQ;D3_Cm_NADNeb-f56}<{41aYq8p4=93d=-=q0Yx#knGYfXVt z+kMxlus}t2T5FEyCN~!}90O_X@@PQpuy;kuGz@bWft%diBTx?d)_xWd_-(!LmVrh**oKg!1CNF&LX4{*j|) zIvjCR0I2UUuuEXh<9}oT_zT#jOrJAHNLFT~Ilh9hGJPI1<5`C-WA{tUYlyMeoy!+U zhA#=p!u1R7DNg9u4|QfED-2TuKI}>p#2P9--z;Bbf4Op*;Q9LCbO&aL2i<0O$ByoI z!9;Ght733FC>Pz>$_mw(F`zU?`m@>gE`9_p*=7o=7av`-&ifU(^)UU`Kg3Kw`h9-1 z6`e6+im=|m2v`pN(2dE%%n8YyQz;#3Q-|x`91z?gj68cMrHl}C25|6(_dIGk*8cA3 zRHB|Nwv{@sP4W+YZM)VKI>RlB`n=Oj~Rzx~M+Khz$N$45rLn6k1nvvD^&HtsMA4`s=MmuOJID@$s8Ph4E zAmSV^+s-z8cfv~Yd(40Sh4JG#F~aB>WFoX7ykaOr3JaJ&Lb49=B8Vk-SQT9%7TYhv z?-Pprt{|=Y5ZQ1?od|A<_IJU93|l4oAfBm?3-wk{O<8ea+`}u%(kub(LFo2zFtd?4 zwpN|2mBNywv+d^y_8#<$r>*5+$wRTCygFLcrwT(qc^n&@9r+}Kd_u@Ithz(6Qb4}A zWo_HdBj#V$VE#l6pD0a=NfB0l^6W^g`vm^sta>Tly?$E&{F?TTX~DsKF~poFfmN%2 z4x`Dc{u{Lkqz&y!33;X}weD}&;7p>xiI&ZUb1H9iD25a(gI|`|;G^NwJPv=1S5e)j z;U;`?n}jnY6rA{V^ zxTd{bK)Gi^odL3l989DQlN+Zs39Xe&otGeY(b5>rlIqfc7Ap4}EC?j<{M=hlH{1+d zw|c}}yx88_xQr`{98Z!d^FNH77=u(p-L{W6RvIn40f-BldeF-YD>p6#)(Qzf)lfZj z?3wAMtPPp>vMehkT`3gToPd%|D8~4`5WK{`#+}{L{jRUMt zrFz+O$C7y8$M&E4@+p+oV5c%uYzbqd2Y%SSgYy#xh4G3hQv>V*BnuKQhBa#=oZB~w{azUB+q%bRe_R^ z>fHBilnRTUfaJ201czL8^~Ix#+qOHSO)A|xWLqOxB$dT2W~)e-r9;bm=;p;RjYahB z*1hegN(VKK+ztr~h1}YP@6cfj{e#|sS`;3tJhIJK=tVJ-*h-5y9n*&cYCSdg#EHE# zSIx=r#qOaLJoVVf6v;(okg6?*L_55atl^W(gm^yjR?$GplNP>BZsBYEf_>wM0Lc;T zhf&gpzOWNxS>m+mN92N0{;4uw`P+9^*|-1~$uXpggj4- z^SFc4`uzj2OwdEVT@}Q`(^EcQ_5(ZtXTql*yGzdS&vrS_w>~~ra|Nb5abwf}Y!uq6R5f&6g2ge~2p(%c< z@O)cz%%rr4*cRJ5f`n@lvHNk@lE1a*96Kw6lJ~B-XfJW%?&-y?;E&?1AacU@`N`!O z6}V>8^%RZ7SQnZ-z$(jsX`amu*5Fj8g!3RTRwK^`2_QHe;_2y_n|6gSaGyPmI#kA0sYV<_qOZc#-2BO%hX)f$s-Z3xlI!ub z^;3ru11DA`4heAu%}HIXo&ctujzE2!6DIGE{?Zs>2}J+p&C$rc7gJC35gxhflorvsb%sGOxpuWhF)dL_&7&Z99=5M0b~Qa;Mo!j&Ti_kXW!86N%n= zSC@6Lw>UQ__F&+&Rzv?gscwAz8IP!n63>SP)^62(HK98nGjLY2*e^OwOq`3O|C92? z;TVhZ2SK%9AGW4ZavTB9?)mUbOoF`V7S=XM;#3EUpR+^oHtdV!GK^nXzCu>tpR|89 zdD{fnvCaN^^LL%amZ^}-E+214g&^56rpdc@yv0b<3}Ys?)f|fXN4oHf$six)-@<;W&&_kj z-B}M5U*1sb4)77aR=@%I?|Wkn-QJVuA96an25;~!gq(g1@O-5VGo7y&E_srxL6ZfS z*R%$gR}dyONgju*D&?geiSj7SZ@ftyA|}(*Y4KbvU!YLsi1EDQQCnb+-cM=K1io78o!v*);o<XwjaQH%)uIP&Zm?)Nfbfn;jIr z)d#!$gOe3QHp}2NBak@yYv3m(CPKkwI|{;d=gi552u?xj9ObCU^DJFQp4t4e1tPzM zvsRIGZ6VF+{6PvqsplMZWhz10YwS={?`~O0Ec$`-!klNUYtzWA^f9m7tkEzCy<_nS z=&<(awFeZvt51>@o_~>PLs05CY)$;}Oo$VDO)?l-{CS1Co=nxjqben*O1BR>#9`0^ zkwk^k-wcLCLGh|XLjdWv0_Hg54B&OzCE^3NCP}~OajK-LuRW53CkV~Su0U>zN%yQP zH8UH#W5P3-!ToO-2k&)}nFe`t+mdqCxxAHgcifup^gKpMObbox9LFK;LP3}0dP-UW z?Zo*^nrQ6*$FtZ(>kLCc2LY*|{!dUn$^RW~m9leoF|@Jy|M5p-G~j%+P0_#orRKf8 zvuu5<*XO!B?1E}-*SY~MOa$6c%2cM+xa8}_8x*aVn~57v&W(0mqN1W`5a7*VN{SUH zXz98DDyCnX2EPl-`Lesf`=AQT%YSDb`$%;(jUTrNen$NPJrlpPDP}prI>Ml!r6bCT;mjsg@X^#&<}CGf0JtR{Ecwd&)2zuhr#nqdgHj+g2n}GK9CHuwO zk>oZxy{vcOL)$8-}L^iVfJHAGfwN$prHjYV0ju}8%jWquw>}_W6j~m<}Jf!G?~r5&Rx)!9JNX!ts#SGe2HzobV5); zpj@&`cNcO&q+%*<%D7za|?m5qlmFK$=MJ_iv{aRs+BGVrs)98BlN^nMr{V_fcl_;jkzRju+c-y?gqBC_@J0dFLq-D9@VN&-`R9U;nv$Hg?>$oe4N&Ht$V_(JR3TG^! zzJsbQbi zFE6-{#9{G{+Z}ww!ycl*7rRdmU#_&|DqPfX3CR1I{Kk;bHwF6jh0opI`UV2W{*|nn zf_Y@%wW6APb&9RrbEN=PQRBEpM(N1w`81s=(xQj6 z-eO0k9=Al|>Ej|Mw&G`%q8e$2xVz1v4DXAi8G};R$y)ww638Y=9y$ZYFDM$}vzusg zUf+~BPX>(SjA|tgaFZr_e0{)+z9i6G#lgt=F_n$d=beAt0Sa0a7>z-?vcjl3e+W}+ z1&9=|vC=$co}-Zh*%3588G?v&U7%N1Qf-wNWJ)(v`iO5KHSkC5&g7CrKu8V}uQGcfcz zmBz#Lbqwqy#Z~UzHgOQ;Q-rPxrRNvl(&u6ts4~0=KkeS;zqURz%!-ERppmd%0v>iRlEf+H$yl{_8TMJzo0 z>n)`On|7=WQdsqhXI?#V{>+~}qt-cQbokEbgwV3QvSP7&hK4R{Z{aGHVS3;+h{|Hz z6$Js}_AJr383c_+6sNR|$qu6dqHXQTc6?(XWPCVZv=)D#6_;D_8P-=zOGEN5&?~8S zl5jQ?NL$c%O)*bOohdNwGIKM#jSAC?BVY={@A#c9GmX0=T(0G}xs`-%f3r=m6-cpK z!%waekyAvm9C3%>sixdZj+I(wQlbB4wv9xKI*T13DYG^T%}zZYJ|0$Oj^YtY+d$V$ zAVudSc-)FMl|54n=N{BnZTM|!>=bhaja?o7s+v1*U$!v!qQ%`T-6fBvmdPbVmro&d zk07TOp*KuxRUSTLRrBj{mjsnF8`d}rMViY8j`jo~Hp$fkv9F_g(jUo#Arp;Xw0M$~ zRIN!B22~$kx;QYmOkos@%|5k)!QypDMVe}1M9tZfkpXKGOxvKXB!=lo`p?|R1l=tA zp(1}c6T3Fwj_CPJwVsYtgeRKg?9?}%oRq0F+r+kdB=bFUdVDRPa;E~~>2$w}>O>v=?|e>#(-Lyx?nbg=ckJ#5U6;RT zNvHhXk$P}m9wSvFyU3}=7!y?Y z=fg$PbV8d7g25&-jOcs{%}wTDKm>!Vk);&rr;O1nvO0VrU&Q?TtYVU=ir`te8SLlS zKSNmV=+vF|ATGg`4$N1uS|n??f}C_4Sz!f|4Ly8#yTW-FBfvS48Tef|-46C(wEO_%pPhUC5$-~Y?!0vFZ^Gu`x=m7X99_?C-`|h zfmMM&Y@zdfitA@KPw4Mc(YHcY1)3*1xvW9V-r4n-9ZuBpFcf{yz+SR{ zo$ZSU_|fgwF~aakGr(9Be`~A|3)B=9`$M-TWKipq-NqRDRQc}ABo*s_5kV%doIX7LRLRau_gd@Rd_aLFXGSU+U?uAqh z8qusWWcvgQ&wu{|sRXmv?sl=xc<$6AR$+cl& zFNh5q1~kffG{3lDUdvEZu5c(aAG~+64FxdlfwY^*;JSS|m~CJusvi-!$XR`6@XtY2 znDHSz7}_Bx7zGq-^5{stTRy|I@N=>*y$zz>m^}^{d&~h;0kYiq8<^Wq7Dz0w31ShO^~LUfW6rfitR0(=3;Uue`Y%y@ex#eKPOW zO~V?)M#AeHB2kovn1v=n^D?2{2jhIQd9t|_Q+c|ZFaWt+r&#yrOu-!4pXAJuxM+Cx z*H&>eZ0v8Y`t}8{TV6smOj=__gFC=eah)mZt9gwz>>W$!>b3O;Rm^Ig*POZP8Rl0f zT~o=Nu1J|lO>}xX&#P58%Yl z83`HRs5#32Qm9mdCrMlV|NKNC+Z~ z9OB8xk5HJ>gBLi+m@(pvpw)1(OaVJKs*$Ou#@Knd#bk+V@y;YXT?)4eP9E5{J%KGtYinNYJUH9PU3A}66c>Xn zZ{Bn0<;8$WCOAL$^NqTjwM?5d=RHgw3!72WRo0c;+houoUA@HWLZM;^U$&sycWrFd zE7ekt9;kb0`lps{>R(}YnXlyGY}5pPd9zBpgXeJTY_jwaJGSJQC#-KJqmh-;ad&F- z-Y)E>!&`Rz!HtCz>%yOJ|v(u7P*I$jqEY3}(Z-orn4 zlI?CYKNl`6I){#2P1h)y(6?i;^z`N3bxTV%wNvQW+eu|x=kbj~s8rhCR*0H=iGkSj zk23lr9kr|p7#qKL=UjgO`@UnvzU)`&fI>1Qs7ubq{@+lK{hH* zvl6eSb9%yngRn^T<;jG1SVa)eA>T^XX=yUS@NCKpk?ovCW1D@!=@kn;l_BrG;hOTC z6K&H{<8K#dI(A+zw-MWxS+~{g$tI7|SfP$EYKxA}LlVO^sT#Oby^grkdZ^^lA}uEF zBSj$weBJG{+Bh@Yffzsw=HyChS(dtLE3i*}Zj@~!_T-Ay7z=B)+*~3|?w`Zd)Co2t zC&4DyB!o&YgSw+fJn6`sn$e)29`kUwAc+1MND7YjV%lO;H2}fNy>hD#=gT ze+-aFNpyKIoXY~Vq-}OWPBe?Rfu^{ps8>Xy%42r@RV#*QV~P83jdlFNgkPN=T|Kt7 zV*M`Rh*30&AWlb$;ae130e@}Tqi3zx2^JQHpM>j$6x`#{mu%tZlwx9Gj@Hc92IuY* zarmT|*d0E~vt6<+r?W^UW0&#U&)8B6+1+;k^2|FWBRP9?C4Rk)HAh&=AS8FS|NQaZ z2j!iZ)nbEyg4ZTp-zHwVlfLC~tXIrv(xrP8PAtR{*c;T24ycA-;auWsya-!kF~CWZ zw_uZ|%urXgUbc@x=L=_g@QJ@m#5beS@6W195Hn7>_}z@Xt{DIEA`A&V82bc^#!q8$ zFh?z_Vn|ozJ;NPd^5uu(9tspo8t%&-U9Ckay-s@DnM*R5rtu|4)~e)`z0P-sy?)kc zs_k&J@0&0!q4~%cKL)2l;N*T&0;mqX5T{Qy60%JtKTQZ-xb%KOcgqwJmb%MOOKk7N zgq})R_6**{8A|6H?fO+2`#QU)p$Ei2&nbj6TpLSIT^D$|`TcSeh+)}VMb}LmvZ{O| ze*1IdCt3+yhdYVxcM)Q_V0bIXLgr6~%JS<<&dxIgfL=Vnx4YHuU@I34JXA|+$_S3~ zy~X#gO_X!cSs^XM{yzDGNM>?v(+sF#<0;AH^YrE8smx<36bUsHbN#y57K8WEu(`qHvQ6cAZPo=J5C(lSmUCZ57Rj6cx!e^rfaI5%w}unz}4 zoX=nt)FVNV%QDJH`o!u9olLD4O5fl)xp+#RloZlaA92o3x4->?rB4`gS$;WO{R;Z3>cG3IgFX2EA?PK^M}@%1%A;?f6}s&CV$cIyEr#q5;yHdNZ9h{| z-=dX+a5elJoDo?Eq&Og!nN6A)5yYpnGEp}?=!C-V)(*~z-+?kY1Q7qs#Rsy%hu_60rdbB+QQNr?S1 z?;xtjUv|*E3}HmuNyB9aFL5H~3Ho0UsmuMZELp1a#CA1g`P{-mT?BchuLEtK}!QZ=3AWakRu~?f9V~3F;TV`5%9Pcs_$gq&CcU}r8gOO zC2&SWPsSG{&o-LIGTBqp6SLQZPvYKp$$7L4WRRZ0BR$Kf0I0SCFkqveCp@f)o8W)! z$%7D1R`&j7W9Q9CGus_)b%+B#J2G;l*FLz#s$hw{BHS~WNLODV#(!u_2Pe&tMsq={ zdm7>_WecWF#D=?eMjLj=-_z`aHMZ=3_-&E8;ibPmM}61i6J3is*=dKf%HC>=xbj4$ zS|Q-hWQ8T5mWde6h@;mS+?k=89?1FU<%qH9B(l&O>k|u_aD|DY*@~(`_pb|B#rJ&g zR0(~(68fpUPz6TdS@4JT5MOPrqDh5_H(eX1$P2SQrkvN8sTxwV>l0)Qq z0pzTuvtEAKRDkKGhhv^jk%|HQ1DdF%5oKq5BS>szk-CIke{%js?~%@$uaN3^Uz6Wf z_iyx{bZ(;9y4X&>LPV=L=d+A}7I4GkK0c1Xts{rrW1Q7apHf-))`BgC^0^F(>At1* za@e7{lq%yAkn*NH8Q1{@{lKhRg*^TfGvv!Sn*ed*x@6>M%aaqySxR|oNadYt1mpUZ z6H(rupHYf&Z z29$5g#|0MX#aR6TZ$@eGxxABRKakDYtD%5BmKp;HbG_ZbT+=81E&=XRk6m_3t9PvD zr5Cqy(v?gHcYvYvXkNH@S#Po~q(_7MOuCAB8G$a9BC##gw^5mW16cML=T=ERL7wsk zzNEayTG?mtB=x*wc@ifBCJ|irFVMOvH)AFRW8WE~U()QT=HBCe@s$dA9O!@`zAAT) zaOZ7l6vyR+Nk_OOF!ZlZmjoImKh)dxFbbR~z(cMhfeX1l7S_`;h|v3gI}n9$sSQ>+3@AFAy9=B_y$)q;Wdl|C-X|VV3w8 z2S#>|5dGA8^9%Bu&fhmVRrTX>Z7{~3V&0UpJNEl0=N32euvDGCJ>#6dUSi&PxFW*s zS`}TB>?}H(T2lxBJ!V#2taV;q%zd6fOr=SGHpoSG*4PDaiG0pdb5`jelVipkEk%FV zThLc@Hc_AL1#D&T4D=w@UezYNJ%0=f3iVRuVL5H?eeZM}4W*bomebEU@e2d`M<~uW zf#Bugwf`VezG|^Qbt6R_=U0}|=k;mIIakz99*>FrsQR{0aQRP6ko?5<7bkDN8evZ& zB@_KqQG?ErKL=1*ZM9_5?Pq%lcS4uLSzN(Mr5=t6xHLS~Ym`UgM@D&VNu8e?_=nSFtF$u@hpPSmI4Vo_t&v?>$~K4y(O~Rb*(MFy_igM7 z*~yYUyR6yQgzWnWMUgDov!!g=lInM+=lOmOk4L`O?{i&qxy&D*_qorRbDwj6?)!ef z#JLd7F6Z2I$S0iYI={rZNk*<{HtIl^mx=h>Cim*04K4+Z4IJtd*-)%6XV2(MCscPiw_a+y*?BKbTS@BZ3AUao^%Zi#PhoY9Vib4N>SE%4>=Jco0v zH_Miey{E;FkdlZSq)e<{`+S3W=*ttvD#hB8w=|2aV*D=yOV}(&p%0LbEWH$&@$X3x~CiF-?ejQ*N+-M zc8zT@3iwkdRT2t(XS`d7`tJQAjRmKAhiw{WOqpuvFp`i@Q@!KMhwKgsA}%@sw8Xo5Y=F zhRJZg)O4uqNWj?V&&vth*H#je6T}}p_<>!Dr#89q@uSjWv~JuW(>FqoJ5^ho0%K?E z9?x_Q;kmcsQ@5=}z@tdljMSt9-Z3xn$k)kEjK|qXS>EfuDmu(Z8|(W?gY6-l z@R_#M8=vxKMAoi&PwnaIYw2COJM@atcgfr=zK1bvjW?9B`-+Voe$Q+H$j!1$Tjn+* z&LY<%)L@;zhnJlB^Og6I&BOR-m?{IW;tyYC%FZ!&Z>kGjHJ6cqM-F z&19n+e1=9AH1VrVeHrIzqlC`w9=*zfmrerF?JMzO&|Mmv;!4DKc(sp+jy^Dx?(8>1 zH&yS_4yL7m&GWX~mdfgH*AB4{CKo;+egw=PrvkTaoBU+P-4u?E|&!c z)DKc;>$$B6u*Zr1SjUh2)FeuWLWHl5TH(UHWkf zLs>7px!c5n;rbe^lO@qlYLzlDVp(z?6rPZel=YB)Uv&n!2{+Mb$-vQl=xKw( zve&>xYx+jW_NJh!FV||r?;hdP*jOXYcLCp>DOtJ?2S^)DkM{{Eb zS$!L$e_o0(^}n3tA1R3-$SNvgBq;DOEo}fNc|tB%%#g4RA3{|euq)p+xd3I8^4E&m zFrD%}nvG^HUAIKe9_{tXB;tl|G<%>yk6R;8L2)KUJw4yHJXUOPM>(-+jxq4R;z8H#>rnJy*)8N+$wA$^F zN+H*3t)eFEgxLw+Nw3};4WV$qj&_D`%ADV2%r zJCPCo%{=z7;`F98(us5JnT(G@sKTZ^;2FVitXyLe-S5(hV&Ium+1pIUB(CZ#h|g)u zSLJJ<@HgrDiA-}V_6B^x1>c9B6%~847JkQ!^KLZ2skm;q*edo;UA)~?SghG8;QbHh z_6M;ouo_1rq9=x$<`Y@EA{C%6-pEV}B(1#sDoe_e1s3^Y>n#1Sw;N|}8D|s|VPd+g z-_$QhCz`vLxxrVMx3ape1xu3*wjx=yKSlM~nFgkNWb4?DDr*!?U)L_VeffF<+!j|b zZ$Wn2$TDv3C3V@BHpSgv3JUif8%hk%OsGZ=OxH@8&4`bbf$`aAMchl^qN>Eyu3JH} z9-S!x8-s4fE=lad%Pkp8hAs~u?|uRnL48O|;*DEU! zuS0{cpk%1E0nc__2%;apFsTm0bKtd&A0~S3Cj^?72-*Owk3V!ZG*PswDfS~}2<8le z5+W^`Y(&R)yVF*tU_s!XMcJS`;(Tr`J0%>p=Z&InR%D3@KEzzI+-2)HK zuoNZ&o=wUC&+*?ofPb0a(E6(<2Amd6%uSu_^-<1?hsxs~0K5^f(LsGqgEF^+0_H=uNk9S0bb!|O8d?m5gQjUKevPaO+*VfSn^2892K~%crWM8+6 z25@V?Y@J<9w%@NXh-2!}SK_(X)O4AM1-WTg>sj1{lj5@=q&dxE^9xng1_z9w9DK>| z6Iybcd0e zyi;Ew!KBRIfGPGytQ6}z}MeXCfLY0?9%RiyagSp_D1?N&c{ zyo>VbJ4Gy`@Fv+5cKgUgs~na$>BV{*em7PU3%lloy_aEovR+J7TfQKh8BJXyL6|P8un-Jnq(ghd!_HEOh$zlv2$~y3krgeH;9zC}V3f`uDtW(%mT#944DQa~^8ZI+zAUu4U(j0YcDfKR$bK#gvn_{JZ>|gZ5+)u?T$w7Q%F^;!Wk?G z(le7r!ufT*cxS}PR6hIVtXa)i`d$-_1KkyBU>qmgz-=T};uxx&sKgv48akIWQ89F{ z0XiY?WM^~;|T8zBOr zs#zuOONzH?svv*jokd5SK8wG>+yMC)LYL|vLqm^PMHcT=`}V$=nIRHe2?h)8WQa6O zPAU}d`1y(>kZiP~Gr=mtJLMu`i<2CspL|q2DqAgAD^7*$xzM`PU4^ga`ilE134XBQ z99P(LhHU@7qvl9Yzg$M`+dlS=x^(m-_3t|h>S}E0bcFMn=C|KamQ)=w2^e)35p`zY zRV8X?d;s^>Cof2SPR&nP3E+-LCkS0J$H!eh8~k0qo$}00b=7!H_I2O+Ro@3O$nPdm ztmbOO^B+IHzQ5w>@@@J4cKw5&^_w6s!s=H%&byAbUtczPQ7}wfTqxxtQNfn*u73Qw zGuWsrky_ajPx-5`R<)6xHf>C(oqGf_Fw|-U*GfS?xLML$kv;h_pZ@Kk$y0X(S+K80 z6^|z)*`5VUkawg}=z`S;VhZhxyDfrE0$(PMurAxl~<>lfZa>JZ288ULK7D` zl9|#L^JL}Y$j*j`0-K6kH#?bRmg#5L3iB4Z)%iF@SqT+Lp|{i`m%R-|ZE94Np7Pa5 zCqC^V3}B(FR340pmF*qaa}M}+h6}mqE~7Sh!9bDv9YRT|>vBNAqv09zXHMlcuhKD| zcjjA(b*XCIwJ33?CB!+;{)vX@9xns_b-VO{i0y?}{!sdXj1GM8+$#v>W7nw;+O_9B z_{4L;C6ol?(?W0<6taGEn1^uG=?Q3i29sE`RfYCaV$3DKc_;?HsL?D_fSYg}SuO5U zOB_f4^vZ_x%o`5|C@9C5+o=mFy@au{s)sKw!UgC&L35aH(sgDxRE2De%(%OT=VUdN ziVLEmdOvJ&5*tCMKRyXctCwQu_RH%;m*$YK&m;jtbdH#Ak~13T1^f89tn`A%QEHWs~jnY~E}p_Z$XC z=?YXLCkzVSK+Id`xZYTegb@W8_baLt-Fq`Tv|=)JPbFsKRm)4UW;yT+J`<)%#ue9DPOkje)YF2fsCilK9MIIK>p*`fkoD5nGfmLwt)!KOT+> zOFq*VZktDDyM3P5UOg`~XL#cbzC}eL%qMB=Q5$d89MKuN#$6|4gx_Jt0Gfn8w&q}%lq4QU%6#jT*MRT% zrLz~C8FYKHawn-EQWN1B75O&quS+Z81(zN)G>~vN8VwC+e+y(`>HcxC{MrJ;H1Z4k zZWuv$w_F0-Ub%MVcpIc){4PGL^I7M{>;hS?;eH!;gmcOE66z3;Z1Phqo(t zVP(Hg6q#0gIKgsg7L7WE!{Y#1nI(45tx2{$34dDd#!Z0NIyrm)HOn5W#7;f4pQci# zDW!FI(g4e668kI9{2+mLwB+=#9bfqgX%!B34V-$wwSN(_cm*^{y0jQtv*4}eO^sOV z*9xoNvX)c9isB}Tgx&ZRjp3kwhTVK?r9;n!x>^XYT z@Q^7zp{rkIs{2mUSE^2!Gf6$6;j~&4=-0cSJJDizZp6LTe8b45;{AKM%v99}{{FfC zz709%u0mC=1KXTo(=TqmZQ;c?$M3z(!xah>aywrj40sc2y3rKFw4jCq+Y+u=CH@_V zxz|qeTwa>+<|H%8Dz5u>ZI5MmjTFwXS-Fv!TDd*`>3{krWoNVx$<133`(ftS?ZPyY z&4@ah^3^i`vL$BZa>O|Nt?ucewzsF)0zX3qmM^|waXr=T0pfIb0*$AwU=?Ipl|1Y; z*Pk6{C-p4MY;j@IJ|DW>QHZQJcp;Z~?8(Q+Kk3^0qJ}SCk^*n4W zu9ZFwLHUx-$6xvaQ)SUQcYd6fF8&x)V`1bIuX@>{mE$b|Yd(qomn3;bPwnDUc0F=; zh*6_((%bqAYQWQ~odER?h>1mkL4kpb3s7`0m@rDKGU*oyF)$j~Ffd4fXV$?`f~rHf zB%Y)@5SXZvfwm10RY5X?TEo)PK_`L6qgBp=#>fO49$D zDq8Ozj0q6213tV5Qq=;fZ0$|KroY{Dz=l@lU^J)?Ko@ti20TRplXzphBi>XGx4bou zEWrkNjz0t5j!_ke{g5I#PUlEU$Km8g8TE|XK=MkU@PT4T><2OVamoK;wJ}3X0L$vX zgd7gNa359*nc)R-0!`2X@FOTB`+oETOPc=ubp5R)VQgY+5BTZZJ2?9QwnO=dnulIUF3gFn;BODC2)65)HeVd%t86sL7Rv^Y+nbn+&l z6BAJY(ETvwI)Ts$aiE8rht4KD*qNyE{8{x6R|%akbTBzw;2+6Echkt+W+`u^XX z_z&x%n '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/tool/gradlew.bat b/tool/gradlew.bat new file mode 100644 index 0000000..25da30d --- /dev/null +++ b/tool/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/tool/migt.gradle.kts b/tool/migt.gradle.kts new file mode 100644 index 0000000..1e3c448 --- /dev/null +++ b/tool/migt.gradle.kts @@ -0,0 +1,67 @@ +plugins { + id("java") +} + +version = "0.0.1" +description = "A new description." + +zapAddOn { + addOnName.set("migt") + zapVersion.set("2.15.0") + + manifest { + author.set("FBK") + + dependencies { + addOns { + register("network") { + version.set(">=0.11.0") + } + } + } + } +} + +repositories { + mavenCentral() +} + +dependencies { + zapAddOn("network") + implementation("org.json:json:20240303") + implementation("com.nimbusds:nimbus-jose-jwt:9.31") + implementation("org.bouncycastle:bcpkix-jdk15on:1.70") + implementation("com.google.code.gson:gson:2.10.1") + implementation("org.seleniumhq.selenium:selenium-java:4.13.0") + implementation("org.apache.santuario:xmlsec:3.0.0") + implementation("com.sun.xml.security:xml-security-impl:1.0") + implementation("com.jayway.jsonpath:json-path:2.9.0") + implementation("net.minidev:json-smart:2.4.10") + implementation("org.apache.httpcomponents:httpclient:4.5.14") + implementation("org.apache.httpcomponents:httpcore:4.4.16") + implementation("com.networknt:json-schema-validator:1.0.78") + implementation("org.apache.commons:commons-text:1.10.0") + implementation("commons-codec:commons-codec:1.16.0") + implementation("org.zaproxy:zap:2.15.0") + implementation("org.zaproxy:zap-clientapi:1.14.0") + testImplementation(platform("org.junit:junit-bom:5.10.0")) + testImplementation("org.junit.jupiter:junit-jupiter") +} + +tasks.test { + useJUnitPlatform() +} + +crowdin { + configuration { + val resourcesPath = "org/zaproxy/addon/${zapAddOn.addOnId.get()}/resources/" + tokens.put("%messagesPath%", resourcesPath) + tokens.put("%helpPath%", resourcesPath) + } +} + +// tasks.register("wrapper") { +// gradleVersion = "5.6.4" +// } + +tasks.register("prepareKotlinBuildScriptModel") {} diff --git a/tool/pom.xml b/tool/pom.xml deleted file mode 100644 index ac0ae04..0000000 --- a/tool/pom.xml +++ /dev/null @@ -1,140 +0,0 @@ - - - 4.0.0 - - FBK.MIG - mig-t - beta - - - - org.json - json - 20231013 - - - com.nimbusds - nimbus-jose-jwt - 9.38-rc3 - - - org.bouncycastle - bcpkix-jdk15on - 1.70 - - - com.google.code.gson - gson - 2.10.1 - - - org.seleniumhq.selenium - selenium-java - 4.16.1 - - - org.apache.santuario - xmlsec - 4.0.1 - - - com.sun.xml.security - xml-security-impl - 1.0 - - - org.junit.jupiter - junit-jupiter-engine - 5.10.1 - test - - - com.jayway.jsonpath - json-path - 2.9.0 - - - net.minidev - json-smart - 2.5.0 - - - org.apache.httpcomponents - httpclient - 4.5.14 - - - org.apache.httpcomponents - httpcore - 4.4.16 - - - com.networknt - json-schema-validator - 1.2.0 - - - org.apache.commons - commons-text - 1.11.0 - - - - - 11 - 11 - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 3.1.2 - - - maven-assembly-plugin - - - - - - - jar-with-dependencies - - - - - make-assembly - package - - single - - - - - - org.apache.maven.plugins - maven-enforcer-plugin - 3.3.0 - - - enforce-maven - - enforce - - - - - 3.0 - - - - - - - - - \ No newline at end of file diff --git a/tool/readme.md b/tool/readme.md deleted file mode 100644 index 5568f31..0000000 --- a/tool/readme.md +++ /dev/null @@ -1,5 +0,0 @@ -Useful Maven commands: - -- mvn versions:use-latest-versions updates all dependencies o lastest version - -- mvn versions:update-properties -> updates maven to match the pom \ No newline at end of file diff --git a/tool/src/main/java/burp/IBurpCollaboratorClientContext.java b/tool/src/main/java/burp/IBurpCollaboratorClientContext.java deleted file mode 100644 index 348fc44..0000000 --- a/tool/src/main/java/burp/IBurpCollaboratorClientContext.java +++ /dev/null @@ -1,97 +0,0 @@ -package burp; - -/* - * @(#)IBurpCollaboratorClientContext.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -import java.util.List; - -/** - * This interface represents an instance of a Burp Collaborator client context, - * which can be used to generate Burp Collaborator payloads and poll the - * Collaborator server for any network interactions that result from using those - * payloads. Extensions can obtain new instances of this class by calling - * IBurpExtenderCallbacks.createBurpCollaboratorClientContext(). - * Note that each Burp Collaborator client context is tied to the Collaborator - * server configuration that was in place at the time the context was created. - */ -public interface IBurpCollaboratorClientContext -{ - - /** - * This method is used to generate new Burp Collaborator payloads. - * - * @param includeCollaboratorServerLocation Specifies whether to include the - * Collaborator server location in the generated payload. - * @return The payload that was generated. - * - * @throws IllegalStateException if Burp Collaborator is disabled - */ - String generatePayload(boolean includeCollaboratorServerLocation); - - /** - * This method is used to retrieve all interactions received by the - * Collaborator server resulting from payloads that were generated for this - * context. - * - * @return The Collaborator interactions that have occurred resulting from - * payloads that were generated for this context. - * - * @throws IllegalStateException if Burp Collaborator is disabled - */ - List fetchAllCollaboratorInteractions(); - - /** - * This method is used to retrieve interactions received by the Collaborator - * server resulting from a single payload that was generated for this - * context. - * - * @param payload The payload for which interactions will be retrieved. - * @return The Collaborator interactions that have occurred resulting from - * the given payload. - * - * @throws IllegalStateException if Burp Collaborator is disabled - */ - List fetchCollaboratorInteractionsFor(String payload); - - /** - * This method is used to retrieve all interactions made by Burp Infiltrator - * instrumentation resulting from payloads that were generated for this - * context. - * - * @return The interactions triggered by the Burp Infiltrator - * instrumentation that have occurred resulting from payloads that were - * generated for this context. - * - * @throws IllegalStateException if Burp Collaborator is disabled - */ - List fetchAllInfiltratorInteractions(); - - /** - * This method is used to retrieve interactions made by Burp Infiltrator - * instrumentation resulting from a single payload that was generated for - * this context. - * - * @param payload The payload for which interactions will be retrieved. - * @return The interactions triggered by the Burp Infiltrator - * instrumentation that have occurred resulting from the given payload. - * - * @throws IllegalStateException if Burp Collaborator is disabled - */ - List fetchInfiltratorInteractionsFor(String payload); - - /** - * This method is used to retrieve the network location of the Collaborator - * server. - * - * @return The hostname or IP address of the Collaborator server. - * - * @throws IllegalStateException if Burp Collaborator is disabled - */ - String getCollaboratorServerLocation(); -} diff --git a/tool/src/main/java/burp/IBurpCollaboratorInteraction.java b/tool/src/main/java/burp/IBurpCollaboratorInteraction.java deleted file mode 100644 index 07ed661..0000000 --- a/tool/src/main/java/burp/IBurpCollaboratorInteraction.java +++ /dev/null @@ -1,41 +0,0 @@ -package burp; - -/* - * @(#)IBurpCollaboratorInteraction.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -import java.util.Map; - -/** - * This interface represents a network interaction that occurred with the Burp - * Collaborator server. - */ -public interface IBurpCollaboratorInteraction -{ - - /** - * This method is used to retrieve a property of the interaction. Properties - * of all interactions are: interaction_id, type, client_ip, and time_stamp. - * Properties of DNS interactions are: query_type and raw_query. The - * raw_query value is Base64-encoded. Properties of HTTP interactions are: - * protocol, request, and response. The request and response values are - * Base64-encoded. - * - * @param name The name of the property to retrieve. - * @return A string representing the property value, or null if not present. - */ - String getProperty(String name); - - /** - * This method is used to retrieve a map containing all properties of the - * interaction. - * - * @return A map containing all properties of the interaction. - */ - Map getProperties(); -} diff --git a/tool/src/main/java/burp/IBurpExtender.java b/tool/src/main/java/burp/IBurpExtender.java deleted file mode 100644 index eaa4bec..0000000 --- a/tool/src/main/java/burp/IBurpExtender.java +++ /dev/null @@ -1,31 +0,0 @@ -package burp; - -/* - * @(#)IBurpExtender.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * All extensions must implement this interface. - * - * Implementations must be called BurpExtender, in the package burp, must be - * declared public, and must provide a default (public, no-argument) - * constructor. - */ -public interface IBurpExtender -{ - /** - * This method is invoked when the extension is loaded. It registers an - * instance of the - * IBurpExtenderCallbacks interface, providing methods that may - * be invoked by the extension to perform various actions. - * - * @param callbacks An - * IBurpExtenderCallbacks object. - */ - void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks); -} diff --git a/tool/src/main/java/burp/IBurpExtenderCallbacks.java b/tool/src/main/java/burp/IBurpExtenderCallbacks.java deleted file mode 100644 index 0478ac6..0000000 --- a/tool/src/main/java/burp/IBurpExtenderCallbacks.java +++ /dev/null @@ -1,1173 +0,0 @@ -package burp; - -/* - * @(#)IBurpExtenderCallbacks.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -import java.awt.Component; -import java.io.OutputStream; -import java.util.List; -import java.util.Map; - -/** - * This interface is used by Burp Suite to pass to extensions a set of callback - * methods that can be used by extensions to perform various actions within - * Burp. - * - * When an extension is loaded, Burp invokes its - * registerExtenderCallbacks() method and passes an instance of the - * IBurpExtenderCallbacks interface. The extension may then invoke - * the methods of this interface as required in order to extend Burp's - * functionality. - */ -public interface IBurpExtenderCallbacks -{ - - /** - * Flag used to identify Burp Suite as a whole. - */ - int TOOL_SUITE = 0x00000001; - /** - * Flag used to identify the Burp Target tool. - */ - int TOOL_TARGET = 0x00000002; - /** - * Flag used to identify the Burp Proxy tool. - */ - int TOOL_PROXY = 0x00000004; - /** - * Flag used to identify the Burp Spider tool. - */ - int TOOL_SPIDER = 0x00000008; - /** - * Flag used to identify the Burp Scanner tool. - */ - int TOOL_SCANNER = 0x00000010; - /** - * Flag used to identify the Burp Intruder tool. - */ - int TOOL_INTRUDER = 0x00000020; - /** - * Flag used to identify the Burp Repeater tool. - */ - int TOOL_REPEATER = 0x00000040; - /** - * Flag used to identify the Burp Sequencer tool. - */ - int TOOL_SEQUENCER = 0x00000080; - /** - * Flag used to identify the Burp Decoder tool. - */ - int TOOL_DECODER = 0x00000100; - /** - * Flag used to identify the Burp Comparer tool. - */ - int TOOL_COMPARER = 0x00000200; - /** - * Flag used to identify the Burp Extender tool. - */ - int TOOL_EXTENDER = 0x00000400; - - /** - * This method is used to set the display name for the current extension, - * which will be displayed within the user interface for the Extender tool. - * - * @param name The extension name. - */ - void setExtensionName(String name); - - /** - * This method is used to obtain an IExtensionHelpers object, - * which can be used by the extension to perform numerous useful tasks. - * - * @return An object containing numerous helper methods, for tasks such as - * building and analyzing HTTP requests. - */ - IExtensionHelpers getHelpers(); - - /** - * This method is used to obtain the current extension's standard output - * stream. Extensions should write all output to this stream, allowing the - * Burp user to configure how that output is handled from within the UI. - * - * @return The extension's standard output stream. - */ - OutputStream getStdout(); - - /** - * This method is used to obtain the current extension's standard error - * stream. Extensions should write all error messages to this stream, - * allowing the Burp user to configure how that output is handled from - * within the UI. - * - * @return The extension's standard error stream. - */ - OutputStream getStderr(); - - /** - * This method prints a line of output to the current extension's standard - * output stream. - * - * @param output The message to print. - */ - void printOutput(String output); - - /** - * This method prints a line of output to the current extension's standard - * error stream. - * - * @param error The message to print. - */ - void printError(String error); - - /** - * This method is used to register a listener which will be notified of - * changes to the extension's state. Note: Any extensions that start - * background threads or open system resources (such as files or database - * connections) should register a listener and terminate threads / close - * resources when the extension is unloaded. - * - * @param listener An object created by the extension that implements the - * IExtensionStateListener interface. - */ - void registerExtensionStateListener(IExtensionStateListener listener); - - /** - * This method is used to retrieve the extension state listeners that are - * registered by the extension. - * - * @return A list of extension state listeners that are currently registered - * by this extension. - */ - List getExtensionStateListeners(); - - /** - * This method is used to remove an extension state listener that has been - * registered by the extension. - * - * @param listener The extension state listener to be removed. - */ - void removeExtensionStateListener(IExtensionStateListener listener); - - /** - * This method is used to register a listener which will be notified of - * requests and responses made by any Burp tool. Extensions can perform - * custom analysis or modification of these messages by registering an HTTP - * listener. - * - * @param listener An object created by the extension that implements the - * IHttpListener interface. - */ - void registerHttpListener(IHttpListener listener); - - /** - * This method is used to retrieve the HTTP listeners that are registered by - * the extension. - * - * @return A list of HTTP listeners that are currently registered by this - * extension. - */ - List getHttpListeners(); - - /** - * This method is used to remove an HTTP listener that has been registered - * by the extension. - * - * @param listener The HTTP listener to be removed. - */ - void removeHttpListener(IHttpListener listener); - - /** - * This method is used to register a listener which will be notified of - * requests and responses being processed by the Proxy tool. Extensions can - * perform custom analysis or modification of these messages, and control - * in-UI message interception, by registering a proxy listener. - * - * @param listener An object created by the extension that implements the - * IProxyListener interface. - */ - void registerProxyListener(IProxyListener listener); - - /** - * This method is used to retrieve the Proxy listeners that are registered - * by the extension. - * - * @return A list of Proxy listeners that are currently registered by this - * extension. - */ - List getProxyListeners(); - - /** - * This method is used to remove a Proxy listener that has been registered - * by the extension. - * - * @param listener The Proxy listener to be removed. - */ - void removeProxyListener(IProxyListener listener); - - /** - * This method is used to register a listener which will be notified of new - * issues that are reported by the Scanner tool. Extensions can perform - * custom analysis or logging of Scanner issues by registering a Scanner - * listener. - * - * @param listener An object created by the extension that implements the - * IScannerListener interface. - */ - void registerScannerListener(IScannerListener listener); - - /** - * This method is used to retrieve the Scanner listeners that are registered - * by the extension. - * - * @return A list of Scanner listeners that are currently registered by this - * extension. - */ - List getScannerListeners(); - - /** - * This method is used to remove a Scanner listener that has been registered - * by the extension. - * - * @param listener The Scanner listener to be removed. - */ - void removeScannerListener(IScannerListener listener); - - /** - * This method is used to register a listener which will be notified of - * changes to Burp's suite-wide target scope. - * - * @param listener An object created by the extension that implements the - * IScopeChangeListener interface. - */ - void registerScopeChangeListener(IScopeChangeListener listener); - - /** - * This method is used to retrieve the scope change listeners that are - * registered by the extension. - * - * @return A list of scope change listeners that are currently registered by - * this extension. - */ - List getScopeChangeListeners(); - - /** - * This method is used to remove a scope change listener that has been - * registered by the extension. - * - * @param listener The scope change listener to be removed. - */ - void removeScopeChangeListener(IScopeChangeListener listener); - - /** - * This method is used to register a factory for custom context menu items. - * When the user invokes a context menu anywhere within Burp, the factory - * will be passed details of the invocation event, and asked to provide any - * custom context menu items that should be shown. - * - * @param factory An object created by the extension that implements the - * IContextMenuFactory interface. - */ - void registerContextMenuFactory(IContextMenuFactory factory); - - /** - * This method is used to retrieve the context menu factories that are - * registered by the extension. - * - * @return A list of context menu factories that are currently registered by - * this extension. - */ - List getContextMenuFactories(); - - /** - * This method is used to remove a context menu factory that has been - * registered by the extension. - * - * @param factory The context menu factory to be removed. - */ - void removeContextMenuFactory(IContextMenuFactory factory); - - /** - * This method is used to register a factory for custom message editor tabs. - * For each message editor that already exists, or is subsequently created, - * within Burp, the factory will be asked to provide a new instance of an - * IMessageEditorTab object, which can provide custom rendering - * or editing of HTTP messages. - * - * @param factory An object created by the extension that implements the - * IMessageEditorTabFactory interface. - */ - void registerMessageEditorTabFactory(IMessageEditorTabFactory factory); - - /** - * This method is used to retrieve the message editor tab factories that are - * registered by the extension. - * - * @return A list of message editor tab factories that are currently - * registered by this extension. - */ - List getMessageEditorTabFactories(); - - /** - * This method is used to remove a message editor tab factory that has been - * registered by the extension. - * - * @param factory The message editor tab factory to be removed. - */ - void removeMessageEditorTabFactory(IMessageEditorTabFactory factory); - - /** - * This method is used to register a provider of Scanner insertion points. - * For each base request that is actively scanned, Burp will ask the - * provider to provide any custom scanner insertion points that are - * appropriate for the request. - * - * @param provider An object created by the extension that implements the - * IScannerInsertionPointProvider interface. - */ - void registerScannerInsertionPointProvider( - IScannerInsertionPointProvider provider); - - /** - * This method is used to retrieve the Scanner insertion point providers - * that are registered by the extension. - * - * @return A list of Scanner insertion point providers that are currently - * registered by this extension. - */ - List getScannerInsertionPointProviders(); - - /** - * This method is used to remove a Scanner insertion point provider that has - * been registered by the extension. - * - * @param provider The Scanner insertion point provider to be removed. - */ - void removeScannerInsertionPointProvider( - IScannerInsertionPointProvider provider); - - /** - * This method is used to register a custom Scanner check. When performing - * scanning, Burp will ask the check to perform active or passive scanning - * on the base request, and report any Scanner issues that are identified. - * - * @param check An object created by the extension that implements the - * IScannerCheck interface. - */ - void registerScannerCheck(IScannerCheck check); - - /** - * This method is used to retrieve the Scanner checks that are registered by - * the extension. - * - * @return A list of Scanner checks that are currently registered by this - * extension. - */ - List getScannerChecks(); - - /** - * This method is used to remove a Scanner check that has been registered by - * the extension. - * - * @param check The Scanner check to be removed. - */ - void removeScannerCheck(IScannerCheck check); - - /** - * This method is used to register a factory for Intruder payloads. Each - * registered factory will be available within the Intruder UI for the user - * to select as the payload source for an attack. When this is selected, the - * factory will be asked to provide a new instance of an - * IIntruderPayloadGenerator object, which will be used to - * generate payloads for the attack. - * - * @param factory An object created by the extension that implements the - * IIntruderPayloadGeneratorFactory interface. - */ - void registerIntruderPayloadGeneratorFactory( - IIntruderPayloadGeneratorFactory factory); - - /** - * This method is used to retrieve the Intruder payload generator factories - * that are registered by the extension. - * - * @return A list of Intruder payload generator factories that are currently - * registered by this extension. - */ - List - getIntruderPayloadGeneratorFactories(); - - /** - * This method is used to remove an Intruder payload generator factory that - * has been registered by the extension. - * - * @param factory The Intruder payload generator factory to be removed. - */ - void removeIntruderPayloadGeneratorFactory( - IIntruderPayloadGeneratorFactory factory); - - /** - * This method is used to register a custom Intruder payload processor. Each - * registered processor will be available within the Intruder UI for the - * user to select as the action for a payload processing rule. - * - * @param processor An object created by the extension that implements the - * IIntruderPayloadProcessor interface. - */ - void registerIntruderPayloadProcessor(IIntruderPayloadProcessor processor); - - /** - * This method is used to retrieve the Intruder payload processors that are - * registered by the extension. - * - * @return A list of Intruder payload processors that are currently - * registered by this extension. - */ - List getIntruderPayloadProcessors(); - - /** - * This method is used to remove an Intruder payload processor that has been - * registered by the extension. - * - * @param processor The Intruder payload processor to be removed. - */ - void removeIntruderPayloadProcessor(IIntruderPayloadProcessor processor); - - /** - * This method is used to register a custom session handling action. Each - * registered action will be available within the session handling rule UI - * for the user to select as a rule action. Users can choose to invoke an - * action directly in its own right, or following execution of a macro. - * - * @param action An object created by the extension that implements the - * ISessionHandlingAction interface. - */ - void registerSessionHandlingAction(ISessionHandlingAction action); - - /** - * This method is used to retrieve the session handling actions that are - * registered by the extension. - * - * @return A list of session handling actions that are currently registered - * by this extension. - */ - List getSessionHandlingActions(); - - /** - * This method is used to remove a session handling action that has been - * registered by the extension. - * - * @param action The extension session handling action to be removed. - */ - void removeSessionHandlingAction(ISessionHandlingAction action); - - /** - * This method is used to unload the extension from Burp Suite. - */ - void unloadExtension(); - - /** - * This method is used to add a custom tab to the main Burp Suite window. - * - * @param tab An object created by the extension that implements the - * ITab interface. - */ - void addSuiteTab(ITab tab); - - /** - * This method is used to remove a previously-added tab from the main Burp - * Suite window. - * - * @param tab An object created by the extension that implements the - * ITab interface. - */ - void removeSuiteTab(ITab tab); - - /** - * This method is used to customize UI components in line with Burp's UI - * style, including font size, colors, table line spacing, etc. The action - * is performed recursively on any child components of the passed-in - * component. - * - * @param component The UI component to be customized. - */ - void customizeUiComponent(Component component); - - /** - * This method is used to create a new instance of Burp's HTTP message - * editor, for the extension to use in its own UI. - * - * @param controller An object created by the extension that implements the - * IMessageEditorController interface. This parameter is - * optional and may be null. If it is provided, then the - * message editor will query the controller when required to obtain details - * about the currently displayed message, including the - * IHttpService for the message, and the associated request or - * response message. If a controller is not provided, then the message - * editor will not support context menu actions, such as sending requests to - * other Burp tools. - * @param editable Indicates whether the editor created should be editable, - * or used only for message viewing. - * @return An object that implements the IMessageEditor - * interface, and which the extension can use in its own UI. - */ - IMessageEditor createMessageEditor( - IMessageEditorController controller, - boolean editable); - - /** - * This method returns the command line arguments that were passed to Burp - * on startup. - * - * @return The command line arguments that were passed to Burp on startup. - */ - String[] getCommandLineArguments(); - - /** - * This method is used to save configuration settings for the extension in a - * persistent way that survives reloads of the extension and of Burp Suite. - * Saved settings can be retrieved using the method - * loadExtensionSetting(). - * - * @param name The name of the setting. - * @param value The value of the setting. If this value is null - * then any existing setting with the specified name will be removed. - */ - void saveExtensionSetting(String name, String value); - - /** - * This method is used to load configuration settings for the extension that - * were saved using the method saveExtensionSetting(). - * - * @param name The name of the setting. - * @return The value of the setting, or null if no value is - * set. - */ - String loadExtensionSetting(String name); - - /** - * This method is used to create a new instance of Burp's plain text editor, - * for the extension to use in its own UI. - * - * @return An object that implements the ITextEditor interface, - * and which the extension can use in its own UI. - */ - ITextEditor createTextEditor(); - - /** - * This method can be used to send an HTTP request to the Burp Repeater - * tool. The request will be displayed in the user interface, but will not - * be issued until the user initiates this action. - * - * @param host The hostname of the remote HTTP server. - * @param port The port of the remote HTTP server. - * @param useHttps Flags whether the protocol is HTTPS or HTTP. - * @param request The full HTTP request. - * @param tabCaption An optional caption which will appear on the Repeater - * tab containing the request. If this value is null then a - * default tab index will be displayed. - */ - void sendToRepeater( - String host, - int port, - boolean useHttps, - byte[] request, - String tabCaption); - - /** - * This method can be used to send an HTTP request to the Burp Intruder - * tool. The request will be displayed in the user interface, and markers - * for attack payloads will be placed into default locations within the - * request. - * - * @param host The hostname of the remote HTTP server. - * @param port The port of the remote HTTP server. - * @param useHttps Flags whether the protocol is HTTPS or HTTP. - * @param request The full HTTP request. - */ - void sendToIntruder( - String host, - int port, - boolean useHttps, - byte[] request); - - /** - * This method can be used to send an HTTP request to the Burp Intruder - * tool. The request will be displayed in the user interface, and markers - * for attack payloads will be placed into the specified locations within - * the request. - * - * @param host The hostname of the remote HTTP server. - * @param port The port of the remote HTTP server. - * @param useHttps Flags whether the protocol is HTTPS or HTTP. - * @param request The full HTTP request. - * @param payloadPositionOffsets A list of index pairs representing the - * payload positions to be used. Each item in the list must be an int[2] - * array containing the start and end offsets for the payload position. - */ - void sendToIntruder( - String host, - int port, - boolean useHttps, - byte[] request, - List payloadPositionOffsets); - - /** - * This method can be used to send data to the Comparer tool. - * - * @param data The data to be sent to Comparer. - */ - void sendToComparer(byte[] data); - - /** - * This method can be used to send a seed URL to the Burp Spider tool. If - * the URL is not within the current Spider scope, the user will be asked if - * they wish to add the URL to the scope. If the Spider is not currently - * running, it will be started. The seed URL will be requested, and the - * Spider will process the application's response in the normal way. - * - * @param url The new seed URL to begin spidering from. - */ - void sendToSpider( - java.net.URL url); - - /** - * This method can be used to send an HTTP request to the Burp Scanner tool - * to perform an active vulnerability scan. If the request is not within the - * current active scanning scope, the user will be asked if they wish to - * proceed with the scan. - * - * @param host The hostname of the remote HTTP server. - * @param port The port of the remote HTTP server. - * @param useHttps Flags whether the protocol is HTTPS or HTTP. - * @param request The full HTTP request. - * @return The resulting scan queue item. - */ - IScanQueueItem doActiveScan( - String host, - int port, - boolean useHttps, - byte[] request); - - /** - * This method can be used to send an HTTP request to the Burp Scanner tool - * to perform an active vulnerability scan, based on a custom list of - * insertion points that are to be scanned. If the request is not within the - * current active scanning scope, the user will be asked if they wish to - * proceed with the scan. - * - * @param host The hostname of the remote HTTP server. - * @param port The port of the remote HTTP server. - * @param useHttps Flags whether the protocol is HTTPS or HTTP. - * @param request The full HTTP request. - * @param insertionPointOffsets A list of index pairs representing the - * positions of the insertion points that should be scanned. Each item in - * the list must be an int[2] array containing the start and end offsets for - * the insertion point. - * @return The resulting scan queue item. - */ - IScanQueueItem doActiveScan( - String host, - int port, - boolean useHttps, - byte[] request, - List insertionPointOffsets); - - /** - * This method can be used to send an HTTP request to the Burp Scanner tool - * to perform a passive vulnerability scan. - * - * @param host The hostname of the remote HTTP server. - * @param port The port of the remote HTTP server. - * @param useHttps Flags whether the protocol is HTTPS or HTTP. - * @param request The full HTTP request. - * @param response The full HTTP response. - */ - void doPassiveScan( - String host, - int port, - boolean useHttps, - byte[] request, - byte[] response); - - /** - * This method can be used to issue HTTP requests and retrieve their - * responses. - * - * @param httpService The HTTP service to which the request should be sent. - * @param request The full HTTP request. - * @return An object that implements the IHttpRequestResponse - * interface, and which the extension can query to obtain the details of the - * response. - */ - IHttpRequestResponse makeHttpRequest( - IHttpService httpService, - byte[] request); - - /** - * This method can be used to issue HTTP requests and retrieve their - * responses. - * - * @param httpService The HTTP service to which the request should be sent. - * @param request The full HTTP request. - * @param forceHttp1 If true then HTTP/1 will be used. - * @return An object that implements the IHttpRequestResponse - * interface, and which the extension can query to obtain the details of the - * response. - */ - IHttpRequestResponse makeHttpRequest( - IHttpService httpService, - byte[] request, - boolean forceHttp1); - - - /** - * This method can be used to issue HTTP requests and retrieve their - * responses. - * - * @param host The hostname of the remote HTTP server. - * @param port The port of the remote HTTP server. - * @param useHttps Flags whether the protocol is HTTPS or HTTP. - * @param request The full HTTP request. - * @return The full response retrieved from the remote server. - */ - byte[] makeHttpRequest( - String host, - int port, - boolean useHttps, - byte[] request); - - /** - * This method can be used to issue HTTP requests and retrieve their - * responses. - * - * @param host The hostname of the remote HTTP server. - * @param port The port of the remote HTTP server. - * @param useHttps Flags whether the protocol is HTTPS or HTTP. - * @param request The full HTTP request. - * @param forceHttp1 If true then HTTP/1 will be used. - * @return The full response retrieved from the remote server. - */ - byte[] makeHttpRequest( - String host, - int port, - boolean useHttps, - byte[] request, - boolean forceHttp1); - - /** - * This method can be used to issue HTTP/2 requests and retrieve their - * responses. - * @param httpService The HTTP service to which the request should be sent. - * @param headers The headers of the request. - * @param body The body of the request. - * @return The full response retrieved from the remote server. - */ - byte[] makeHttp2Request( - IHttpService httpService, - List headers, - byte[] body); - - /** - * This method can be used to issue HTTP/2 requests and retrieve their - * responses. You can use this to force the network stack to send this - * request using HTTP/2. - * @param httpService The HTTP service to which the request should be sent. - * @param headers The headers of the request. - * @param body The body of the request. - * @param forceHttp2 Whether or not to force HTTP/2 for this request. - * @return The full response retrieved from the remote server. - */ - byte[] makeHttp2Request( - IHttpService httpService, - List headers, - byte[] body, - boolean forceHttp2); - - /** - * This method can be used to issue HTTP/2 requests and retrieve their - * responses. You can use this to make the network stack send this request - * using a specific named connection. - * @param httpService The HTTP service to which the request should be sent. - * @param headers The headers of the request. - * @param body The body of the request. - * @param forceHttp2 Whether or not to force HTTP/2 for this request. - * @param connectionIdentifier The identifier for the connection you want to use. - * @return The full response retrieved from the remote server. - */ - byte[] makeHttp2Request( - IHttpService httpService, - List headers, - byte[] body, - boolean forceHttp2, - String connectionIdentifier); - - /** - * This method can be used to query whether a specified URL is within the - * current Suite-wide scope. - * - * @param url The URL to query. - * @return Returns true if the URL is within the current - * Suite-wide scope. - */ - boolean isInScope(java.net.URL url); - - /** - * This method can be used to include the specified URL in the Suite-wide - * scope. - * - * @param url The URL to include in the Suite-wide scope. - */ - void includeInScope(java.net.URL url); - - /** - * This method can be used to exclude the specified URL from the Suite-wide - * scope. - * - * @param url The URL to exclude from the Suite-wide scope. - */ - void excludeFromScope(java.net.URL url); - - /** - * This method can be used to display a specified message in the Burp Suite - * alerts tab. - * - * @param message The alert message to display. - */ - void issueAlert(String message); - - /** - * This method returns details of all items in the Proxy history. - * - * @return The contents of the Proxy history. - */ - IHttpRequestResponse[] getProxyHistory(); - - /** - * This method returns details of items in the site map. - * - * @param urlPrefix This parameter can be used to specify a URL prefix, in - * order to extract a specific subset of the site map. The method performs a - * simple case-sensitive text match, returning all site map items whose URL - * begins with the specified prefix. If this parameter is null, the entire - * site map is returned. - * - * @return Details of items in the site map. - */ - IHttpRequestResponse[] getSiteMap(String urlPrefix); - - /** - * This method returns all of the current scan issues for URLs matching the - * specified literal prefix. - * - * @param urlPrefix This parameter can be used to specify a URL prefix, in - * order to extract a specific subset of scan issues. The method performs a - * simple case-sensitive text match, returning all scan issues whose URL - * begins with the specified prefix. If this parameter is null, all issues - * are returned. - * @return Details of the scan issues. - */ - IScanIssue[] getScanIssues(String urlPrefix); - - /** - * This method is used to generate a report for the specified Scanner - * issues. The report format can be specified. For all other reporting - * options, the default settings that appear in the reporting UI wizard are - * used. - * - * @param format The format to be used in the report. Accepted values are - * HTML and XML. - * @param issues The Scanner issues to be reported. - * @param file The file to which the report will be saved. - */ - void generateScanReport( - String format, IScanIssue[] issues, - java.io.File file); - - /** - * This method is used to retrieve the contents of Burp's session handling - * cookie jar. Extensions that provide an - * ISessionHandlingAction can query and update the cookie jar - * in order to handle unusual session handling mechanisms. - * - * @return A list of ICookie objects representing the contents - * of Burp's session handling cookie jar. - */ - List getCookieJarContents(); - - /** - * This method is used to update the contents of Burp's session handling - * cookie jar. Extensions that provide an - * ISessionHandlingAction can query and update the cookie jar - * in order to handle unusual session handling mechanisms. - * - * @param cookie An ICookie object containing details of the - * cookie to be updated. If the cookie jar already contains a cookie that - * matches the specified domain and name, then that cookie will be updated - * with the new value and expiration, unless the new value is - * null, in which case the cookie will be removed. If the - * cookie jar does not already contain a cookie that matches the specified - * domain and name, then the cookie will be added. - */ - void updateCookieJar(ICookie cookie); - - /** - * This method can be used to add an item to Burp's site map with the - * specified request/response details. This will overwrite the details of - * any existing matching item in the site map. - * - * @param item Details of the item to be added to the site map - */ - void addToSiteMap(IHttpRequestResponse item); - - /** - * This method can be used to restore Burp's state from a specified saved - * state file. This method blocks until the restore operation is completed, - * and must not be called from the event dispatch thread. - * - * @param file The file containing Burp's saved state. - * @deprecated State files have been replaced with Burp project files. - */ - @Deprecated - void restoreState(java.io.File file); - - /** - * This method can be used to save Burp's state to a specified file. This - * method blocks until the save operation is completed, and must not be - * called from the event dispatch thread. - * - * @param file The file to save Burp's state in. - * @deprecated State files have been replaced with Burp project files. - */ - @Deprecated - void saveState(java.io.File file); - - /** - * This method is no longer supported. Please use saveConfigAsJson() instead. - * - * @return A Map of name/value Strings reflecting Burp's current - * configuration. - * @deprecated Use saveConfigAsJson() instead. - */ - @Deprecated - Map saveConfig(); - - /** - * This method is no longer supported. Please use loadConfigFromJson() instead. - * - * @param config A map of name/value Strings to use as Burp's new - * configuration. - * @deprecated Use loadConfigFromJson() instead. - */ - @Deprecated - void loadConfig(Map config); - - /** - * This method causes Burp to save its current project-level configuration - * in JSON format. This is the same format that can be saved and loaded via - * the Burp user interface. To include only certain sections of the - * configuration, you can optionally supply the path to each section that - * should be included, for example: "project_options.connections". If no - * paths are provided, then the entire configuration will be saved. - * - * @param configPaths A list of Strings representing the path to each - * configuration section that should be included. - * @return A String representing the current configuration in JSON format. - */ - String saveConfigAsJson(String... configPaths); - - /** - * This method causes Burp to load a new project-level configuration from - * the JSON String provided. This is the same format that can be saved and - * loaded via the Burp user interface. Partial configurations are - * acceptable, and any settings not specified will be left unmodified. - * - * Any user-level configuration options contained in the input will be - * ignored. - * - * @param config A JSON String containing the new configuration. - */ - void loadConfigFromJson(String config); - - /** - * This method sets the master interception mode for Burp Proxy. - * - * @param enabled Indicates whether interception of Proxy messages should be - * enabled. - */ - void setProxyInterceptionEnabled(boolean enabled); - - /** - * This method retrieves information about the version of Burp in which the - * extension is running. It can be used by extensions to dynamically adjust - * their behavior depending on the functionality and APIs supported by the - * current version. - * - * @return An array of Strings comprised of: the product name (e.g. Burp - * Suite Professional), the major version (e.g. 1.5), the minor version - * (e.g. 03) - */ - String[] getBurpVersion(); - - /** - * This method retrieves the absolute path name of the file from which the - * current extension was loaded. - * - * @return The absolute path name of the file from which the current - * extension was loaded. - */ - String getExtensionFilename(); - - /** - * This method determines whether the current extension was loaded as a BApp - * (a Burp App from the BApp Store). - * - * @return Returns true if the current extension was loaded as a BApp. - */ - boolean isExtensionBapp(); - - /** - * This method can be used to shut down Burp programmatically, with an - * optional prompt to the user. If the method returns, the user canceled the - * shutdown prompt. - * - * @param promptUser Indicates whether to prompt the user to confirm the - * shutdown. - */ - void exitSuite(boolean promptUser); - - /** - * This method is used to create a temporary file on disk containing the - * provided data. Extensions can use temporary files for long-term storage - * of runtime data, avoiding the need to retain that data in memory. - * - * @param buffer The data to be saved to a temporary file. - * @return An object that implements the ITempFile interface. - */ - ITempFile saveToTempFile(byte[] buffer); - - /** - * This method is used to save the request and response of an - * IHttpRequestResponse object to temporary files, so that they - * are no longer held in memory. Extensions can used this method to convert - * IHttpRequestResponse objects into a form suitable for - * long-term storage. - * - * @param httpRequestResponse The IHttpRequestResponse object - * whose request and response messages are to be saved to temporary files. - * @return An object that implements the - * IHttpRequestResponsePersisted interface. - */ - IHttpRequestResponsePersisted saveBuffersToTempFiles( - IHttpRequestResponse httpRequestResponse); - - /** - * This method is used to apply markers to an HTTP request or response, at - * offsets into the message that are relevant for some particular purpose. - * Markers are used in various situations, such as specifying Intruder - * payload positions, Scanner insertion points, and highlights in Scanner - * issues. - * - * @param httpRequestResponse The IHttpRequestResponse object - * to which the markers should be applied. - * @param requestMarkers A list of index pairs representing the offsets of - * markers to be applied to the request message. Each item in the list must - * be an int[2] array containing the start and end offsets for the marker. - * The markers in the list should be in sequence and not overlapping. This - * parameter is optional and may be null if no request markers - * are required. - * @param responseMarkers A list of index pairs representing the offsets of - * markers to be applied to the response message. Each item in the list must - * be an int[2] array containing the start and end offsets for the marker. - * The markers in the list should be in sequence and not overlapping. This - * parameter is optional and may be null if no response markers - * are required. - * @return An object that implements the - * IHttpRequestResponseWithMarkers interface. - */ - IHttpRequestResponseWithMarkers applyMarkers( - IHttpRequestResponse httpRequestResponse, - List requestMarkers, - List responseMarkers); - - /** - * This method is used to obtain the descriptive name for the Burp tool - * identified by the tool flag provided. - * - * @param toolFlag A flag identifying a Burp tool ( TOOL_PROXY, - * TOOL_SCANNER, etc.). Tool flags are defined within this - * interface. - * @return The descriptive name for the specified tool. - */ - String getToolName(int toolFlag); - - /** - * This method is used to register a new Scanner issue. Note: - * Wherever possible, extensions should implement custom Scanner checks - * using IScannerCheck and report issues via those checks, so - * as to integrate with Burp's user-driven workflow, and ensure proper - * consolidation of duplicate reported issues. This method is only designed - * for tasks outside of the normal testing workflow, such as importing - * results from other scanning tools. - * - * @param issue An object created by the extension that implements the - * IScanIssue interface. - */ - void addScanIssue(IScanIssue issue); - - /** - * This method is used to create a new Burp Collaborator client context, - * which can be used to generate Burp Collaborator payloads and poll the - * Collaborator server for any network interactions that result from using - * those payloads. - * - * @return A new instance of IBurpCollaboratorClientContext - * that can be used to generate Collaborator payloads and retrieve - * interactions. - */ - IBurpCollaboratorClientContext createBurpCollaboratorClientContext(); - - /** - * This method parses the specified request and returns details of each - * request parameter. - * - * @param request The request to be parsed. - * @return An array of: String[] { name, value, type } - * containing details of the parameters contained within the request. - * @deprecated Use IExtensionHelpers.analyzeRequest() instead. - */ - @Deprecated - String[][] getParameters(byte[] request); - - /** - * This method parses the specified request and returns details of each HTTP - * header. - * - * @param message The request to be parsed. - * @return An array of HTTP headers. - * @deprecated Use IExtensionHelpers.analyzeRequest() or - * IExtensionHelpers.analyzeResponse() instead. - */ - @Deprecated - String[] getHeaders(byte[] message); - - /** - * This method can be used to register a new menu item which will appear on - * the various context menus that are used throughout Burp Suite to handle - * user-driven actions. - * - * @param menuItemCaption The caption to be displayed on the menu item. - * @param menuItemHandler The handler to be invoked when the user clicks on - * the menu item. - * @deprecated Use registerContextMenuFactory() instead. - */ - @Deprecated - void registerMenuItem( - String menuItemCaption, - IMenuItemHandler menuItemHandler); -} diff --git a/tool/src/main/java/burp/IContextMenuFactory.java b/tool/src/main/java/burp/IContextMenuFactory.java deleted file mode 100644 index 74d7d82..0000000 --- a/tool/src/main/java/burp/IContextMenuFactory.java +++ /dev/null @@ -1,39 +0,0 @@ -package burp; - -/* - * @(#)IContextMenuFactory.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ - -import javax.swing.JMenuItem; -import java.util.List; - -/** - * Extensions can implement this interface and then call - * IBurpExtenderCallbacks.registerContextMenuFactory() to register - * a factory for custom context menu items. - */ -public interface IContextMenuFactory -{ - /** - * This method will be called by Burp when the user invokes a context menu - * anywhere within Burp. The factory can then provide any custom context - * menu items that should be displayed in the context menu, based on the - * details of the menu invocation. - * - * @param invocation An object that implements the - * IContextMenuInvocation interface, which the extension can - * query to obtain details of the context menu invocation. - * @return A list of custom menu items (which may include sub-menus, - * checkbox menu items, etc.) that should be displayed. Extensions may - * return - * null from this method, to indicate that no menu items are - * required. - */ - List createMenuItems(IContextMenuInvocation invocation); -} diff --git a/tool/src/main/java/burp/IContextMenuInvocation.java b/tool/src/main/java/burp/IContextMenuInvocation.java deleted file mode 100644 index 8c56e29..0000000 --- a/tool/src/main/java/burp/IContextMenuInvocation.java +++ /dev/null @@ -1,156 +0,0 @@ -package burp; - -/* - * @(#)IContextMenuInvocation.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -import java.awt.event.InputEvent; - -/** - * This interface is used when Burp calls into an extension-provided - * IContextMenuFactory with details of a context menu invocation. - * The custom context menu factory can query this interface to obtain details of - * the invocation event, in order to determine what menu items should be - * displayed. - */ -public interface IContextMenuInvocation -{ - /** - * Used to indicate that the context menu is being invoked in a request - * editor. - */ - byte CONTEXT_MESSAGE_EDITOR_REQUEST = 0; - /** - * Used to indicate that the context menu is being invoked in a response - * editor. - */ - byte CONTEXT_MESSAGE_EDITOR_RESPONSE = 1; - /** - * Used to indicate that the context menu is being invoked in a non-editable - * request viewer. - */ - byte CONTEXT_MESSAGE_VIEWER_REQUEST = 2; - /** - * Used to indicate that the context menu is being invoked in a non-editable - * response viewer. - */ - byte CONTEXT_MESSAGE_VIEWER_RESPONSE = 3; - /** - * Used to indicate that the context menu is being invoked in the Target - * site map tree. - */ - byte CONTEXT_TARGET_SITE_MAP_TREE = 4; - /** - * Used to indicate that the context menu is being invoked in the Target - * site map table. - */ - byte CONTEXT_TARGET_SITE_MAP_TABLE = 5; - /** - * Used to indicate that the context menu is being invoked in the Proxy - * history. - */ - byte CONTEXT_PROXY_HISTORY = 6; - /** - * Used to indicate that the context menu is being invoked in the Scanner - * results. - */ - byte CONTEXT_SCANNER_RESULTS = 7; - /** - * Used to indicate that the context menu is being invoked in the Intruder - * payload positions editor. - */ - byte CONTEXT_INTRUDER_PAYLOAD_POSITIONS = 8; - /** - * Used to indicate that the context menu is being invoked in an Intruder - * attack results. - */ - byte CONTEXT_INTRUDER_ATTACK_RESULTS = 9; - /** - * Used to indicate that the context menu is being invoked in a search - * results window. - */ - byte CONTEXT_SEARCH_RESULTS = 10; - - /** - * This method can be used to retrieve the native Java input event that was - * the trigger for the context menu invocation. - * - * @return The InputEvent that was the trigger for the context - * menu invocation. - */ - InputEvent getInputEvent(); - - /** - * This method can be used to retrieve the Burp tool within which the - * context menu was invoked. - * - * @return A flag indicating the Burp tool within which the context menu was - * invoked. Burp tool flags are defined in the - * IBurpExtenderCallbacks interface. - */ - int getToolFlag(); - - /** - * This method can be used to retrieve the context within which the menu was - * invoked. - * - * @return An index indicating the context within which the menu was - * invoked. The indices used are defined within this interface. - */ - byte getInvocationContext(); - - /** - * This method can be used to retrieve the bounds of the user's selection - * into the current message, if applicable. - * - * @return An int[2] array containing the start and end offsets of the - * user's selection in the current message. If the user has not made any - * selection in the current message, both offsets indicate the position of - * the caret within the editor. If the menu is not being invoked from a - * message editor, the method returns null. - */ - int[] getSelectionBounds(); - - /** - * This method can be used to retrieve details of the HTTP requests / - * responses that were shown or selected by the user when the context menu - * was invoked. - * - * Note: For performance reasons, the objects returned from this - * method are tied to the originating context of the messages within the - * Burp UI. For example, if a context menu is invoked on the Proxy intercept - * panel, then the - * IHttpRequestResponse returned by this method will reflect - * the current contents of the interception panel, and this will change when - * the current message has been forwarded or dropped. If your extension - * needs to store details of the message for which the context menu has been - * invoked, then you should query those details from the - * IHttpRequestResponse at the time of invocation, or you - * should use - * IBurpExtenderCallbacks.saveBuffersToTempFiles() to create a - * persistent read-only copy of the - * IHttpRequestResponse. - * - * @return An array of IHttpRequestResponse objects - * representing the items that were shown or selected by the user when the - * context menu was invoked. This method returns null if no - * messages are applicable to the invocation. - */ - IHttpRequestResponse[] getSelectedMessages(); - - /** - * This method can be used to retrieve details of the Scanner issues that - * were selected by the user when the context menu was invoked. - * - * @return An array of IScanIssue objects representing the - * issues that were selected by the user when the context menu was invoked. - * This method returns null if no Scanner issues are applicable - * to the invocation. - */ - IScanIssue[] getSelectedIssues(); -} diff --git a/tool/src/main/java/burp/ICookie.java b/tool/src/main/java/burp/ICookie.java deleted file mode 100644 index 0641cba..0000000 --- a/tool/src/main/java/burp/ICookie.java +++ /dev/null @@ -1,61 +0,0 @@ -package burp; - -/* - * @(#)ICookie.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -import java.util.Date; - -/** - * This interface is used to hold details about an HTTP cookie. - */ -public interface ICookie -{ - /** - * This method is used to retrieve the domain for which the cookie is in - * scope. - * - * @return The domain for which the cookie is in scope. Note: For - * cookies that have been analyzed from responses (by calling - * IExtensionHelpers.analyzeResponse() and then - * IResponseInfo.getCookies(), the domain will be - * null if the response did not explicitly set a domain - * attribute for the cookie. - */ - String getDomain(); - - /** - * This method is used to retrieve the path for which the cookie is in - * scope. - * - * @return The path for which the cookie is in scope or null if none is set. - */ - String getPath(); - - /** - * This method is used to retrieve the expiration time for the cookie. - * - * @return The expiration time for the cookie, or - * null if none is set (i.e., for non-persistent session - * cookies). - */ - Date getExpiration(); - - /** - * This method is used to retrieve the name of the cookie. - * - * @return The name of the cookie. - */ - String getName(); - - /** - * This method is used to retrieve the value of the cookie. - * @return The value of the cookie. - */ - String getValue(); -} diff --git a/tool/src/main/java/burp/IExtensionHelpers.java b/tool/src/main/java/burp/IExtensionHelpers.java deleted file mode 100644 index 1666ace..0000000 --- a/tool/src/main/java/burp/IExtensionHelpers.java +++ /dev/null @@ -1,367 +0,0 @@ -package burp; - -/* - * @(#)IExtensionHelpers.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -import java.net.URL; -import java.util.List; - -/** - * This interface contains a number of helper methods, which extensions can use - * to assist with various common tasks that arise for Burp extensions. - * - * Extensions can call IBurpExtenderCallbacks.getHelpers to obtain - * an instance of this interface. - */ -public interface IExtensionHelpers -{ - - /** - * This method can be used to analyze an HTTP request, and obtain various - * key details about it. - * - * @param request An IHttpRequestResponse object containing the - * request to be analyzed. - * @return An IRequestInfo object that can be queried to obtain - * details about the request. - */ - IRequestInfo analyzeRequest(IHttpRequestResponse request); - - /** - * This method can be used to analyze an HTTP request, and obtain various - * key details about it. - * - * @param httpService The HTTP service associated with the request. This is - * optional and may be null, in which case the resulting - * IRequestInfo object will not include the full request URL. - * @param request The request to be analyzed. - * @return An IRequestInfo object that can be queried to obtain - * details about the request. - */ - IRequestInfo analyzeRequest(IHttpService httpService, byte[] request); - - /** - * This method can be used to analyze an HTTP request, and obtain various - * key details about it. The resulting IRequestInfo object will - * not include the full request URL. To obtain the full URL, use one of the - * other overloaded analyzeRequest() methods. - * - * @param request The request to be analyzed. - * @return An IRequestInfo object that can be queried to obtain - * details about the request. - */ - IRequestInfo analyzeRequest(byte[] request); - - /** - * This method can be used to analyze an HTTP response, and obtain various - * key details about it. - * - * @param response The response to be analyzed. - * @return An IResponseInfo object that can be queried to - * obtain details about the response. - */ - IResponseInfo analyzeResponse(byte[] response); - - /** - * This method can be used to retrieve details of a specified parameter - * within an HTTP request. Note: Use analyzeRequest() to - * obtain details of all parameters within the request. - * - * @param request The request to be inspected for the specified parameter. - * @param parameterName The name of the parameter to retrieve. - * @return An IParameter object that can be queried to obtain - * details about the parameter, or null if the parameter was - * not found. - */ - IParameter getRequestParameter(byte[] request, String parameterName); - - /** - * This method can be used to URL-decode the specified data. - * - * @param data The data to be decoded. - * @return The decoded data. - */ - String urlDecode(String data); - - /** - * This method can be used to URL-encode the specified data. Any characters - * that do not need to be encoded within HTTP requests are not encoded. - * - * @param data The data to be encoded. - * @return The encoded data. - */ - String urlEncode(String data); - - /** - * This method can be used to URL-decode the specified data. - * - * @param data The data to be decoded. - * @return The decoded data. - */ - byte[] urlDecode(byte[] data); - - /** - * This method can be used to URL-encode the specified data. Any characters - * that do not need to be encoded within HTTP requests are not encoded. - * - * @param data The data to be encoded. - * @return The encoded data. - */ - byte[] urlEncode(byte[] data); - - /** - * This method can be used to Base64-decode the specified data. - * - * @param data The data to be decoded. - * @return The decoded data. - */ - byte[] base64Decode(String data); - - /** - * This method can be used to Base64-decode the specified data. - * - * @param data The data to be decoded. - * @return The decoded data. - */ - byte[] base64Decode(byte[] data); - - /** - * This method can be used to Base64-encode the specified data. - * - * @param data The data to be encoded. - * @return The encoded data. - */ - String base64Encode(String data); - - /** - * This method can be used to Base64-encode the specified data. - * - * @param data The data to be encoded. - * @return The encoded data. - */ - String base64Encode(byte[] data); - - /** - * This method can be used to convert data from String form into an array of - * bytes. The conversion does not reflect any particular character set, and - * a character with the hex representation 0xWXYZ will always be converted - * into a byte with the representation 0xYZ. It performs the opposite - * conversion to the method bytesToString(), and byte-based - * data that is converted to a String and back again using these two methods - * is guaranteed to retain its integrity (which may not be the case with - * conversions that reflect a given character set). - * - * @param data The data to be converted. - * @return The converted data. - */ - byte[] stringToBytes(String data); - - /** - * This method can be used to convert data from an array of bytes into - * String form. The conversion does not reflect any particular character - * set, and a byte with the representation 0xYZ will always be converted - * into a character with the hex representation 0x00YZ. It performs the - * opposite conversion to the method stringToBytes(), and - * byte-based data that is converted to a String and back again using these - * two methods is guaranteed to retain its integrity (which may not be the - * case with conversions that reflect a given character set). - * - * @param data The data to be converted. - * @return The converted data. - */ - String bytesToString(byte[] data); - - /** - * This method searches a piece of data for the first occurrence of a - * specified pattern. It works on byte-based data in a way that is similar - * to the way the native Java method String.indexOf() works on - * String-based data. - * - * @param data The data to be searched. - * @param pattern The pattern to be searched for. - * @param caseSensitive Flags whether or not the search is case-sensitive. - * @param from The offset within data where the search should - * begin. - * @param to The offset within data where the search should - * end. - * @return The offset of the first occurrence of the pattern within the - * specified bounds, or -1 if no match is found. - */ - int indexOf( - byte[] data, - byte[] pattern, - boolean caseSensitive, - int from, - int to); - - /** - * This method builds an HTTP message containing the specified headers and - * message body. If applicable, the Content-Length header will be added or - * updated, based on the length of the body. - * - * @param headers A list of headers to include in the message. - * @param body The body of the message, of null if the message - * has an empty body. - * @return The resulting full HTTP message. - */ - byte[] buildHttpMessage(List headers, byte[] body); - - /** - * This method creates a GET request to the specified URL. The headers used - * in the request are determined by the Request headers settings as - * configured in Burp Spider's options. - * - * @param url The URL to which the request should be made. - * @return A request to the specified URL. - */ - byte[] buildHttpRequest(URL url); - - /** - * This method adds a new parameter to an HTTP request, and if appropriate - * updates the Content-Length header. - * - * @param request The request to which the parameter should be added. - * @param parameter An IParameter object containing details of - * the parameter to be added. Supported parameter types are: - * PARAM_URL, PARAM_BODY and - * PARAM_COOKIE. - * @return A new HTTP request with the new parameter added. - */ - byte[] addParameter(byte[] request, IParameter parameter); - - /** - * This method removes a parameter from an HTTP request, and if appropriate - * updates the Content-Length header. - * - * @param request The request from which the parameter should be removed. - * @param parameter An IParameter object containing details of - * the parameter to be removed. Supported parameter types are: - * PARAM_URL, PARAM_BODY and - * PARAM_COOKIE. - * @return A new HTTP request with the parameter removed. - */ - byte[] removeParameter(byte[] request, IParameter parameter); - - /** - * This method updates the value of a parameter within an HTTP request, and - * if appropriate updates the Content-Length header. Note: This - * method can only be used to update the value of an existing parameter of a - * specified type. If you need to change the type of an existing parameter, - * you should first call removeParameter() to remove the - * parameter with the old type, and then call addParameter() to - * add a parameter with the new type. - * - * @param request The request containing the parameter to be updated. - * @param parameter An IParameter object containing details of - * the parameter to be updated. Supported parameter types are: - * PARAM_URL, PARAM_BODY and - * PARAM_COOKIE. - * @return A new HTTP request with the parameter updated. - */ - byte[] updateParameter(byte[] request, IParameter parameter); - - /** - * This method can be used to toggle a request's method between GET and - * POST. Parameters are relocated between the URL query string and message - * body as required, and the Content-Length header is created or removed as - * applicable. - * - * @param request The HTTP request whose method should be toggled. - * @return A new HTTP request using the toggled method. - */ - byte[] toggleRequestMethod(byte[] request); - - /** - * This method constructs an IHttpService object based on the - * details provided. - * - * @param host The HTTP service host. - * @param port The HTTP service port. - * @param protocol The HTTP service protocol. - * @return An IHttpService object based on the details - * provided. - */ - IHttpService buildHttpService(String host, int port, String protocol); - - /** - * This method constructs an IHttpService object based on the - * details provided. - * - * @param host The HTTP service host. - * @param port The HTTP service port. - * @param useHttps Flags whether the HTTP service protocol is HTTPS or HTTP. - * @return An IHttpService object based on the details - * provided. - */ - IHttpService buildHttpService(String host, int port, boolean useHttps); - - /** - * This method constructs an IParameter object based on the - * details provided. - * - * @param name The parameter name. - * @param value The parameter value. - * @param type The parameter type, as defined in the IParameter - * interface. - * @return An IParameter object based on the details provided. - */ - IParameter buildParameter(String name, String value, byte type); - - /** - * This method constructs an IHttpHeader object based on the - * details provided. - * - * @param name The header name. - * @param value The header value. - * @return An IHttpHeader object based on the details provided. - */ - IHttpHeader buildHeader(String name, String value); - - /** - * This method constructs an IScannerInsertionPoint object - * based on the details provided. It can be used to quickly create a simple - * insertion point based on a fixed payload location within a base request. - * - * @param insertionPointName The name of the insertion point. - * @param baseRequest The request from which to build scan requests. - * @param from The offset of the start of the payload location. - * @param to The offset of the end of the payload location. - * @return An IScannerInsertionPoint object based on the - * details provided. - */ - IScannerInsertionPoint makeScannerInsertionPoint( - String insertionPointName, - byte[] baseRequest, - int from, - int to); - - /** - * This method analyzes one or more responses to identify variations in a - * number of attributes and returns an IResponseVariations - * object that can be queried to obtain details of the variations. - * - * @param responses The responses to analyze. - * @return An IResponseVariations object representing the - * variations in the responses. - */ - IResponseVariations analyzeResponseVariations(byte[]... responses); - - /** - * This method analyzes one or more responses to identify the number of - * occurrences of the specified keywords and returns an - * IResponseKeywords object that can be queried to obtain - * details of the number of occurrences of each keyword. - * - * @param keywords The keywords to look for. - * @param responses The responses to analyze. - * @return An IResponseKeywords object representing the counts - * of the keywords appearing in the responses. - */ - IResponseKeywords analyzeResponseKeywords(List keywords, byte[]... responses); -} diff --git a/tool/src/main/java/burp/IExtensionStateListener.java b/tool/src/main/java/burp/IExtensionStateListener.java deleted file mode 100644 index 8817525..0000000 --- a/tool/src/main/java/burp/IExtensionStateListener.java +++ /dev/null @@ -1,27 +0,0 @@ -package burp; - -/* - * @(#)IExtensionStateListener.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * Extensions can implement this interface and then call - * IBurpExtenderCallbacks.registerExtensionStateListener() to - * register an extension state listener. The listener will be notified of - * changes to the extension's state. Note: Any extensions that start - * background threads or open system resources (such as files or database - * connections) should register a listener and terminate threads / close - * resources when the extension is unloaded. - */ -public interface IExtensionStateListener -{ - /** - * This method is called when the extension is unloaded. - */ - void extensionUnloaded(); -} diff --git a/tool/src/main/java/burp/IHttpHeader.java b/tool/src/main/java/burp/IHttpHeader.java deleted file mode 100644 index 512a41e..0000000 --- a/tool/src/main/java/burp/IHttpHeader.java +++ /dev/null @@ -1,27 +0,0 @@ -package burp; -/* - * @(#)IHttpHeader.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * This interface is used to hold details about an HTTP/2 header. - */ -public interface IHttpHeader -{ - /** - * This method is used to retrieve the name of the header. - * @return The name of the header. - */ - String getName(); - - /** - * This method is used to retrieve the value of the header. - * @return The value of the header. - */ - String getValue(); -} diff --git a/tool/src/main/java/burp/IHttpListener.java b/tool/src/main/java/burp/IHttpListener.java deleted file mode 100644 index 1901a05..0000000 --- a/tool/src/main/java/burp/IHttpListener.java +++ /dev/null @@ -1,38 +0,0 @@ -package burp; - -/* - * @(#)IHttpListener.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * Extensions can implement this interface and then call - * IBurpExtenderCallbacks.registerHttpListener() to register an - * HTTP listener. The listener will be notified of requests and responses made - * by any Burp tool. Extensions can perform custom analysis or modification of - * these messages by registering an HTTP listener. - */ -public interface IHttpListener -{ - /** - * This method is invoked when an HTTP request is about to be issued, and - * when an HTTP response has been received. - * - * @param toolFlag A flag indicating the Burp tool that issued the request. - * Burp tool flags are defined in the - * IBurpExtenderCallbacks interface. - * @param messageIsRequest Flags whether the method is being invoked for a - * request or response. - * @param messageInfo Details of the request / response to be processed. - * Extensions can call the setter methods on this object to update the - * current message and so modify Burp's behavior. - */ - void processHttpMessage( - int toolFlag, - boolean messageIsRequest, - IHttpRequestResponse messageInfo); -} diff --git a/tool/src/main/java/burp/IHttpRequestResponse.java b/tool/src/main/java/burp/IHttpRequestResponse.java deleted file mode 100644 index cc316e9..0000000 --- a/tool/src/main/java/burp/IHttpRequestResponse.java +++ /dev/null @@ -1,102 +0,0 @@ -package burp; - -/* - * @(#)IHttpRequestResponse.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * This interface is used to retrieve and update details about HTTP messages. - * - * Note: The setter methods generally can only be used before the message - * has been processed, and not in read-only contexts. The getter methods - * relating to response details can only be used after the request has been - * issued. - */ -public interface IHttpRequestResponse -{ - /** - * This method is used to retrieve the request message. - * - * @return The request message. - */ - byte[] getRequest(); - - /** - * This method is used to update the request message. - * - * @param message The new request message. - */ - void setRequest(byte[] message); - - /** - * This method is used to retrieve the response message. - * - * @return The response message. - */ - byte[] getResponse(); - - /** - * This method is used to update the response message. - * - * @param message The new response message. - */ - void setResponse(byte[] message); - - /** - * This method is used to retrieve the user-annotated comment for this item, - * if applicable. - * - * @return The user-annotated comment for this item, or null if none is set. - */ - String getComment(); - - /** - * This method is used to update the user-annotated comment for this item. - * - * @param comment The comment to be assigned to this item. - */ - void setComment(String comment); - - /** - * This method is used to retrieve the user-annotated highlight for this - * item, if applicable. - * - * @return The user-annotated highlight for this item, or null if none is - * set. - */ - String getHighlight(); - - /** - * This method is used to update the user-annotated highlight for this item. - * - * @param color The highlight color to be assigned to this item. Accepted - * values are: red, orange, yellow, green, cyan, blue, pink, magenta, gray, - * or a null String to clear any existing highlight. - */ - void setHighlight(String color); - - /** - * This method is used to retrieve the HTTP service for this request / - * response. - * - * @return An - * IHttpService object containing details of the HTTP service. - */ - IHttpService getHttpService(); - - /** - * This method is used to update the HTTP service for this request / - * response. - * - * @param httpService An - * IHttpService object containing details of the new HTTP - * service. - */ - void setHttpService(IHttpService httpService); - -} diff --git a/tool/src/main/java/burp/IHttpRequestResponsePersisted.java b/tool/src/main/java/burp/IHttpRequestResponsePersisted.java deleted file mode 100644 index 1d75427..0000000 --- a/tool/src/main/java/burp/IHttpRequestResponsePersisted.java +++ /dev/null @@ -1,25 +0,0 @@ -package burp; - -/* - * @(#)IHttpRequestResponsePersisted.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * This interface is used for an - * IHttpRequestResponse object whose request and response messages - * have been saved to temporary files using - * IBurpExtenderCallbacks.saveBuffersToTempFiles(). - */ -public interface IHttpRequestResponsePersisted extends IHttpRequestResponse -{ - /** - * This method is deprecated and no longer performs any action. - */ - @Deprecated - void deleteTempFiles(); -} diff --git a/tool/src/main/java/burp/IHttpRequestResponseWithMarkers.java b/tool/src/main/java/burp/IHttpRequestResponseWithMarkers.java deleted file mode 100644 index e9f98d8..0000000 --- a/tool/src/main/java/burp/IHttpRequestResponseWithMarkers.java +++ /dev/null @@ -1,44 +0,0 @@ -package burp; - -/* - * @(#)IHttpRequestResponseWithMarkers.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -import java.util.List; - -/** - * This interface is used for an - * IHttpRequestResponse object that has had markers applied. - * Extensions can create instances of this interface using - * IBurpExtenderCallbacks.applyMarkers(), or provide their own - * implementation. Markers are used in various situations, such as specifying - * Intruder payload positions, Scanner insertion points, and highlights in - * Scanner issues. - */ -public interface IHttpRequestResponseWithMarkers extends IHttpRequestResponse -{ - /** - * This method returns the details of the request markers. - * - * @return A list of index pairs representing the offsets of markers for the - * request message. Each item in the list is an int[2] array containing the - * start and end offsets for the marker. The method may return - * null if no request markers are defined. - */ - List getRequestMarkers(); - - /** - * This method returns the details of the response markers. - * - * @return A list of index pairs representing the offsets of markers for the - * response message. Each item in the list is an int[2] array containing the - * start and end offsets for the marker. The method may return - * null if no response markers are defined. - */ - List getResponseMarkers(); -} diff --git a/tool/src/main/java/burp/IHttpService.java b/tool/src/main/java/burp/IHttpService.java deleted file mode 100644 index bb87a8e..0000000 --- a/tool/src/main/java/burp/IHttpService.java +++ /dev/null @@ -1,39 +0,0 @@ -package burp; - -/* - * @(#)IHttpService.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * This interface is used to provide details about an HTTP service, to which - * HTTP requests can be sent. - */ -public interface IHttpService -{ - /** - * This method returns the hostname or IP address for the service. - * - * @return The hostname or IP address for the service. - */ - String getHost(); - - /** - * This method returns the port number for the service. - * - * @return The port number for the service. - */ - int getPort(); - - /** - * This method returns the protocol for the service. - * - * @return The protocol for the service. Expected values are "http" or - * "https". - */ - String getProtocol(); -} diff --git a/tool/src/main/java/burp/IInterceptedProxyMessage.java b/tool/src/main/java/burp/IInterceptedProxyMessage.java deleted file mode 100644 index 4100e9a..0000000 --- a/tool/src/main/java/burp/IInterceptedProxyMessage.java +++ /dev/null @@ -1,116 +0,0 @@ -package burp; - -/* - * @(#)IInterceptedProxyMessage.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -import java.net.InetAddress; - -/** - * This interface is used to represent an HTTP message that has been intercepted - * by Burp Proxy. Extensions can register an - * IProxyListener to receive details of proxy messages using this - * interface. * - */ -public interface IInterceptedProxyMessage -{ - /** - * This action causes Burp Proxy to follow the current interception rules to - * determine the appropriate action to take for the message. - */ - int ACTION_FOLLOW_RULES = 0; - /** - * This action causes Burp Proxy to present the message to the user for - * manual review or modification. - */ - int ACTION_DO_INTERCEPT = 1; - /** - * This action causes Burp Proxy to forward the message to the remote server - * or client, without presenting it to the user. - */ - int ACTION_DONT_INTERCEPT = 2; - /** - * This action causes Burp Proxy to drop the message. - */ - int ACTION_DROP = 3; - /** - * This action causes Burp Proxy to follow the current interception rules to - * determine the appropriate action to take for the message, and then make a - * second call to processProxyMessage. - */ - int ACTION_FOLLOW_RULES_AND_REHOOK = 0x10; - /** - * This action causes Burp Proxy to present the message to the user for - * manual review or modification, and then make a second call to - * processProxyMessage. - */ - int ACTION_DO_INTERCEPT_AND_REHOOK = 0x11; - /** - * This action causes Burp Proxy to skip user interception, and then make a - * second call to processProxyMessage. - */ - int ACTION_DONT_INTERCEPT_AND_REHOOK = 0x12; - - /** - * This method retrieves a unique reference number for this - * request/response. - * - * @return An identifier that is unique to a single request/response pair. - * Extensions can use this to correlate details of requests and responses - * and perform processing on the response message accordingly. - */ - int getMessageReference(); - - /** - * This method retrieves details of the intercepted message. - * - * @return An IHttpRequestResponse object containing details of - * the intercepted message. - */ - IHttpRequestResponse getMessageInfo(); - - /** - * This method retrieves the currently defined interception action. The - * default action is - * ACTION_FOLLOW_RULES. If multiple proxy listeners are - * registered, then other listeners may already have modified the - * interception action before it reaches the current listener. This method - * can be used to determine whether this has occurred. - * - * @return The currently defined interception action. Possible values are - * defined within this interface. - */ - int getInterceptAction(); - - /** - * This method is used to update the interception action. - * - * @param interceptAction The new interception action. Possible values are - * defined within this interface. - */ - void setInterceptAction(int interceptAction); - - /** - * This method retrieves the name of the Burp Proxy listener that is - * processing the intercepted message. - * - * @return The name of the Burp Proxy listener that is processing the - * intercepted message. The format is the same as that shown in the Proxy - * Listeners UI - for example, "127.0.0.1:8080". - */ - String getListenerInterface(); - - /** - * This method retrieves the client IP address from which the request for - * the intercepted message was received. - * - * @return The client IP address from which the request for the intercepted - * message was received. - */ - InetAddress getClientIpAddress(); -} diff --git a/tool/src/main/java/burp/IIntruderAttack.java b/tool/src/main/java/burp/IIntruderAttack.java deleted file mode 100644 index b0900ea..0000000 --- a/tool/src/main/java/burp/IIntruderAttack.java +++ /dev/null @@ -1,31 +0,0 @@ -package burp; - -/* - * @(#)IIntruderAttack.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * This interface is used to hold details about an Intruder attack. - */ -public interface IIntruderAttack -{ - /** - * This method is used to retrieve the HTTP service for the attack. - * - * @return The HTTP service for the attack. - */ - IHttpService getHttpService(); - - /** - * This method is used to retrieve the request template for the attack. - * - * @return The request template for the attack. - */ - byte[] getRequestTemplate(); - -} diff --git a/tool/src/main/java/burp/IIntruderPayloadGenerator.java b/tool/src/main/java/burp/IIntruderPayloadGenerator.java deleted file mode 100644 index 9307c5b..0000000 --- a/tool/src/main/java/burp/IIntruderPayloadGenerator.java +++ /dev/null @@ -1,50 +0,0 @@ -package burp; - -/* - * @(#)IIntruderPayloadGenerator.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * This interface is used for custom Intruder payload generators. Extensions - * that have registered an - * IIntruderPayloadGeneratorFactory must return a new instance of - * this interface when required as part of a new Intruder attack. - */ -public interface IIntruderPayloadGenerator -{ - /** - * This method is used by Burp to determine whether the payload generator is - * able to provide any further payloads. - * - * @return Extensions should return - * false when all the available payloads have been used up, - * otherwise - * true. - */ - boolean hasMorePayloads(); - - /** - * This method is used by Burp to obtain the value of the next payload. - * - * @param baseValue The base value of the current payload position. This - * value may be - * null if the concept of a base value is not applicable (e.g. - * in a battering ram attack). - * @return The next payload to use in the attack. - */ - byte[] getNextPayload(byte[] baseValue); - - /** - * This method is used by Burp to reset the state of the payload generator - * so that the next call to - * getNextPayload() returns the first payload again. This - * method will be invoked when an attack uses the same payload generator for - * more than one payload position, for example in a sniper attack. - */ - void reset(); -} diff --git a/tool/src/main/java/burp/IIntruderPayloadGeneratorFactory.java b/tool/src/main/java/burp/IIntruderPayloadGeneratorFactory.java deleted file mode 100644 index f765b0a..0000000 --- a/tool/src/main/java/burp/IIntruderPayloadGeneratorFactory.java +++ /dev/null @@ -1,40 +0,0 @@ -package burp; - -/* - * @(#)IIntruderPayloadGeneratorFactory.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * Extensions can implement this interface and then call - * IBurpExtenderCallbacks.registerIntruderPayloadGeneratorFactory() - * to register a factory for custom Intruder payloads. - */ -public interface IIntruderPayloadGeneratorFactory -{ - /** - * This method is used by Burp to obtain the name of the payload generator. - * This will be displayed as an option within the Intruder UI when the user - * selects to use extension-generated payloads. - * - * @return The name of the payload generator. - */ - String getGeneratorName(); - - /** - * This method is used by Burp when the user starts an Intruder attack that - * uses this payload generator. - * - * @param attack An - * IIntruderAttack object that can be queried to obtain details - * about the attack in which the payload generator will be used. - * @return A new instance of - * IIntruderPayloadGenerator that will be used to generate - * payloads for the attack. - */ - IIntruderPayloadGenerator createNewInstance(IIntruderAttack attack); -} diff --git a/tool/src/main/java/burp/IIntruderPayloadProcessor.java b/tool/src/main/java/burp/IIntruderPayloadProcessor.java deleted file mode 100644 index dc80757..0000000 --- a/tool/src/main/java/burp/IIntruderPayloadProcessor.java +++ /dev/null @@ -1,45 +0,0 @@ -package burp; - -/* - * @(#)IIntruderPayloadProcessor.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * Extensions can implement this interface and then call - * IBurpExtenderCallbacks.registerIntruderPayloadProcessor() to - * register a custom Intruder payload processor. - */ -public interface IIntruderPayloadProcessor -{ - /** - * This method is used by Burp to obtain the name of the payload processor. - * This will be displayed as an option within the Intruder UI when the user - * selects to use an extension-provided payload processor. - * - * @return The name of the payload processor. - */ - String getProcessorName(); - - /** - * This method is invoked by Burp each time the processor should be applied - * to an Intruder payload. - * - * @param currentPayload The value of the payload to be processed. - * @param originalPayload The value of the original payload prior to - * processing by any already-applied processing rules. - * @param baseValue The base value of the payload position, which will be - * replaced with the current payload. - * @return The value of the processed payload. This may be - * null to indicate that the current payload should be skipped, - * and the attack will move directly to the next payload. - */ - byte[] processPayload( - byte[] currentPayload, - byte[] originalPayload, - byte[] baseValue); -} diff --git a/tool/src/main/java/burp/IMenuItemHandler.java b/tool/src/main/java/burp/IMenuItemHandler.java deleted file mode 100644 index cc1f2da..0000000 --- a/tool/src/main/java/burp/IMenuItemHandler.java +++ /dev/null @@ -1,36 +0,0 @@ -package burp; - -/* - * @(#)IMenuItemHandler.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * Extensions can implement this interface and then call - * IBurpExtenderCallbacks.registerMenuItem() to register a custom - * context menu item. - * - * @deprecated Use - * IContextMenuFactory instead. - */ -@Deprecated -public interface IMenuItemHandler -{ - /** - * This method is invoked by Burp Suite when the user clicks on a custom - * menu item which the extension has registered with Burp. - * - * @param menuItemCaption The caption of the menu item which was clicked. - * This parameter enables extensions to provide a single implementation - * which handles multiple different menu items. - * @param messageInfo Details of the HTTP message(s) for which the context - * menu was displayed. - */ - void menuItemClicked( - String menuItemCaption, - IHttpRequestResponse[] messageInfo); -} diff --git a/tool/src/main/java/burp/IMessageEditor.java b/tool/src/main/java/burp/IMessageEditor.java deleted file mode 100644 index fd0c740..0000000 --- a/tool/src/main/java/burp/IMessageEditor.java +++ /dev/null @@ -1,77 +0,0 @@ -package burp; - -/* - * @(#)IMessageEditor.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -import java.awt.Component; - -/** - * This interface is used to provide extensions with an instance of Burp's HTTP - * message editor, for the extension to use in its own UI. Extensions should - * call IBurpExtenderCallbacks.createMessageEditor() to obtain an - * instance of this interface. - */ -public interface IMessageEditor -{ - - /** - * This method returns the UI component of the editor, for extensions to add - * to their own UI. - * - * @return The UI component of the editor. - */ - Component getComponent(); - - /** - * This method is used to display an HTTP message in the editor. - * - * @param message The HTTP message to be displayed. - * @param isRequest Flags whether the message is an HTTP request or - * response. - */ - void setMessage(byte[] message, boolean isRequest); - - /** - * This method is used to retrieve the currently displayed message, which - * may have been modified by the user. - * - * @return The currently displayed HTTP message. - */ - byte[] getMessage(); - - /** - * This method is used to determine whether the current message has been - * modified by the user. - * - * @return An indication of whether the current message has been modified by - * the user since it was first displayed. - */ - boolean isMessageModified(); - - /** - * This method returns the data that is currently selected by the user. - * - * @return The data that is currently selected by the user, or - * null if no selection is made. - */ - byte[] getSelectedData(); - - /** - * This method can be used to retrieve the bounds of the user's selection - * into the displayed message, if applicable. - * - * @return An int[2] array containing the start and end offsets of the - * user's selection within the displayed message. If the user has not made - * any selection in the current message, both offsets indicate the position - * of the caret within the editor. For some editor views, the concept of - * selection within the message does not apply, in which case this method - * returns null. - */ - int[] getSelectionBounds(); -} diff --git a/tool/src/main/java/burp/IMessageEditorController.java b/tool/src/main/java/burp/IMessageEditorController.java deleted file mode 100644 index 7fda272..0000000 --- a/tool/src/main/java/burp/IMessageEditorController.java +++ /dev/null @@ -1,49 +0,0 @@ -package burp; - -/* - * @(#)IMessageEditorController.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * This interface is used by an - * IMessageEditor to obtain details about the currently displayed - * message. Extensions that create instances of Burp's HTTP message editor can - * optionally provide an implementation of - * IMessageEditorController, which the editor will invoke when it - * requires further information about the current message (for example, to send - * it to another Burp tool). Extensions that provide custom editor tabs via an - * IMessageEditorTabFactory will receive a reference to an - * IMessageEditorController object for each tab instance they - * generate, which the tab can invoke if it requires further information about - * the current message. - */ -public interface IMessageEditorController -{ - /** - * This method is used to retrieve the HTTP service for the current message. - * - * @return The HTTP service for the current message. - */ - IHttpService getHttpService(); - - /** - * This method is used to retrieve the HTTP request associated with the - * current message (which may itself be a response). - * - * @return The HTTP request associated with the current message. - */ - byte[] getRequest(); - - /** - * This method is used to retrieve the HTTP response associated with the - * current message (which may itself be a request). - * - * @return The HTTP response associated with the current message. - */ - byte[] getResponse(); -} diff --git a/tool/src/main/java/burp/IMessageEditorTab.java b/tool/src/main/java/burp/IMessageEditorTab.java deleted file mode 100644 index 853c35e..0000000 --- a/tool/src/main/java/burp/IMessageEditorTab.java +++ /dev/null @@ -1,103 +0,0 @@ -package burp; - -/* - * @(#)IMessageEditorTab.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -import java.awt.Component; - -/** - * Extensions that register an - * IMessageEditorTabFactory must return instances of this - * interface, which Burp will use to create custom tabs within its HTTP message - * editors. - */ -public interface IMessageEditorTab -{ - /** - * This method returns the caption that should appear on the custom tab when - * it is displayed. Note: Burp invokes this method once when the tab - * is first generated, and the same caption will be used every time the tab - * is displayed. - * - * @return The caption that should appear on the custom tab when it is - * displayed. - */ - String getTabCaption(); - - /** - * This method returns the component that should be used as the contents of - * the custom tab when it is displayed. Note: Burp invokes this - * method once when the tab is first generated, and the same component will - * be used every time the tab is displayed. - * - * @return The component that should be used as the contents of the custom - * tab when it is displayed. - */ - Component getUiComponent(); - - /** - * The hosting editor will invoke this method before it displays a new HTTP - * message, so that the custom tab can indicate whether it should be enabled - * for that message. - * - * @param content The message that is about to be displayed, or a zero-length - * array if the existing message is to be cleared. - * @param isRequest Indicates whether the message is a request or a - * response. - * @return The method should return - * true if the custom tab is able to handle the specified - * message, and so will be displayed within the editor. Otherwise, the tab - * will be hidden while this message is displayed. - */ - boolean isEnabled(byte[] content, boolean isRequest); - - /** - * The hosting editor will invoke this method to display a new message or to - * clear the existing message. This method will only be called with a new - * message if the tab has already returned - * true to a call to - * isEnabled() with the same message details. - * - * @param content The message that is to be displayed, or - * null if the tab should clear its contents and disable any - * editable controls. - * @param isRequest Indicates whether the message is a request or a - * response. - */ - void setMessage(byte[] content, boolean isRequest); - - /** - * This method returns the currently displayed message. - * - * @return The currently displayed message. - */ - byte[] getMessage(); - - /** - * This method is used to determine whether the currently displayed message - * has been modified by the user. The hosting editor will always call - * getMessage() before calling this method, so any pending - * edits should be completed within - * getMessage(). - * - * @return The method should return - * true if the user has modified the current message since it - * was first displayed. - */ - boolean isModified(); - - /** - * This method is used to retrieve the data that is currently selected by - * the user. - * - * @return The data that is currently selected by the user. This may be - * null if no selection is currently made. - */ - byte[] getSelectedData(); -} diff --git a/tool/src/main/java/burp/IMessageEditorTabFactory.java b/tool/src/main/java/burp/IMessageEditorTabFactory.java deleted file mode 100644 index 45f46df..0000000 --- a/tool/src/main/java/burp/IMessageEditorTabFactory.java +++ /dev/null @@ -1,39 +0,0 @@ -package burp; - -/* - * @(#)IMessageEditorTabFactory.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * Extensions can implement this interface and then call - * IBurpExtenderCallbacks.registerMessageEditorTabFactory() to - * register a factory for custom message editor tabs. This allows extensions to - * provide custom rendering or editing of HTTP messages, within Burp's own HTTP - * editor. - */ -public interface IMessageEditorTabFactory -{ - /** - * Burp will call this method once for each HTTP message editor, and the - * factory should provide a new instance of an - * IMessageEditorTab object. - * - * @param controller An - * IMessageEditorController object, which the new tab can query - * to retrieve details about the currently displayed message. This may be - * null for extension-invoked message editors where the - * extension has not provided an editor controller. - * @param editable Indicates whether the hosting editor is editable or - * read-only. - * @return A new - * IMessageEditorTab object for use within the message editor. - */ - IMessageEditorTab createNewInstance( - IMessageEditorController controller, - boolean editable); -} diff --git a/tool/src/main/java/burp/IParameter.java b/tool/src/main/java/burp/IParameter.java deleted file mode 100644 index 367c7fd..0000000 --- a/tool/src/main/java/burp/IParameter.java +++ /dev/null @@ -1,104 +0,0 @@ -package burp; - -/* - * @(#)IParameter.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * This interface is used to hold details about an HTTP request parameter. - */ -public interface IParameter -{ - /** - * Used to indicate a parameter within the URL query string. - */ - byte PARAM_URL = 0; - /** - * Used to indicate a parameter within the message body. - */ - byte PARAM_BODY = 1; - /** - * Used to indicate an HTTP cookie. - */ - byte PARAM_COOKIE = 2; - /** - * Used to indicate an item of data within an XML structure. - */ - byte PARAM_XML = 3; - /** - * Used to indicate the value of a tag attribute within an XML structure. - */ - byte PARAM_XML_ATTR = 4; - /** - * Used to indicate the value of a parameter attribute within a multi-part - * message body (such as the name of an uploaded file). - */ - byte PARAM_MULTIPART_ATTR = 5; - /** - * Used to indicate an item of data within a JSON structure. - */ - byte PARAM_JSON = 6; - - /** - * This method is used to retrieve the parameter type. - * - * @return The parameter type. The available types are defined within this - * interface. - */ - byte getType(); - - /** - * This method is used to retrieve the parameter name. - * - * @return The parameter name. - */ - String getName(); - - /** - * This method is used to retrieve the parameter value. - * - * @return The parameter value. - */ - String getValue(); - - /** - * This method is used to retrieve the start offset of the parameter name - * within the HTTP request. - * - * @return The start offset of the parameter name within the HTTP request, - * or -1 if the parameter is not associated with a specific request. - */ - int getNameStart(); - - /** - * This method is used to retrieve the end offset of the parameter name - * within the HTTP request. - * - * @return The end offset of the parameter name within the HTTP request, or - * -1 if the parameter is not associated with a specific request. - */ - int getNameEnd(); - - /** - * This method is used to retrieve the start offset of the parameter value - * within the HTTP request. - * - * @return The start offset of the parameter value within the HTTP request, - * or -1 if the parameter is not associated with a specific request. - */ - int getValueStart(); - - /** - * This method is used to retrieve the end offset of the parameter value - * within the HTTP request. - * - * @return The end offset of the parameter value within the HTTP request, or - * -1 if the parameter is not associated with a specific request. - */ - int getValueEnd(); -} diff --git a/tool/src/main/java/burp/IProxyListener.java b/tool/src/main/java/burp/IProxyListener.java deleted file mode 100644 index daaf55c..0000000 --- a/tool/src/main/java/burp/IProxyListener.java +++ /dev/null @@ -1,37 +0,0 @@ -package burp; - -/* - * @(#)IProxyListener.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * Extensions can implement this interface and then call - * IBurpExtenderCallbacks.registerProxyListener() to register a - * Proxy listener. The listener will be notified of requests and responses being - * processed by the Proxy tool. Extensions can perform custom analysis or - * modification of these messages, and control in-UI message interception, by - * registering a proxy listener. - */ -public interface IProxyListener -{ - /** - * This method is invoked when an HTTP message is being processed by the - * Proxy. - * - * @param messageIsRequest Indicates whether the HTTP message is a request - * or a response. - * @param message An - * IInterceptedProxyMessage object that extensions can use to - * query and update details of the message, and control whether the message - * should be intercepted and displayed to the user for manual review or - * modification. - */ - void processProxyMessage( - boolean messageIsRequest, - IInterceptedProxyMessage message); -} diff --git a/tool/src/main/java/burp/IRequestInfo.java b/tool/src/main/java/burp/IRequestInfo.java deleted file mode 100644 index a98ff80..0000000 --- a/tool/src/main/java/burp/IRequestInfo.java +++ /dev/null @@ -1,95 +0,0 @@ -package burp; - -/* - * @(#)IRequestInfo.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -import java.net.URL; -import java.util.List; - -/** - * This interface is used to retrieve key details about an HTTP request. - * Extensions can obtain an - * IRequestInfo object for a given request by calling - * IExtensionHelpers.analyzeRequest(). - */ -public interface IRequestInfo -{ - /** - * Used to indicate that there is no content. - */ - byte CONTENT_TYPE_NONE = 0; - /** - * Used to indicate URL-encoded content. - */ - byte CONTENT_TYPE_URL_ENCODED = 1; - /** - * Used to indicate multi-part content. - */ - byte CONTENT_TYPE_MULTIPART = 2; - /** - * Used to indicate XML content. - */ - byte CONTENT_TYPE_XML = 3; - /** - * Used to indicate JSON content. - */ - byte CONTENT_TYPE_JSON = 4; - /** - * Used to indicate AMF content. - */ - byte CONTENT_TYPE_AMF = 5; - /** - * Used to indicate unknown content. - */ - byte CONTENT_TYPE_UNKNOWN = -1; - - /** - * This method is used to obtain the HTTP method used in the request. - * - * @return The HTTP method used in the request. - */ - String getMethod(); - - /** - * This method is used to obtain the URL in the request. - * - * @return The URL in the request. - */ - URL getUrl(); - - /** - * This method is used to obtain the HTTP headers contained in the request. - * - * @return The HTTP headers contained in the request. - */ - List getHeaders(); - - /** - * This method is used to obtain the parameters contained in the request. - * - * @return The parameters contained in the request. - */ - List getParameters(); - - /** - * This method is used to obtain the offset within the request where the - * message body begins. - * - * @return The offset within the request where the message body begins. - */ - int getBodyOffset(); - - /** - * This method is used to obtain the content type of the message body. - * - * @return An indication of the content type of the message body. Available - * types are defined within this interface. - */ - byte getContentType(); -} diff --git a/tool/src/main/java/burp/IResponseInfo.java b/tool/src/main/java/burp/IResponseInfo.java deleted file mode 100644 index 9152a20..0000000 --- a/tool/src/main/java/burp/IResponseInfo.java +++ /dev/null @@ -1,73 +0,0 @@ -package burp; - -/* - * @(#)IResponseInfo.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -import java.util.List; - -/** - * This interface is used to retrieve key details about an HTTP response. - * Extensions can obtain an - * IResponseInfo object for a given response by calling - * IExtensionHelpers.analyzeResponse(). - */ -public interface IResponseInfo -{ - /** - * This method is used to obtain the HTTP headers contained in the response. - * - * @return The HTTP headers contained in the response. - */ - List getHeaders(); - - /** - * This method is used to obtain the offset within the response where the - * message body begins. - * - * @return The offset within the response where the message body begins. - */ - int getBodyOffset(); - - /** - * This method is used to obtain the HTTP status code contained in the - * response. - * - * @return The HTTP status code contained in the response. - */ - short getStatusCode(); - - /** - * This method is used to obtain details of the HTTP cookies set in the - * response. - * - * @return A list of ICookie objects representing the cookies - * set in the response, if any. - */ - List getCookies(); - - /** - * This method is used to obtain the MIME type of the response, as stated in - * the HTTP headers. - * - * @return A textual label for the stated MIME type, or an empty String if - * this is not known or recognized. The possible labels are the same as - * those used in the main Burp UI. - */ - String getStatedMimeType(); - - /** - * This method is used to obtain the MIME type of the response, as inferred - * from the contents of the HTTP message body. - * - * @return A textual label for the inferred MIME type, or an empty String if - * this is not known or recognized. The possible labels are the same as - * those used in the main Burp UI. - */ - String getInferredMimeType(); -} diff --git a/tool/src/main/java/burp/IResponseKeywords.java b/tool/src/main/java/burp/IResponseKeywords.java deleted file mode 100644 index adbcfd0..0000000 --- a/tool/src/main/java/burp/IResponseKeywords.java +++ /dev/null @@ -1,58 +0,0 @@ -package burp; - -/* - * @(#)IResponseKeywords.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -import java.util.List; - -/** - * This interface is used to represent the counts of keywords appearing in a - * number of HTTP responses. - */ -public interface IResponseKeywords -{ - - /** - * This method is used to obtain the list of keywords whose counts vary - * between the analyzed responses. - * - * @return The keywords whose counts vary between the analyzed responses. - */ - List getVariantKeywords(); - - /** - * This method is used to obtain the list of keywords whose counts do not - * vary between the analyzed responses. - * - * @return The keywords whose counts do not vary between the analyzed - * responses. - */ - List getInvariantKeywords(); - - /** - * This method is used to obtain the number of occurrences of an individual - * keyword in a response. - * - * @param keyword The keyword whose count will be retrieved. - * @param responseIndex The index of the response. Note responses are - * indexed from zero in the order they were originally supplied to the - * IExtensionHelpers.analyzeResponseKeywords() and - * IResponseKeywords.updateWith() methods. - * @return The number of occurrences of the specified keyword for the - * specified response. - */ - int getKeywordCount(String keyword, int responseIndex); - - /** - * This method is used to update the analysis based on additional responses. - * - * @param responses The new responses to include in the analysis. - */ - void updateWith(byte[]... responses); -} diff --git a/tool/src/main/java/burp/IResponseVariations.java b/tool/src/main/java/burp/IResponseVariations.java deleted file mode 100644 index 8ca65ef..0000000 --- a/tool/src/main/java/burp/IResponseVariations.java +++ /dev/null @@ -1,62 +0,0 @@ -package burp; - -/* - * @(#)IResponseVariations.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -import java.util.List; - -/** - * This interface is used to represent variations between a number HTTP - * responses, according to various attributes. - */ -public interface IResponseVariations -{ - - /** - * This method is used to obtain the list of attributes that vary between - * the analyzed responses. - * - * @return The attributes that vary between the analyzed responses. - */ - List getVariantAttributes(); - - /** - * This method is used to obtain the list of attributes that do not vary - * between the analyzed responses. - * - * @return The attributes that do not vary between the analyzed responses. - */ - List getInvariantAttributes(); - - /** - * This method is used to obtain the value of an individual attribute in a - * response. Note that the values of some attributes are intrinsically - * meaningful (e.g. a word count) while the values of others are less so - * (e.g. a checksum of the HTML tag names). - * - * @param attributeName The name of the attribute whose value will be - * retrieved. Extension authors can obtain the list of supported attributes - * by generating an IResponseVariations object for a single - * response and calling - * IResponseVariations.getInvariantAttributes(). - * @param responseIndex The index of the response. Note that responses are - * indexed from zero in the order they were originally supplied to the - * IExtensionHelpers.analyzeResponseVariations() and - * IResponseVariations.updateWith() methods. - * @return The value of the specified attribute for the specified response. - */ - int getAttributeValue(String attributeName, int responseIndex); - - /** - * This method is used to update the analysis based on additional responses. - * - * @param responses The new responses to include in the analysis. - */ - void updateWith(byte[]... responses); -} diff --git a/tool/src/main/java/burp/IScanIssue.java b/tool/src/main/java/burp/IScanIssue.java deleted file mode 100644 index f18a1bd..0000000 --- a/tool/src/main/java/burp/IScanIssue.java +++ /dev/null @@ -1,123 +0,0 @@ -package burp; - -/* - * @(#)IScanIssue.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * This interface is used to retrieve details of Scanner issues. Extensions can - * obtain details of issues by registering an IScannerListener or - * by calling IBurpExtenderCallbacks.getScanIssues(). Extensions - * can also add custom Scanner issues by registering an - * IScannerCheck or calling - * IBurpExtenderCallbacks.addScanIssue(), and providing their own - * implementations of this interface. Note that issue descriptions and other - * text generated by extensions are subject to an HTML whitelist that allows - * only formatting tags and simple hyperlinks. - */ -public interface IScanIssue -{ - - /** - * This method returns the URL for which the issue was generated. - * - * @return The URL for which the issue was generated. - */ - java.net.URL getUrl(); - - /** - * This method returns the name of the issue type. - * - * @return The name of the issue type (e.g. "SQL injection"). - */ - String getIssueName(); - - /** - * This method returns a numeric identifier of the issue type. See the Burp - * Scanner documentation for a listing of all the issue types. - * - * @return A numeric identifier of the issue type. - */ - int getIssueType(); - - /** - * This method returns the issue severity level. - * - * @return The issue severity level. Expected values are "High", "Medium", - * "Low", "Information" or "False positive". - * - */ - String getSeverity(); - - /** - * This method returns the issue confidence level. - * - * @return The issue confidence level. Expected values are "Certain", "Firm" - * or "Tentative". - */ - String getConfidence(); - - /** - * This method returns a background description for this type of issue. - * - * @return A background description for this type of issue, or - * null if none applies. A limited set of HTML tags may be - * used. - */ - String getIssueBackground(); - - /** - * This method returns a background description of the remediation for this - * type of issue. - * - * @return A background description of the remediation for this type of - * issue, or null if none applies. A limited set of HTML tags - * may be used. - */ - String getRemediationBackground(); - - /** - * This method returns detailed information about this specific instance of - * the issue. - * - * @return Detailed information about this specific instance of the issue, - * or null if none applies. A limited set of HTML tags may be - * used. - */ - String getIssueDetail(); - - /** - * This method returns detailed information about the remediation for this - * specific instance of the issue. - * - * @return Detailed information about the remediation for this specific - * instance of the issue, or null if none applies. A limited - * set of HTML tags may be used. - */ - String getRemediationDetail(); - - /** - * This method returns the HTTP messages on the basis of which the issue was - * generated. - * - * @return The HTTP messages on the basis of which the issue was generated. - * Note: The items in this array should be instances of - * IHttpRequestResponseWithMarkers if applicable, so that - * details of the relevant portions of the request and response messages are - * available. - */ - IHttpRequestResponse[] getHttpMessages(); - - /** - * This method returns the HTTP service for which the issue was generated. - * - * @return The HTTP service for which the issue was generated. - */ - IHttpService getHttpService(); - -} diff --git a/tool/src/main/java/burp/IScanQueueItem.java b/tool/src/main/java/burp/IScanQueueItem.java deleted file mode 100644 index 5c9e1c2..0000000 --- a/tool/src/main/java/burp/IScanQueueItem.java +++ /dev/null @@ -1,81 +0,0 @@ -package burp; - -/* - * @(#)IScanQueueItem.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * This interface is used to retrieve details of items in the Burp Scanner - * active scan queue. Extensions can obtain references to scan queue items by - * calling - * IBurpExtenderCallbacks.doActiveScan(). - */ -public interface IScanQueueItem -{ - /** - * This method returns a description of the status of the scan queue item. - * - * @return A description of the status of the scan queue item. - */ - String getStatus(); - - /** - * This method returns an indication of the percentage completed for the - * scan queue item. - * - * @return An indication of the percentage completed for the scan queue - * item. - */ - @Deprecated - byte getPercentageComplete(); - - /** - * This method returns the number of requests that have been made for the - * scan queue item. - * - * @return The number of requests that have been made for the scan queue - * item. - */ - int getNumRequests(); - - /** - * This method returns the number of network errors that have occurred for - * the scan queue item. - * - * @return The number of network errors that have occurred for the scan - * queue item. - */ - int getNumErrors(); - - /** - * This method returns the number of attack insertion points being used for - * the scan queue item. - * - * @return The number of attack insertion points being used for the scan - * queue item. - */ - int getNumInsertionPoints(); - - /** - * This method allows the scan queue item to be canceled. - */ - void cancel(); - - /** - * This method returns details of the issues generated for the scan queue - * item. Note: different items within the scan queue may contain - * duplicated versions of the same issues - for example, if the same request - * has been scanned multiple times. Duplicated issues are consolidated in - * the main view of scan results. Extensions can register an - * IScannerListener to get details only of unique, newly - * discovered Scanner issues post-consolidation. - * - * @return Details of the issues generated for the scan queue item. - */ - IScanIssue[] getIssues(); -} diff --git a/tool/src/main/java/burp/IScannerCheck.java b/tool/src/main/java/burp/IScannerCheck.java deleted file mode 100644 index 5445b0f..0000000 --- a/tool/src/main/java/burp/IScannerCheck.java +++ /dev/null @@ -1,83 +0,0 @@ -package burp; - -/* - * @(#)IScannerCheck.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -import java.util.List; - -/** - * Extensions can implement this interface and then call - * IBurpExtenderCallbacks.registerScannerCheck() to register a - * custom Scanner check. When performing scanning, Burp will ask the check to - * perform active or passive scanning on the base request, and report any - * Scanner issues that are identified. - */ -public interface IScannerCheck -{ - - /** - * The Scanner invokes this method for each base request / response that is - * passively scanned. Note: Extensions should only analyze the - * HTTP messages provided during passive scanning, and should not make any - * new HTTP requests of their own. - * - * @param baseRequestResponse The base HTTP request / response that should - * be passively scanned. - * @return A list of IScanIssue objects, or null - * if no issues are identified. - */ - List doPassiveScan(IHttpRequestResponse baseRequestResponse); - - /** - * The Scanner invokes this method for each insertion point that is actively - * scanned. Extensions may issue HTTP requests as required to carry out - * active scanning, and should use the - * IScannerInsertionPoint object provided to build scan - * requests for particular payloads. - * Note: - * Scan checks should submit raw non-encoded payloads to insertion points, - * and the insertion point has responsibility for performing any data - * encoding that is necessary given the nature and location of the insertion - * point. - * - * @param baseRequestResponse The base HTTP request / response that should - * be actively scanned. - * @param insertionPoint An IScannerInsertionPoint object that - * can be queried to obtain details of the insertion point being tested, and - * can be used to build scan requests for particular payloads. - * @return A list of IScanIssue objects, or null - * if no issues are identified. - */ - List doActiveScan( - IHttpRequestResponse baseRequestResponse, - IScannerInsertionPoint insertionPoint); - - /** - * The Scanner invokes this method when the custom Scanner check has - * reported multiple issues for the same URL path. This can arise either - * because there are multiple distinct vulnerabilities, or because the same - * (or a similar) request has been scanned more than once. The custom check - * should determine whether the issues are duplicates. In most cases, where - * a check uses distinct issue names or descriptions for distinct issues, - * the consolidation process will simply be a matter of comparing these - * features for the two issues. - * - * @param existingIssue An issue that was previously reported by this - * Scanner check. - * @param newIssue An issue at the same URL path that has been newly - * reported by this Scanner check. - * @return An indication of which issue(s) should be reported in the main - * Scanner results. The method should return -1 to report the - * existing issue only, 0 to report both issues, and - * 1 to report the new issue only. - */ - int consolidateDuplicateIssues( - IScanIssue existingIssue, - IScanIssue newIssue); -} diff --git a/tool/src/main/java/burp/IScannerInsertionPoint.java b/tool/src/main/java/burp/IScannerInsertionPoint.java deleted file mode 100644 index fe25c95..0000000 --- a/tool/src/main/java/burp/IScannerInsertionPoint.java +++ /dev/null @@ -1,174 +0,0 @@ -package burp; - -/* - * @(#)IScannerInsertionPoint.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * This interface is used to define an insertion point for use by active Scanner - * checks. Extensions can obtain instances of this interface by registering an - * IScannerCheck, or can create instances for use by Burp's own - * scan checks by registering an - * IScannerInsertionPointProvider. - */ -public interface IScannerInsertionPoint -{ - - /** - * Used to indicate where the payload is inserted into the value of a URL - * parameter. - */ - byte INS_PARAM_URL = 0x00; - /** - * Used to indicate where the payload is inserted into the value of a body - * parameter. - */ - byte INS_PARAM_BODY = 0x01; - /** - * Used to indicate where the payload is inserted into the value of an HTTP - * cookie. - */ - byte INS_PARAM_COOKIE = 0x02; - /** - * Used to indicate where the payload is inserted into the value of an item - * of data within an XML data structure. - */ - byte INS_PARAM_XML = 0x03; - /** - * Used to indicate where the payload is inserted into the value of a tag - * attribute within an XML structure. - */ - byte INS_PARAM_XML_ATTR = 0x04; - /** - * Used to indicate where the payload is inserted into the value of a - * parameter attribute within a multi-part message body (such as the name of - * an uploaded file). - */ - byte INS_PARAM_MULTIPART_ATTR = 0x05; - /** - * Used to indicate where the payload is inserted into the value of an item - * of data within a JSON structure. - */ - byte INS_PARAM_JSON = 0x06; - /** - * Used to indicate where the payload is inserted into the value of an AMF - * parameter. - */ - byte INS_PARAM_AMF = 0x07; - /** - * Used to indicate where the payload is inserted into the value of an HTTP - * request header. - */ - byte INS_HEADER = 0x20; - /** - * Used to indicate where the payload is inserted into a URL path folder. - */ - byte INS_URL_PATH_FOLDER = 0x21; - /** - * Used to indicate where the payload is inserted into a URL path folder. - * This is now deprecated; use INS_URL_PATH_FOLDER instead. - */ - @Deprecated - byte INS_URL_PATH_REST = INS_URL_PATH_FOLDER; - /** - * Used to indicate where the payload is inserted into the name of an added - * URL parameter. - */ - byte INS_PARAM_NAME_URL = 0x22; - /** - * Used to indicate where the payload is inserted into the name of an added - * body parameter. - */ - byte INS_PARAM_NAME_BODY = 0x23; - /** - * Used to indicate where the payload is inserted into the body of the HTTP - * request. - */ - byte INS_ENTIRE_BODY = 0x24; - /** - * Used to indicate where the payload is inserted into the URL path - * filename. - */ - byte INS_URL_PATH_FILENAME = 0x25; - /** - * Used to indicate where the payload is inserted at a location manually - * configured by the user. - */ - byte INS_USER_PROVIDED = 0x40; - /** - * Used to indicate where the insertion point is provided by an - * extension-registered - * IScannerInsertionPointProvider. - */ - byte INS_EXTENSION_PROVIDED = 0x41; - /** - * Used to indicate where the payload is inserted at an unknown location - * within the request. - */ - byte INS_UNKNOWN = 0x7f; - - /** - * This method returns the name of the insertion point. - * - * @return The name of the insertion point (for example, a description of a - * particular request parameter). - */ - String getInsertionPointName(); - - /** - * This method returns the base value for this insertion point. - * - * @return the base value that appears in this insertion point in the base - * request being scanned, or null if there is no value in the - * base request that corresponds to this insertion point. - */ - String getBaseValue(); - - /** - * This method is used to build a request with the specified payload placed - * into the insertion point. There is no requirement for extension-provided - * insertion points to adjust the Content-Length header in requests if the - * body length has changed, although Burp-provided insertion points will - * always do this and will return a request with a valid Content-Length - * header. - * Note: - * Scan checks should submit raw non-encoded payloads to insertion points, - * and the insertion point has responsibility for performing any data - * encoding that is necessary given the nature and location of the insertion - * point. - * - * @param payload The payload that should be placed into the insertion - * point. - * @return The resulting request. - */ - byte[] buildRequest(byte[] payload); - - /** - * This method is used to determine the offsets of the payload value within - * the request, when it is placed into the insertion point. Scan checks may - * invoke this method when reporting issues, so as to highlight the relevant - * part of the request within the UI. - * - * @param payload The payload that should be placed into the insertion - * point. - * @return An int[2] array containing the start and end offsets of the - * payload within the request, or null if this is not applicable (for - * example, where the insertion point places a payload into a serialized - * data structure, the raw payload may not literally appear anywhere within - * the resulting request). - */ - int[] getPayloadOffsets(byte[] payload); - - /** - * This method returns the type of the insertion point. - * - * @return The type of the insertion point. Available types are defined in - * this interface. - */ - byte getInsertionPointType(); -} diff --git a/tool/src/main/java/burp/IScannerInsertionPointProvider.java b/tool/src/main/java/burp/IScannerInsertionPointProvider.java deleted file mode 100644 index 4365ccf..0000000 --- a/tool/src/main/java/burp/IScannerInsertionPointProvider.java +++ /dev/null @@ -1,38 +0,0 @@ -package burp; - -/* - * @(#)IScannerInsertionPointProvider.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -import java.util.List; - -/** - * Extensions can implement this interface and then call - * IBurpExtenderCallbacks.registerScannerInsertionPointProvider() - * to register a factory for custom Scanner insertion points. - */ -public interface IScannerInsertionPointProvider -{ - /** - * When a request is actively scanned, the Scanner will invoke this method, - * and the provider should provide a list of custom insertion points that - * will be used in the scan. Note: these insertion points are used in - * addition to those that are derived from Burp Scanner's configuration, and - * those provided by any other Burp extensions. - * - * @param baseRequestResponse The base request that will be actively - * scanned. - * @return A list of - * IScannerInsertionPoint objects that should be used in the - * scanning, or - * null if no custom insertion points are applicable for this - * request. - */ - List getInsertionPoints( - IHttpRequestResponse baseRequestResponse); -} diff --git a/tool/src/main/java/burp/IScannerListener.java b/tool/src/main/java/burp/IScannerListener.java deleted file mode 100644 index 21c0f4e..0000000 --- a/tool/src/main/java/burp/IScannerListener.java +++ /dev/null @@ -1,30 +0,0 @@ -package burp; - -/* - * @(#)IScannerListener.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * Extensions can implement this interface and then call - * IBurpExtenderCallbacks.registerScannerListener() to register a - * Scanner listener. The listener will be notified of new issues that are - * reported by the Scanner tool. Extensions can perform custom analysis or - * logging of Scanner issues by registering a Scanner listener. - */ -public interface IScannerListener -{ - /** - * This method is invoked when a new issue is added to Burp Scanner's - * results. - * - * @param issue An - * IScanIssue object that the extension can query to obtain - * details about the new issue. - */ - void newScanIssue(IScanIssue issue); -} diff --git a/tool/src/main/java/burp/IScopeChangeListener.java b/tool/src/main/java/burp/IScopeChangeListener.java deleted file mode 100644 index 30b7918..0000000 --- a/tool/src/main/java/burp/IScopeChangeListener.java +++ /dev/null @@ -1,25 +0,0 @@ -package burp; - -/* - * @(#)IScopeChangeListener.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * Extensions can implement this interface and then call - * IBurpExtenderCallbacks.registerScopeChangeListener() to register - * a scope change listener. The listener will be notified whenever a change - * occurs to Burp's suite-wide target scope. - */ -public interface IScopeChangeListener -{ - /** - * This method is invoked whenever a change occurs to Burp's suite-wide - * target scope. - */ - void scopeChanged(); -} diff --git a/tool/src/main/java/burp/ISessionHandlingAction.java b/tool/src/main/java/burp/ISessionHandlingAction.java deleted file mode 100644 index 1a147d4..0000000 --- a/tool/src/main/java/burp/ISessionHandlingAction.java +++ /dev/null @@ -1,51 +0,0 @@ -package burp; - -/* - * @(#)ISessionHandlingAction.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * Extensions can implement this interface and then call - * IBurpExtenderCallbacks.registerSessionHandlingAction() to - * register a custom session handling action. Each registered action will be - * available within the session handling rule UI for the user to select as a - * rule action. Users can choose to invoke an action directly in its own right, - * or following execution of a macro. - */ -public interface ISessionHandlingAction -{ - /** - * This method is used by Burp to obtain the name of the session handling - * action. This will be displayed as an option within the session handling - * rule editor when the user selects to execute an extension-provided - * action. - * - * @return The name of the action. - */ - String getActionName(); - - /** - * This method is invoked when the session handling action should be - * executed. This may happen as an action in its own right, or as a - * sub-action following execution of a macro. - * - * @param currentRequest The base request that is currently being processed. - * The action can query this object to obtain details about the base - * request. It can issue additional requests of its own if necessary, and - * can use the setter methods on this object to update the base request. - * @param macroItems If the action is invoked following execution of a - * macro, this parameter contains the result of executing the macro. - * Otherwise, it is - * null. Actions can use the details of the macro items to - * perform custom analysis of the macro to derive values of non-standard - * session handling tokens, etc. - */ - void performAction( - IHttpRequestResponse currentRequest, - IHttpRequestResponse[] macroItems); -} diff --git a/tool/src/main/java/burp/ITab.java b/tool/src/main/java/burp/ITab.java deleted file mode 100644 index f547993..0000000 --- a/tool/src/main/java/burp/ITab.java +++ /dev/null @@ -1,38 +0,0 @@ -package burp; - -/* - * @(#)ITab.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -import java.awt.Component; - -/** - * This interface is used to provide Burp with details of a custom tab that will - * be added to Burp's UI, using a method such as - * IBurpExtenderCallbacks.addSuiteTab(). - */ -public interface ITab -{ - /** - * Burp uses this method to obtain the caption that should appear on the - * custom tab when it is displayed. - * - * @return The caption that should appear on the custom tab when it is - * displayed. - */ - String getTabCaption(); - - /** - * Burp uses this method to obtain the component that should be used as the - * contents of the custom tab when it is displayed. - * - * @return The component that should be used as the contents of the custom - * tab when it is displayed. - */ - Component getUiComponent(); -} diff --git a/tool/src/main/java/burp/ITempFile.java b/tool/src/main/java/burp/ITempFile.java deleted file mode 100644 index c9247ef..0000000 --- a/tool/src/main/java/burp/ITempFile.java +++ /dev/null @@ -1,33 +0,0 @@ -package burp; - -/* - * @(#)ITempFile.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -/** - * This interface is used to hold details of a temporary file that has been - * created via a call to - * IBurpExtenderCallbacks.saveToTempFile(). - * - */ -public interface ITempFile -{ - /** - * This method is used to retrieve the contents of the buffer that was saved - * in the temporary file. - * - * @return The contents of the buffer that was saved in the temporary file. - */ - byte[] getBuffer(); - - /** - * This method is deprecated and no longer performs any action. - */ - @Deprecated - void delete(); -} diff --git a/tool/src/main/java/burp/ITextEditor.java b/tool/src/main/java/burp/ITextEditor.java deleted file mode 100644 index ca2526a..0000000 --- a/tool/src/main/java/burp/ITextEditor.java +++ /dev/null @@ -1,90 +0,0 @@ -package burp; - -/* - * @(#)ITextEditor.java - * - * Copyright PortSwigger Ltd. All rights reserved. - * - * This code may be used to extend the functionality of Burp Suite Community Edition - * and Burp Suite Professional, provided that this usage does not violate the - * license terms for those products. - */ -import java.awt.Component; - -/** - * This interface is used to provide extensions with an instance of Burp's raw - * text editor, for the extension to use in its own UI. Extensions should call - * IBurpExtenderCallbacks.createTextEditor() to obtain an instance - * of this interface. - */ -public interface ITextEditor -{ - /** - * This method returns the UI component of the editor, for extensions to add - * to their own UI. - * - * @return The UI component of the editor. - */ - Component getComponent(); - - /** - * This method is used to control whether the editor is currently editable. - * This status can be toggled on and off as required. - * - * @param editable Indicates whether the editor should be currently - * editable. - */ - void setEditable(boolean editable); - - /** - * This method is used to update the currently displayed text in the editor. - * - * @param text The text to be displayed. - */ - void setText(byte[] text); - - /** - * This method is used to retrieve the currently displayed text. - * - * @return The currently displayed text. - */ - byte[] getText(); - - /** - * This method is used to determine whether the user has modified the - * contents of the editor. - * - * @return An indication of whether the user has modified the contents of - * the editor since the last call to - * setText(). - */ - boolean isTextModified(); - - /** - * This method is used to obtain the currently selected text. - * - * @return The currently selected text, or - * null if the user has not made any selection. - */ - byte[] getSelectedText(); - - /** - * This method can be used to retrieve the bounds of the user's selection - * into the displayed text, if applicable. - * - * @return An int[2] array containing the start and end offsets of the - * user's selection within the displayed text. If the user has not made any - * selection in the current message, both offsets indicate the position of - * the caret within the editor. - */ - int[] getSelectionBounds(); - - /** - * This method is used to update the search expression that is shown in the - * search bar below the editor. The editor will automatically highlight any - * regions of the displayed text that match the search expression. - * - * @param expression The search expression. - */ - void setSearchExpression(String expression); -} diff --git a/tool/src/main/java/migt/API.java b/tool/src/main/java/migt/API.java deleted file mode 100644 index 30a5fbf..0000000 --- a/tool/src/main/java/migt/API.java +++ /dev/null @@ -1,7 +0,0 @@ -package migt; - -/** - * The API class stores the API of a Module, this class is inherited by all the other APIs - */ -public class API { -} diff --git a/tool/src/main/java/migt/BurpExtender.java b/tool/src/main/java/migt/BurpExtender.java deleted file mode 100644 index 19ac787..0000000 --- a/tool/src/main/java/migt/BurpExtender.java +++ /dev/null @@ -1,191 +0,0 @@ -package migt; - -import burp.*; - -import javax.swing.*; -import java.awt.*; -import java.io.OutputStream; -import java.io.PrintStream; -import java.util.Arrays; -import java.util.Objects; - -/** - * Main class executed by Burp - */ -public class BurpExtender implements IBurpExtender, ITab, IProxyListener { - - public static IExtensionHelpers helpers; - public static PrintStream printStream; - public static PrintStream errorStream; - public IBurpExtenderCallbacks callbacks; - private Main mainPane; // The GUI - - /** - * Main function creating the extension - * - * @param callbacks The callbacks received by Burp - */ - public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { - System.out.println("Initializing extension"); - - this.callbacks = callbacks; - helpers = callbacks.getHelpers(); - - callbacks.setExtensionName("MIG Testing tool"); - - //The UI is created - SwingUtilities.invokeLater(() -> { - // setup output stream - OutputStream stdOut = callbacks.getStdout(); - OutputStream stdErr = callbacks.getStderr(); - printStream = new PrintStream(stdOut); - errorStream = new PrintStream(stdErr); - - mainPane = new Main(); - mainPane.callbacks = callbacks; - mainPane.messageViewer = callbacks.createMessageEditor(mainPane.controller, false); - mainPane.splitPane.setRightComponent(mainPane.messageViewer.getComponent()); - - // add the custom tab to Burp's UI - callbacks.addSuiteTab(BurpExtender.this); - - // register ourselves as an HTTP listener - callbacks.registerProxyListener(BurpExtender.this); - //callbacks.registerHttpListener(BurpExtender.this); - }); - } - - @Override - public String getTabCaption() { - return "MIG-T"; - } - - @Override - public Component getUiComponent() { - return mainPane; - } - - /** - * Proxy's listener function which is called wheter a new message arrives. Note that if the received message is a - * request, you cannot access the response - * - * @param messageIsRequest Indicates whether the HTTP message is a request - * or a response. - * @param proxy_message An - * IInterceptedProxyMessage object that extensions can use to - * query and update details of the message, and control whether the message - * should be intercepted and displayed to the user for manual review or - */ - @Override - public void processProxyMessage(boolean messageIsRequest, IInterceptedProxyMessage proxy_message) { - String port = proxy_message.getListenerInterface().split(":")[1]; - IHttpRequestResponse messageInfo = proxy_message.getMessageInfo(); - - HTTPReqRes message = new HTTPReqRes( - messageInfo, - helpers, - messageIsRequest, - proxy_message.getMessageReference() - ); - - if (mainPane.INTERCEPT_ENABLED) { - /* Check at which port of the proxy the message has been received - if it is different from the one of the session avoid message*/ - if (!port.equals(mainPane.actual_operation.session_port)) { - return; - } - - // Log the received message by adding it to the list of received messages - log_message(messageIsRequest, proxy_message); - - MessageType msg_type = null; - try { - msg_type = MessageType.getFromList(mainPane.messageTypes, - mainPane.actual_operation.getMessageType()); - } catch (Exception e) { - e.printStackTrace(); - mainPane.actual_operation.applicable = false; - } - - // Check that the given message matches the message type specified in the test - boolean matchMessage = message.matches_msg_type(msg_type, messageIsRequest); - - if (matchMessage) { - // If the operation's action is an intercept - if (Objects.requireNonNull(mainPane.actual_operation.getAction()) == Operation.Action.INTERCEPT) { - try { - processMatchedMsg(msg_type, messageInfo, message); - if (mainPane.actual_operation.then != null & - mainPane.actual_operation.then == Operation.Then.DROP) { - proxy_message.setInterceptAction(IInterceptedProxyMessage.ACTION_DROP); - } - } catch (Exception e) { - e.printStackTrace(); - mainPane.actual_operation.applicable = false; - } - } - } - } - - if (mainPane.recording) { - if (!messageIsRequest) { // do not remove - synchronized (mainPane.interceptedMessages) { - IHttpRequestResponsePersisted actual = callbacks.saveBuffersToTempFiles(messageInfo); - mainPane.interceptedMessages.add( - new HTTPReqRes(actual, helpers) - ); - if (mainPane.defaultSession != null) { - mainPane.defaultSession.addMessage(actual, helpers, mainPane.FILTERING); - } - } - } - } - } - - /** - * @param msg_type the message type to be used - * @param messageInfo the original intercepted messageInfo to being able to edit the message - * @param message a custom parsed message to be used in opeations - */ - private void processMatchedMsg(MessageType msg_type, - IHttpRequestResponse messageInfo, - HTTPReqRes message) { - messageInfo.setHighlight("red"); - - mainPane.actual_operation.setAPI(new Operation_API(message, msg_type.msg_to_process_is_request)); - mainPane.actual_operation.execute(); - - // if message has been edited inside operation update the value - try { - if (mainPane.actual_operation.processed_message != null) { - if (msg_type.msg_to_process_is_request) { - if (!Arrays.equals(messageInfo.getRequest(), mainPane.actual_operation.processed_message)) { - messageInfo.setRequest(mainPane.actual_operation.processed_message); - } - } else { - if (!Arrays.equals(messageInfo.getResponse(), mainPane.actual_operation.processed_message)) { - messageInfo.setResponse(mainPane.actual_operation.processed_message); - } - } - } - } catch (UnsupportedOperationException e) { - // This is thrown when an already issued request is being substituted - System.err.println("Warning, edited message that has already been sent"); - } - resume(); - } - - /** - * Tells the lock on the Execute Actives process to resume the execution - */ - private void resume() { - // Resume the execution thread - synchronized (mainPane.waiting) { - mainPane.waiting.notify(); - } - } - - private void log_message(boolean isRequest, IInterceptedProxyMessage message) { - mainPane.actual_operation.log_messages.add(message); - } -} diff --git a/tool/src/main/java/migt/ExecuteActives.java b/tool/src/main/java/migt/ExecuteActives.java deleted file mode 100644 index 5b74a61..0000000 --- a/tool/src/main/java/migt/ExecuteActives.java +++ /dev/null @@ -1,289 +0,0 @@ -package migt; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.regex.PatternSyntaxException; - -/** - * Class which executes actives tests, has to be run as a thread - */ -public class ExecuteActives implements Runnable { - final Object waiting; // the lock on which the thread will wait - ExecuteActiveListener listener; - List actives; - - /** - * Instantiate the class - * - * @param actives the list of Active tests - * @param waiting an object which will serve as a lock between this class and BurpExtender to stop and resume the - * execution - */ - public ExecuteActives(List actives, Object waiting) { - this.actives = actives; - this.waiting = waiting; - } - - /** - * Method to register an ExecuteActiveListener, used to receive callbacks - * - * @param listener the listener - */ - public void registerExecuteActivesListener(ExecuteActiveListener listener) { - this.listener = listener; - } - - /** - * The run method, which runs the Tests - */ - @Override - public void run() { - listener.onExecuteStart(); - for (Test actual_test : actives) { - listener.onNewTest(actual_test); - - Map isInPause = new HashMap<>(); - Map clearCookieAsked = new HashMap<>(); - - for (Session s : actual_test.sessions) { - s = listener.onNewSession(s); - if (s == null) { - listener.onError(actual_test); - } - isInPause.put(s.name, false); - } - - Map executions = new HashMap<>(); - AtomicInteger alive_count = new AtomicInteger(); - - for (Operation op : actual_test.operations) { - try { - // if the operation is a session control operation - if (op.isSessionOp) { - actual_test.applicable = true; - switch (op.getSessionAction()) { - case START: { - Session selected = actual_test.getSession(op.getSession()); - if (selected == null) { - actual_test.error_str = "Invalid session name, maybe you didn't declare it?"; - actual_test.error = true; - break; - } - selected.ex.registerExecuteTrackListener(new ExecuteTrackListener() { - @Override - public void onExecuteDone(boolean errors, String current_url, String sessionName) { - if (actual_test.resultSession.equals("") || - actual_test.resultSession.equals(sessionName)) { - - if (actual_test.result == Test.ResultType.CORRECT_FLOW) { - if (errors || current_url.contains("error")) { - actual_test.success = false; - } - } else if (actual_test.result == Test.ResultType.INCORRECT_FLOW) { - actual_test.success = errors; // Difficult to read - } else if (actual_test.result == Test.ResultType.ASSERT_ONLY) { - //at this point, all the asserts have been executed, and if they failed - // they already returned a false result - } - synchronized (waiting) { - waiting.notify(); - } - } - if (errors) { - synchronized (waiting) { - waiting.notify(); - } - } - alive_count.getAndDecrement(); - } - - @Override - public void onExecuteDone(boolean forceResult, String sessionName) { - if (actual_test.resultSession.equals("") || - actual_test.resultSession.equals(sessionName)) { - - actual_test.success = forceResult; - synchronized (waiting) { - waiting.notify(); - } - } - alive_count.getAndDecrement(); - } - - @Override - public void onError(String sessionName) { - if (actual_test.resultSession.equals("") || - actual_test.resultSession.equals(sessionName)) { - actual_test.applicable = false; - } - synchronized (waiting) { - waiting.notify(); - } - alive_count.getAndDecrement(); - } - - @Override - public Boolean onAskPause(String sessionName) { - if (isInPause.get(sessionName) != null) { - return isInPause.get(sessionName); - } else { - return false; - } - } - - @Override - public Boolean onAskStop(String sessionName) { - return null; - } - - @Override - public Boolean onAskClearCookie(String sessionName) { - if (clearCookieAsked.get(sessionName) != null) { - boolean tmp = clearCookieAsked.get(sessionName); - clearCookieAsked.replace(sessionName, false); - return tmp; - } else { - return false; - } - } - - @Override - public void onNextSessionAction(SessionTrackAction last_action, - SessionTrackAction last_open, - SessionTrackAction last_click, - String last_url, - String session_name) throws ParsingException { - Session s = actual_test.getSession(session_name); - s.last_action = last_action; - s.last_open = last_open; - s.last_click = last_click; - s.last_url = last_url; - } - - @Override - public Track onUpdateTrack(String sessionName) throws ParsingException { - return actual_test.getSession(sessionName).track; - } - - @Override - public void onSetVar(Var v) { - actual_test.vars.add(v); - } - }); - - Thread t = new Thread(selected.ex); - t.setName(op.getSession()); - executions.put(op.getSession(), t); - alive_count.addAndGet(1); - executions.get(op.getSession()).start(); - break; - } - - case PAUSE: - isInPause.replace(op.getSession(), true); - break; - - case RESUME: - isInPause.replace(op.getSession(), false); - break; - - case STOP: { - Session selected = actual_test.getSession(op.getSession()); - if (selected == null) { - actual_test.error_str = "Invalid session name, maybe you didn't declare it?"; - actual_test.error = true; - break; - } - - executions.get(op.getSession()).interrupt(); - break; - } - case CLEAR_COOKIES: - Session selected = actual_test.getSession(op.getSession()); - if (selected == null) { - actual_test.error_str = "Invalid session name, maybe you didn't declare it?"; - actual_test.error = true; - break; - } - clearCookieAsked.put(selected.name, true); - break; - } - - List act_vars = actual_test.vars; - List updated_vars = op.executeSessionOps(actual_test, act_vars); - actual_test.vars = updated_vars; - - } else { - //if it is a normal operation - - if (!op.from_session.equals("")) { - op.session_port = actual_test.getSession(op.from_session).port; - } else if (!op.to_session.equals("")) { - op.session_port = actual_test.getSession(op.to_session).port; - } else { - op.session_port = "8080"; - } - - if (op.api == null) { - op.api = new Operation_API(actual_test.vars); - } else { - op.api.vars = actual_test.vars; - } - - listener.onNewProcessOperation(op); - - synchronized (this.waiting) { - try { - this.waiting.wait(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - op = listener.onOperationDone(); // Take the operation from the caller - - actual_test.vars = op.api.vars; - actual_test.vars = op.executeSessionOps(actual_test, actual_test.vars); - - if (op.applicable) { - actual_test.success = op.result; - actual_test.applicable = true; - if (!op.result) { - for (String key : executions.keySet()) { - executions.get(key).interrupt(); - } - break; - } - } else { - actual_test.applicable = false; - for (String key : executions.keySet()) { - executions.get(key).interrupt(); - } - break; - } - } - } catch (ParsingException | PatternSyntaxException e) { - e.printStackTrace(); - listener.onError(actual_test); - actual_test.applicable = false; - for (String key : executions.keySet()) { - executions.get(key).interrupt(); - } - break; - } - } - while (alive_count.get() != 0) { - try { - Thread.sleep(100); - } catch (InterruptedException interruptedException) { - interruptedException.printStackTrace(); - //alive_count.getAndDecrement(); - } - } - listener.onTestDone(actual_test); - } - listener.onExecuteDone(); - } -} diff --git a/tool/src/main/java/migt/ExecutePassiveListener.java b/tool/src/main/java/migt/ExecutePassiveListener.java deleted file mode 100644 index dbb4652..0000000 --- a/tool/src/main/java/migt/ExecutePassiveListener.java +++ /dev/null @@ -1,58 +0,0 @@ -package migt; - -import java.util.ArrayList; -import java.util.List; - -/** - * Listener class for the ExecutePassive Object - */ -public interface ExecutePassiveListener { - /** - * This method is called when the ExecutePassive thread is started, it is used to tell when the thread should start - * executing the passives test. if it returns true, the thread will start - * - * @return true if you want the thread to start - */ - boolean onWaitToStart(); - - /** - * Called when the ExecutePassives thread is started - */ - void onExecuteStart(); - - /** - * Called when the ExecutePassives thread has ended - * - * @param passives_test The list of executed passive tests - */ - void onExecuteDone(List passives_test); - - /** - * Called when there is an error in the execution - * - * @param msg the error message - */ - void onError(String msg); - - /** - * When a new session has to be executed this method is called. This is thought to fill the session with - * the right values from the GUI class. - * - * @param s the session to be initiated - * @return the session with the filled values - */ - Session onNewSession(Session s) throws ParsingException; - - /** - * Called before the track of the session is executed. Usually used to start the recording of the messages in - * the GUI class - */ - void onBeforeExecuteTrack(); - - /** - * Called when a track ends its execution - * - * @return the list of intercepted messages - */ - ArrayList onTrackExecuteDone(); -} diff --git a/tool/src/main/java/migt/ExecutePassives.java b/tool/src/main/java/migt/ExecutePassives.java deleted file mode 100644 index 213dd4f..0000000 --- a/tool/src/main/java/migt/ExecutePassives.java +++ /dev/null @@ -1,211 +0,0 @@ -package migt; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -/** - * Class used to execute passive tests, it implements Runnable, it should be executed as a Thread. To communicate with - * the tread you can use the ExecutePassivesListener listener class. - */ -public class ExecutePassives implements Runnable { - final Object lock = new Object(); - public List passives; - ExecutePassiveListener listener; - List messageTypes; - boolean finished; - boolean execution_error; - - /** - * Used to instantiate an ExecutePassives object - * - * @param passiveTests The list of passive tests to execute - * @param listener the listener for this ExecutePassives Object, used to communicate with the thread - * @param msg_types the list of message types needed by the tests - */ - public ExecutePassives(List passiveTests, - ExecutePassiveListener listener, - List msg_types) { - this.passives = passiveTests; - this.listener = listener; - this.messageTypes = msg_types; - this.finished = false; - this.execution_error = false; - } - - /** - * Starts the execution of the passive tests - */ - @Override - public void run() { - // This first piece is thought to make the thread wait before start - while (!listener.onWaitToStart()) { - try { - //TODO: fix busy waiting - Thread.sleep(1000); - } catch (InterruptedException e) { - return; - } - } - - listener.onExecuteStart(); - execution_error = false; - - HashMap> batch = null; - try { - batch = Tools.batchPassivesFromSession(passives); - } catch (ParsingException e) { - e.printStackTrace(); - //lblOutput.setText(e.getMessage()); - return; - } - - for (String sessionName : batch.keySet()) { - List actual_batch = batch.get(sessionName); - - Session act_session = actual_batch.get(0).sessions.get(0); - try { - if (act_session.messages.size() == 0) { - act_session = listener.onNewSession(act_session); - } - } catch (ParsingException e) { - listener.onError("Error in retrieving session"); - return; - } - - Session executedSession = act_session; - //If sessions already executed, don't re-execute them - if (act_session.messages.size() == 0) { - executedSession = executePassiveTestSession(act_session); - for (Test t : actual_batch) { - t.sessions.set(0, executedSession); - } - batch.put(sessionName, actual_batch); - if (execution_error) { - return; - } - } - - // Execute all the tests for each session - for (Test actual_test : actual_batch) { - System.out.println("Actual test name: " + actual_test.getName()); - - boolean res = false; - try { - res = actual_test.execute(executedSession.messages, messageTypes); - } catch (ParsingException e) { - actual_test.applicable = false; - } - - System.out.println("Actual test result: " + res); - actual_test.success = res; - } - // Remove used session - executedSession = null; - } - passives = Tools.debatchPassive(batch); - listener.onExecuteDone(passives); - } - - /** - * Executes a passive test's session, to gather messages needed to execute the passive - * tests. - * - * @param session the session to be executed - * @return the same session, executed, that will contain the intercepted messages - */ - public Session executePassiveTestSession(Session session) { - //FIXME: session's track is assumed to be present - - synchronized (lock) { - finished = false; - } - - ExecuteTrackListener track_listener = new ExecuteTrackListener() { - @Override - public void onExecuteDone(boolean errors, String current_url, String sessionName) { - ArrayList intercepted_messages = listener.onTrackExecuteDone(); - session.messages = intercepted_messages; - - if (errors) { - listener.onError("Error in executing track for session " + sessionName); - execution_error = true; - } - synchronized (lock) { - lock.notify(); - finished = true; - } - } - - @Override - public void onExecuteDone(boolean forceResult, String sessionName) { - ArrayList intercepted_messages = listener.onTrackExecuteDone(); - session.messages = intercepted_messages; - - synchronized (lock) { - lock.notify(); - finished = true; - } - } - - @Override - public void onError(String sessionName) { - listener.onError("Error in executing track for session " + sessionName); - execution_error = true; - synchronized (lock) { - lock.notify(); - finished = true; - } - } - - @Override - public Boolean onAskPause(String sessionName) { - return false; - } - - @Override - public Boolean onAskStop(String sessionName) { - return false; - } - - @Override - public Boolean onAskClearCookie(String sessionName) { - return null; - } - - @Override - public void onNextSessionAction(SessionTrackAction last_action, - SessionTrackAction last_open, - SessionTrackAction last_click, - String last_url, - String session_name) { - } - - @Override - public Track onUpdateTrack(String sessionName) throws ParsingException { - return null; - } - - @Override - public void onSetVar(Var v) { - } - }; - - listener.onBeforeExecuteTrack(); - session.ex.registerExecuteTrackListener(track_listener); - new Thread(session.ex).start(); //ex is assumed to be initialized - - // Waits execution to be finished - synchronized (lock) { - while (!finished) { - try { - lock.wait(); - } catch (InterruptedException e) { - break; - } - } - } - - return session; - } -} diff --git a/tool/src/main/java/migt/ExecuteTrack.java b/tool/src/main/java/migt/ExecuteTrack.java deleted file mode 100644 index b6a96ab..0000000 --- a/tool/src/main/java/migt/ExecuteTrack.java +++ /dev/null @@ -1,463 +0,0 @@ -package migt; - -import org.openqa.selenium.*; -import org.openqa.selenium.chrome.ChromeDriver; -import org.openqa.selenium.chrome.ChromeOptions; -import org.openqa.selenium.firefox.FirefoxDriver; -import org.openqa.selenium.firefox.FirefoxOptions; -import org.openqa.selenium.remote.CapabilityType; -import org.openqa.selenium.support.ui.ExpectedConditions; -import org.openqa.selenium.support.ui.WebDriverWait; - -import java.io.File; -import java.time.Duration; - -/** - * Class that executes a Session Track (series of user actions). It launches a browser with Selenium to automate the - * actions - */ -public class ExecuteTrack implements Runnable { - private static String snapshot = ""; - private final boolean chrome_selected; - private final String driver_path; - private final boolean isHeadless; - private final Track track; - public String current_url; - public String port = ""; - public String sessionName; - public boolean isInPause; - public ExecuteTrackListener listener; - private Boolean isReqClearCookies = false; - - /** - * Instantiate the ExecuteTrack Object - * - * @param isHeadless - * @param chrome_selected tells if chrome is selected as a browser to be used, otherwise firefox is used - * @param driver_path the path to the chosen browser's driver - * @param track the track to be executed - * @param port the port set in the browser that the HTTP(S) proxy is listening to - * @param sessionName The name of the session to be executed - */ - public ExecuteTrack(boolean isHeadless, - boolean chrome_selected, - String driver_path, - Track track, - String port, - String sessionName) { - this.chrome_selected = chrome_selected; - this.driver_path = driver_path; - this.track = track; //TODO: can't I just take the session object? - this.isHeadless = isHeadless; - this.port = port; - this.sessionName = sessionName; //TODO: can't I just take the session object? - } - - /** - * Registers the execute track listener, used to communicate with the ExecuteTrack thread - * - * @param listener the listener - */ - public void registerExecuteTrackListener(ExecuteTrackListener listener) { - this.listener = listener; - } - - /** - * Runs the session track - */ - @Override - public void run() { - WebDriver driver; - int TIMEOUT = 10; - - if (chrome_selected) { - ChromeOptions options = new ChromeOptions(); - options.addArguments("ignore-certificate-errors"); - options.addArguments("window-size=1280,1400"); - options.addArguments("--proxy-bypass-list=<-loopback>"); - options.addArguments("--remote-allow-origins=*"); - Proxy proxy = new Proxy(); - proxy.setHttpProxy("localhost:" + port); - proxy.setSslProxy("localhost:" + port); - options.setCapability(CapabilityType.PROXY, proxy); - //options.setHeadless(isHeadless); - - System.setProperty("webdriver.chrome.driver", driver_path); - try { - driver = new ChromeDriver(options); - } catch (SessionNotCreatedException e) { - e.printStackTrace(); - return; - } - - } else { - FirefoxOptions options = new FirefoxOptions(); - options.addArguments("-width=1280"); - options.addArguments("-height=1400"); - Proxy proxy = new Proxy(); - proxy.setHttpProxy("localhost:" + port); - proxy.setSslProxy("localhost:" + port); - options.setCapability(CapabilityType.PROXY, proxy); - //options.setHeadless(isHeadless); - - System.setProperty("webdriver.gecko.driver", driver_path); - try { - driver = new FirefoxDriver(options); - } catch (SessionNotCreatedException e) { - e.printStackTrace(); - return; - } - } - - WebElement currentElement = null; - int act_window_index = 0; - - SessionTrackAction last_action = null; - SessionTrackAction last_open = null; - SessionTrackAction last_click = null; - String last_url = ""; - - int startindex = 0; - - beforeloop: - - try { - for (int i = startindex; i < track.getTrack().size(); i++) { - last_action = track.getTrack().get(i); - - try { - last_url = driver.getCurrentUrl(); - } catch (WebDriverException e) { - } - - if (track.getTrack().get(i).action == SessionOperation.SessAction.CLICK) { - last_click = track.getTrack().get(i); - } - if (track.getTrack().get(i).action == SessionOperation.SessAction.OPEN) { - last_open = track.getTrack().get(i); - } - listener.onNextSessionAction(last_action, last_open, last_click, last_url, sessionName); - - isInPause = listener.onAskPause(sessionName); - - while (isInPause) { - isInPause = listener.onAskPause(sessionName); - Thread.sleep(1000); - } - - Thread.sleep(1000); // Don't delete, it is useful - - SessionTrackAction action = track.getTrack().get(i); - - currentElement = null; - - Object[] tmp = driver.getWindowHandles().toArray(); - String[] windows = new String[tmp.length]; - int j = 0; - for (Object o : tmp) { - String act = (String) o; - windows[j++] = act; - } - - int windows_count = windows.length; - int windows_checked = 0; - - try { - driver.getWindowHandle(); - } catch (NoSuchWindowException e) { - act_window_index = 0; - driver.switchTo().window(windows[act_window_index]); - } - - switch (action.action) { - case WAIT: { - String time = action.elem; - int time_int = Integer.parseInt(time); - - Thread.sleep(time_int); - continue; - } - case ALERT: { - if (action.elem != null) { - Alert alert = null; - int c = 0; - while (c++ < 10) { - try { - alert = driver.switchTo().alert(); - break; - } catch (NoAlertPresentException e) { - Thread.sleep(1000); - continue; - } - } - if (action.elem.equals("accept")) { - alert.accept(); - } else if (action.elem.equals("dimiss")) { - alert.dismiss(); - } - } else { - throw new ParsingException("invalid alert action"); - } - } - case CLEAR_COOKIES: { - driver.manage().deleteAllCookies(); - continue; - } - case OPEN: { - driver.get(action.elem); - continue; - } - case SET_VAR: { - Var v = new Var(action.elem, action.content); - listener.onSetVar(v); - continue; - } - case TYPE: - case CLICK: - case SNAPSHOT: - case DIFF: - case EQUALS: - case ASSERT_NOT_VISIBLE: - case ASSERT_NOT_CLICKABLE: - case ASSERT_CLICKABLE: - case ASSERT_VISIBLE: - case ASSERT_ELEM_CONTENT_HAS: - case ASSERT_ELEM_CONTENT_IS: - case ASSERT_ELEM_CLASS_HAS: - case ASSERT_ELEM_CLASS_IS: - case ASSERT_ELEM_HAS_ATTRIBUTE: - case ASSERT_ELEM_NOT_HAS_ATTRIBUTE: { - String searchBy = action.elem_type; - String identifier = action.elem_source; - - while (windows_checked != windows_count) { - try { - boolean is_snapshot = action.action == SessionOperation.SessAction.SNAPSHOT || - action.action == SessionOperation.SessAction.DIFF || - action.action == SessionOperation.SessAction.EQUALS; - - By by = null; - // Checks for the presence of a valid item to search - switch (searchBy.trim()) { - case "id": - by = By.id(identifier.trim()); - break; - case "xpath": - by = By.xpath(identifier.trim()); - break; - case "link": - by = By.linkText(identifier.trim()); - break; - case "name": - by = By.name(identifier.trim()); - break; - case "class": - by = By.className(identifier.trim()); - break; - default: - throw new ParsingException("invalid session track command"); - } - - if (action.action == SessionOperation.SessAction.ASSERT_VISIBLE) { - new WebDriverWait(driver, Duration.ofSeconds(TIMEOUT)).until( - ExpectedConditions.visibilityOfElementLocated(by)); - } else if (is_snapshot) { - new WebDriverWait(driver, Duration.ofSeconds(TIMEOUT)).until( - ExpectedConditions.presenceOfElementLocated(by)); - } else { - new WebDriverWait(driver, Duration.ofSeconds(TIMEOUT)).until( - ExpectedConditions.elementToBeClickable(by)); - } - - currentElement = driver.findElement(by); - - } catch (TimeoutException | NoSuchWindowException e) { - if (act_window_index < windows_count - 1) { - driver.switchTo().window(windows[++act_window_index]); - } else { - act_window_index = 0; - driver.switchTo().window(windows[act_window_index]); - } - windows_checked++; - } catch (UnhandledAlertException ex) { - Alert alert = driver.switchTo().alert(); - if (alert != null) - alert.accept(); - } - if (currentElement != null) break; - } - if (currentElement == null) { - if (action.action == SessionOperation.SessAction.ASSERT_CLICKABLE - || action.action == SessionOperation.SessAction.ASSERT_VISIBLE) { - listener.onExecuteDone(false, sessionName); - driver.close(); - return; - } - throw new TimeoutException(identifier.trim() + " Could not be focused"); - } - break; - } - - default: { - System.err.printf("error in session %s track", sessionName); - throw new ParsingException("invalid session track command" + action); - } - } - - if (currentElement != null) { - switch (action.action) { - case CLICK: - new WebDriverWait(driver, Duration.ofSeconds(TIMEOUT)) - .until(ExpectedConditions.elementToBeClickable(currentElement)) - .click(); - break; - case TYPE: - new WebDriverWait(driver, Duration.ofSeconds(TIMEOUT)) - .until(ExpectedConditions.elementToBeClickable(currentElement)) - .sendKeys(action.content); - break; - case SNAPSHOT: - new WebDriverWait(driver, Duration.ofSeconds(TIMEOUT)) - .until(ExpectedConditions.visibilityOf(currentElement)); - File f = currentElement.getScreenshotAs(OutputType.FILE); - snapshot = currentElement.getScreenshotAs(OutputType.BASE64); - f.renameTo(new File("./snapshot.png")); - break; - case DIFF: - case EQUALS: - new WebDriverWait(driver, Duration.ofSeconds(TIMEOUT)) - .until(ExpectedConditions.visibilityOf(currentElement)); - String diff = currentElement.getScreenshotAs(OutputType.BASE64); - File f2 = currentElement.getScreenshotAs(OutputType.FILE); - f2.renameTo(new File("./diff.png")); - if (action.action == SessionOperation.SessAction.DIFF) { - if (diff.equals(snapshot)) { - listener.onExecuteDone(true, current_url, sessionName); - driver.close(); - return; - } - } else { - if (!diff.equals(snapshot)) { - listener.onExecuteDone(true, current_url, sessionName); - driver.close(); - return; - } - } - break; - case ASSERT_VISIBLE: - case ASSERT_CLICKABLE: - listener.onExecuteDone(true, sessionName); - driver.close(); - return; - case ASSERT_NOT_CLICKABLE: - case ASSERT_NOT_VISIBLE: - listener.onExecuteDone(false, sessionName); - driver.close(); - return; - case ASSERT_ELEM_CONTENT_IS: { - String content = currentElement.getText(); - if (!content.equals(action.content)) { - listener.onExecuteDone(false, sessionName); - driver.close(); - return; - } - break; - } - case ASSERT_ELEM_CONTENT_HAS: { - String content = currentElement.getText(); - if (!content.contains(action.content)) { - listener.onExecuteDone(false, sessionName); - driver.close(); - return; - } - break; - } - case ASSERT_ELEM_CLASS_IS: { - String classtxt = currentElement.getAttribute("class"); - if (!classtxt.equals(action.content)) { - listener.onExecuteDone(false, sessionName); - driver.close(); - return; - } - break; - } - case ASSERT_ELEM_CLASS_HAS: { - String classtxt = currentElement.getAttribute("class"); - if (!classtxt.contains(action.content)) { - listener.onExecuteDone(false, sessionName); - driver.close(); - return; - } - break; - } - case ASSERT_ELEM_HAS_ATTRIBUTE: { - String attr = currentElement.getAttribute(action.content); - if (attr == null) { - listener.onExecuteDone(false, sessionName); - driver.close(); - return; - } - break; - } - case ASSERT_ELEM_NOT_HAS_ATTRIBUTE: { - String attr = currentElement.getAttribute(action.content); - if (attr != null) { - listener.onExecuteDone(false, sessionName); - driver.close(); - return; - } - break; - } - } - } - - listener.onNextSessionAction(last_action, last_open, last_click, last_url, sessionName); - - isReqClearCookies = listener.onAskClearCookie(sessionName); - if (isReqClearCookies != null) { - if (isReqClearCookies) { - driver.manage().deleteAllCookies(); - - isReqClearCookies = false; - } - } - - Track new_track = listener.onUpdateTrack(sessionName); - - if (new_track != null && !new_track.equals(track)) { - SessionTrackAction last = track.getTrack().get(i); - int last_index = new_track.getTrack().lastIndexOf(last); - if (last_index == -1) { - throw new ParsingException("Error in resuming the track after edit"); - } - startindex = new_track.getTrack().indexOf(last) + 1; - break beforeloop; - } - - } - } catch (InterruptedException interruptedException) { - System.out.printf("Session %s stopped, no checks necessary", sessionName); - } catch (ParsingException | NumberFormatException | ArrayIndexOutOfBoundsException e) { - System.err.println(e.getMessage()); - current_url = ""; - listener.onError(sessionName); - driver.close(); - return; - } catch (TimeoutException e) { - System.err.println(e.getMessage()); - current_url = ""; - listener.onExecuteDone(true, current_url, sessionName); - driver.close(); - return; - } catch (WebDriverException error) { - current_url = ""; - System.out.printf("ERROR SELENIUM WEBDRIVER (session %s) =>\n %s", sessionName, error); - listener.onExecuteDone(true, current_url, sessionName); - driver.close(); - return; - } - current_url = ""; - driver.close(); - listener.onExecuteDone(false, current_url, sessionName); - } -} diff --git a/tool/src/main/java/migt/Main.java b/tool/src/main/java/migt/Main.java deleted file mode 100644 index 82a8d88..0000000 --- a/tool/src/main/java/migt/Main.java +++ /dev/null @@ -1,1711 +0,0 @@ -package migt; - -import burp.IBurpExtenderCallbacks; -import burp.IHttpService; -import burp.IMessageEditor; -import burp.IMessageEditorController; -import com.google.gson.Gson; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import javax.swing.*; -import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.table.DefaultTableModel; -import java.awt.*; -import java.awt.event.MouseEvent; -import java.io.*; -import java.util.List; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * Sets a JTextArea as an output of an OutputStream, used by the redirect of the stdout and stderr - */ -class CustomOutputStream extends OutputStream { - private final JTextArea textArea; - - public CustomOutputStream(JTextArea textArea) { - this.textArea = textArea; - } - - @Override - public void write(int b) { - // Redirect the byte written to the OutputStream to the JTextArea - retry: - try { - textArea.append(String.valueOf((char) b)); - textArea.setCaretPosition(textArea.getDocument().getLength()); // Auto-scroll to the bottom - } catch (java.lang.Error e) { - // for some reason write lock acquire is interrupted sometimes - break retry; // TODO: fix - } - } -} - -/** - * This class contains the GUI for the plugin, also a lot of functionality methods - */ -public class Main extends JSplitPane { - private static DefaultTableModel resultTableModel; - private static DefaultTableModel testTableModel; - final Object waiting = new Object(); - final String LOG_FOLDER = "logs/"; - private final String[] foundTableColNames = {"Op. num", "Message Type", "message section", "check/regex", "index", "result"}; - private final String[] testSuiteColNames = { - "Test name", - "Description", - "References", - "Statement in Ref. to Test", - "Affected Entity", - "Mitigations", - "Result"}; - private final Object[][] foundData = {}; - private final Object lock2 = new Object(); - public ArrayList interceptedMessages; - String SAVE_FILE_PATH = ""; - String RECORD_FILE_PATH = ""; - boolean FILTERING = true; - String MSG_DEF_PATH = "msg_def.json"; - String CONFIG_FILE_PATH = "config.json"; - //GUI - JTable resultTable; - JTable testTable; - JPanel trackContainer; - JPanel inputContainer; - JLabel lblTrack; - JLabel lblnextTestBrowser; - JLabel lblInfo; - JLabel lblOutput; - JLabel lbldriver; - JButton btnTestTrack; - JButton btnselectChrome; - JButton btnselectFirefox; - JButton btnExecuteSuite; - JButton btnSetRecording; - JButton btnLoadMessages; - JButton btnSetOffline; - JButton btnExecuteTrack; - JButton btnSaveToFile; - JButton btndriverSelector; - JTextArea txtScript; - JTextArea txtSearch; - JTextArea txtSessionConfig; - JTextArea txt_out_debug_tab; - JTextArea txt_err_debug_tab; - JFileChooser driverSelector; - JSplitPane splitPane; - IMessageEditor messageViewer; - IMessageEditorController controller; - JTabbedPane top_tabbed; - JTabbedPane bot_tabbed; - Map bot_tabs_index; - HTTPReqRes viewedMessage; - IBurpExtenderCallbacks callbacks; - List sessions_names; - Map session_port; - Session defaultSession; - TestSuite testSuite; - boolean INTERCEPT_ENABLED; - boolean recording = false; - boolean OFFLINE = false; - boolean SAVE_TO_FILE = false; - Operation actual_operation; - ExecuteActives ex; - List messageTypes; - private Integer DEFAULT_PORT = 8080; - private String DRIVER_PATH = ""; - private List actives; - private Map sessions_text; - private List passives; - private Thread active_ex; - private boolean active_ex_finished = false; - - /** - * Constructor of the plugin UI - */ - public Main() { - super(JSplitPane.VERTICAL_SPLIT); - //initialize vars - init(); - - set_std_out_redirect(); - - this.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); - - // Top part of the UI ------------------------------------------------------------------------------------------ - GridBagLayout top_layout = new GridBagLayout(); - top_layout.columnWidths = new int[]{230, 230, 230, 230, 100, 100, 100}; - top_layout.rowHeights = new int[]{20, 48, 48, 48, 48}; - top_layout.columnWeights = new double[]{1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0}; - top_layout.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0}; - - trackContainer = new JPanel(); - trackContainer.setLayout(top_layout); - - setup_tab_track(); - setup_tab_butons(); - - this.setTopComponent(trackContainer); - - // Bottom part ------------------------------------------------------------------------------------------------- - - GridBagLayout bot_layout = new GridBagLayout(); - bot_layout.columnWidths = new int[]{500, 500, 100}; - bot_layout.rowHeights = new int[]{15, 20, 20, 20, 30}; - bot_layout.columnWeights = new double[]{Double.MIN_VALUE, 0.0}; - bot_layout.rowWeights = new double[]{0.0, Double.MIN_VALUE, 0.0, 0.0, 0.0}; - - bot_tabbed = new JTabbedPane(); - - JPanel tab_input_json = setup_tab_input_json(bot_layout); - bot_tabs_index.put("Input JSON", 0); - bot_tabbed.addTab("Input JSON", tab_input_json); - - JScrollPane tab_suite_result = setup_tab_suite_result(bot_layout); - bot_tabs_index.put("Test Suite Result", 1); - bot_tabbed.addTab("Test Suite Result", tab_suite_result); - - JSplitPane tab_test_result = setup_tab_test_result(bot_layout); - bot_tabs_index.put("Test Result", 2); - bot_tabbed.addTab("Test Result", tab_test_result); - - JPanel tab_session_config = setup_tab_session_config(bot_layout); - bot_tabs_index.put("session config", 3); - bot_tabbed.addTab("session config", tab_session_config); - - JPanel tab_debug_panel = setub_tab_debug(bot_layout); - bot_tabs_index.put("debug tab", 4); - bot_tabbed.addTab("debug tab", tab_debug_panel); - - //Set Bottom Part - this.setBottomComponent(bot_tabbed); - - readMsgDefFile(); - readConfigFile(); - if (!DRIVER_PATH.equals("")) { - lbldriver.setText("Driver Selected"); - btndriverSelector.setBackground(Color.GREEN); - btnTestTrack.setEnabled(true); - } - } - - /** - * Function used to add an item to the resultTableModel. Contains the results of the tests - * - * @param data the string array containing the data, also a row - */ - private static void addItem(String[] data) { - resultTableModel.addRow(data); - } - - public void init() { - interceptedMessages = new ArrayList<>(); - testSuite = new TestSuite(); - passives = new ArrayList<>(); - actives = new ArrayList<>(); - sessions_names = new ArrayList<>(); - INTERCEPT_ENABLED = false; - sessions_text = new HashMap<>(); - messageTypes = new ArrayList<>(); - session_port = new HashMap<>(); - bot_tabs_index = new HashMap<>(); - txt_out_debug_tab = new JTextArea(); - txt_err_debug_tab = new JTextArea(); - } - - /** - * Set a redirect of the stdout and stderr to the txtboxes in the debug tab of the GUI - */ - private void set_std_out_redirect() { - // Stdout out redirect - PrintStream printStream = new PrintStream( - new CustomOutputStream(txt_out_debug_tab)); - System.setOut(printStream); - - // stderr out redirect - PrintStream printStream_err = new PrintStream( - new CustomOutputStream(txt_err_debug_tab)); - System.setErr(printStream_err); - } - - /** - * Function used to read the message definition file - */ - private void readMsgDefFile() { - File msg_def_file = new File(MSG_DEF_PATH); - try { - if (msg_def_file.createNewFile()) { - // if file does not exist - FileWriter w = new FileWriter(MSG_DEF_PATH); - w.write(Tools.getDefaultJSONMsgType()); - w.close(); - } - - // read content - StringBuilder content = new StringBuilder(); - Scanner myReader = new Scanner(msg_def_file); - - while (myReader.hasNextLine()) { - content.append(myReader.nextLine()); - } - myReader.close(); - messageTypes = Tools.readMsgTypesFromJson(content.toString()); // load messsage types - - } catch (ParsingException e) { - lblOutput.setText("Invalid message type in message type definition file"); - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - lblOutput.setText("cannot create message definition file"); - } - } - - /** - * Function used to read the json config file - */ - private void readConfigFile() { - File config_file = new File(CONFIG_FILE_PATH); - try { - if (!config_file.createNewFile()) { - StringBuilder content = new StringBuilder(); - Scanner myReader = new Scanner(config_file); - while (myReader.hasNextLine()) { - content.append(myReader.nextLine()); - } - myReader.close(); - - JSONObject obj = new JSONObject(content.toString()); - String last_driver_path = obj.getString("last_driver_path"); - String last_used_browser = obj.getString("last_browser_used"); - DEFAULT_PORT = obj.getInt("default_port"); - - if (!last_driver_path.isEmpty()) { - DRIVER_PATH = last_driver_path; - } - - switch (last_used_browser) { - case "firefox": { - btnselectChrome.setEnabled(true); - btnselectFirefox.setEnabled(false); - break; - } - case "chrome": { - btnselectChrome.setEnabled(false); - btnselectFirefox.setEnabled(true); - break; - } - } - } else { - FileWriter w = new FileWriter(CONFIG_FILE_PATH); - w.write(Tools.getDefaultJSONConfig()); - w.close(); - } - } catch (IOException e) { - lblOutput.setText("cannot create message definition file: " + e); - } catch (JSONException e) { - lblOutput.setText("Invalid config file: " + e); - } - } - - /** - * Function that edits the config file. - * - * @param key the key of the config to change - * @param value the new value of the config - */ - private void editConfigFile(String key, String value) { - File config_file = new File(CONFIG_FILE_PATH); - try { - if (config_file.createNewFile()) { - FileWriter w = new FileWriter(CONFIG_FILE_PATH); - w.write(Tools.getDefaultJSONConfig()); - w.close(); - } - - StringBuilder content = new StringBuilder(); - Scanner myReader = new Scanner(config_file); - while (myReader.hasNextLine()) { - content.append(myReader.nextLine()); - } - myReader.close(); - - JSONObject obj = new JSONObject(content.toString()); - obj.remove(key); - obj.put(key, value); - - FileWriter w = new FileWriter(CONFIG_FILE_PATH); - w.write(obj.toString()); - w.close(); - } catch (IOException e) { - lblOutput.setText("cannot create message definition file: " + e); - } catch (JSONException e) { - lblOutput.setText("Invalid config file: " + e); - } - } - - /** - * This function parses the given jsonInput string of the language - * - * @param jsonInput the json input - */ - private void readJSONinput(String jsonInput) { - sessions_names.clear(); - txtSearch.setBorder(BorderFactory.createEmptyBorder()); - setJSONError(false, ""); - - try { - JSONObject obj = new JSONObject(jsonInput); - List tests = new ArrayList<>(); - - //Getting Test suite data - String suite_name = obj.getJSONObject("test suite").getString("name"); - String suite_description = obj.getJSONObject("test suite").getString("description"); - - if (obj.getJSONObject("test suite").has("filter messages")) { - FILTERING = obj.getJSONObject("test suite").getBoolean("filter messages"); - } - - //Array of Tests - JSONArray arrTests = obj.getJSONArray("tests"); - for (int i = 0; i < arrTests.length(); i++) { - JSONObject act_test = arrTests.getJSONObject(i).getJSONObject("test"); - - Test test = new Test(act_test, messageTypes); - tests.add(test); - - for (Session s : test.sessions) { - if (!sessions_names.contains(s.name)) { - sessions_names.add(s.name); - session_port.put(s.name, DEFAULT_PORT.toString()); // set default port to session - } - } - } - updateSessionTabs(); - updateTxtSessionConfig(); - - this.testSuite = new TestSuite(suite_name, suite_description, tests); - lblInfo.setText("JSON read successfully, Test Suite Object has been created"); - - } catch (ParsingException e) { - e.printStackTrace(); - - setJSONError(true, "Problem in parsing JSON: " + e.getMessage()); - } catch (Exception e) { - e.printStackTrace(); - - setJSONError(true, "problem in reading json test suite"); - } - } - - /** - * This function reads the selected file deserializing the messages and creating a new Session - * - * @return if the reading has been succesfull - */ - private boolean readSavedMessages() { - if (!SAVE_FILE_PATH.isEmpty()) { - try { - if (defaultSession == null) { - defaultSession = new Session("default"); - defaultSession.isOffline = true; - - File f = new File(SAVE_FILE_PATH); - Scanner r = new Scanner(f); - - Gson json = new Gson(); - while (r.hasNextLine()) { - HTTPReqRes tmp = json.fromJson(r.nextLine(), HTTPReqRes.class); - defaultSession.messages.add(tmp); - } - } else { - System.out.println("main session already created, skipping message reading from file"); - } - return true; - } catch (FileNotFoundException fileNotFoundException) { - fileNotFoundException.printStackTrace(); - return false; - } - } else { - return false; - } - } - - /** - * Method which executes the entire test suite - */ - private void executeSuite() { - // clears all previously saved tests - actives.clear(); - passives.clear(); - actual_operation = null; - ex = null; - active_ex_finished = false; - - // clears the test suite result table - DefaultTableModel dm = (DefaultTableModel) resultTable.getModel(); - dm.getDataVector().removeAllElements(); - dm.fireTableDataChanged(); - - System.out.println("Number of test found: " + testSuite.getTests().size()); - for (Test t : testSuite.getTests()) { - if (t.isActive) { - actives.add(t); - } else { - passives.add(t); - } - } - - if (OFFLINE) { - if (!readSavedMessages()) { - System.err.println("Can't read message file"); - lblOutput.setText("Can't read message file"); - return; - } - }//TODO: re-enable OFFLINE mode - /* else if (passives.size() > 0 && defaultSession == null && actives.size() == 0) { - lblOutput.setText("Track need to be run for passive tests before executing tests"); - return; - } - */ - - if (actives.isEmpty()) { - synchronized (lock2) { - active_ex_finished = true; - } - } - - // Execute active tests - if (!actives.isEmpty()) { - try { - for (String key : session_port.keySet()) { - if (session_port.get(key).equals("")) { - lblOutput.setText("session port not configured"); - return; - } - } - ex = new ExecuteActives(actives, waiting); - - editConfigFile("last_browser_used", btnselectChrome.isEnabled() ? "firefox" : "chrome"); - - ex.registerExecuteActivesListener(new ExecuteActiveListener() { - @Override - public void onExecuteStart() { - INTERCEPT_ENABLED = false; - actual_operation = new Operation(); - lblOutput.setText("Executing active tests"); - } - - @Override - public void onExecuteDone() { - if (passives.isEmpty()) { - update_gui_test_results(); - - lblOutput.setText("Done. Executed Passive Tests: " - + (passives.isEmpty() ? 0 : passives.size()) - + " - Active Tests: " - + (testSuite.getTests().size() - (passives.isEmpty() ? 0 : passives.size()))); - } else { - lblOutput.setText("Executed Active tests, now doing passives"); - } - synchronized (lock2) { - active_ex_finished = true; - } - } - - - @Override - public void onNewProcessOperation(Operation op) { - INTERCEPT_ENABLED = true; - actual_operation = op; - } - - @Override - public Operation onOperationDone() { - INTERCEPT_ENABLED = false; - Operation tmp = actual_operation; - - actual_operation = new Operation(); - return tmp; - } - - @Override - public Session onNewSession(Session s) { - Track track = null; - try { - track = s.setTrackFromString(getSessionTxt(s.name)); - } catch (ParsingException e) { - lblOutput.setText("error in parsing session track"); - return null; - } - String port = session_port.get(s.name); - s.port = port; - s.track = track; - - s.ex = new ExecuteTrack(false, - !btnselectChrome.isEnabled(), - DRIVER_PATH, - track, - port, - s.name); - return s; - } - - @Override - public void onNewTest(Test actual_test) { - actual_operation = null; - } - - public void onTestDone(Test actual_test) { - int indx = testSuite.tests.indexOf(actual_test); - if (indx != -1) { - System.out.printf("Saving test %s in test results", actual_test.getName()); - testSuite.tests.set(indx, actual_test); - } - } - - @Override - public void onError(Test actual_test) { - System.err.println("Error executing the test:" + actual_test.name); - synchronized (lock2) { - active_ex_finished = true; - } - } - }); - - active_ex = new Thread(ex); - active_ex.start(); - - } catch (Exception er) { - er.printStackTrace(); - System.out.println(er.getLocalizedMessage() + "nad" + er.getMessage() + "2" + er); - - lblOutput.setText("PROBLEM IN Executing Suite, check it please"); - } - } - - // Execute passive tests - if (!passives.isEmpty()) { - // TODO: Add offline clause - /* - if (defaultSession.messages.size() == 0) { - lblOutput.setText("no message found"); - return; - } - */ - - ExecutePassiveListener listener = new ExecutePassiveListener() { - @Override - public boolean onWaitToStart() { - // waits for active test execution to finish before doing passives - synchronized (lock2) { - return active_ex_finished; - } - } - - @Override - public void onExecuteStart() { - lblOutput.setText("Executing passive tests"); - } - - @Override - public void onExecuteDone(List passives_test) { - //Actives are already executed, because passives are started after actives - lblOutput.setText("Done. Executed Passive Tests: " - + (passives.isEmpty() ? 0 : passives.size()) - + " - Active Tests: " - + (testSuite.getTests().size() - (passives.isEmpty() ? 0 : passives.size()))); - - passives = passives_test; - - update_gui_test_results(); - } - - @Override - public void onError(String msg) { - lblOutput.setText(msg); - } - - @Override - public Session onNewSession(Session s) throws ParsingException { - s.setTrackFromString(getSessionTxt(s.name)); - - String port = session_port.get(s.name); - s.port = port; - - s.ex = new ExecuteTrack(false, - !btnselectChrome.isEnabled(), - DRIVER_PATH, - s.track, - port, - s.name); - - return s; - } - - @Override - public void onBeforeExecuteTrack() { - //Clear previous interceptedMessages if any - interceptedMessages.clear(); - //Tell Burp Extender class to record the intercepted messages from now on - recording = true; - } - - @Override - public ArrayList onTrackExecuteDone() { - recording = false; - return interceptedMessages; - } - }; - - ExecutePassives expa = new ExecutePassives( - passives, - listener, - messageTypes); - - new Thread(expa).start(); - } - } - - /** - * Function used to get the text of the text area of a certain session using the session name - * - * @param session_name the name of the session to get the text - * @return the content of the session tab if it is not empty, otherwise the main session text tab - */ - private String getSessionTxt(String session_name) { - if (sessions_text.containsKey(session_name)) { - JTextArea t = (JTextArea) sessions_text.get(session_name); - if (t.getText().equals("")) { - return txtScript.getText(); - } else { - return t.getText(); - } - } - return null; - } - - /** - * Update the session config tab, using the session_port variable, at the same time reads if there are changes, and - * updates the session_port variable - */ - private void updateTxtSessionConfig() throws ParsingException { - setSession_configError(false, ""); - - String text = txtSessionConfig.getText(); - Pattern p = Pattern.compile("\\n"); - Matcher m = p.matcher(text); - text = m.replaceAll(""); - - if (text.equals("")) { - String tmp = ""; - for (String s : session_port.keySet()) { - tmp += s; - tmp += ":" + session_port.get(s) + ";\n"; - } - txtSessionConfig.setText(tmp); - return; - } - - String[] text_list = text.split(";"); - - for (String row : text_list) { - String[] splitted = row.trim().split(":"); - if (splitted.length == 0) continue; - if (splitted.length <= 1) { - String[] splitted2 = new String[]{"", ""}; - splitted2[0] = splitted[0]; - splitted = splitted2; - } - String port = splitted[1].trim(); - p = Pattern.compile("^\\d+$"); - m = p.matcher(splitted[1].trim()); - if (!m.find()) { - setSession_configError(true, "invalid port"); - - throw new ParsingException("Invalid port"); - } - if (session_port.containsKey(splitted[0].trim())) { - session_port.replace(splitted[0].trim(), splitted[1].trim()); - } else { - session_port.put((splitted[0]).trim(), splitted[1].trim()); - } - } - String tmp = ""; - for (String key : session_port.keySet()) { - tmp += key + ": " + session_port.get(key) + ";\n"; - } - - txtSessionConfig.setText(tmp); - } - - /** - * This function updates the session tabs in the gui to match the actual value in session_names - */ - private void updateSessionTabs() { - List present = new ArrayList<>(); - - for (int i = 1; i < top_tabbed.getTabCount(); i++) { - present.add(top_tabbed.getTitleAt(i)); - if (!sessions_names.contains(top_tabbed.getTitleAt(i))) { - top_tabbed.remove(i); - } - } - for (String name : sessions_names) { - if (!present.contains(name)) { - JTextArea tmp = new JTextArea(); - JScrollPane sp = new JScrollPane(tmp, - JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, - JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - sessions_text.put(name, tmp); - top_tabbed.add(name, sp); - } - } - JTextArea tmp = new JTextArea(); - JScrollPane sp = new JScrollPane(tmp, - JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, - JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - } - - /** - * Function used to set the JSON textbox with a red colour to highlight an error. - * - * @param isInError true to highlight an error, false to remove the highlight - * @param msg the error message to display - */ - private void setJSONError(boolean isInError, String msg) { - if (isInError) { - txtSearch.setBorder(BorderFactory.createLineBorder(Color.RED, 3)); - lblOutput.setText(msg); - bot_tabbed.setBackgroundAt(bot_tabs_index.get("Input JSON"), Color.RED); - } else { - txtSearch.setBorder(BorderFactory.createEmptyBorder()); - lblOutput.setText(""); - bot_tabbed.setBackgroundAt(bot_tabs_index.get("Input JSON"), Color.white); - } - } - - /** - * Function used to set the session config textbox with a red border to highlight an error. - * - * @param isInError true to highlight an error, false to remove highlight - * @param msg The error message to display - */ - private void setSession_configError(boolean isInError, String msg) { - if (isInError) { - txtSessionConfig.setBorder(BorderFactory.createLineBorder(Color.RED, 3)); - lblOutput.setText(msg); - bot_tabbed.setBackgroundAt(bot_tabs_index.get("session config"), Color.RED); - } else { - txtSessionConfig.setBorder(BorderFactory.createEmptyBorder()); - lblOutput.setText(""); - bot_tabbed.setBackgroundAt(bot_tabs_index.get("session config"), Color.white); - } - } - - /** - * Function used to update the gui test results after the tests are executed - */ - private void update_gui_test_results() { - for (Test t : testSuite.getTests()) { - String esito = ""; - if (t.applicable) { - esito = t.success ? "passed" : "failed"; - } else { - esito = "not applicable"; - } - String[] tmp = new String[]{t.getName(), - t.getDescription(), - t.references, - t.violated_properties, - t.affected_entity, - t.mitigations, - esito}; - System.out.println(t.getName() + " " + esito); - addItem(tmp); - } - - testSuite.log_test_suite(LOG_FOLDER); - - btnExecuteSuite.setEnabled(false); - } - - private void setup_tab_track() { - lblTrack = new JLabel("Session track "); - GridBagConstraints gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(10, 0, 0, 0); - gbc.gridx = 0; - gbc.gridy = 0; - trackContainer.add(lblTrack, gbc); - - txtScript = new JTextArea(); - gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.fill = GridBagConstraints.BOTH; - gbc.insets = new Insets(10, 0, 0, 10); - gbc.gridx = 0; - gbc.gridy = 1; - gbc.gridwidth = 4; - gbc.gridheight = 4; - - JScrollPane scrollPane1 = new JScrollPane(txtScript, - JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, - JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - - top_tabbed = new JTabbedPane(); - top_tabbed.add("main", scrollPane1); - trackContainer.add(top_tabbed, gbc); - } - - private void setup_tab_butons() { - driverSelector = new JFileChooser(); - btndriverSelector = new JButton("Select Driver"); - - btndriverSelector.addActionListener(actionEvent -> { - int returnVal = driverSelector.showOpenDialog(Main.this); - if (returnVal == JFileChooser.APPROVE_OPTION) { - File file = driverSelector.getSelectedFile(); - DRIVER_PATH = file.getPath(); - editConfigFile("last_driver_path", DRIVER_PATH); - lbldriver.setText("Driver Selected"); - btndriverSelector.setBackground(Color.GREEN); - btnTestTrack.setEnabled(true); - } else if ((returnVal == JFileChooser.ERROR) || (returnVal == JFileChooser.ERROR_OPTION)) { - lbldriver.setText("Driver:error during file selection"); - System.out.println("error during file selection"); - btnTestTrack.setEnabled(false); - - btndriverSelector.setBackground(Color.RED); - } else { - lbldriver.setText("Driver file still not selected"); - btnTestTrack.setEnabled(false); - btndriverSelector.setBackground(Color.RED); - } - }); - - GridBagConstraints gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 0, 0, 10); - gbc.gridx = 4; - gbc.gridy = 1; - trackContainer.add(btndriverSelector, gbc); - - lbldriver = new JLabel("Driver file still not selected"); - gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.SOUTHEAST; - gbc.insets = new Insets(10, 0, 0, 30); - gbc.gridx = 3; - gbc.gridy = 0; - trackContainer.add(lbldriver, gbc); - - btnTestTrack = new JButton("Test track"); - btnTestTrack.setEnabled(true); - - btnTestTrack.addActionListener(e -> { - ExecuteTrackListener listener = new ExecuteTrackListener() { - @Override - public void onExecuteDone(boolean errors, String current_url, String sessionName) { - if (errors) { - lblOutput.setText("Error in executing track"); - } else { - lblOutput.setText("Track Executed correctly"); - } - } - - @Override - public void onExecuteDone(boolean forceResult, String sessionName) { - if (forceResult) { - lblOutput.setText("Track Executed correctly"); - } else { - lblOutput.setText("Error in executing track"); - } - } - - @Override - public void onError(String sessionName) { - lblOutput.setText("Error in executing track"); - } - - @Override - public Boolean onAskPause(String sessionName) { - return false; - } - - @Override - public Boolean onAskStop(String sessionName) { - return false; - } - - @Override - public Boolean onAskClearCookie(String sessionName) { - return null; - } - - @Override - public void onNextSessionAction(SessionTrackAction last_action, - SessionTrackAction last_open, - SessionTrackAction last_click, - String last_url, - String session_name) { - - } - - @Override - public Track onUpdateTrack(String sessionName) throws ParsingException { - return null; - } - - @Override - public void onSetVar(Var v) { - } - }; - recording = false; - defaultSession = new Session("temp"); - - Track track = null; - try { - track = defaultSession.setTrackFromString(txtScript.getText()); - } catch (ParsingException exc) { - lblOutput.setText("Error in parsing session track"); - } - - defaultSession = null; - ExecuteTrack ex = new ExecuteTrack(false, - !btnselectChrome.isEnabled(), - DRIVER_PATH, - track, - DEFAULT_PORT.toString(), - "test"); - ex.registerExecuteTrackListener(listener); - new Thread(ex).start(); - }); - - gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 0, 0, 0); - gbc.gridx = 5; - gbc.gridy = 1; - btnTestTrack.setPreferredSize(new Dimension(100, 20)); - trackContainer.add(btnTestTrack, gbc); - - btnselectChrome = new JButton("Use Chrome"); - btnselectChrome.setEnabled(false); - btnselectChrome.addActionListener(actionEvent -> { - btnTestTrack.setEnabled(false); - lbldriver.setText("Driver file still not selected"); - btndriverSelector.setBackground(Color.RED); - - btnselectChrome.setEnabled(false); - btnselectFirefox.setEnabled(true); - lblnextTestBrowser.setText("Chrome"); - }); - - lblnextTestBrowser = new JLabel("Firefox"); - gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 0, 0, 0); - gbc.gridx = 4; - gbc.gridy = 2; - trackContainer.add(btnselectChrome, gbc); - - btnselectFirefox = new JButton("Use Firefox"); - btnselectFirefox.setEnabled(true); - btnselectFirefox.addActionListener(actionEvent -> { - btnTestTrack.setEnabled(false); - lbldriver.setText("Driver file still not selected"); - btndriverSelector.setBackground(Color.RED); - - btnselectFirefox.setEnabled(false); - btnselectChrome.setEnabled(true); - lblnextTestBrowser.setText("Firefox"); - }); - - gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 0, 0, 0); - gbc.gridx = 4; - gbc.gridy = 3; - btnselectFirefox.setPreferredSize(new Dimension(100, 20)); - trackContainer.add(btnselectFirefox, gbc); - - JFileChooser messageSaver = new JFileChooser(); - btnSetRecording = new JButton("Record"); - btnSetRecording.setEnabled(true); - - // Recording button listener - btnSetRecording.addActionListener(actionEvent -> { - if (btnSetRecording.isEnabled()) { - int returnVal = messageSaver.showOpenDialog(Main.this); - if (returnVal == JFileChooser.APPROVE_OPTION) { - File file = messageSaver.getSelectedFile(); - RECORD_FILE_PATH = file.getPath(); - lblOutput.setText("File selected"); - - SAVE_TO_FILE = true; - - defaultSession = new Session("default"); - try { - defaultSession.setTrackFromString(txtScript.getText()); - } catch (ParsingException e) { - lblOutput.setText("Error in parsing session track"); - return; - } - - btnSetRecording.setBackground(new Color(255, 0, 0)); - btnSetRecording.setText("recording.."); - - recording = true; - - ExecuteTrackListener listener = new ExecuteTrackListener() { - @Override - public void onExecuteDone(boolean errors, String current_url, String sessionName) { - recording = false; - - if (errors) { - lblOutput.setText("Errore nell'esecuzione della traccia"); - } - - lblOutput.setText("Track recorded"); - - if (SAVE_TO_FILE) { - FileWriter w; - try { - w = new FileWriter(RECORD_FILE_PATH); - - for (HTTPReqRes actual : defaultSession.messages) { - Gson geson = new Gson(); - String serialized = geson.toJson(actual); - w.write(serialized + "\n"); - } - w.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - btnSetRecording.setBackground(Color.white); - btnSetRecording.setText("record"); - } - - @Override - public void onExecuteDone(boolean forceResult, String sessionName) { - - } - - @Override - public void onError(String sessionName) { - lblOutput.setText("Errore nell'esecuzione della traccia"); - } - - @Override - public Boolean onAskPause(String sessionName) { - return false; - } - - @Override - public Boolean onAskStop(String sessionName) { - return false; - } - - @Override - public Boolean onAskClearCookie(String sessionName) { - return false; - } - - @Override - public void onNextSessionAction(SessionTrackAction last_action, - SessionTrackAction last_open, - SessionTrackAction last_click, - String last_url, - String session_name) { - - } - - @Override - public Track onUpdateTrack(String sessionName) throws ParsingException { - return null; - } - - @Override - public void onSetVar(Var v) { - } - }; - - ExecuteTrack ex = new ExecuteTrack(false, - !btnselectChrome.isEnabled(), - DRIVER_PATH, - defaultSession.track, - defaultSession.port, - "main"); - ex.registerExecuteTrackListener(listener); - new Thread(ex).start(); - - } else if ((returnVal == JFileChooser.ERROR) || (returnVal == JFileChooser.ERROR_OPTION)) { - lblOutput.setText("error in selecting output file"); - System.out.println("error in selecting output file"); - } else { - lbldriver.setText("Messages still not loaded"); - } - } - }); - - gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 0, 0, 0); - gbc.gridx = 5; - gbc.gridy = 2; - btnSetRecording.setPreferredSize(new Dimension(100, 20)); - trackContainer.add(btnSetRecording, gbc); - - // Button Execute track - btnExecuteTrack = new JButton("execute track"); - btnExecuteTrack.addActionListener(actionEvent -> { - defaultSession = new Session("default"); - try { - defaultSession.setTrackFromString(txtScript.getText()); - } catch (ParsingException e) { - lblOutput.setText("Error in parsing session track"); - return; - } - btnExecuteTrack.setText("executing.."); - interceptedMessages.clear(); - recording = true; - - ExecuteTrackListener listener = new ExecuteTrackListener() { - @Override - public void onExecuteDone(boolean errors, String current_url, String sessionName) { - recording = false; - - if (errors) { - lblOutput.setText("Errore nell'esecuzione della traccia"); - } - - lblOutput.setText("Track executed"); - - btnExecuteTrack.setText("execute track"); - } - - @Override - public void onExecuteDone(boolean forceResult, String sessionName) { - - } - - @Override - public void onError(String sessionName) { - lblOutput.setText("Errore nell'esecuzione della traccia"); - } - - @Override - public Boolean onAskPause(String sessionName) { - return false; - } - - @Override - public Boolean onAskStop(String sessionName) { - return false; - } - - @Override - public Boolean onAskClearCookie(String sessionName) { - return null; - } - - @Override - public void onNextSessionAction(SessionTrackAction last_action, - SessionTrackAction last_open, - SessionTrackAction last_click, - String last_url, - String session_name) { - } - - @Override - public Track onUpdateTrack(String sessionName) throws ParsingException { - return null; - } - - @Override - public void onSetVar(Var v) { - } - }; - - editConfigFile("last_browser_used", btnselectChrome.isEnabled() ? "firefox" : "chrome"); - - ExecuteTrack ex = new ExecuteTrack(false, - !btnselectChrome.isEnabled(), - DRIVER_PATH, - defaultSession.track, - defaultSession.port, - "main"); - ex.registerExecuteTrackListener(listener); - new Thread(ex).start(); - }); - - gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 0, 0, 0); - gbc.gridx = 5; - gbc.gridy = 3; - btnSetRecording.setPreferredSize(new Dimension(100, 20)); - trackContainer.add(btnExecuteTrack, gbc); - - gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 0, 0, 0); - gbc.gridx = 4; - gbc.gridy = 4; - trackContainer.add(new JLabel("Next test will use"), gbc); - - gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(20, 0, 0, 0); - gbc.gridx = 4; - gbc.gridy = 4; - trackContainer.add(lblnextTestBrowser, gbc); - - JFileChooser messageLoader = new JFileChooser(); - btnLoadMessages = new JButton("load messages"); - btnLoadMessages.setEnabled(true); - btnLoadMessages.addActionListener(actionEvent -> { - if (btnLoadMessages.getBackground() == Color.GREEN) { - btnSetOffline.setEnabled(false); - btnSetOffline.setBackground(Color.white); - btnLoadMessages.setBackground(Color.white); - btnLoadMessages.setText("load messages"); - OFFLINE = false; - SAVE_FILE_PATH = ""; - - } else { - int returnVal = messageLoader.showOpenDialog(Main.this); - if (returnVal == JFileChooser.APPROVE_OPTION) { - File file = messageLoader.getSelectedFile(); - SAVE_FILE_PATH = file.getPath(); - lblOutput.setText("Messages selected"); - btnLoadMessages.setBackground(Color.GREEN); - btnLoadMessages.setText("unload"); - btnSetOffline.setEnabled(true); - } else if ((returnVal == JFileChooser.ERROR) || (returnVal == JFileChooser.ERROR_OPTION)) { - lblOutput.setText("error in selecting messages"); - System.out.println("error in selecting messages"); - btnSetOffline.setEnabled(false); - btnLoadMessages.setBackground(Color.RED); - } else { - lbldriver.setText("Messages still not loaded"); - btnSetOffline.setEnabled(false); - btnLoadMessages.setBackground(Color.RED); - } - } - }); - - gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 0, 0, 0); - gbc.gridx = 6; - gbc.gridy = 1; - trackContainer.add(btnLoadMessages, gbc); - - btnSetOffline = new JButton("offline mode"); - btnSetOffline.setEnabled(false); - btnSetOffline.addActionListener(actionEvent -> { - if (btnSetOffline.getBackground() == Color.green) { - OFFLINE = false; - btnSetOffline.setBackground(Color.white); - } else { - btnSetOffline.setBackground(Color.green); - OFFLINE = true; - } - }); - gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 0, 0, 0); - gbc.gridx = 6; - gbc.gridy = 2; - trackContainer.add(btnSetOffline, gbc); - - btnSaveToFile = new JButton("save"); - btnSaveToFile.addActionListener(actionEvent -> { - if (SAVE_TO_FILE) { - FileWriter w; - try { - w = new FileWriter(RECORD_FILE_PATH); - - for (HTTPReqRes actual : defaultSession.messages) { - Gson geson = new Gson(); - String serialized = geson.toJson(actual); - w.write(serialized + "\n"); - } - w.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - }); - gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(0, 0, 0, 0); - gbc.gridx = 6; - gbc.gridy = 3; - trackContainer.add(btnSaveToFile, gbc); - } - - private JPanel setup_tab_input_json(GridBagLayout bottom_layout) { - inputContainer = new JPanel(); - inputContainer.setLayout(bottom_layout); - - lblInfo = new JLabel(" "); - GridBagConstraints gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(10, 10, 10, 10); - gbc.gridx = 0; - gbc.gridy = 0; - gbc.gridwidth = 2; - inputContainer.add(lblInfo, gbc); - - lblOutput = new JLabel(" "); - gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(10, 10, 10, 10); - gbc.gridx = 0; - gbc.gridy = 4; - gbc.gridwidth = 2; - inputContainer.add(lblOutput, gbc); - - txtSearch = new JTextArea(); - - JScrollPane scrollPane2 = new JScrollPane(txtSearch, - JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.fill = GridBagConstraints.BOTH; - gbc.insets = new Insets(10, 10, 10, 10); - gbc.gridx = 0; - gbc.gridy = 1; - gbc.gridwidth = 2; - gbc.gridheight = 3; - inputContainer.add(scrollPane2, gbc); - - JButton btnReadJSON = new JButton("Read JSON"); - gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(10, 10, 10, 10); - gbc.gridx = 2; - gbc.gridy = 2; - inputContainer.add(btnReadJSON, gbc); - - btnReadJSON.addActionListener(e -> { - testSuite = new TestSuite(); - - readMsgDefFile(); // Updates the Message Definitions - readJSONinput(txtSearch.getText()); - - try { - updateTxtSessionConfig(); - } catch (ParsingException exc) { - setJSONError(true, "error in updating the session config"); - } - - lblOutput.setText("Number of Tests: " + testSuite.getTests().size()); - - if (testSuite.getTests().size() > 0) { - btnExecuteSuite.setEnabled(true); - } - }); - - JButton btnStop = new JButton("Stop"); - gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(10, 10, 10, 10); - gbc.gridx = 2; - gbc.gridy = 1; - inputContainer.add(btnStop, gbc); - - btnStop.addActionListener(e -> { - if (active_ex != null) { - active_ex.interrupt(); - INTERCEPT_ENABLED = false; - active_ex.stop(); - } - }); - - btnExecuteSuite = new JButton("Execute Test Suite"); - btnExecuteSuite.setEnabled(false); - - - btnExecuteSuite.addActionListener(e -> { - executeSuite(); - }); - - gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.SOUTHWEST; - gbc.insets = new Insets(10, 10, 10, 10); - gbc.gridx = 2; - gbc.gridy = 3; - inputContainer.add(btnExecuteSuite, gbc); - return inputContainer; - } - - private JScrollPane setup_tab_suite_result(GridBagLayout bottom_layout) { - resultTableModel = new DefaultTableModel(foundData, testSuiteColNames) { - @Override - public boolean isCellEditable(int row, int column) { - return false; - } - }; - - resultTable = new JTable(resultTableModel) { - //Implement table cell tool tips. - public String getToolTipText(MouseEvent e) { - String tip = null; - java.awt.Point p = e.getPoint(); - int rowIndex = rowAtPoint(p); - int colIndex = columnAtPoint(p); - - try { - tip = getValueAt(rowIndex, colIndex).toString(); - } catch (RuntimeException e1) { - //catch null pointer exception if mouse is over an empty line - } - return tip; - } - }; - - resultTable.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() { - @Override - public Component getTableCellRendererComponent(JTable table, - Object value, - boolean isSelected, - boolean hasFocus, - int row, - int column) { - final Component c = super.getTableCellRendererComponent(table, - value, - isSelected, - hasFocus, - row, - column); - if (value == null) return c; - if (value.equals("failed")) { - c.setBackground(Color.RED); - } else { - c.setBackground(Color.WHITE); - } - return c; - } - }); - - JScrollPane scrollPane = new JScrollPane(resultTable, - JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - - resultTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); - - // Adds all the test result to the result table - resultTable.getSelectionModel().addListSelectionListener(event -> { - if (resultTable.getSelectedRow() > -1) { - - int row = resultTable.getSelectedRow(); - //BurpSuite.getTests.get(row).getTable(); - - DefaultTableModel dm = (DefaultTableModel) testTable.getModel(); - dm.getDataVector().removeAllElements(); - dm.fireTableDataChanged(); - - for (String[] act : testSuite.getTests().get(row).getRows()) { - - ((DefaultTableModel) testTable.getModel()).addRow(act); - } - } - }); - return scrollPane; - } - - private JSplitPane setup_tab_test_result(GridBagLayout bottom_layout) { - // Test Result Tab - testTableModel = new DefaultTableModel(foundData, foundTableColNames) { - @Override - public boolean isCellEditable(int row, int column) { - return false; - } - }; - - testTable = new JTable(testTableModel); - // Add all the operations result to the table - testTable.getSelectionModel().addListSelectionListener(listSelectionEvent -> { - if (!listSelectionEvent.getValueIsAdjusting()) { - if (testTable.getSelectedRow() == -1) return; - int index = Integer.parseInt((String) testTable.getModel().getValueAt(testTable.getSelectedRow(), 4)); - int op_index = Integer.parseInt((String) testTable.getModel().getValueAt(testTable.getSelectedRow(), 0)); - - Operation op = testSuite.tests.get(resultTable.getSelectedRow()).operations.get(op_index); - - MessageType msg_type; - try { - msg_type = MessageType.getFromList(messageTypes, op.getMessageType()); - } catch (ParsingException e) { - throw new RuntimeException(e); - } - - for (HTTPReqRes m : op.matchedMessages) { - if (m.index == index) { - if (msg_type.msg_to_process_is_request) { - messageViewer.setMessage(m.getRequest(), true); - } else { - messageViewer.setMessage(m.getResponse(), false); - } - break; - } - } - } - }); - - JScrollPane scrollPane3 = new JScrollPane(testTable, - JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - - splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); - - splitPane.setLeftComponent(scrollPane3); - - controller = new IMessageEditorController() { - @Override - public IHttpService getHttpService() { - return new IHttpService() { - @Override - public String getHost() { - return null; - } - - @Override - public int getPort() { - return 0; - } - - @Override - public String getProtocol() { - return null; - } - }; - } - - @Override - public byte[] getRequest() { - return viewedMessage.getRequest(); - } - - @Override - public byte[] getResponse() { - return viewedMessage.getResponse(); - } - }; - return splitPane; - } - - private JPanel setup_tab_session_config(GridBagLayout bottom_layout) { - JPanel sessionConfig = new JPanel(); - sessionConfig.setLayout(bottom_layout); - - txtSessionConfig = new JTextArea(); - JScrollPane scrollPane5 = new JScrollPane(txtSessionConfig, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - GridBagConstraints gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.fill = GridBagConstraints.BOTH; - gbc.insets = new Insets(10, 10, 10, 10); - gbc.gridx = 0; - gbc.gridy = 1; - gbc.gridwidth = 2; - gbc.gridheight = 3; - sessionConfig.add(scrollPane5, gbc); - - JButton btnSetSessionConfig = new JButton("save"); - btnSetSessionConfig.addActionListener(actionEvent -> { - try { - updateTxtSessionConfig(); - } catch (ParsingException e) { - e.printStackTrace(); - } - }); - - gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.fill = GridBagConstraints.BOTH; - gbc.insets = new Insets(10, 10, 10, 10); - gbc.gridx = 2; - gbc.gridy = 3; - gbc.gridwidth = 1; - gbc.gridheight = 1; - sessionConfig.add(btnSetSessionConfig, gbc); - - return sessionConfig; - } - - private JPanel setub_tab_debug(GridBagLayout bottom_layout) { - // Debug Tab - JPanel debugPanel = new JPanel(); - - debugPanel.setLayout(bottom_layout); - - txt_out_debug_tab.setEditable(false); - JScrollPane scrollPane6 = new JScrollPane(txt_out_debug_tab, - JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, - JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - GridBagConstraints constraints1 = new GridBagConstraints(); - constraints1.gridx = 0; - constraints1.gridy = 1; - constraints1.gridwidth = 1; // One cell wide - constraints1.gridheight = 3; // One cell high - constraints1.fill = GridBagConstraints.BOTH; - constraints1.weightx = 0.5; // Half of the horizontal space - constraints1.weighty = 0.5; // Half of the vertical space - debugPanel.add(scrollPane6, constraints1); - - txt_err_debug_tab.setEditable(false); - JScrollPane scrollPane7 = new JScrollPane(txt_err_debug_tab, - JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, - JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - GridBagConstraints constraints2 = new GridBagConstraints(); - constraints2.gridx = 1; - constraints2.gridy = 1; - constraints2.gridwidth = 1; // One cell wide - constraints2.gridheight = 3; // One cell high - constraints2.fill = GridBagConstraints.BOTH; - constraints2.weightx = 0.5; // Half of the horizontal space - constraints2.weighty = 0.5; // Half of the vertical space - debugPanel.add(scrollPane7, constraints2); - - JButton btn_clear = new JButton("clear"); - GridBagConstraints gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.insets = new Insets(10, 10, 10, 10); - gbc.gridx = 2; - gbc.gridy = 1; - debugPanel.add(btn_clear, gbc); - - btn_clear.addActionListener(e -> { - txt_out_debug_tab.setText(""); - txt_err_debug_tab.setText(""); - }); - - JLabel lbl_left = new JLabel("Output log"); - gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(10, 10, 10, 10); - gbc.gridx = 0; - gbc.gridy = 0; - gbc.gridwidth = 2; - debugPanel.add(lbl_left, gbc); - - JLabel lbl_right = new JLabel("Error log"); - gbc = new GridBagConstraints(); - gbc.anchor = GridBagConstraints.WEST; - gbc.fill = GridBagConstraints.HORIZONTAL; - gbc.insets = new Insets(10, 10, 10, 10); - gbc.gridx = 1; - gbc.gridy = 0; - gbc.gridwidth = 2; - debugPanel.add(lbl_right, gbc); - - return debugPanel; - } -} \ No newline at end of file diff --git a/tool/src/main/java/migt/Marker.java b/tool/src/main/java/migt/Marker.java deleted file mode 100644 index 752bb1f..0000000 --- a/tool/src/main/java/migt/Marker.java +++ /dev/null @@ -1,29 +0,0 @@ -package migt; - -import java.util.Objects; - -/** - * Class used to mark User Actions to be managed by session actions - */ -public class Marker { - String name; - - /** - * Constructor to instantiate a new marker object - * - * @param _name name of the marker - */ - public Marker(String _name) { - name = _name; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Marker marker = (Marker) o; - - return Objects.equals(name, marker.name); - } -} \ No newline at end of file diff --git a/tool/src/main/java/migt/MessageOperation.java b/tool/src/main/java/migt/MessageOperation.java deleted file mode 100644 index 7e0acef..0000000 --- a/tool/src/main/java/migt/MessageOperation.java +++ /dev/null @@ -1,468 +0,0 @@ -package migt; - -import org.json.JSONObject; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static migt.MessageOperation.MessageOperationActions.SAVE; -import static migt.Tools.getVariableByName; - -/** - * The class storing a MessageOperation object - */ -public class MessageOperation extends Module { - HTTPReqRes.MessageSection from; - String what; - String to; - MessageOperationActions action; - String save_as; // The name of the variable to save the parameter's value - String use; - MessageOpType type; - boolean url_decode = true; // enable or disable url decoding of url parameters - - // GENERATE POC - String template; - String output_path; - - /** - * Used to Instantiate the class - */ - public MessageOperation() { - init(); - } - - public MessageOperation(JSONObject message_op_json) throws ParsingException { - init(); - - java.util.Iterator keys = message_op_json.keys(); - while (keys.hasNext()) { - String key = keys.next(); - - switch (key) { - case "from": - from = HTTPReqRes.MessageSection.fromString(message_op_json.getString("from")); - break; - case "remove parameter": - what = message_op_json.getString("remove parameter"); - action = MessageOperationActions.REMOVE_PARAMETER; - break; - case "remove match word": - what = message_op_json.getString("remove match word"); - action = MessageOperationActions.REMOVE_MATCH_WORD; - break; - case "edit": - what = message_op_json.getString("edit"); - action = MessageOperationActions.EDIT; - break; - case "edit regex": - what = message_op_json.getString("edit regex"); - action = MessageOperationActions.EDIT_REGEX; - break; - case "in": - to = message_op_json.getString("in"); - break; - case "add": - what = message_op_json.getString("add"); - action = MessageOperationActions.ADD; - break; - case "this": - to = message_op_json.getString("this"); - break; - case "save": - what = message_op_json.getString("save"); - action = SAVE; - break; - case "save match": - what = message_op_json.getString("save match"); - action = MessageOperationActions.SAVE_MATCH; - break; - case "as": - save_as = message_op_json.getString("as"); - break; - case "use": - use = message_op_json.getString("use"); - break; - case "type": - type = MessageOpType.fromString( - message_op_json.getString("type")); - break; - case "template": - template = message_op_json.getString("template"); - break; - case "output_path": - output_path = message_op_json.getString("output_path"); - break; - case "url decode": - url_decode = message_op_json.getBoolean("url decode"); - break; - default: - throw new ParsingException("Message operation key \" " + key + "\" not valid"); - } - } - } - - /** - * Returns the adding of a message operation, decides if the value to be inserted/edited should be a variable or - * a typed value and return it - * - * @param m the message operation which has to be examined - * @return the adding to be used in add/edit - * @throws ParsingException if the variable name is not valid or the variable has not been initiated - */ - public static String getAdding(MessageOperation m, List vars) throws ParsingException { - if (!m.use.isEmpty()) { - Var v = getVariableByName(m.use, vars); - return v.get_value_string(); - } else { - - return m.to; - } - } - - public void init() { - this.what = ""; - this.to = ""; - this.save_as = ""; - this.use = ""; - this.type = MessageOpType.HTTP; - this.template = ""; - this.output_path = ""; - } - - public void loader(Operation_API api) { - this.imported_api = api; - } - - public Operation_API exporter() { - if (imported_api instanceof Operation_API) { - return (Operation_API) this.imported_api; - } - return null; - } - - /** - * Given an operation, and a message, execute the Message operations contained in the operation - * - * @return the updated Operation with the result - * @throws ParsingException if parsing of names is not successfull - */ - public void execute() { - Pattern pattern; - Matcher matcher; - try { - if (type == MessageOperation.MessageOpType.GENERATE_POC) { - if (!((Operation_API) imported_api).is_request) { - throw new ParsingException("Invalid POC generation, message should be a request"); - } - - if (!template.equals("csrf")) { - System.out.println("CSRF template not supported"); - return; // other templates not supported yet - } - - String poc = Tools.generate_CSRF_POC(((Operation_API) imported_api).message); - - try { - File myObj = new File(output_path); - myObj.createNewFile(); - } catch (IOException e) { - throw new ParsingException("Invalid POC generation output path: " - + output_path + " " + e.getMessage()); - } - try { - FileWriter myWriter = new FileWriter(output_path); - myWriter.write(poc); - myWriter.close(); - } catch (IOException e) { - throw new ParsingException("Something went wrong while writing output file for POC generator: " - + output_path + " " + e.getMessage()); - } - } else { - if (action == null) { - throw new ParsingException("Invalid action in message operation"); - } - - switch (action) { - case REMOVE_PARAMETER: - switch (from) { - case URL: - if (!((Operation_API) imported_api).is_request) { - throw new ParsingException("Searching URL in response"); - } - String url_header = ((Operation_API) imported_api).message.getUrlHeader(); - pattern = Pattern.compile("&?" + Pattern.quote(what) + "=[^& ]*((?=&)|(?= ))"); - matcher = pattern.matcher(url_header); - String new_url = matcher.replaceFirst(""); - ((Operation_API) imported_api).message.setUrlHeader(new_url); - break; - - case HEAD: - ((Operation_API) imported_api).message.removeHeadParameter(((Operation_API) imported_api).is_request, what); - break; - - case BODY: - String body = new String(((Operation_API) imported_api).message.getBody(((Operation_API) imported_api).is_request)); - pattern = Pattern.compile(Pattern.quote(what)); - matcher = pattern.matcher(body); - ((Operation_API) imported_api).message.setBody(((Operation_API) imported_api).is_request, matcher.replaceAll("")); - break; - } - break; - - case ADD: - if (getAdding(this, ((Operation_API) imported_api).vars) == null | - getAdding(this, ((Operation_API) imported_api).vars).isEmpty()) { - // TODO: should raise exception or set operation not applicable? - break; - } - switch (from) { - case HEAD: { - ((Operation_API) imported_api).message. - addHeadParameter( - ((Operation_API) imported_api).is_request, - what, - getAdding(this, ((Operation_API) imported_api).vars)); - break; - } - case BODY: { - String tmp = new String( - ((Operation_API) imported_api).message - .getBody(((Operation_API) imported_api).is_request)); - tmp = tmp + getAdding(this, ((Operation_API) imported_api).vars); - ((Operation_API) imported_api).message.setBody(((Operation_API) imported_api).is_request, tmp); - break; - } - case URL: - if (!((Operation_API) imported_api).is_request) { - throw new ParsingException("Searching URL in response"); - } - String header_0 = ((Operation_API) imported_api).message.getUrlHeader(); - - pattern = Pattern.compile("&?" + Pattern.quote(what) + "=[^& ]*((?=&)|(?= ))"); - matcher = pattern.matcher(header_0); - - String newHeader_0 = ""; - boolean found = false; - while (matcher.find() & !found) { - String before = header_0.substring(0, matcher.end()); - String after = header_0.substring(matcher.end()); - newHeader_0 = before + getAdding(this, ((Operation_API) imported_api).vars) + after; - found = true; - } - ((Operation_API) imported_api).message.setUrlHeader(newHeader_0); - break; - } - break; - - case EDIT: - byte[] msg = Tools.editMessageParam( - what, - from, - ((Operation_API) imported_api).message, - ((Operation_API) imported_api).is_request, - getAdding(this, ((Operation_API) imported_api).vars), - true); - - if (((Operation_API) imported_api).message.isRequest) { - ((Operation_API) imported_api).message.setRequest(msg); - } else { - ((Operation_API) imported_api).message.setResponse(msg); - } - break; - - case EDIT_REGEX: - msg = Tools.editMessage( - what, - this, - ((Operation_API) imported_api).message, - ((Operation_API) imported_api).is_request, - getAdding(this, ((Operation_API) imported_api).vars)); - - if (((Operation_API) imported_api).message.isRequest) { - ((Operation_API) imported_api).message.setRequest(msg); - } else { - ((Operation_API) imported_api).message.setResponse(msg); - } - break; - - case REMOVE_MATCH_WORD: - switch (from) { - case HEAD: { - List headers = ((Operation_API) imported_api).message.getHeaders(((Operation_API) imported_api).is_request); - pattern = Pattern.compile(Pattern.quote(what)); - List new_headers = new ArrayList<>(); - - for (String header : headers) { - matcher = pattern.matcher(header); - new_headers.add(matcher.replaceAll("")); - } - - ((Operation_API) imported_api).message.setHeaders(((Operation_API) imported_api).is_request, new_headers); - break; - } - case BODY: { - pattern = Pattern.compile(Pattern.quote(what)); - matcher = pattern.matcher(new String(((Operation_API) imported_api).message.getBody(((Operation_API) imported_api).is_request))); - ((Operation_API) imported_api).message.setBody(((Operation_API) imported_api).is_request, matcher.replaceAll("")); - break; - } - case URL: - if (!((Operation_API) imported_api).is_request) { - throw new ParsingException("Searching URL in response"); - } - String header_0 = ((Operation_API) imported_api).message.getUrlHeader(); - - pattern = Pattern.compile(what); - matcher = pattern.matcher(header_0); - String newHeader_0 = matcher.replaceFirst(""); - - ((Operation_API) imported_api).message.setUrlHeader(newHeader_0); - break; - } - break; - - case SAVE: - case SAVE_MATCH: - switch (from) { - case HEAD: { - String value = action == SAVE ? - ((Operation_API) imported_api).message.getHeadParam(((Operation_API) imported_api).is_request, what) : - ((Operation_API) imported_api).message.getHeadRegex(((Operation_API) imported_api).is_request, what); - - if (value.isEmpty()) { - System.out.println("Warning: saved head parameter \"" + - what + - "\" that has an empty value"); - } - - Var v = new Var(save_as, value); - ((Operation_API) imported_api).vars.add(v); - } - break; - case BODY: { - String value = ((Operation_API) imported_api).message.getBodyRegex(((Operation_API) imported_api).is_request, what); - - if (value.isEmpty()) { - System.out.println("Warning: saved body regex \"" + - what + - "\" that matched an empty value"); - } - - Var v = new Var(save_as, value); - ((Operation_API) imported_api).vars.add(v); - } - break; - case URL: { - if (!((Operation_API) imported_api).is_request) { - throw new ParsingException("Trying to acces the url of a response message"); - } - - String value = action == SAVE ? - ((Operation_API) imported_api).message.getUrlParam(what, !url_decode) : - ((Operation_API) imported_api).message.getUrlRegex(what); - - if (value.isEmpty()) { - System.out.println("Warning: saved URL parameter \"" + - what + - "\" that has an empty value"); - } - - Var v = new Var(save_as, value); - ((Operation_API) imported_api).vars.add(v); - } - break; - } - break; - } - } - applicable = true; - } catch (StackOverflowError | ParsingException e) { - e.printStackTrace(); - // applicable is already false - } - } - - /** - * All the possible actions of a MessageOperation - */ - public enum MessageOperationActions { - REMOVE_PARAMETER, - REMOVE_MATCH_WORD, - EDIT, - EDIT_REGEX, - ADD, - SAVE, - SAVE_MATCH, - ENCODE; - - /** - * From a string get the corresponding enum value - * - * @param input the string - * @return the enum value - * @throws ParsingException if the input is malformed - */ - public static MessageOperationActions fromString(String input) throws ParsingException { - if (input != null) { - switch (input) { - case "remove parameter": - return REMOVE_PARAMETER; - case "remove match word": - return REMOVE_MATCH_WORD; - case "edit": - return EDIT; - case "edit regex": - return EDIT_REGEX; - case "add": - return ADD; - case "save": - return SAVE; - case "save match": - return SAVE_MATCH; - case "encode": - return ENCODE; - default: - throw new ParsingException("invalid Message operation action \"" + input + "\""); - } - } else { - throw new NullPointerException(); - } - } - } - - /** - * The possible types of messageOps - */ - public enum MessageOpType { - HTTP, - GENERATE_POC; - - /** - * From a string get the corresponding enum value - * - * @param input the string - * @return the enum value - * @throws ParsingException if the input is malformed - */ - public static MessageOpType fromString(String input) throws ParsingException { - if (input != null) { - switch (input) { - case "http": - return HTTP; - case "generate_poc": - return GENERATE_POC; - default: - throw new ParsingException("invalid message Op Type"); - } - } else { - throw new NullPointerException(); - } - } - } -} diff --git a/tool/src/main/java/migt/Operation_API.java b/tool/src/main/java/migt/Operation_API.java deleted file mode 100644 index bb8a955..0000000 --- a/tool/src/main/java/migt/Operation_API.java +++ /dev/null @@ -1,23 +0,0 @@ -package migt; - -import java.util.ArrayList; -import java.util.List; - -/** - * This class provides an API for an Operation module, to be used by other modules. - */ -public class Operation_API extends API { - public HTTPReqRes message; - public List vars; - boolean is_request; - - public Operation_API(HTTPReqRes message, boolean is_request) { - this.message = message; - this.is_request = is_request; - this.vars = new ArrayList<>(); - } - - public Operation_API(List vars) { - this.vars = vars; - } -} diff --git a/tool/src/main/java/migt/ParsingException.java b/tool/src/main/java/migt/ParsingException.java deleted file mode 100644 index 4083c35..0000000 --- a/tool/src/main/java/migt/ParsingException.java +++ /dev/null @@ -1,15 +0,0 @@ -package migt; - -/** - * Exception raised when the parsing of the language fails - */ -public class ParsingException extends Exception { - /** - * Raised when there is a problem in the parsing of the json - * - * @param errorMessage the error message to be displayed - */ - public ParsingException(String errorMessage) { - super(errorMessage); - } -} diff --git a/tool/src/main/java/org/zaproxy/addon/migt/API.java b/tool/src/main/java/org/zaproxy/addon/migt/API.java new file mode 100644 index 0000000..f3a8189 --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/API.java @@ -0,0 +1,23 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; + +/** The API class stores the API of a Module, this class is inherited by all the other APIs */ +public class API {} diff --git a/tool/src/main/java/migt/At_Hash_check.java b/tool/src/main/java/org/zaproxy/addon/migt/At_Hash_check.java similarity index 76% rename from tool/src/main/java/migt/At_Hash_check.java rename to tool/src/main/java/org/zaproxy/addon/migt/At_Hash_check.java index 5cdbabf..d81df72 100644 --- a/tool/src/main/java/migt/At_Hash_check.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/At_Hash_check.java @@ -1,21 +1,38 @@ -package migt; - -import org.apache.commons.codec.binary.Base64; -import org.json.JSONException; -import org.json.JSONObject; +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; +import org.apache.commons.codec.binary.Base64; +import org.json.JSONException; +import org.json.JSONObject; /** - * Module used to check the correctness of the at_hash parameter inside of the id_token wrt to the released access_token - * https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken + * Module used to check the correctness of the at_hash parameter inside of the id_token wrt to the + * released access_token https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken */ public class At_Hash_check extends Module { - public At_Hash_check() { - } + public At_Hash_check() {} @Override public void loader(API api) { @@ -98,7 +115,9 @@ public void execute() { // remove "=" characters at_hash_generated = at_hash_generated.replaceAll("=", ""); - applicable = true; // this means that all the steps that precedes the check were accomplished correctly + applicable = + true; // this means that all the steps that precedes the check were accomplished + // correctly // check previous value is equal to at_hash result = at_hash_generated.equals(at_hash); diff --git a/tool/src/main/java/migt/At_Hash_update.java b/tool/src/main/java/org/zaproxy/addon/migt/At_Hash_update.java similarity index 73% rename from tool/src/main/java/migt/At_Hash_update.java rename to tool/src/main/java/org/zaproxy/addon/migt/At_Hash_update.java index c56b5a2..26df4af 100644 --- a/tool/src/main/java/migt/At_Hash_update.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/At_Hash_update.java @@ -1,16 +1,34 @@ -package migt; - -import org.apache.commons.codec.binary.Base64; -import org.json.JSONException; -import org.json.JSONObject; +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; +import org.apache.commons.codec.binary.Base64; +import org.json.JSONException; +import org.json.JSONObject; /** - * Module used to check the correctness of the at_hash parameter inside of the id_token wrt to the released access_token - * https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken + * Module used to check the correctness of the at_hash parameter inside of the id_token wrt to the + * released access_token https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken */ public class At_Hash_update extends Module { String sign_key = ""; @@ -99,14 +117,15 @@ public void execute() { // remove "=" characters at_hash_generated = at_hash_generated.replaceAll("=", ""); - j.payload = Tools.editJson( - EditOperation.Jwt_action.EDIT, - j.payload, - "$.at_hash", - null, - "", - at_hash_generated, - ""); + j.payload = + Tools.editJson( + EditOperation.Jwt_action.EDIT, + j.payload, + "$.at_hash", + null, + "", + at_hash_generated, + ""); String new_id_token = ""; try { @@ -122,7 +141,9 @@ public void execute() { ((Operation_API) imported_api).message.setBody(false, o.toString()); - applicable = true; // this means that all the steps that precedes the check were accomplished correctly + applicable = + true; // this means that all the steps that precedes the check were accomplished + // correctly result = true; } diff --git a/tool/src/main/java/migt/Check.java b/tool/src/main/java/org/zaproxy/addon/migt/Check.java similarity index 73% rename from tool/src/main/java/migt/Check.java rename to tool/src/main/java/org/zaproxy/addon/migt/Check.java index e04a49c..f0c1c0c 100644 --- a/tool/src/main/java/migt/Check.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/Check.java @@ -1,25 +1,52 @@ -package migt; +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.jayway.jsonpath.JsonPath; -import com.networknt.schema.*; -import org.apache.commons.text.StringEscapeUtils; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - +import com.networknt.schema.JsonSchema; +import com.networknt.schema.JsonSchemaException; +import com.networknt.schema.JsonSchemaFactory; +import com.networknt.schema.SpecVersion; +import com.networknt.schema.ValidationMessage; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; - -import static migt.Check.CheckOps.*; +import org.apache.commons.text.StringEscapeUtils; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; /** - * Check Object class. This object is used in Operations to check that a parameter or some text is in as specified. + * Check Object class. This object is used in Operations to check that a parameter or some text is + * in as specified. */ public class Check extends Module { String what; // what to search @@ -104,10 +131,14 @@ public Check(JSONObject json_check) throws ParsingException { } break; case "is present": - this.op = json_check.getBoolean("is present") ? CheckOps.IS_PRESENT : - IS_NOT_PRESENT; - this.op_val = json_check.getBoolean("is present") ? - "is present" : "is not present"; + this.op = + json_check.getBoolean("is present") + ? CheckOps.IS_PRESENT + : CheckOps.IS_NOT_PRESENT; + this.op_val = + json_check.getBoolean("is present") + ? "is present" + : "is not present"; break; case "is in": this.op = CheckOps.IS_IN; @@ -141,7 +172,7 @@ public Check(JSONObject json_check) throws ParsingException { } break; case "is subset of": - this.op = IS_SUBSET_OF; + this.op = CheckOps.IS_SUBSET_OF; if (json_check.has("use variable")) { // inside "is subset of" a string with the name of the var is expected this.op_val = json_check.getString("is subset of"); @@ -156,30 +187,31 @@ public Check(JSONObject json_check) throws ParsingException { } break; case "json schema compliant": - this.op = JSON_SCHEMA_COMPLIANT; + this.op = CheckOps.JSON_SCHEMA_COMPLIANT; this.op_val = json_check.getString("json schema compliant"); break; case "matches regex": - this.op = MATCHES_REGEX; + this.op = CheckOps.MATCHES_REGEX; this.op_val = json_check.getString("matches regex"); break; case "not matches regex": - this.op = NOT_MATCHES_REGEX; + this.op = CheckOps.NOT_MATCHES_REGEX; this.op_val = json_check.getString("not matches regex"); break; case "url decode": url_decode = json_check.getBoolean("url decode"); break; default: - throw new ParsingException("Invalid key:\"" + key + "\" used in Check Operation"); + throw new ParsingException( + "Invalid key:\"" + key + "\" used in Check Operation"); } } catch (JSONException e) { throw new ParsingException("error in parsing check: " + e); } catch (ClassCastException e) { - throw new ParsingException("Only allowed values in arrays are Strings, if you are using integers or " + - "floats, please convert them as strings"); + throw new ParsingException( + "Only allowed values in arrays are Strings, if you are using integers or " + + "floats, please convert them as strings"); } - } if (regex.equals("") && what.equals("")) @@ -241,14 +273,13 @@ private boolean execute_regex(String input) throws ParsingException { /** * Execute the check over a message (in an Operation) * - * @param message the message to check + * @param message the message to check * @param isRequest tells if the message is a request or a response * @return the result of the check * @throws ParsingException if something wrong is found wrt the language */ - private boolean execute_http(HTTPReqRes message, - boolean isRequest, - List vars) throws ParsingException { + private boolean execute_http(HTTPReqRes message, boolean isRequest, List vars) + throws ParsingException { if (use_variable) { // if use_variable is set, substitute the value to check with the variable value @@ -281,7 +312,7 @@ private boolean execute_http(HTTPReqRes message, if (msg_str.isEmpty()) { applicable = true; - return this.op != null && op == IS_NOT_PRESENT; + return this.op != null && op == CheckOps.IS_NOT_PRESENT; } msg_str = url_decode(msg_str); @@ -294,13 +325,17 @@ private boolean execute_http(HTTPReqRes message, if (this.isParamCheck) { if (in == CheckIn.BODY) { applicable = false; - throw new ParsingException("Invalid check operation, cannot do \"check param\" over body, " + - "use \"check_regex instead\""); + throw new ParsingException( + "Invalid check operation, cannot do \"check param\" over body, " + + "use \"check_regex instead\""); } - Pattern p = this.in == CheckIn.URL ? - Pattern.compile("(?<=[?&]" + Pattern.quote(this.what) + "=)[^\\r\\n&]*") : - Pattern.compile("(?<=" + Pattern.quote(this.what) + ":\\s?)[^\\r\\n]*"); + Pattern p = + this.in == CheckIn.URL + ? Pattern.compile( + "(?<=[?&]" + Pattern.quote(this.what) + "=)[^\\r\\n&]*") + : Pattern.compile( + "(?<=" + Pattern.quote(this.what) + ":\\s?)[^\\r\\n]*"); // TODO: this could be done better by using message methods Matcher m = p.matcher(msg_str); @@ -317,13 +352,13 @@ private boolean execute_http(HTTPReqRes message, applicable = true; if (!msg_str.contains(this.what)) { if (this.op != null) { - return this.op == IS_NOT_PRESENT; + return this.op == CheckOps.IS_NOT_PRESENT; } else { return false; } } else { if (this.op != null) { - return this.op != IS_NOT_PRESENT; + return this.op != CheckOps.IS_NOT_PRESENT; } } } @@ -333,11 +368,12 @@ private boolean execute_http(HTTPReqRes message, private String url_decode(String string) { if (url_decode) { if (string.contains("+")) { - System.err.println("Warning! During a check on the value\"" + - ((string.length() > 10) ? string.substring(0, 9) + "..." : string) + - "\" a '+' symbol has been" + - "converted to a space, as it has been interpreted as url-encoded character. If you want to avoid" + - "this behaviour use 'url decode' tag set to false inside the check to disable url-decoding"); + System.err.println( + "Warning! During a check on the value\"" + + ((string.length() > 10) ? string.substring(0, 9) + "..." : string) + + "\" a '+' symbol has been" + + "converted to a space, as it has been interpreted as url-encoded character. If you want to avoid" + + "this behaviour use 'url decode' tag set to false inside the check to disable url-decoding"); } try { string = URLDecoder.decode(string, StandardCharsets.UTF_8); @@ -358,7 +394,8 @@ private boolean execute_json(List vars) throws ParsingException { DecodeOperation_API tmp = ((DecodeOperation_API) this.imported_api); if (isParamCheck) { - throw new ParsingException("Cannot execute a 'check param' in a json, please use 'check'"); + throw new ParsingException( + "Cannot execute a 'check param' in a json, please use 'check'"); } Var v = null; @@ -383,18 +420,21 @@ private boolean execute_json(List vars) throws ParsingException { String j = ""; switch (in) { - case JWT_HEADER: { - j = tmp.jwt.header; - break; - } - case JWT_PAYLOAD: { - j = tmp.jwt.payload; - break; - } - case JWT_SIGNATURE: { - j = tmp.jwt.signature; - break; - } + case JWT_HEADER: + { + j = tmp.jwt.header; + break; + } + case JWT_PAYLOAD: + { + j = tmp.jwt.payload; + break; + } + case JWT_SIGNATURE: + { + j = tmp.jwt.signature; + break; + } } // if a regex is present, execute it @@ -409,16 +449,19 @@ private boolean execute_json(List vars) throws ParsingException { try { Object found_obj = JsonPath.read(j, what); - if (op == IS_PRESENT | op == IS_NOT_PRESENT) { + if (op == CheckOps.IS_PRESENT | op == CheckOps.IS_NOT_PRESENT) { // whatever is the type of the value, if it is found return the result applicable = true; - return op == IS_PRESENT; + return op == CheckOps.IS_PRESENT; } if (found_obj instanceof net.minidev.json.JSONArray) { // the value is a list, allowed ops are: contains/not-contains - if (!(op == CONTAINS | op == NOT_CONTAINS | op == IS_SUBSET_OF)) { - throw new ParsingException("Check error, used " + op.toString() + " over a matched list"); + if (!(op == CheckOps.CONTAINS + | op == CheckOps.NOT_CONTAINS + | op == CheckOps.IS_SUBSET_OF)) { + throw new ParsingException( + "Check error, used " + op.toString() + " over a matched list"); } Iterator i = ((net.minidev.json.JSONArray) found_obj).iterator(); @@ -428,19 +471,19 @@ private boolean execute_json(List vars) throws ParsingException { try { String elem = String.valueOf(i.next()); new_array.add(elem); - } catch (java.lang.ClassCastException e) { - throw new ParsingException("Cannot convert element in jwt matched array to string"); + } catch (ClassCastException e) { + throw new ParsingException( + "Cannot convert element in jwt matched array to string"); } } found_array = new_array; value_is_array = true; - } else if (found_obj instanceof java.lang.String) { + } else if (found_obj instanceof String) { // the value is a string, can do all ops found = (String) found_obj; - } else if (found_obj instanceof java.lang.Double | - found_obj instanceof java.lang.Integer) { + } else if (found_obj instanceof Double | found_obj instanceof Integer) { // the value is an double or integer, convert to string found = String.valueOf(found_obj); } else if (found_obj instanceof LinkedHashMap) { @@ -451,8 +494,8 @@ private boolean execute_json(List vars) throws ParsingException { } catch (com.jayway.jsonpath.PathNotFoundException e) { applicable = true; - return op == IS_NOT_PRESENT; - } catch (java.lang.ClassCastException e) { + return op == CheckOps.IS_NOT_PRESENT; + } catch (ClassCastException e) { throw new ParsingException("Error in check, json matched value cast exception: " + e); } @@ -464,8 +507,7 @@ private boolean execute_json(List vars) throws ParsingException { case IS_NOT: return !op_val.equals(found); case CONTAINS: - if (!value_is_array) - return found.contains(op_val); + if (!value_is_array) return found.contains(op_val); else { // the matched value is an array if (!value_list.isEmpty()) { @@ -482,10 +524,9 @@ private boolean execute_json(List vars) throws ParsingException { } } case NOT_CONTAINS: - if (!value_is_array) - return !found.contains(op_val); + if (!value_is_array) return !found.contains(op_val); else { - //the matched value is an array + // the matched value is an array if (!value_list.isEmpty()) { // check against a value array for (String elem : value_list) { @@ -509,36 +550,44 @@ private boolean execute_json(List vars) throws ParsingException { return !value_list.contains(found); case IS_SUBSET_OF: if (!value_is_array) - throw new ParsingException("Matched single element in jwt, but should be an array when using IS SUBSET OF"); + throw new ParsingException( + "Matched single element in jwt, but should be an array when using IS SUBSET OF"); return value_list.containsAll(found_array); - case MATCHES_REGEX: { - if (value_is_array) throw new ParsingException("Check error: cannot execute a regex over a list"); - Pattern p = Pattern.compile(op_val); - Matcher m = p.matcher(found); - return m.find(); - } - case NOT_MATCHES_REGEX: { - if (value_is_array) throw new ParsingException("Check error: cannot execute a regex over a list"); - Pattern p = Pattern.compile(op_val); - Matcher m = p.matcher(found); - return !m.find(); - } - case JSON_SCHEMA_COMPLIANT: { - JsonSchema schema = null; - JsonNode node = null; - try { - // parse the schema - schema = getJsonSchemaFromStringContent(op_val); - ObjectMapper mapper = new ObjectMapper(); - node = mapper.readTree(found); - } catch (JsonProcessingException e) { - throw new ParsingException(e.getMessage()); + case MATCHES_REGEX: + { + if (value_is_array) + throw new ParsingException( + "Check error: cannot execute a regex over a list"); + Pattern p = Pattern.compile(op_val); + Matcher m = p.matcher(found); + return m.find(); } + case NOT_MATCHES_REGEX: + { + if (value_is_array) + throw new ParsingException( + "Check error: cannot execute a regex over a list"); + Pattern p = Pattern.compile(op_val); + Matcher m = p.matcher(found); + return !m.find(); + } + case JSON_SCHEMA_COMPLIANT: + { + JsonSchema schema = null; + JsonNode node = null; + try { + // parse the schema + schema = getJsonSchemaFromStringContent(op_val); + ObjectMapper mapper = new ObjectMapper(); + node = mapper.readTree(found); + } catch (JsonProcessingException e) { + throw new ParsingException(e.getMessage()); + } - Set errors = schema.validate(node); - return errors.isEmpty(); - } + Set errors = schema.validate(node); + return errors.isEmpty(); + } } return false; @@ -553,7 +602,8 @@ private boolean execute_json(List vars) throws ParsingException { public boolean do_check(String val_to_check) throws ParsingException { try { if (this.op == null && val_to_check.length() != 0) { - // if it passed all the splits without errors, the param is present, but no checks are specified + // if it passed all the splits without errors, the param is present, but no checks + // are specified // so result is true return true; } @@ -579,30 +629,33 @@ public boolean do_check(String val_to_check) throws ParsingException { } break; case IS_PRESENT: - return !val_to_check.isEmpty(); // if it gets to this, the searched param is already found + return !val_to_check + .isEmpty(); // if it gets to this, the searched param is already found case IS_NOT_PRESENT: return val_to_check.isEmpty(); case IS_IN: return value_list.contains(val_to_check); case IS_NOT_IN: return !value_list.contains(val_to_check); - case MATCHES_REGEX: { - Pattern p = Pattern.compile(op_val); - Matcher m = p.matcher(val_to_check); - return m.find(); - } - case NOT_MATCHES_REGEX: { - Pattern p = Pattern.compile(op_val); - Matcher m = p.matcher(val_to_check); - return !m.find(); - } + case MATCHES_REGEX: + { + Pattern p = Pattern.compile(op_val); + Matcher m = p.matcher(val_to_check); + return m.find(); + } + case NOT_MATCHES_REGEX: + { + Pattern p = Pattern.compile(op_val); + Matcher m = p.matcher(val_to_check); + return !m.find(); + } default: throw new ParsingException("Unsupported operand for Check in a message: " + op); } } catch (ArrayIndexOutOfBoundsException e) { - //e.printStackTrace(); + // e.printStackTrace(); if (this.op != null) { - if (this.op != IS_NOT_PRESENT) { + if (this.op != CheckOps.IS_NOT_PRESENT) { return false; } } else { @@ -615,14 +668,12 @@ public boolean do_check(String val_to_check) throws ParsingException { /** * Executes the given check (without API). Used to match messages with msg_types usually. * - * @param message the message to check + * @param message the message to check * @param isRequest if the message is a request or a response * @return the result of the check (passed or not passed) */ - public boolean execute(HTTPReqRes message, - boolean isRequest, - List vars) throws ParsingException { - + public boolean execute(HTTPReqRes message, boolean isRequest, List vars) + throws ParsingException { result = execute_http(message, isRequest, vars); return result; @@ -636,11 +687,11 @@ public boolean execute(HTTPReqRes message, public void execute(List vars) throws ParsingException { if (imported_api instanceof Operation_API) { // If is inside a standard Operation - result = execute_http( - ((Operation_API) imported_api).message, - ((Operation_API) imported_api).is_request, - vars - ); + result = + execute_http( + ((Operation_API) imported_api).message, + ((Operation_API) imported_api).is_request, + vars); } else if (imported_api instanceof DecodeOperation_API) { // if inside a decode operation switch (((DecodeOperation_API) imported_api).type) { @@ -648,10 +699,10 @@ public void execute(List vars) throws ParsingException { result = execute_json(vars); break; case NONE: - //TODO + // TODO break; case XML: - //TODO + // TODO break; } } @@ -671,18 +722,20 @@ public String toString() { } public String toStringExtended() { - String template = "Check:\n" + - "\tWhat: %s\n" + - "\tIs it a param check? %b\n" + - "\tregex: %s\n" + - "\tWhere: %s\n" + - "\tOp: %s\n" + - "\tOp val: %s\n" + - "\tValue list: %s\n" + - "\tUse variable: %b\n" + - "\tUrl decode: %b\n"; - - return String.format(template, + String template = + "Check:\n" + + "\tWhat: %s\n" + + "\tIs it a param check? %b\n" + + "\tregex: %s\n" + + "\tWhere: %s\n" + + "\tOp: %s\n" + + "\tOp val: %s\n" + + "\tValue list: %s\n" + + "\tUse variable: %b\n" + + "\tUrl decode: %b\n"; + + return String.format( + template, StringEscapeUtils.escapeJava(what), isParamCheck, StringEscapeUtils.escapeJava(regex), @@ -694,7 +747,8 @@ public String toStringExtended() { url_decode); } - protected JsonSchema getJsonSchemaFromStringContent(String schemaContent) throws ParsingException { + protected JsonSchema getJsonSchemaFromStringContent(String schemaContent) + throws ParsingException { JsonSchema res = null; try { JsonSchemaFactory factory = JsonSchemaFactory.getInstance(SpecVersion.VersionFlag.V4); @@ -705,9 +759,7 @@ protected JsonSchema getJsonSchemaFromStringContent(String schemaContent) throws return res; } - /** - * enum containing all the possible check operations - */ + /** enum containing all the possible check operations */ public enum CheckOps { IS, IS_NOT, @@ -727,7 +779,8 @@ public enum CheckOps { * * @param input the input string * @return the CheckOps enum value - * @throws ParsingException if the input string does not correspond to any of the possible check operations + * @throws ParsingException if the input string does not correspond to any of the possible + * check operations */ public static CheckOps fromString(String input) throws ParsingException { if (input != null) { @@ -759,9 +812,7 @@ public static CheckOps fromString(String input) throws ParsingException { } } - /** - * Used in the Check operation, to specify where is the content to check. - */ + /** Used in the Check operation, to specify where is the content to check. */ public enum CheckIn { // standard message HEAD, diff --git a/tool/src/main/java/migt/DecodeOperation.java b/tool/src/main/java/org/zaproxy/addon/migt/DecodeOperation.java similarity index 73% rename from tool/src/main/java/migt/DecodeOperation.java rename to tool/src/main/java/org/zaproxy/addon/migt/DecodeOperation.java index 10f9b9b..aa432ef 100644 --- a/tool/src/main/java/migt/DecodeOperation.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/DecodeOperation.java @@ -1,36 +1,61 @@ -package migt; +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; -import com.jayway.jsonpath.JsonPath; -import org.apache.commons.text.StringEscapeUtils; -import org.json.JSONArray; -import org.json.JSONObject; +import static org.zaproxy.addon.migt.Tools.executeDecodeOps; +import static org.zaproxy.addon.migt.Tools.executeEditOps; +import com.jayway.jsonpath.JsonPath; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.util.*; +import java.util.ArrayList; +import java.util.Base64; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.DataFormatException; import java.util.zip.Deflater; import java.util.zip.Inflater; +import org.apache.commons.text.StringEscapeUtils; +import org.json.JSONArray; +import org.json.JSONObject; -import static migt.Tools.executeDecodeOps; -import static migt.Tools.executeEditOps; - -/** - * This class stores a decode operation - */ +/** This class stores a decode operation */ public class DecodeOperation extends Module { public String decoded_content; // the decoded content public String decode_target; // aka decode_param how to decode the raw content public boolean is_regex; - public DecodeOperationFrom from; // where the raw content is. Depending on the containing module, can be other things + public DecodeOperationFrom + from; // where the raw content is. Depending on the containing module, can be other + // things public List encodings; // the list of encoding to decode and rencode public DecodeOpType type; // the type of the decoded param (used only to edit its content) public List checks; // the list of checks to be executed - public List decodeOperations; // a list of decode operations to execute them recursevly + public List + decodeOperations; // a list of decode operations to execute them recursevly public List editOperations; // a list of edit operations public boolean check_jwt = false; JWT jwt; @@ -48,7 +73,7 @@ public DecodeOperation() { */ public DecodeOperation(JSONObject decode_op_json) throws ParsingException { init(); - java.util.Iterator keys = decode_op_json.keys(); + Iterator keys = decode_op_json.keys(); while (keys.hasNext()) { String key = keys.next(); @@ -69,8 +94,7 @@ public DecodeOperation(JSONObject decode_op_json) throws ParsingException { while (it.hasNext()) { String act_enc = (String) it.next(); - this.encodings.add( - Encoding.fromString(act_enc)); + this.encodings.add(Encoding.fromString(act_enc)); } break; case "from": @@ -105,19 +129,21 @@ public DecodeOperation(JSONObject decode_op_json) throws ParsingException { jwt.decrypt = true; jwt.private_key_pem_enc = decode_op_json.getString("jwe encrypt"); jwt.public_key_pem_enc = decode_op_json.getString("jwe decrypt"); + break; default: - throw new ParsingException("Invalid key:\"" + key + "\" used in decode operation"); + throw new ParsingException( + "Invalid key:\"" + key + "\" used in decode operation"); } } } /** - * Decode the given string, with the given ordered encodings - * Example taken from - * Saml Raider + * Decode the given string, with the given ordered encodings Example taken from Saml + * Raider * * @param encodings the ordered list of encodings to be applied - * @param encoded the string to be decoded + * @param encoded the string to be decoded * @return the decoded string * @throws ParsingException if the decoding fails */ @@ -144,7 +170,9 @@ public static String decode(List encodings, String encoded) throws Par if (isActualString) { actual = java.net.URLDecoder.decode(actual, StandardCharsets.UTF_8); } else { - actual = java.net.URLDecoder.decode(new String(actual_b), StandardCharsets.UTF_8); + actual = + java.net.URLDecoder.decode( + new String(actual_b), StandardCharsets.UTF_8); isActualString = true; } break; @@ -159,7 +187,7 @@ public static String decode(List encodings, String encoded) throws Par isActualString = false; } catch (IOException | DataFormatException ioException) { ioException.printStackTrace(); - //ioException.printStackTrace(); + // ioException.printStackTrace(); } try { @@ -169,7 +197,7 @@ public static String decode(List encodings, String encoded) throws Par isActualString = false; } } catch (IOException | DataFormatException ioException) { - //ioException.printStackTrace(); + // ioException.printStackTrace(); } } else { @@ -178,7 +206,7 @@ public static String decode(List encodings, String encoded) throws Par done = true; } catch (IOException | DataFormatException ioException) { - //ioException.printStackTrace(); + // ioException.printStackTrace(); } try { @@ -201,12 +229,13 @@ public static String decode(List encodings, String encoded) throws Par } /** - * Encode the given string, with the given encodings (in the specified order) - * Example taken from - * Saml Raider + * Encode the given string, with the given encodings (in the specified order) Example taken from + * Saml + * Raider * * @param encodings the ordered list of encodings to be applied - * @param decoded the string to be encoded + * @param decoded the string to be encoded * @return the encoded string */ public static String encode(List encodings, String decoded) { @@ -216,7 +245,6 @@ public static String encode(List encodings, String decoded) { for (Encoding e : encodings) { switch (e) { case BASE64: - if (isActualString) { actual = Base64.getEncoder().encodeToString(actual.getBytes()); } else { @@ -226,24 +254,30 @@ public static String encode(List encodings, String decoded) { break; case URL: - if (isActualString) { - actual = URLEncoder.encode(actual); + try { + actual = URLEncoder.encode(actual, StandardCharsets.UTF_8.toString()); + } catch (UnsupportedEncodingException ex) { + throw new RuntimeException(ex); + } } else { actual = new String(actual_b); - actual = URLEncoder.encode(actual); + try { + actual = URLEncoder.encode(actual, StandardCharsets.UTF_8.toString()); + } catch (UnsupportedEncodingException ex) { + throw new RuntimeException(ex); + } isActualString = true; } break; case DEFLATE: - if (isActualString) { try { actual_b = compress(actual.getBytes(StandardCharsets.UTF_8), true); } catch (IOException ioException) { - //ioException.printStackTrace(); + // ioException.printStackTrace(); } isActualString = false; } else { @@ -251,7 +285,7 @@ public static String encode(List encodings, String decoded) { actual_b = compress(actual_b, true); } catch (IOException ioException) { - //ioException.printStackTrace(); + // ioException.printStackTrace(); } isActualString = false; } @@ -266,16 +300,17 @@ public static String encode(List encodings, String decoded) { } /** - * Also named Inflate, taken from - * here + * Also named Inflate, taken from here * * @param data the data to be decompressed (inflated) * @param gzip true to use gzip * @return returns the decompressed data - * @throws IOException if something goes wrong + * @throws IOException if something goes wrong * @throws DataFormatException if something goes wrong */ - public static byte[] decompress(byte[] data, boolean gzip) throws IOException, DataFormatException { + public static byte[] decompress(byte[] data, boolean gzip) + throws IOException, DataFormatException { Inflater inflater = new Inflater(true); inflater.setInput(data); @@ -294,8 +329,8 @@ public static byte[] decompress(byte[] data, boolean gzip) throws IOException, D } /** - * Also named Deflate, taken from - * here + * Also named Deflate, taken from here * * @param data data to be compressed (deflated) * @param gzip true to use gzip @@ -323,51 +358,51 @@ public static byte[] compress(byte[] data, boolean gzip) throws IOException { } /** - * Decodes a parameter from a message, given the message section and the list of encodings to be applied during - * decoding + * Decodes a parameter from a message, given the message section and the list of encodings to be + * applied during decoding * - * @param ms The message section that contains the parameter to be decoded - * @param encodings The list of encodings to be applied to decode the parameter - * @param messageInfo The message to be decoded - * @param isRequest True if the message containing the parameter is a request + * @param ms The message section that contains the parameter to be decoded + * @param encodings The list of encodings to be applied to decode the parameter + * @param messageInfo The message to be decoded + * @param isRequest True if the message containing the parameter is a request * @param decode_param The name of the parameter to be decoded * @return The decoded parameter as a string * @throws ParsingException If problems are encountered during decoding */ - public String decodeParam(DecodeOperationFrom ms, - List encodings, - HTTPReqRes messageInfo, - Boolean isRequest, - String decode_param) throws ParsingException { + public String decodeParam( + DecodeOperationFrom ms, + List encodings, + HTTPReqRes messageInfo, + Boolean isRequest, + String decode_param) + throws ParsingException { String decoded_param = ""; if (is_regex) { switch (ms) { case HEAD: - decoded_param = decode( - encodings, messageInfo.getHeadRegex(isRequest, decode_param)); + decoded_param = + decode(encodings, messageInfo.getHeadRegex(isRequest, decode_param)); break; case BODY: - decoded_param = decode( - encodings, messageInfo.getBodyRegex(isRequest, decode_param)); + decoded_param = + decode(encodings, messageInfo.getBodyRegex(isRequest, decode_param)); break; case URL: - decoded_param = decode( - encodings, messageInfo.getUrlRegex(decode_param)); + decoded_param = decode(encodings, messageInfo.getUrlRegex(decode_param)); break; } } else { switch (ms) { case HEAD: - decoded_param = decode( - encodings, messageInfo.getHeadParam(isRequest, decode_param)); + decoded_param = + decode(encodings, messageInfo.getHeadParam(isRequest, decode_param)); break; case BODY: - decoded_param = decode( - encodings, messageInfo.getBodyRegex(isRequest, decode_param)); + decoded_param = + decode(encodings, messageInfo.getBodyRegex(isRequest, decode_param)); break; case URL: - decoded_param = decode( - encodings, messageInfo.getUrlParam(decode_param)); + decoded_param = decode(encodings, messageInfo.getUrlParam(decode_param)); break; } } @@ -391,6 +426,7 @@ public void init() { } @Override + @SuppressWarnings("unchecked") public DecodeOperation_API getAPI() { api = new DecodeOperation_API(this); return (DecodeOperation_API) api; @@ -447,34 +483,38 @@ public API exporter() throws ParsingException { switch (from) { case HEAD: if (is_regex) { - ((Operation_API) imported_api).message.editHeadRegex( - ((Operation_API) imported_api).is_request, decode_target, encoded); + ((Operation_API) imported_api) + .message.editHeadRegex( + ((Operation_API) imported_api).is_request, + decode_target, + encoded); } else { - ((Operation_API) imported_api).message.editHeadParam( - ((Operation_API) imported_api).is_request, decode_target, encoded - ); + ((Operation_API) imported_api) + .message.editHeadParam( + ((Operation_API) imported_api).is_request, + decode_target, + encoded); } break; case BODY: - ((Operation_API) imported_api).message.editBodyRegex( - ((Operation_API) imported_api).is_request, decode_target, encoded - ); + ((Operation_API) imported_api) + .message.editBodyRegex( + ((Operation_API) imported_api).is_request, + decode_target, + encoded); break; case URL: if (is_regex) { - ((Operation_API) imported_api).message.editUrlRegex( - decode_target, encoded - ); + ((Operation_API) imported_api).message.editUrlRegex(decode_target, encoded); } else { - ((Operation_API) imported_api).message.editUrlParam( - decode_target, encoded - ); + ((Operation_API) imported_api).message.editUrlParam(decode_target, encoded); } break; case JWT_HEADER: case JWT_PAYLOAD: case JWT_SIGNATURE: - throw new ParsingException("invalid from section in decode operation should be a message section"); + throw new ParsingException( + "invalid from section in decode operation should be a message section"); } // the previous function should already have updated the message inside api @@ -492,12 +532,13 @@ public API exporter() throws ParsingException { */ public void execute(List vars) throws ParsingException { if (imported_api instanceof Operation_API) { - decoded_content = decodeParam( - from, - encodings, - ((Operation_API) imported_api).message, - ((Operation_API) imported_api).is_request, - decode_target); + decoded_content = + decodeParam( + from, + encodings, + ((Operation_API) imported_api).message, + ((Operation_API) imported_api).is_request, + decode_target); } else if (imported_api instanceof DecodeOperation_API) { switch (from) { @@ -572,8 +613,8 @@ public void execute(List vars) throws ParsingException { } /** - * Execute a list of checks inside a decode operation. This function uses the APIs Sets also the result to the - * decode op + * Execute a list of checks inside a decode operation. This function uses the APIs Sets also the + * result to the decode op * * @return the result, for convenience * @throws ParsingException if errors are found @@ -595,17 +636,19 @@ public boolean executeChecks(List vars) throws ParsingException { * @return the extended string representation of this decode operation */ public String toStringExtended() { - String template = "Decode operation:\n" + - "\tfrom: %s\n" + - "\tdecode target: %s\n" + - "\tis regex %b\n" + - "\tencodings: %s\n" + - "\ttype: %s\n" + - "\tdecoded content: %s\n" + - "\tdecode operations: %s\n"; - - - return String.format(template, from, + String template = + "Decode operation:\n" + + "\tfrom: %s\n" + + "\tdecode target: %s\n" + + "\tis regex %b\n" + + "\tencodings: %s\n" + + "\ttype: %s\n" + + "\tdecoded content: %s\n" + + "\tdecode operations: %s\n"; + + return String.format( + template, + from, StringEscapeUtils.escapeJava(decode_target), is_regex, encodings, @@ -614,9 +657,7 @@ public String toStringExtended() { decodeOperations); } - /** - * Used in decode operation to specify where to search for the content to decode - */ + /** Used in decode operation to specify where to search for the content to decode */ public enum DecodeOperationFrom { // standard message HEAD, @@ -651,9 +692,7 @@ public static DecodeOperationFrom fromString(String input) throws ParsingExcepti } } - /** - * The possible encodings to be used - */ + /** The possible encodings to be used */ public enum Encoding { BASE64, URL, @@ -684,9 +723,7 @@ public static Encoding fromString(String input) throws ParsingException { } } - /** - * Used to specify the type of decoded content, only when that content has to be edited. - */ + /** Used to specify the type of decoded content, only when that content has to be edited. */ public enum DecodeOpType { JWT, NONE, @@ -707,4 +744,4 @@ public static DecodeOpType fromString(String input) throws ParsingException { } } } -} \ No newline at end of file +} diff --git a/tool/src/main/java/migt/DecodeOperation_API.java b/tool/src/main/java/org/zaproxy/addon/migt/DecodeOperation_API.java similarity index 56% rename from tool/src/main/java/migt/DecodeOperation_API.java rename to tool/src/main/java/org/zaproxy/addon/migt/DecodeOperation_API.java index 218f746..0897e79 100644 --- a/tool/src/main/java/migt/DecodeOperation_API.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/DecodeOperation_API.java @@ -1,4 +1,23 @@ -package migt; +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; public class DecodeOperation_API extends API { public DecodeOperation.DecodeOpType type; // the type of the decoded param @@ -31,7 +50,8 @@ public void init() { jwt = new JWT(); } - public String getDecodedContent(DecodeOperation.DecodeOperationFrom dopfrom) throws ParsingException { + public String getDecodedContent(DecodeOperation.DecodeOperationFrom dopfrom) + throws ParsingException { switch (dopfrom) { case HEAD: throw new ParsingException("cannot decode from header in a recursive decode"); @@ -41,17 +61,20 @@ public String getDecodedContent(DecodeOperation.DecodeOperationFrom dopfrom) thr throw new ParsingException("cannot decode from url in a recursive decode"); case JWT_HEADER: if (type != DecodeOperation.DecodeOpType.JWT) - throw new ParsingException("cannot decode in a jwt header if previous decode was not a jwt"); + throw new ParsingException( + "cannot decode in a jwt header if previous decode was not a jwt"); return jwt.header; case JWT_PAYLOAD: if (type != DecodeOperation.DecodeOpType.JWT) - throw new ParsingException("cannot decode in a jwt payload if previous decode was not a jwt"); + throw new ParsingException( + "cannot decode in a jwt payload if previous decode was not a jwt"); return jwt.payload; case JWT_SIGNATURE: if (type != DecodeOperation.DecodeOpType.JWT) - throw new ParsingException("cannot decode in a jwt signature if previous decode was not a jwt"); + throw new ParsingException( + "cannot decode in a jwt signature if previous decode was not a jwt"); return jwt.signature; default: throw new UnsupportedOperationException("invalid Decode operation from"); diff --git a/tool/src/main/java/migt/EditOperation.java b/tool/src/main/java/org/zaproxy/addon/migt/EditOperation.java similarity index 60% rename from tool/src/main/java/migt/EditOperation.java rename to tool/src/main/java/org/zaproxy/addon/migt/EditOperation.java index 40ff343..2c8c90f 100644 --- a/tool/src/main/java/migt/EditOperation.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/EditOperation.java @@ -1,20 +1,38 @@ -package migt; +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; + +import static org.zaproxy.addon.migt.Tools.getVariableByName; import com.jayway.jsonpath.PathNotFoundException; -import org.json.JSONArray; -import org.json.JSONObject; -import org.w3c.dom.Document; -import org.xml.sax.SAXException; -import samlraider.application.SamlTabController; -import samlraider.helpers.XMLHelpers; - import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; - -import static migt.Tools.getVariableByName; +import org.json.JSONArray; +import org.json.JSONObject; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; +import org.zaproxy.addon.migt.samlraider.application.SamlTabController; +import org.zaproxy.addon.migt.samlraider.helpers.XMLHelpers; public class EditOperation extends Module { // XML @@ -53,7 +71,7 @@ public class EditOperation extends Module { public EditOperation(JSONObject eop_json) throws ParsingException { init(); - java.util.Iterator keys = eop_json.keys(); + Iterator keys = eop_json.keys(); while (keys.hasNext()) { String key = keys.next(); @@ -115,10 +133,9 @@ public EditOperation(JSONObject eop_json) throws ParsingException { case "xml attribute": xml_attr = eop_json.getString("xml attribute"); break; - // JWT + // JWT case "jwt from": - jwt_section = Jwt_section.getFromString( - eop_json.getString("jwt from")); + jwt_section = Jwt_section.getFromString(eop_json.getString("jwt from")); break; case "jwt remove": jwt_action = Jwt_action.REMOVE; @@ -163,8 +180,7 @@ public EditOperation(JSONObject eop_json) throws ParsingException { while (it.hasNext()) { String act_enc = (String) it.next(); - this.encodings.add( - DecodeOperation.Encoding.fromString(act_enc)); + this.encodings.add(DecodeOperation.Encoding.fromString(act_enc)); } break; case "encode": @@ -201,14 +217,13 @@ public EditOperation(JSONObject eop_json) throws ParsingException { validate(); } - /** - * Validate this object's content. Used to check if the parsed tags are valid. - */ + /** Validate this object's content. Used to check if the parsed tags are valid. */ @Override public void validate() throws ParsingException { if (action == MessageOperation.MessageOperationActions.ENCODE) { if (encodings.isEmpty()) { - throw new ParsingException("Using encode in Edit Operation, but not providing encodings"); + throw new ParsingException( + "Using encode in Edit Operation, but not providing encodings"); } } } @@ -243,19 +258,26 @@ public void execute_decodeOperation_API(List vars) throws ParsingException DecodeOperation_API tmp_imported_api = (DecodeOperation_API) imported_api; switch (((DecodeOperation_API) imported_api).type) { + + // ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo case XML: - //SAML Remove signatures + // SAML Remove signatures if (self_sign | remove_signature) { Document document = null; try { XMLHelpers xmlHelpers = new XMLHelpers(); - document = xmlHelpers.getXMLDocumentOfSAMLMessage(((DecodeOperation_API) imported_api).xml); - saml_original_cert = xmlHelpers.getCertificate(document.getDocumentElement()); + document = + xmlHelpers.getXMLDocumentOfSAMLMessage( + ((DecodeOperation_API) imported_api).xml); + saml_original_cert = + xmlHelpers.getCertificate(document.getDocumentElement()); if (saml_original_cert == null) { System.out.println("SAML Certificate not found in decoded parameter"); applicable = false; } - edited_xml = SamlTabController.removeSignature_edit(((DecodeOperation_API) imported_api).xml); + edited_xml = + SamlTabController.removeSignature_edit( + ((DecodeOperation_API) imported_api).xml); } catch (SAXException e) { e.printStackTrace(); } @@ -263,55 +285,57 @@ public void execute_decodeOperation_API(List vars) throws ParsingException switch (xml_action) { case ADD_TAG: - edited_xml = XML.addTag(edited_xml, - xml_tag, - xml_action_name, - value, - xml_occurrency); + edited_xml = + XML.addTag( + edited_xml, + xml_tag, + xml_action_name, + value, + xml_occurrency); break; case ADD_ATTR: - edited_xml = XML.addTagAttribute(edited_xml, - xml_tag, - xml_action_name, - value, - xml_occurrency); + edited_xml = + XML.addTagAttribute( + edited_xml, + xml_tag, + xml_action_name, + value, + xml_occurrency); break; case EDIT_TAG: - edited_xml = XML.editTagValue(edited_xml, - xml_action_name, - value, - xml_occurrency); + edited_xml = + XML.editTagValue( + edited_xml, xml_action_name, value, xml_occurrency); break; case EDIT_ATTR: - edited_xml = XML.editTagAttributes(edited_xml, - xml_tag, - xml_action_name, - value, - xml_occurrency); + edited_xml = + XML.editTagAttributes( + edited_xml, + xml_tag, + xml_action_name, + value, + xml_occurrency); break; case REMOVE_TAG: - edited_xml = XML.removeTag(edited_xml, - xml_action_name, - xml_occurrency); + edited_xml = XML.removeTag(edited_xml, xml_action_name, xml_occurrency); break; case REMOVE_ATTR: - edited_xml = XML.removeTagAttribute(edited_xml, - xml_tag, - xml_action_name, - xml_occurrency); - break; - case SAVE_TAG: { - String to_save = XML.getTagValaue(edited_xml, - xml_action_name, - xml_occurrency); - Var v = new Var(save_as, to_save); - vars.add(v); - break; - } + edited_xml = + XML.removeTagAttribute( + edited_xml, xml_tag, xml_action_name, xml_occurrency); + break; + case SAVE_TAG: + { + String to_save = + XML.getTagValaue(edited_xml, xml_action_name, xml_occurrency); + Var v = new Var(save_as, to_save); + vars.add(v); + break; + } case SAVE_ATTR: - String to_save = XML.getTagAttributeValue(edited_xml, - xml_tag, xml_action_name, - xml_occurrency); + String to_save = + XML.getTagAttributeValue( + edited_xml, xml_tag, xml_action_name, xml_occurrency); Var v = new Var(save_as, to_save); vars.add(v); break; @@ -319,28 +343,50 @@ public void execute_decodeOperation_API(List vars) throws ParsingException if (self_sign && !edited_xml.equals("")) { // SAML re-sign - edited_xml = SamlTabController.resignAssertion_edit(edited_xml, saml_original_cert); + edited_xml = + SamlTabController.resignAssertion_edit(edited_xml, saml_original_cert); } tmp_imported_api.xml = edited_xml; applicable = true; break; - + // ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo case JWT: if (jwt_section != null) { // if only sign, there will be no jwt section try { switch (jwt_section) { case HEADER: - tmp_imported_api.jwt.header = Tools.editJson( - jwt_action, tmp_imported_api.jwt.header, what, vars, save_as, value, key_add); + tmp_imported_api.jwt.header = + Tools.editJson( + jwt_action, + tmp_imported_api.jwt.header, + what, + vars, + save_as, + value, + key_add); break; case PAYLOAD: - tmp_imported_api.jwt.payload = Tools.editJson( - jwt_action, tmp_imported_api.jwt.payload, what, vars, save_as, value, key_add); + tmp_imported_api.jwt.payload = + Tools.editJson( + jwt_action, + tmp_imported_api.jwt.payload, + what, + vars, + save_as, + value, + key_add); break; case SIGNATURE: - tmp_imported_api.jwt.signature = Tools.editJson( - jwt_action, tmp_imported_api.jwt.signature, what, vars, save_as, value, key_add); + tmp_imported_api.jwt.signature = + Tools.editJson( + jwt_action, + tmp_imported_api.jwt.signature, + what, + vars, + save_as, + value, + key_add); break; } } catch (PathNotFoundException e) { @@ -404,125 +450,122 @@ public void execute_decodeOperation_API(List vars) throws ParsingException public void execute_Operation_API() throws ParsingException { HTTPReqRes message = ((Operation_API) imported_api).message; - boolean is_request = ((Operation_API) imported_api).is_request; // if the message to edit is the request + boolean is_request = + ((Operation_API) imported_api).is_request; // if the message to edit is the request switch (msg_from) { - case URL: { - if (!is_request) { - throw new RuntimeException("trying to access the URL of a response message"); - } + case URL: + { + if (!is_request) { + throw new RuntimeException( + "trying to access the URL of a response message"); + } - switch (action) { - case REMOVE_PARAMETER: - message.removeUrlParam(what); - break; - case REMOVE_MATCH_WORD: - // TODO: remove, can be done with edit regex - break; - case EDIT: - message.editUrlParam(what, value); - break; - case EDIT_REGEX: - message.editUrlRegex(what, value); - break; - case ADD: - message.addUrlParam(what, value); - break; - case ENCODE: - String old_value = message.getUrlParam(what); - String new_value = DecodeOperation.encode( - encodings, - old_value - ); - message.editUrlParam(what, new_value); - break; + switch (action) { + case REMOVE_PARAMETER: + message.removeUrlParam(what); + break; + case REMOVE_MATCH_WORD: + // TODO: remove, can be done with edit regex + break; + case EDIT: + message.editUrlParam(what, value); + break; + case EDIT_REGEX: + message.editUrlRegex(what, value); + break; + case ADD: + message.addUrlParam(what, value); + break; + case ENCODE: + String old_value = message.getUrlParam(what); + String new_value = DecodeOperation.encode(encodings, old_value); + message.editUrlParam(what, new_value); + break; + } + break; } - break; - } - case HEAD: { - switch (action) { - case REMOVE_PARAMETER: - message.removeHeadParameter(is_request, what); - break; - case REMOVE_MATCH_WORD: - // TODO: remove, can be done with edit regex - break; - case EDIT: - message.editHeadParam(is_request, what, value); - break; - case EDIT_REGEX: - // For each header applies regex - message.editHeadRegex(is_request, what, value); - break; - case ADD: - message.addHeadParameter(is_request, what, value); - break; - case ENCODE: - String old_value = message.getHeadParam(is_request, what); - String new_value = DecodeOperation.encode( - encodings, - old_value - ); - message.editHeadParam(is_request, what, new_value); - break; + case HEAD: + { + switch (action) { + case REMOVE_PARAMETER: + message.removeHeadParameter(is_request, what); + break; + case REMOVE_MATCH_WORD: + // TODO: remove, can be done with edit regex + break; + case EDIT: + message.editHeadParam(is_request, what, value); + break; + case EDIT_REGEX: + // For each header applies regex + message.editHeadRegex(is_request, what, value); + break; + case ADD: + message.addHeadParameter(is_request, what, value); + break; + case ENCODE: + String old_value = message.getHeadParam(is_request, what); + String new_value = DecodeOperation.encode(encodings, old_value); + message.editHeadParam(is_request, what, new_value); + break; + } + break; } - break; - } - case BODY: { - switch (action) { - // TODO add also edits based on Content-Type? - case REMOVE_PARAMETER: - // nothing - break; - case REMOVE_MATCH_WORD: - // nothing - break; - case EDIT: - // nothing - break; - case EDIT_REGEX: - // edit matched value - message.editBodyRegex(is_request, what, value); - break; - case ADD: - // append value - message.addBody(is_request, value); - break; - case ENCODE: - // encode matched value - String old_value = message.getBodyRegex(is_request, what); - String new_value = DecodeOperation.encode( - encodings, - old_value - ); - message.editBodyRegex(is_request, what, new_value); - break; + case BODY: + { + switch (action) { + // TODO add also edits based on Content-Type? + case REMOVE_PARAMETER: + // nothing + break; + case REMOVE_MATCH_WORD: + // nothing + break; + case EDIT: + // nothing + break; + case EDIT_REGEX: + // edit matched value + message.editBodyRegex(is_request, what, value); + break; + case ADD: + // append value + message.addBody(is_request, value); + break; + case ENCODE: + // encode matched value + String old_value = message.getBodyRegex(is_request, what); + String new_value = DecodeOperation.encode(encodings, old_value); + message.editBodyRegex(is_request, what, new_value); + break; + } + break; } - break; - } - case RAW: { - switch (action) { - //TODO - case REMOVE_PARAMETER: - break; - case REMOVE_MATCH_WORD: - break; - case EDIT: - break; - case EDIT_REGEX: - // TODO - break; - case ADD: - break; - case ENCODE: - //TODO - break; + case RAW: + { + switch (action) { + // TODO + case REMOVE_PARAMETER: + break; + case REMOVE_MATCH_WORD: + break; + case EDIT: + break; + case EDIT_REGEX: + // TODO + break; + case ADD: + break; + case ENCODE: + // TODO + break; + } + break; } - break; - } } applicable = true; // check if there is a better place for this ((Operation_API) imported_api).message = message; @@ -542,9 +585,7 @@ public void execute(List vars) throws ParsingException { } } - /** - * The possible XML actions are the ones described in this enum - */ + /** The possible XML actions are the ones described in this enum */ public enum XmlAction { ADD_TAG, ADD_ATTR, @@ -590,9 +631,7 @@ public static XmlAction fromString(String input) throws ParsingException { } } - /** - * Defines the possible actions to be done on a decoded parameter interpreted as plain text - */ + /** Defines the possible actions to be done on a decoded parameter interpreted as plain text */ public enum TxtAction { REMOVE, EDIT, @@ -626,9 +665,7 @@ public static TxtAction fromString(String input) throws ParsingException { } } - /** - * Defines the possible actions to be done on a JWT token - */ + /** Defines the possible actions to be done on a JWT token */ public enum Jwt_action { REMOVE, EDIT, @@ -636,9 +673,7 @@ public enum Jwt_action { SAVE } - /** - * Defines the possible JWT token sections - */ + /** Defines the possible JWT token sections */ public enum Jwt_section { HEADER, PAYLOAD, diff --git a/tool/src/main/java/migt/ExecuteActiveListener.java b/tool/src/main/java/org/zaproxy/addon/migt/ExecuteActiveListener.java similarity index 53% rename from tool/src/main/java/migt/ExecuteActiveListener.java rename to tool/src/main/java/org/zaproxy/addon/migt/ExecuteActiveListener.java index 1504d9d..51a5aff 100644 --- a/tool/src/main/java/migt/ExecuteActiveListener.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/ExecuteActiveListener.java @@ -1,22 +1,36 @@ -package migt; - -/** - * Listener class for ExecuteActive class +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. */ +package org.zaproxy.addon.migt; + +/** Listener class for ExecuteActive class */ public interface ExecuteActiveListener { - /** - * Called when the execution of an ExecuteActivez is started - */ + /** Called when the execution of an ExecuteActivez is started */ void onExecuteStart(); - /** - * Called when the execution of an ExecuteActive is finished - */ + /** Called when the execution of an ExecuteActive is finished */ void onExecuteDone(); /** - * Called during the execution of an ExecuteActive, when a new operation is found and has to be executed + * Called during the execution of an ExecuteActive, when a new operation is found and has to be + * executed * * @param op the operation which has to be executed */ @@ -30,8 +44,8 @@ public interface ExecuteActiveListener { Operation onOperationDone(); /** - * During an ExecuteActive execution, if a new session is found, this function is called, with the session as an - * argument. You can return the session initiating it + * During an ExecuteActive execution, if a new session is found, this function is called, with + * the session as an argument. You can return the session initiating it * * @param s the session * @return the initiated session diff --git a/tool/src/main/java/org/zaproxy/addon/migt/ExecuteActives.java b/tool/src/main/java/org/zaproxy/addon/migt/ExecuteActives.java new file mode 100644 index 0000000..ef8439a --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/ExecuteActives.java @@ -0,0 +1,332 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.PatternSyntaxException; + +/** Class which executes actives tests, has to be run as a thread */ +public class ExecuteActives implements Runnable { + final Object waiting; // the lock on which the thread will wait + ExecuteActiveListener listener; + List actives; + + /** + * Instantiate the class + * + * @param actives the list of Active tests + * @param waiting an object which will serve as a lock between this class and BurpExtender to + * stop and resume the execution + */ + public ExecuteActives(List actives, Object waiting) { + this.actives = actives; + this.waiting = waiting; + } + + /** + * Method to register an ExecuteActiveListener, used to receive callbacks + * + * @param listener the listener + */ + public void registerExecuteActivesListener(ExecuteActiveListener listener) { + this.listener = listener; + } + + /** The run method, which runs the Tests */ + @Override + public void run() { + listener.onExecuteStart(); + for (Test actual_test : actives) { + listener.onNewTest(actual_test); + + Map isInPause = new HashMap<>(); + Map clearCookieAsked = new HashMap<>(); + + for (Session s : actual_test.sessions) { + s = listener.onNewSession(s); + if (s == null) { + listener.onError(actual_test); + } + isInPause.put(s.name, false); + } + + Map executions = new HashMap<>(); + AtomicInteger alive_count = new AtomicInteger(); + + for (Operation op : actual_test.operations) { + try { + // if the operation is a session control operation + if (op.isSessionOp) { + actual_test.applicable = true; + switch (op.getSessionAction()) { + case START: + { + Session selected = actual_test.getSession(op.getSession()); + if (selected == null) { + actual_test.error_str = + "Invalid session name, maybe you didn't declare it?"; + actual_test.error = true; + break; + } + selected.ex.registerExecuteTrackListener( + new ExecuteTrackListener() { + @Override + public void onExecuteDone( + boolean errors, + String current_url, + String sessionName) { + if (actual_test.resultSession.equals("") + || actual_test.resultSession.equals( + sessionName)) { + + if (actual_test.result + == Test.ResultType.CORRECT_FLOW) { + if (errors + || current_url.contains( + "error")) { + actual_test.success = false; + } + } else if (actual_test.result + == Test.ResultType.INCORRECT_FLOW) { + actual_test.success = + errors; // Difficult to read + } else if (actual_test.result + == Test.ResultType.ASSERT_ONLY) { + // at this point, all the asserts have + // been executed, and if they failed + // they already returned a false result + } + synchronized (waiting) { + waiting.notify(); + } + } + if (errors) { + synchronized (waiting) { + waiting.notify(); + } + } + alive_count.getAndDecrement(); + } + + @Override + public void onExecuteDone( + boolean forceResult, String sessionName) { + if (actual_test.resultSession.equals("") + || actual_test.resultSession.equals( + sessionName)) { + + actual_test.success = forceResult; + synchronized (waiting) { + waiting.notify(); + } + } + alive_count.getAndDecrement(); + } + + @Override + public void onError(String sessionName) { + if (actual_test.resultSession.equals("") + || actual_test.resultSession.equals( + sessionName)) { + actual_test.applicable = false; + } + synchronized (waiting) { + waiting.notify(); + } + alive_count.getAndDecrement(); + } + + @Override + public Boolean onAskPause(String sessionName) { + if (isInPause.get(sessionName) != null) { + return isInPause.get(sessionName); + } else { + return false; + } + } + + @Override + public Boolean onAskStop(String sessionName) { + return null; + } + + @Override + public Boolean onAskClearCookie( + String sessionName) { + if (clearCookieAsked.get(sessionName) != null) { + boolean tmp = + clearCookieAsked.get(sessionName); + clearCookieAsked.replace( + sessionName, false); + return tmp; + } else { + return false; + } + } + + @Override + public void onNextSessionAction( + SessionTrackAction last_action, + SessionTrackAction last_open, + SessionTrackAction last_click, + String last_url, + String session_name) + throws ParsingException { + Session s = + actual_test.getSession(session_name); + s.last_action = last_action; + s.last_open = last_open; + s.last_click = last_click; + s.last_url = last_url; + } + + @Override + public Track onUpdateTrack(String sessionName) + throws ParsingException { + return actual_test.getSession(sessionName) + .track; + } + + @Override + public void onSetVar(Var v) { + actual_test.vars.add(v); + } + }); + + Thread t = new Thread(selected.ex); + t.setName(op.getSession()); + executions.put(op.getSession(), t); + alive_count.addAndGet(1); + executions.get(op.getSession()).start(); + break; + } + + case PAUSE: + isInPause.replace(op.getSession(), true); + break; + + case RESUME: + isInPause.replace(op.getSession(), false); + break; + + case STOP: + { + Session selected = actual_test.getSession(op.getSession()); + if (selected == null) { + actual_test.error_str = + "Invalid session name, maybe you didn't declare it?"; + actual_test.error = true; + break; + } + + executions.get(op.getSession()).interrupt(); + break; + } + case CLEAR_COOKIES: + Session selected = actual_test.getSession(op.getSession()); + if (selected == null) { + actual_test.error_str = + "Invalid session name, maybe you didn't declare it?"; + actual_test.error = true; + break; + } + clearCookieAsked.put(selected.name, true); + break; + } + + List act_vars = actual_test.vars; + List updated_vars = op.executeSessionOps(actual_test, act_vars); + actual_test.vars = updated_vars; + + } else { + // if it is a normal operation + + if (!op.from_session.equals("")) { + op.session_port = actual_test.getSession(op.from_session).port; + } else if (!op.to_session.equals("")) { + op.session_port = actual_test.getSession(op.to_session).port; + } else { + op.session_port = "8080"; + } + + if (op.api == null) { + op.api = new Operation_API(actual_test.vars); + } else { + op.api.vars = actual_test.vars; + } + + listener.onNewProcessOperation(op); + + synchronized (this.waiting) { + try { + this.waiting.wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + op = listener.onOperationDone(); // Take the operation from the caller + + actual_test.vars = op.api.vars; + actual_test.vars = op.executeSessionOps(actual_test, actual_test.vars); + + if (op.applicable) { + actual_test.success = op.result; + actual_test.applicable = true; + if (!op.result) { + for (String key : executions.keySet()) { + executions.get(key).interrupt(); + } + break; + } + } else { + actual_test.applicable = false; + for (String key : executions.keySet()) { + executions.get(key).interrupt(); + } + break; + } + } + } catch (ParsingException | PatternSyntaxException e) { + e.printStackTrace(); + listener.onError(actual_test); + actual_test.applicable = false; + for (String key : executions.keySet()) { + executions.get(key).interrupt(); + } + break; + } + } + while (alive_count.get() != 0) { + try { + Thread.sleep(100); + } catch (InterruptedException interruptedException) { + interruptedException.printStackTrace(); + // alive_count.getAndDecrement(); + } + } + listener.onTestDone(actual_test); + } + listener.onExecuteDone(); + } +} diff --git a/tool/src/main/java/org/zaproxy/addon/migt/ExecutePassiveListener.java b/tool/src/main/java/org/zaproxy/addon/migt/ExecutePassiveListener.java new file mode 100644 index 0000000..1ee0bd7 --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/ExecutePassiveListener.java @@ -0,0 +1,73 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; + +import java.util.ArrayList; +import java.util.List; + +/** Listener class for the ExecutePassive Object */ +public interface ExecutePassiveListener { + /** + * This method is called when the ExecutePassive thread is started, it is used to tell when the + * thread should start executing the passives test. if it returns true, the thread will start + * + * @return true if you want the thread to start + */ + boolean onWaitToStart(); + + /** Called when the ExecutePassives thread is started */ + void onExecuteStart(); + + /** + * Called when the ExecutePassives thread has ended + * + * @param passives_test The list of executed passive tests + */ + void onExecuteDone(List passives_test); + + /** + * Called when there is an error in the execution + * + * @param msg the error message + */ + void onError(String msg); + + /** + * When a new session has to be executed this method is called. This is thought to fill the + * session with the right values from the GUI class. + * + * @param s the session to be initiated + * @return the session with the filled values + */ + Session onNewSession(Session s) throws ParsingException; + + /** + * Called before the track of the session is executed. Usually used to start the recording of + * the messages in the GUI class + */ + void onBeforeExecuteTrack(); + + /** + * Called when a track ends its execution + * + * @return the list of intercepted messages + */ + ArrayList onTrackExecuteDone(); +} diff --git a/tool/src/main/java/org/zaproxy/addon/migt/ExecutePassives.java b/tool/src/main/java/org/zaproxy/addon/migt/ExecutePassives.java new file mode 100644 index 0000000..eacbaad --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/ExecutePassives.java @@ -0,0 +1,228 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * Class used to execute passive tests, it implements Runnable, it should be executed as a Thread. + * To communicate with the tread you can use the ExecutePassivesListener listener class. + */ +public class ExecutePassives implements Runnable { + final Object lock = new Object(); + public List passives; + ExecutePassiveListener listener; + List messageTypes; + boolean finished; + boolean execution_error; + + /** + * Used to instantiate an ExecutePassives object + * + * @param passiveTests The list of passive tests to execute + * @param listener the listener for this ExecutePassives Object, used to communicate with the + * thread + * @param msg_types the list of message types needed by the tests + */ + public ExecutePassives( + List passiveTests, ExecutePassiveListener listener, List msg_types) { + this.passives = passiveTests; + this.listener = listener; + this.messageTypes = msg_types; + this.finished = false; + this.execution_error = false; + } + + /** Starts the execution of the passive tests */ + @Override + public void run() { + // This first piece is thought to make the thread wait before start + while (!listener.onWaitToStart()) { + try { + // TODO: fix busy waiting + Thread.sleep(1000); + } catch (InterruptedException e) { + return; + } + } + + listener.onExecuteStart(); + execution_error = false; + + HashMap> batch = null; + try { + batch = Tools.batchPassivesFromSession(passives); + } catch (ParsingException e) { + e.printStackTrace(); + // lblOutput.setText(e.getMessage()); + return; + } + + for (String sessionName : batch.keySet()) { + List actual_batch = batch.get(sessionName); + + Session act_session = actual_batch.get(0).sessions.get(0); + try { + if (act_session.messages.size() == 0) { + act_session = listener.onNewSession(act_session); + } + } catch (ParsingException e) { + listener.onError("Error in retrieving session"); + return; + } + + Session executedSession = act_session; + // If sessions already executed, don't re-execute them + if (act_session.messages.size() == 0) { + executedSession = executePassiveTestSession(act_session); + for (Test t : actual_batch) { + t.sessions.set(0, executedSession); + } + batch.put(sessionName, actual_batch); + if (execution_error) { + return; + } + } + + // Execute all the tests for each session + for (Test actual_test : actual_batch) { + System.out.println("Actual test name: " + actual_test.getName()); + + boolean res = false; + try { + res = actual_test.execute(executedSession.messages, messageTypes); + } catch (ParsingException e) { + actual_test.applicable = false; + } + + System.out.println("Actual test result: " + res); + actual_test.success = res; + } + // Remove used session + executedSession = null; + } + passives = Tools.debatchPassive(batch); + listener.onExecuteDone(passives); + } + + /** + * Executes a passive test's session, to gather messages needed to execute the passive tests. + * + * @param session the session to be executed + * @return the same session, executed, that will contain the intercepted messages + */ + public Session executePassiveTestSession(Session session) { + // FIXME: session's track is assumed to be present + + synchronized (lock) { + finished = false; + } + + ExecuteTrackListener track_listener = + new ExecuteTrackListener() { + @Override + public void onExecuteDone( + boolean errors, String current_url, String sessionName) { + ArrayList intercepted_messages = listener.onTrackExecuteDone(); + session.messages = intercepted_messages; + + if (errors) { + listener.onError("Error in executing track for session " + sessionName); + execution_error = true; + } + synchronized (lock) { + lock.notify(); + finished = true; + } + } + + @Override + public void onExecuteDone(boolean forceResult, String sessionName) { + ArrayList intercepted_messages = listener.onTrackExecuteDone(); + session.messages = intercepted_messages; + + synchronized (lock) { + lock.notify(); + finished = true; + } + } + + @Override + public void onError(String sessionName) { + listener.onError("Error in executing track for session " + sessionName); + execution_error = true; + synchronized (lock) { + lock.notify(); + finished = true; + } + } + + @Override + public Boolean onAskPause(String sessionName) { + return false; + } + + @Override + public Boolean onAskStop(String sessionName) { + return false; + } + + @Override + public Boolean onAskClearCookie(String sessionName) { + return null; + } + + @Override + public void onNextSessionAction( + SessionTrackAction last_action, + SessionTrackAction last_open, + SessionTrackAction last_click, + String last_url, + String session_name) {} + + @Override + public Track onUpdateTrack(String sessionName) throws ParsingException { + return null; + } + + @Override + public void onSetVar(Var v) {} + }; + + listener.onBeforeExecuteTrack(); + session.ex.registerExecuteTrackListener(track_listener); + new Thread(session.ex).start(); // ex is assumed to be initialized + + // Waits execution to be finished + synchronized (lock) { + while (!finished) { + try { + lock.wait(); + } catch (InterruptedException e) { + break; + } + } + } + + return session; + } +} diff --git a/tool/src/main/java/org/zaproxy/addon/migt/ExecuteTrack.java b/tool/src/main/java/org/zaproxy/addon/migt/ExecuteTrack.java new file mode 100644 index 0000000..6b4c560 --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/ExecuteTrack.java @@ -0,0 +1,516 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; + +import java.io.File; +import java.time.Duration; +import org.openqa.selenium.Alert; +import org.openqa.selenium.By; +import org.openqa.selenium.NoAlertPresentException; +import org.openqa.selenium.NoSuchWindowException; +import org.openqa.selenium.OutputType; +import org.openqa.selenium.Proxy; +import org.openqa.selenium.SessionNotCreatedException; +import org.openqa.selenium.TimeoutException; +import org.openqa.selenium.UnhandledAlertException; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebDriverException; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.firefox.FirefoxOptions; +import org.openqa.selenium.remote.CapabilityType; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +/** + * Class that executes a Session Track (series of user actions). It launches a browser with Selenium + * to automate the actions + */ +public class ExecuteTrack implements Runnable { + private static String snapshot = ""; + private final boolean chrome_selected; + private final String driver_path; + private final boolean isHeadless; + private final Track track; + public String current_url; + public String port = ""; + public String sessionName; + public boolean isInPause; + public ExecuteTrackListener listener; + private Boolean isReqClearCookies = false; + + /** + * Instantiate the ExecuteTrack Object + * + * @param isHeadless + * @param chrome_selected tells if chrome is selected as a browser to be used, otherwise firefox + * is used + * @param driver_path the path to the chosen browser's driver + * @param track the track to be executed + * @param port the port set in the browser that the HTTP(S) proxy is listening to + * @param sessionName The name of the session to be executed + */ + public ExecuteTrack( + boolean isHeadless, + boolean chrome_selected, + String driver_path, + Track track, + String port, + String sessionName) { + this.chrome_selected = chrome_selected; + this.driver_path = driver_path; + this.track = track; // TODO: can't I just take the session object? + this.isHeadless = isHeadless; + this.port = port; + this.sessionName = sessionName; // TODO: can't I just take the session object? + } + + /** + * Registers the execute track listener, used to communicate with the ExecuteTrack thread + * + * @param listener the listener + */ + public void registerExecuteTrackListener(ExecuteTrackListener listener) { + this.listener = listener; + } + + /** Runs the session track */ + @Override + public void run() { + WebDriver driver; + int TIMEOUT = 10; + + if (chrome_selected) { + ChromeOptions options = new ChromeOptions(); + options.addArguments("ignore-certificate-errors"); + options.addArguments("window-size=1280,1400"); + options.addArguments("--proxy-bypass-list=<-loopback>"); + options.addArguments("--remote-allow-origins=*"); + Proxy proxy = new Proxy(); + proxy.setHttpProxy("localhost:" + port); + proxy.setSslProxy("localhost:" + port); + options.setCapability(CapabilityType.PROXY, proxy); + // options.setHeadless(isHeadless); + + System.setProperty("webdriver.chrome.driver", driver_path); + try { + driver = new ChromeDriver(options); + } catch (SessionNotCreatedException e) { + e.printStackTrace(); + return; + } + + } else { + FirefoxOptions options = new FirefoxOptions(); + options.addArguments("-width=1280"); + options.addArguments("-height=1400"); + Proxy proxy = new Proxy(); + proxy.setHttpProxy("localhost:" + port); + proxy.setSslProxy("localhost:" + port); + options.setCapability(CapabilityType.PROXY, proxy); + // options.setHeadless(isHeadless); + + System.setProperty("webdriver.gecko.driver", driver_path); + try { + driver = new FirefoxDriver(options); + } catch (SessionNotCreatedException e) { + e.printStackTrace(); + return; + } + } + + WebElement currentElement = null; + int act_window_index = 0; + + SessionTrackAction last_action = null; + SessionTrackAction last_open = null; + SessionTrackAction last_click = null; + String last_url = ""; + + int startindex = 0; + + beforeloop: + try { + for (int i = startindex; i < track.getTrack().size(); i++) { + last_action = track.getTrack().get(i); + + try { + last_url = driver.getCurrentUrl(); + } catch (WebDriverException e) { + } + + if (track.getTrack().get(i).action == SessionOperation.SessAction.CLICK) { + last_click = track.getTrack().get(i); + } + if (track.getTrack().get(i).action == SessionOperation.SessAction.OPEN) { + last_open = track.getTrack().get(i); + } + listener.onNextSessionAction( + last_action, last_open, last_click, last_url, sessionName); + + isInPause = listener.onAskPause(sessionName); + + while (isInPause) { + isInPause = listener.onAskPause(sessionName); + Thread.sleep(1000); + } + + Thread.sleep(1000); // Don't delete, it is useful + + SessionTrackAction action = track.getTrack().get(i); + + currentElement = null; + + Object[] tmp = driver.getWindowHandles().toArray(); + String[] windows = new String[tmp.length]; + int j = 0; + for (Object o : tmp) { + String act = (String) o; + windows[j++] = act; + } + + int windows_count = windows.length; + int windows_checked = 0; + + try { + driver.getWindowHandle(); + } catch (NoSuchWindowException e) { + act_window_index = 0; + driver.switchTo().window(windows[act_window_index]); + } + + switch (action.action) { + case WAIT: + { + String time = action.elem; + int time_int = Integer.parseInt(time); + + Thread.sleep(time_int); + continue; + } + case ALERT: + { + if (action.elem != null) { + Alert alert = null; + int c = 0; + while (c++ < 10) { + try { + alert = driver.switchTo().alert(); + break; + } catch (NoAlertPresentException e) { + Thread.sleep(1000); + continue; + } + } + if (action.elem.equals("accept")) { + alert.accept(); + } else if (action.elem.equals("dimiss")) { + alert.dismiss(); + } + } else { + throw new ParsingException("invalid alert action"); + } + continue; + } + case CLEAR_COOKIES: + { + driver.manage().deleteAllCookies(); + continue; + } + case OPEN: + { + driver.get(action.elem); + continue; + } + case SET_VAR: + { + Var v = new Var(action.elem, action.content); + listener.onSetVar(v); + continue; + } + case TYPE: + case CLICK: + case SNAPSHOT: + case DIFF: + case EQUALS: + case ASSERT_NOT_VISIBLE: + case ASSERT_NOT_CLICKABLE: + case ASSERT_CLICKABLE: + case ASSERT_VISIBLE: + case ASSERT_ELEM_CONTENT_HAS: + case ASSERT_ELEM_CONTENT_IS: + case ASSERT_ELEM_CLASS_HAS: + case ASSERT_ELEM_CLASS_IS: + case ASSERT_ELEM_HAS_ATTRIBUTE: + case ASSERT_ELEM_NOT_HAS_ATTRIBUTE: + { + String searchBy = action.elem_type; + String identifier = action.elem_source; + + while (windows_checked != windows_count) { + try { + boolean is_snapshot = + action.action == SessionOperation.SessAction.SNAPSHOT + || action.action + == SessionOperation.SessAction.DIFF + || action.action + == SessionOperation.SessAction.EQUALS; + + By by = null; + // Checks for the presence of a valid item to search + switch (searchBy.trim()) { + case "id": + by = By.id(identifier.trim()); + break; + case "xpath": + by = By.xpath(identifier.trim()); + break; + case "link": + by = By.linkText(identifier.trim()); + break; + case "name": + by = By.name(identifier.trim()); + break; + case "class": + by = By.className(identifier.trim()); + break; + default: + throw new ParsingException( + "invalid session track command"); + } + + if (action.action + == SessionOperation.SessAction.ASSERT_VISIBLE) { + new WebDriverWait(driver, Duration.ofSeconds(TIMEOUT)) + .until( + ExpectedConditions + .visibilityOfElementLocated(by)); + } else if (is_snapshot) { + new WebDriverWait(driver, Duration.ofSeconds(TIMEOUT)) + .until( + ExpectedConditions.presenceOfElementLocated( + by)); + } else { + new WebDriverWait(driver, Duration.ofSeconds(TIMEOUT)) + .until(ExpectedConditions.elementToBeClickable(by)); + } + + currentElement = driver.findElement(by); + + } catch (TimeoutException | NoSuchWindowException e) { + if (act_window_index < windows_count - 1) { + driver.switchTo().window(windows[++act_window_index]); + } else { + act_window_index = 0; + driver.switchTo().window(windows[act_window_index]); + } + windows_checked++; + } catch (UnhandledAlertException ex) { + Alert alert = driver.switchTo().alert(); + if (alert != null) alert.accept(); + } + if (currentElement != null) break; + } + if (currentElement == null) { + if (action.action == SessionOperation.SessAction.ASSERT_CLICKABLE + || action.action + == SessionOperation.SessAction.ASSERT_VISIBLE) { + listener.onExecuteDone(false, sessionName); + driver.close(); + return; + } + throw new TimeoutException( + identifier.trim() + " Could not be focused"); + } + break; + } + + default: + { + System.err.printf("error in session %s track", sessionName); + throw new ParsingException("invalid session track command" + action); + } + } + + if (currentElement != null) { + switch (action.action) { + case CLICK: + new WebDriverWait(driver, Duration.ofSeconds(TIMEOUT)) + .until(ExpectedConditions.elementToBeClickable(currentElement)) + .click(); + break; + case TYPE: + new WebDriverWait(driver, Duration.ofSeconds(TIMEOUT)) + .until(ExpectedConditions.elementToBeClickable(currentElement)) + .sendKeys(action.content); + break; + case SNAPSHOT: + new WebDriverWait(driver, Duration.ofSeconds(TIMEOUT)) + .until(ExpectedConditions.visibilityOf(currentElement)); + File f = currentElement.getScreenshotAs(OutputType.FILE); + snapshot = currentElement.getScreenshotAs(OutputType.BASE64); + f.renameTo(new File("./snapshot.png")); + break; + case DIFF: + case EQUALS: + new WebDriverWait(driver, Duration.ofSeconds(TIMEOUT)) + .until(ExpectedConditions.visibilityOf(currentElement)); + String diff = currentElement.getScreenshotAs(OutputType.BASE64); + File f2 = currentElement.getScreenshotAs(OutputType.FILE); + f2.renameTo(new File("./diff.png")); + if (action.action == SessionOperation.SessAction.DIFF) { + if (diff.equals(snapshot)) { + listener.onExecuteDone(true, current_url, sessionName); + driver.close(); + return; + } + } else { + if (!diff.equals(snapshot)) { + listener.onExecuteDone(true, current_url, sessionName); + driver.close(); + return; + } + } + break; + case ASSERT_VISIBLE: + case ASSERT_CLICKABLE: + listener.onExecuteDone(true, sessionName); + driver.close(); + return; + case ASSERT_NOT_CLICKABLE: + case ASSERT_NOT_VISIBLE: + listener.onExecuteDone(false, sessionName); + driver.close(); + return; + case ASSERT_ELEM_CONTENT_IS: + { + String content = currentElement.getText(); + if (!content.equals(action.content)) { + listener.onExecuteDone(false, sessionName); + driver.close(); + return; + } + break; + } + case ASSERT_ELEM_CONTENT_HAS: + { + String content = currentElement.getText(); + if (!content.contains(action.content)) { + listener.onExecuteDone(false, sessionName); + driver.close(); + return; + } + break; + } + case ASSERT_ELEM_CLASS_IS: + { + String classtxt = currentElement.getAttribute("class"); + if (!classtxt.equals(action.content)) { + listener.onExecuteDone(false, sessionName); + driver.close(); + return; + } + break; + } + case ASSERT_ELEM_CLASS_HAS: + { + String classtxt = currentElement.getAttribute("class"); + if (!classtxt.contains(action.content)) { + listener.onExecuteDone(false, sessionName); + driver.close(); + return; + } + break; + } + case ASSERT_ELEM_HAS_ATTRIBUTE: + { + String attr = currentElement.getAttribute(action.content); + if (attr == null) { + listener.onExecuteDone(false, sessionName); + driver.close(); + return; + } + break; + } + case ASSERT_ELEM_NOT_HAS_ATTRIBUTE: + { + String attr = currentElement.getAttribute(action.content); + if (attr != null) { + listener.onExecuteDone(false, sessionName); + driver.close(); + return; + } + break; + } + } + } + + listener.onNextSessionAction( + last_action, last_open, last_click, last_url, sessionName); + + isReqClearCookies = listener.onAskClearCookie(sessionName); + if (isReqClearCookies != null) { + if (isReqClearCookies) { + driver.manage().deleteAllCookies(); + + isReqClearCookies = false; + } + } + + Track new_track = listener.onUpdateTrack(sessionName); + + if (new_track != null && !new_track.equals(track)) { + SessionTrackAction last = track.getTrack().get(i); + int last_index = new_track.getTrack().lastIndexOf(last); + if (last_index == -1) { + throw new ParsingException("Error in resuming the track after edit"); + } + startindex = new_track.getTrack().indexOf(last) + 1; + break beforeloop; + } + } + } catch (InterruptedException interruptedException) { + System.out.printf("Session %s stopped, no checks necessary", sessionName); + } catch (ParsingException | NumberFormatException | ArrayIndexOutOfBoundsException e) { + System.err.println(e.getMessage()); + current_url = ""; + listener.onError(sessionName); + driver.close(); + return; + } catch (TimeoutException e) { + System.err.println(e.getMessage()); + current_url = ""; + listener.onExecuteDone(true, current_url, sessionName); + driver.close(); + return; + } catch (WebDriverException error) { + current_url = ""; + System.out.printf("ERROR SELENIUM WEBDRIVER (session %s) =>\n %s", sessionName, error); + listener.onExecuteDone(true, current_url, sessionName); + driver.close(); + return; + } + current_url = ""; + driver.close(); + listener.onExecuteDone(false, current_url, sessionName); + } +} diff --git a/tool/src/main/java/migt/ExecuteTrackListener.java b/tool/src/main/java/org/zaproxy/addon/migt/ExecuteTrackListener.java similarity index 56% rename from tool/src/main/java/migt/ExecuteTrackListener.java rename to tool/src/main/java/org/zaproxy/addon/migt/ExecuteTrackListener.java index f341947..840a258 100644 --- a/tool/src/main/java/migt/ExecuteTrackListener.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/ExecuteTrackListener.java @@ -1,34 +1,50 @@ -package migt; - -/** - * Listener for the ExectuteTrack Object +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. */ +package org.zaproxy.addon.migt; + +/** Listener for the ExectuteTrack Object */ public interface ExecuteTrackListener { /** * Called when the execution of the track is finished * - * @param errors true if errors are found during execution of the track + * @param errors true if errors are found during execution of the track * @param current_url the url which the track execution stopped * @param sessionName The name of the session which was linked to the execution */ void onExecuteDone(boolean errors, String current_url, String sessionName); /** - * Called when the execution of the track is finished, and you want to force - * the result of the test + * Called when the execution of the track is finished, and you want to force the result of the + * test * - * @param forceResult the final result the test will have, independently of - * correct or incorrect flow + * @param forceResult the final result the test will have, independently of correct or incorrect + * flow * @param sessionName The name of the session which was linked to the execution */ void onExecuteDone(boolean forceResult, String sessionName); /** - * Called when the Browser crashes or some error is present, it is different - * from the onExecuteDone having the error parameter set to true, this method - * is called when an error not related to the test, and that does not have to - * influence the result of the test is found. + * Called when the Browser crashes or some error is present, it is different from the + * onExecuteDone having the error parameter set to true, this method is called when an error not + * related to the test, and that does not have to influence the result of the test is found. */ void onError(String sessionName); @@ -57,21 +73,23 @@ public interface ExecuteTrackListener { Boolean onAskClearCookie(String sessionName); /** - * Called whether a new session action is executed. This is used to update the listener on the actions that are - * being executed + * Called whether a new session action is executed. This is used to update the listener on the + * actions that are being executed * - * @param last_action the last User action executed at this point - * @param last_open the last open User action executed to this point - * @param last_click the last click User action executed to this point - * @param last_url the last url User action executed to this point + * @param last_action the last User action executed at this point + * @param last_open the last open User action executed to this point + * @param last_click the last click User action executed to this point + * @param last_url the last url User action executed to this point * @param session_name the name of the session that is executing * @throws ParsingException If problems are encounter retrieving these parameters */ - void onNextSessionAction(SessionTrackAction last_action, - SessionTrackAction last_open, - SessionTrackAction last_click, - String last_url, - String session_name) throws ParsingException; + void onNextSessionAction( + SessionTrackAction last_action, + SessionTrackAction last_open, + SessionTrackAction last_click, + String last_url, + String session_name) + throws ParsingException; /** * With this method is possible to update the Session track during execution diff --git a/tool/src/main/java/org/zaproxy/addon/migt/GUIclass.java b/tool/src/main/java/org/zaproxy/addon/migt/GUIclass.java new file mode 100644 index 0000000..5767cdb --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/GUIclass.java @@ -0,0 +1,1871 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; + +import com.google.gson.Gson; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Point; +import java.awt.event.MouseEvent; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.JTable; +import javax.swing.JTextArea; +import javax.swing.ListSelectionModel; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableModel; +import org.json.JSONArray; +import org.json.JSONObject; +import org.parosproxy.paros.network.HttpMalformedHeaderException; + +/** + * Sets a JTextArea as an output of an OutputStream, used by the redirect of the stdout and stderr + */ +class CustomOutputStream extends OutputStream { + private final JTextArea textArea; + + public CustomOutputStream(JTextArea textArea) { + this.textArea = textArea; + } + + @Override + public void write(int b) { + // Redirect the byte written to the OutputStream to the JTextArea + retry: + try { + textArea.append(String.valueOf((char) b)); + textArea.setCaretPosition( + textArea.getDocument().getLength()); // Auto-scroll to the bottom + } catch (Error e) { + // for some reason write lock acquire is interrupted sometimes + break retry; // TODO: fix + } + } +} + +/** This class contains the GUI for the plugin, also a lot of functionality methods */ +public class GUIclass extends JSplitPane { + private static final long serialVersionUID = 1L; + private static DefaultTableModel resultTableModel; + private static DefaultTableModel testTableModel; + final transient Object waiting = new Object(); + final String LOG_FOLDER = "logs/"; + private final String[] foundTableColNames = { + "Op. num", "Message Type", "message section", "check/regex", "index", "result" + }; + private final String[] testSuiteColNames = { + "Test name", + "Description", + "References", + "Statement in Ref. to Test", + "Affected Entity", + "Mitigations", + "Result" + }; + private final transient Object[][] foundData = {}; + private final transient Object lock2 = new Object(); + public ArrayList interceptedMessages; + String SAVE_FILE_PATH = ""; + String RECORD_FILE_PATH = ""; + boolean FILTERING = true; + String MSG_DEF_PATH = "msg_def.json"; + String CONFIG_FILE_PATH = "config.json"; + // GUI + JTable resultTable; + JTable testTable; + JPanel trackContainer; + JPanel inputContainer; + JLabel lblTrack; + JLabel lblnextTestBrowser; + JLabel lblInfo; + JLabel lblOutput; + JLabel lbldriver; + JButton btnTestTrack; + JButton btnselectChrome; + JButton btnselectFirefox; + JButton btnExecuteSuite; + JButton btnSetRecording; + JButton btnLoadMessages; + JButton btnSetOffline; + JButton btnExecuteTrack; + JButton btnSaveToFile; + JButton btndriverSelector; + JTextArea txtScript; + JTextArea txtSearch; + JTextArea txtSessionConfig; + JTextArea txt_out_debug_tab; + JTextArea txt_err_debug_tab; + JFileChooser driverSelector; + JSplitPane splitPane; + ReqResPanel messageViewer; + JTabbedPane top_tabbed; + JTabbedPane bot_tabbed; + transient Map bot_tabs_index; + transient HTTPReqRes viewedMessage; + transient List sessions_names; + transient Map session_port; + transient Session defaultSession; + transient TestSuite testSuite; + boolean INTERCEPT_ENABLED; + boolean recording = false; + boolean OFFLINE = false; + boolean SAVE_TO_FILE = false; + transient Operation actual_operation; + transient ExecuteActives ex; + transient List messageTypes; + private Integer DEFAULT_PORT = 8080; + private String DRIVER_PATH = ""; + private transient List actives; + private transient Map sessions_text; + private transient List passives; + private transient Thread active_ex; + private boolean active_ex_finished = false; + + /** Constructor of the plugin UI */ + public GUIclass() { + super(JSplitPane.VERTICAL_SPLIT); + // initialize vars + init(); + + set_std_out_redirect(); + + this.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + + // Top part of the UI + // ------------------------------------------------------------------------------------------ + GridBagLayout top_layout = new GridBagLayout(); + top_layout.columnWidths = new int[] {230, 230, 230, 230, 100, 100, 100}; + top_layout.rowHeights = new int[] {20, 48, 48, 48, 48}; + top_layout.columnWeights = new double[] {1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0}; + top_layout.rowWeights = new double[] {0.0, 0.0, 0.0, 0.0, 0.0}; + + trackContainer = new JPanel(); + trackContainer.setLayout(top_layout); + + setup_tab_track(); + setup_tab_butons(); + + this.setTopComponent(trackContainer); + + // Bottom part + // ------------------------------------------------------------------------------------------------- + + GridBagLayout bot_layout = new GridBagLayout(); + bot_layout.columnWidths = new int[] {500, 500, 100}; + bot_layout.rowHeights = new int[] {15, 20, 20, 20, 30}; + bot_layout.columnWeights = new double[] {Double.MIN_VALUE, 0.0}; + bot_layout.rowWeights = new double[] {0.0, Double.MIN_VALUE, 0.0, 0.0, 0.0}; + + bot_tabbed = new JTabbedPane(); + + JPanel tab_input_json = setup_tab_input_json(bot_layout); + bot_tabs_index.put("Input JSON", 0); + bot_tabbed.addTab("Input JSON", tab_input_json); + + JScrollPane tab_suite_result = setup_tab_suite_result(bot_layout); + bot_tabs_index.put("Test Suite Result", 1); + bot_tabbed.addTab("Test Suite Result", tab_suite_result); + + JSplitPane tab_test_result = setup_tab_test_result(bot_layout); + bot_tabs_index.put("Test Result", 2); + bot_tabbed.addTab("Test Result", tab_test_result); + + JPanel tab_session_config = setup_tab_session_config(bot_layout); + bot_tabs_index.put("session config", 3); + bot_tabbed.addTab("session config", tab_session_config); + + JPanel tab_debug_panel = setub_tab_debug(bot_layout); + bot_tabs_index.put("debug tab", 4); + bot_tabbed.addTab("debug tab", tab_debug_panel); + + // Set Bottom Part + this.setBottomComponent(bot_tabbed); + + readMsgDefFile(); + readConfigFile(); + if (!DRIVER_PATH.equals("")) { + lbldriver.setText("Driver Selected"); + btndriverSelector.setBackground(Color.GREEN); + btnTestTrack.setEnabled(true); + } + } + + /** + * Function used to add an item to the resultTableModel. Contains the results of the tests + * + * @param data the string array containing the data, also a row + */ + private static void addItem(String[] data) { + resultTableModel.addRow(data); + } + + // BusinessLogic --> contiene creazione DebugTab + public void init() { + interceptedMessages = new ArrayList<>(); + testSuite = new TestSuite(); + passives = new ArrayList<>(); + actives = new ArrayList<>(); + sessions_names = new ArrayList<>(); + INTERCEPT_ENABLED = false; + sessions_text = new HashMap<>(); + messageTypes = new ArrayList<>(); + session_port = new HashMap<>(); + bot_tabs_index = new HashMap<>(); + txt_out_debug_tab = new JTextArea(); + txt_err_debug_tab = new JTextArea(); + } + + /** Set a redirect of the stdout and stderr to the txtboxes in the debug tab of the GUI */ + private void set_std_out_redirect() { + // Stdout out redirect + PrintStream printStream = new PrintStream(new CustomOutputStream(txt_out_debug_tab)); + System.setOut(printStream); + + // stderr out redirect + PrintStream printStream_err = new PrintStream(new CustomOutputStream(txt_err_debug_tab)); + System.setErr(printStream_err); + } + + /** + * BusinessLogic --> contiene operazioni GUI (stampa errori) Function used to read the message + * definition file + */ + private void readMsgDefFile() { + File msg_def_file = new File(MSG_DEF_PATH); + try { + if (msg_def_file.createNewFile()) { + // if file does not exist + FileWriter w = new FileWriter(MSG_DEF_PATH); + w.write(Tools.getDefaultJSONMsgType()); + w.close(); + } + + // read content + StringBuilder content = new StringBuilder(); + Scanner myReader = new Scanner(msg_def_file); + + while (myReader.hasNextLine()) { + content.append(myReader.nextLine()); + } + myReader.close(); + messageTypes = Tools.readMsgTypesFromJson(content.toString()); // load messsage types + + } catch (ParsingException e) { + lblOutput.setText("Invalid message type in message type definition file"); + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + lblOutput.setText("cannot create message definition file"); + } + } + + /** + * BusinessLogic --> contiene operazioni GUI (impostazione Tab di default) + * + *

Function used to read the json config file + */ + private void readConfigFile() { + File config_file = new File(CONFIG_FILE_PATH); + try { + if (!config_file.createNewFile()) { + StringBuilder content = new StringBuilder(); + Scanner myReader = new Scanner(config_file); + while (myReader.hasNextLine()) { + content.append(myReader.nextLine()); + } + myReader.close(); + + JSONObject obj = new JSONObject(content.toString()); + String last_driver_path = obj.getString("last_driver_path"); + String last_used_browser = obj.getString("last_browser_used"); + DEFAULT_PORT = obj.getInt("default_port"); + + if (!last_driver_path.isEmpty()) { + DRIVER_PATH = last_driver_path; + } + + switch (last_used_browser) { + case "firefox": + { + btnselectChrome.setEnabled(true); + btnselectFirefox.setEnabled(false); + break; + } + case "chrome": + { + btnselectChrome.setEnabled(false); + btnselectFirefox.setEnabled(true); + break; + } + } + } else { + FileWriter w = new FileWriter(CONFIG_FILE_PATH); + w.write(Tools.getDefaultJSONConfig()); + w.close(); + } + } catch (IOException e) { + lblOutput.setText("cannot create message definition file: " + e); + } + // TODO sistema, commentata temporaneamente perche dava errore + // catch (JSONException e) { + // lblOutput.setText("Invalid config file: " + e); + // } + } + + /** + * BusinessLogic --> contiene operazioni GUI (stampa errori) Function that edits the config + * file. + * + * @param key the key of the config to change + * @param value the new value of the config + */ + private void editConfigFile(String key, String value) { + File config_file = new File(CONFIG_FILE_PATH); + try { + if (config_file.createNewFile()) { + FileWriter w = new FileWriter(CONFIG_FILE_PATH); + w.write(Tools.getDefaultJSONConfig()); + w.close(); + } + + StringBuilder content = new StringBuilder(); + Scanner myReader = new Scanner(config_file); + while (myReader.hasNextLine()) { + content.append(myReader.nextLine()); + } + myReader.close(); + + JSONObject obj = new JSONObject(content.toString()); + obj.remove(key); + obj.put(key, value); + + FileWriter w = new FileWriter(CONFIG_FILE_PATH); + w.write(obj.toString()); + w.close(); + } catch (IOException e) { + lblOutput.setText("cannot create message definition file: " + e); + } + + // TODO sistema, commentata temporaneamente perche dava errore + // catch (JSONException e) { + // lblOutput.setText("Invalid config file: " + e); + // } + } + + /** + * BusinessLogic --> preleva json da GUI (modifiche GUI + stampa errori) + richiama le funzioni + * updateSessionTabs(); e updateTxtSessionConfig(); + * + *

This function parses the given jsonInput string of the language + * + * @param jsonInput the json input + */ + private void readJSONinput(String jsonInput) { + sessions_names.clear(); + txtSearch.setBorder(BorderFactory.createEmptyBorder()); + setJSONError(false, ""); + + try { + JSONObject obj = new JSONObject(jsonInput); + List tests = new ArrayList<>(); + + // Getting Test suite data + String suite_name = obj.getJSONObject("test suite").getString("name"); + String suite_description = obj.getJSONObject("test suite").getString("description"); + + if (obj.getJSONObject("test suite").has("filter messages")) { + FILTERING = obj.getJSONObject("test suite").getBoolean("filter messages"); + } + + // Array of Tests + probabilmente qua è dove devo fare le modifiche + JSONArray arrTests = obj.getJSONArray("tests"); + for (int i = 0; i < arrTests.length(); i++) { + JSONObject act_test = arrTests.getJSONObject(i).getJSONObject("test"); + + Test test = new Test(act_test, messageTypes); + tests.add(test); + + for (Session s : test.sessions) { + if (!sessions_names.contains(s.name)) { + sessions_names.add(s.name); + session_port.put( + s.name, DEFAULT_PORT.toString()); // set default port to session + } + } + } + updateSessionTabs(); + updateTxtSessionConfig(); + + this.testSuite = new TestSuite(suite_name, suite_description, tests); + lblInfo.setText("JSON read successfully, Test Suite Object has been created"); + + } catch (ParsingException e) { + e.printStackTrace(); + + setJSONError(true, "Problem in parsing JSON: " + e.getMessage()); + } catch (Exception e) { + e.printStackTrace(); + + setJSONError(true, "problem in reading json test suite"); + } + } + + /** + * BusinessLogic This function reads the selected file deserializing the messages and creating a + * new Session + * + * @return if the reading has been succesfull + */ + private boolean readSavedMessages() { + if (!SAVE_FILE_PATH.isEmpty()) { + try { + if (defaultSession == null) { + defaultSession = new Session("default"); + defaultSession.isOffline = true; + + File f = new File(SAVE_FILE_PATH); + Scanner r = new Scanner(f); + + Gson json = new Gson(); + while (r.hasNextLine()) { + HTTPReqRes tmp = json.fromJson(r.nextLine(), HTTPReqRes.class); + defaultSession.messages.add(tmp); + } + } else { + System.out.println( + "main session already created, skipping message reading from file"); + } + return true; + } catch (FileNotFoundException fileNotFoundException) { + fileNotFoundException.printStackTrace(); + return false; + } + } else { + return false; + } + } + + /** BusinessLogic --> update della GUI Method which executes the entire test suite */ + private void executeSuite() { + // clears all previously saved tests + actives.clear(); + passives.clear(); + actual_operation = null; + ex = null; + active_ex_finished = false; + + // clears the test suite result table + DefaultTableModel dm = (DefaultTableModel) resultTable.getModel(); + dm.getDataVector().removeAllElements(); + dm.fireTableDataChanged(); + + System.out.println("Number of test found: " + testSuite.getTests().size()); + for (Test t : testSuite.getTests()) { + if (t.isActive) { + actives.add(t); + } else { + passives.add(t); + } + } + + if (OFFLINE) { + if (!readSavedMessages()) { + System.err.println("Can't read message file"); + lblOutput.setText("Can't read message file"); + return; + } + } // TODO: re-enable OFFLINE mode + /* else if (passives.size() > 0 && defaultSession == null && actives.size() == 0) { + lblOutput.setText("Track need to be run for passive tests before executing tests"); + return; + } + */ + + if (actives.isEmpty()) { + synchronized (lock2) { + active_ex_finished = true; + } + } + + // Execute active tests + if (!actives.isEmpty()) { + try { + for (String key : session_port.keySet()) { + if (session_port.get(key).equals("")) { + lblOutput.setText("session port not configured"); + return; + } + } + ex = new ExecuteActives(actives, waiting); + + editConfigFile( + "last_browser_used", btnselectChrome.isEnabled() ? "firefox" : "chrome"); + + ex.registerExecuteActivesListener( + new ExecuteActiveListener() { + @Override + public void onExecuteStart() { + INTERCEPT_ENABLED = false; + actual_operation = new Operation(); + lblOutput.setText("Executing active tests"); + } + + @Override + public void onExecuteDone() { + if (passives.isEmpty()) { + update_gui_test_results(); + + lblOutput.setText( + "Done. Executed Passive Tests: " + + (passives.isEmpty() ? 0 : passives.size()) + + " - Active Tests: " + + (testSuite.getTests().size() + - (passives.isEmpty() + ? 0 + : passives.size()))); + } else { + lblOutput.setText("Executed Active tests, now doing passives"); + } + synchronized (lock2) { + active_ex_finished = true; + } + } + + @Override + public void onNewProcessOperation(Operation op) { + INTERCEPT_ENABLED = true; + actual_operation = op; + } + + @Override + public Operation onOperationDone() { + INTERCEPT_ENABLED = false; + Operation tmp = actual_operation; + + actual_operation = new Operation(); + return tmp; + } + + @Override + public Session onNewSession(Session s) { + Track track = null; + try { + track = s.setTrackFromString(getSessionTxt(s.name)); + } catch (ParsingException e) { + lblOutput.setText("error in parsing session track"); + return null; + } + String port = session_port.get(s.name); + s.port = port; + s.track = track; + + s.ex = + new ExecuteTrack( + false, + !btnselectChrome.isEnabled(), + DRIVER_PATH, + track, + port, + s.name); + return s; + } + + @Override + public void onNewTest(Test actual_test) { + actual_operation = null; + } + + public void onTestDone(Test actual_test) { + int indx = testSuite.tests.indexOf(actual_test); + if (indx != -1) { + System.out.printf( + "Saving test %s in test results", + actual_test.getName()); + testSuite.tests.set(indx, actual_test); + } + } + + @Override + public void onError(Test actual_test) { + System.err.println("Error executing the test:" + actual_test.name); + synchronized (lock2) { + active_ex_finished = true; + } + } + }); + + active_ex = new Thread(ex); + active_ex.start(); + + } catch (Exception er) { + er.printStackTrace(); + System.out.println(er.getLocalizedMessage() + "nad" + er.getMessage() + "2" + er); + + lblOutput.setText("PROBLEM IN Executing Suite, check it please"); + } + } + + // Execute passive tests + if (!passives.isEmpty()) { + // TODO: Add offline clause + /* + //this was alreary commented + + if (defaultSession.messages.size() == 0) { + lblOutput.setText("no message found"); + return; + } + */ + + ExecutePassiveListener listener = + new ExecutePassiveListener() { + @Override + public boolean onWaitToStart() { + // waits for active test execution to finish before doing passives + synchronized (lock2) { + return active_ex_finished; + } + } + + @Override + public void onExecuteStart() { + lblOutput.setText("Executing passive tests"); + } + + @Override + public void onExecuteDone(List passives_test) { + // Actives are already executed, because passives are started after + // actives + lblOutput.setText( + "Done. Executed Passive Tests: " + + (passives.isEmpty() ? 0 : passives.size()) + + " - Active Tests: " + + (testSuite.getTests().size() + - (passives.isEmpty() ? 0 : passives.size()))); + + passives = passives_test; + + update_gui_test_results(); + } + + @Override + public void onError(String msg) { + lblOutput.setText(msg); + } + + @Override + public Session onNewSession(Session s) throws ParsingException { + s.setTrackFromString(getSessionTxt(s.name)); + + String port = session_port.get(s.name); + s.port = port; + + s.ex = + new ExecuteTrack( + false, + !btnselectChrome.isEnabled(), + DRIVER_PATH, + s.track, + port, + s.name); + + return s; + } + + @Override + public void onBeforeExecuteTrack() { + // Clear previous interceptedMessages if any + interceptedMessages.clear(); + // Tell Burp Extender class to record the intercepted messages from now + // on + recording = true; + } + + @Override + public ArrayList onTrackExecuteDone() { + recording = false; + return interceptedMessages; + } + }; + + ExecutePassives expa = new ExecutePassives(passives, listener, messageTypes); + + new Thread(expa).start(); + } + } + + /** + * Function used to get the text of the text area of a certain session using the session name + * + * @param session_name the name of the session to get the text + * @return the content of the session tab if it is not empty, otherwise the main session text + * tab + */ + private String getSessionTxt(String session_name) { + if (sessions_text.containsKey(session_name)) { + JTextArea t = (JTextArea) sessions_text.get(session_name); + if (t.getText().equals("")) { + return txtScript.getText(); + } else { + return t.getText(); + } + } + return null; + } + + /** + * BusinessLogic? --> anche GUI Update the session config tab, using the session_port variable, + * at the same time reads if there are changes, and updates the session_port variable + */ + private void updateTxtSessionConfig() throws ParsingException { + setSession_configError(false, ""); + + String text = txtSessionConfig.getText(); + Pattern p = Pattern.compile("\\n"); + Matcher m = p.matcher(text); + text = m.replaceAll(""); + + if (text.equals("")) { + String tmp = ""; + for (String s : session_port.keySet()) { + tmp += s; + tmp += ":" + session_port.get(s) + ";\n"; + } + txtSessionConfig.setText(tmp); + return; + } + + String[] text_list = text.split(";"); + + for (String row : text_list) { + String[] splitted = row.trim().split(":"); + if (splitted.length == 0) continue; + if (splitted.length <= 1) { + String[] splitted2 = new String[] {"", ""}; + splitted2[0] = splitted[0]; + splitted = splitted2; + } + String port = splitted[1].trim(); + p = Pattern.compile("^\\d+$"); + m = p.matcher(splitted[1].trim()); + if (!m.find()) { + setSession_configError(true, "invalid port"); + + throw new ParsingException("Invalid port"); + } + if (session_port.containsKey(splitted[0].trim())) { + session_port.replace(splitted[0].trim(), splitted[1].trim()); + } else { + session_port.put((splitted[0]).trim(), splitted[1].trim()); + } + } + String tmp = ""; + for (String key : session_port.keySet()) { + tmp += key + ": " + session_port.get(key) + ";\n"; + } + + txtSessionConfig.setText(tmp); + } + + /** + * This function updates the session tabs in the gui to match the actual value in session_names + */ + private void updateSessionTabs() { + List present = new ArrayList<>(); + + for (int i = 1; i < top_tabbed.getTabCount(); i++) { + present.add(top_tabbed.getTitleAt(i)); + if (!sessions_names.contains(top_tabbed.getTitleAt(i))) { + top_tabbed.remove(i); + } + } + for (String name : sessions_names) { + if (!present.contains(name)) { + JTextArea tmp = new JTextArea(); + JScrollPane sp = + new JScrollPane( + tmp, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + sessions_text.put(name, tmp); + top_tabbed.add(name, sp); + } + } + JTextArea tmp = new JTextArea(); + JScrollPane sp = + new JScrollPane( + tmp, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + } + + /** + * Function used to set the JSON textbox with a red colour to highlight an error. + * + * @param isInError true to highlight an error, false to remove the highlight + * @param msg the error message to display + */ + private void setJSONError(boolean isInError, String msg) { + if (isInError) { + txtSearch.setBorder(BorderFactory.createLineBorder(Color.RED, 3)); + lblOutput.setText(msg); + bot_tabbed.setBackgroundAt(bot_tabs_index.get("Input JSON"), Color.RED); + } else { + txtSearch.setBorder(BorderFactory.createEmptyBorder()); + lblOutput.setText(""); + bot_tabbed.setBackgroundAt(bot_tabs_index.get("Input JSON"), Color.white); + } + } + + /** + * Function used to set the session config textbox with a red border to highlight an error. + * + * @param isInError true to highlight an error, false to remove highlight + * @param msg The error message to display + */ + private void setSession_configError(boolean isInError, String msg) { + if (isInError) { + txtSessionConfig.setBorder(BorderFactory.createLineBorder(Color.RED, 3)); + lblOutput.setText(msg); + bot_tabbed.setBackgroundAt(bot_tabs_index.get("session config"), Color.RED); + } else { + txtSessionConfig.setBorder(BorderFactory.createEmptyBorder()); + lblOutput.setText(""); + bot_tabbed.setBackgroundAt(bot_tabs_index.get("session config"), Color.white); + } + } + + /** Function used to update the gui test results after the tests are executed */ + private void update_gui_test_results() { + for (Test t : testSuite.getTests()) { + String esito = ""; + if (t.applicable) { + esito = t.success ? "passed" : "failed"; + } else { + esito = "not applicable"; + } + String[] tmp = + new String[] { + t.getName(), + t.getDescription(), + t.references, + t.violated_properties, + t.affected_entity, + t.mitigations, + esito + }; + System.out.println(t.getName() + " " + esito); + addItem(tmp); + } + + testSuite.log_test_suite(LOG_FOLDER); + + btnExecuteSuite.setEnabled(false); + } + + private void setup_tab_track() { + lblTrack = new JLabel("Session track "); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.insets = new Insets(10, 0, 0, 0); + gbc.gridx = 0; + gbc.gridy = 0; + trackContainer.add(lblTrack, gbc); + + txtScript = new JTextArea(); + gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.BOTH; + gbc.insets = new Insets(10, 0, 0, 10); + gbc.gridx = 0; + gbc.gridy = 1; + gbc.gridwidth = 4; + gbc.gridheight = 4; + + JScrollPane scrollPane1 = + new JScrollPane( + txtScript, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + + top_tabbed = new JTabbedPane(); + top_tabbed.add("main", scrollPane1); + trackContainer.add(top_tabbed, gbc); + } + + private void setup_tab_butons() { + driverSelector = new JFileChooser(); + btndriverSelector = new JButton("Select Driver"); + + btndriverSelector.addActionListener( + actionEvent -> { + int returnVal = driverSelector.showOpenDialog(GUIclass.this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = driverSelector.getSelectedFile(); + DRIVER_PATH = file.getPath(); + editConfigFile("last_driver_path", DRIVER_PATH); + lbldriver.setText("Driver Selected"); + btndriverSelector.setBackground(Color.GREEN); + btnTestTrack.setEnabled(true); + } else if ((returnVal == JFileChooser.ERROR) + || (returnVal == JFileChooser.ERROR_OPTION)) { + lbldriver.setText("Driver:error during file selection"); + System.out.println("error during file selection"); + btnTestTrack.setEnabled(false); + + btndriverSelector.setBackground(Color.RED); + } else { + lbldriver.setText("Driver file still not selected"); + btnTestTrack.setEnabled(false); + btndriverSelector.setBackground(Color.RED); + } + }); + + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(0, 0, 0, 10); + gbc.gridx = 4; + gbc.gridy = 1; + trackContainer.add(btndriverSelector, gbc); + + lbldriver = new JLabel("Driver file still not selected"); + gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.SOUTHEAST; + gbc.insets = new Insets(10, 0, 0, 30); + gbc.gridx = 3; + gbc.gridy = 0; + trackContainer.add(lbldriver, gbc); + + btnTestTrack = new JButton("Test track"); + btnTestTrack.setEnabled(true); + + btnTestTrack.addActionListener( + e -> { + ExecuteTrackListener listener = + new ExecuteTrackListener() { + @Override + public void onExecuteDone( + boolean errors, String current_url, String sessionName) { + if (errors) { + lblOutput.setText("Error in executing track"); + } else { + lblOutput.setText("Track Executed correctly"); + } + } + + @Override + public void onExecuteDone(boolean forceResult, String sessionName) { + if (forceResult) { + lblOutput.setText("Track Executed correctly"); + } else { + lblOutput.setText("Error in executing track"); + } + } + + @Override + public void onError(String sessionName) { + lblOutput.setText("Error in executing track"); + } + + @Override + public Boolean onAskPause(String sessionName) { + return false; + } + + @Override + public Boolean onAskStop(String sessionName) { + return false; + } + + @Override + public Boolean onAskClearCookie(String sessionName) { + return null; + } + + @Override + public void onNextSessionAction( + SessionTrackAction last_action, + SessionTrackAction last_open, + SessionTrackAction last_click, + String last_url, + String session_name) {} + + @Override + public Track onUpdateTrack(String sessionName) + throws ParsingException { + return null; + } + + @Override + public void onSetVar(Var v) {} + }; + recording = false; + defaultSession = new Session("temp"); + + Track track = null; + try { + track = defaultSession.setTrackFromString(txtScript.getText()); + } catch (ParsingException exc) { + lblOutput.setText("Error in parsing session track"); + } + + defaultSession = null; + ExecuteTrack ex = + new ExecuteTrack( + false, + !btnselectChrome.isEnabled(), + DRIVER_PATH, + track, + DEFAULT_PORT.toString(), + "test"); + ex.registerExecuteTrackListener(listener); + new Thread(ex).start(); + }); + + gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(0, 0, 0, 0); + gbc.gridx = 5; + gbc.gridy = 1; + btnTestTrack.setPreferredSize(new Dimension(100, 20)); + trackContainer.add(btnTestTrack, gbc); + + btnselectChrome = new JButton("Use Chrome"); + btnselectChrome.setEnabled(false); + btnselectChrome.addActionListener( + actionEvent -> { + btnTestTrack.setEnabled(false); + lbldriver.setText("Driver file still not selected"); + btndriverSelector.setBackground(Color.RED); + + btnselectChrome.setEnabled(false); + btnselectFirefox.setEnabled(true); + lblnextTestBrowser.setText("Chrome"); + }); + + lblnextTestBrowser = new JLabel("Firefox"); + gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(0, 0, 0, 0); + gbc.gridx = 4; + gbc.gridy = 2; + trackContainer.add(btnselectChrome, gbc); + + btnselectFirefox = new JButton("Use Firefox"); + btnselectFirefox.setEnabled(true); + btnselectFirefox.addActionListener( + actionEvent -> { + btnTestTrack.setEnabled(false); + lbldriver.setText("Driver file still not selected"); + btndriverSelector.setBackground(Color.RED); + + btnselectFirefox.setEnabled(false); + btnselectChrome.setEnabled(true); + lblnextTestBrowser.setText("Firefox"); + }); + + gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(0, 0, 0, 0); + gbc.gridx = 4; + gbc.gridy = 3; + btnselectFirefox.setPreferredSize(new Dimension(100, 20)); + trackContainer.add(btnselectFirefox, gbc); + + JFileChooser messageSaver = new JFileChooser(); + btnSetRecording = new JButton("Record"); + btnSetRecording.setEnabled(true); + + // Recording button listener + btnSetRecording.addActionListener( + actionEvent -> { + if (btnSetRecording.isEnabled()) { + int returnVal = messageSaver.showOpenDialog(GUIclass.this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = messageSaver.getSelectedFile(); + RECORD_FILE_PATH = file.getPath(); + lblOutput.setText("File selected"); + + SAVE_TO_FILE = true; + + defaultSession = new Session("default"); + try { + defaultSession.setTrackFromString(txtScript.getText()); + } catch (ParsingException e) { + lblOutput.setText("Error in parsing session track"); + return; + } + + btnSetRecording.setBackground(new Color(255, 0, 0)); + btnSetRecording.setText("recording.."); + + recording = true; + + ExecuteTrackListener listener = + new ExecuteTrackListener() { + @Override + public void onExecuteDone( + boolean errors, + String current_url, + String sessionName) { + recording = false; + + if (errors) { + lblOutput.setText( + "Errore nell'esecuzione della traccia"); + } + + lblOutput.setText("Track recorded"); + + if (SAVE_TO_FILE) { + FileWriter w; + try { + w = new FileWriter(RECORD_FILE_PATH); + + for (HTTPReqRes actual : + defaultSession.messages) { + Gson geson = new Gson(); + String serialized = geson.toJson(actual); + w.write(serialized + "\n"); + } + w.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + btnSetRecording.setBackground(Color.white); + btnSetRecording.setText("record"); + } + + @Override + public void onExecuteDone( + boolean forceResult, String sessionName) {} + + @Override + public void onError(String sessionName) { + lblOutput.setText( + "Errore nell'esecuzione della traccia"); + } + + @Override + public Boolean onAskPause(String sessionName) { + return false; + } + + @Override + public Boolean onAskStop(String sessionName) { + return false; + } + + @Override + public Boolean onAskClearCookie(String sessionName) { + return false; + } + + @Override + public void onNextSessionAction( + SessionTrackAction last_action, + SessionTrackAction last_open, + SessionTrackAction last_click, + String last_url, + String session_name) {} + + @Override + public Track onUpdateTrack(String sessionName) + throws ParsingException { + return null; + } + + @Override + public void onSetVar(Var v) {} + }; + + ExecuteTrack ex = + new ExecuteTrack( + false, + !btnselectChrome.isEnabled(), + DRIVER_PATH, + defaultSession.track, + defaultSession.port, + "main"); + ex.registerExecuteTrackListener(listener); + new Thread(ex).start(); + + } else if ((returnVal == JFileChooser.ERROR) + || (returnVal == JFileChooser.ERROR_OPTION)) { + lblOutput.setText("error in selecting output file"); + System.out.println("error in selecting output file"); + } else { + lbldriver.setText("Messages still not loaded"); + } + } + }); + + gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(0, 0, 0, 0); + gbc.gridx = 5; + gbc.gridy = 2; + btnSetRecording.setPreferredSize(new Dimension(100, 20)); + trackContainer.add(btnSetRecording, gbc); + + // Button Execute track + btnExecuteTrack = new JButton("execute track"); + btnExecuteTrack.addActionListener( + actionEvent -> { + defaultSession = new Session("default"); + try { + defaultSession.setTrackFromString(txtScript.getText()); + } catch (ParsingException e) { + lblOutput.setText("Error in parsing session track"); + return; + } + btnExecuteTrack.setText("executing.."); + interceptedMessages.clear(); + recording = true; + + ExecuteTrackListener listener = + new ExecuteTrackListener() { + @Override + public void onExecuteDone( + boolean errors, String current_url, String sessionName) { + recording = false; + + if (errors) { + lblOutput.setText("Errore nell'esecuzione della traccia"); + } + + lblOutput.setText("Track executed"); + + btnExecuteTrack.setText("execute track"); + } + + @Override + public void onExecuteDone( + boolean forceResult, String sessionName) {} + + @Override + public void onError(String sessionName) { + lblOutput.setText("Errore nell'esecuzione della traccia"); + } + + @Override + public Boolean onAskPause(String sessionName) { + return false; + } + + @Override + public Boolean onAskStop(String sessionName) { + return false; + } + + @Override + public Boolean onAskClearCookie(String sessionName) { + return null; + } + + @Override + public void onNextSessionAction( + SessionTrackAction last_action, + SessionTrackAction last_open, + SessionTrackAction last_click, + String last_url, + String session_name) {} + + @Override + public Track onUpdateTrack(String sessionName) + throws ParsingException { + return null; + } + + @Override + public void onSetVar(Var v) {} + }; + + editConfigFile( + "last_browser_used", + btnselectChrome.isEnabled() ? "firefox" : "chrome"); + + ExecuteTrack ex = + new ExecuteTrack( + false, + !btnselectChrome.isEnabled(), + DRIVER_PATH, + defaultSession.track, + defaultSession.port, + "main"); + ex.registerExecuteTrackListener(listener); + new Thread(ex).start(); + }); + + gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(0, 0, 0, 0); + gbc.gridx = 5; + gbc.gridy = 3; + btnSetRecording.setPreferredSize(new Dimension(100, 20)); + trackContainer.add(btnExecuteTrack, gbc); + + gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(0, 0, 0, 0); + gbc.gridx = 4; + gbc.gridy = 4; + trackContainer.add(new JLabel("Next test will use"), gbc); + + gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(20, 0, 0, 0); + gbc.gridx = 4; + gbc.gridy = 4; + trackContainer.add(lblnextTestBrowser, gbc); + + JFileChooser messageLoader = new JFileChooser(); + btnLoadMessages = new JButton("load messages"); + btnLoadMessages.setEnabled(true); + btnLoadMessages.addActionListener( + actionEvent -> { + if (btnLoadMessages.getBackground() == Color.GREEN) { + btnSetOffline.setEnabled(false); + btnSetOffline.setBackground(Color.white); + btnLoadMessages.setBackground(Color.white); + btnLoadMessages.setText("load messages"); + OFFLINE = false; + SAVE_FILE_PATH = ""; + + } else { + int returnVal = messageLoader.showOpenDialog(GUIclass.this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = messageLoader.getSelectedFile(); + SAVE_FILE_PATH = file.getPath(); + lblOutput.setText("Messages selected"); + btnLoadMessages.setBackground(Color.GREEN); + btnLoadMessages.setText("unload"); + btnSetOffline.setEnabled(true); + } else if ((returnVal == JFileChooser.ERROR) + || (returnVal == JFileChooser.ERROR_OPTION)) { + lblOutput.setText("error in selecting messages"); + System.out.println("error in selecting messages"); + btnSetOffline.setEnabled(false); + btnLoadMessages.setBackground(Color.RED); + } else { + lbldriver.setText("Messages still not loaded"); + btnSetOffline.setEnabled(false); + btnLoadMessages.setBackground(Color.RED); + } + } + }); + + gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(0, 0, 0, 0); + gbc.gridx = 6; + gbc.gridy = 1; + trackContainer.add(btnLoadMessages, gbc); + + btnSetOffline = new JButton("offline mode"); + btnSetOffline.setEnabled(false); + btnSetOffline.addActionListener( + actionEvent -> { + if (btnSetOffline.getBackground() == Color.green) { + OFFLINE = false; + btnSetOffline.setBackground(Color.white); + } else { + btnSetOffline.setBackground(Color.green); + OFFLINE = true; + } + }); + gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(0, 0, 0, 0); + gbc.gridx = 6; + gbc.gridy = 2; + trackContainer.add(btnSetOffline, gbc); + + btnSaveToFile = new JButton("save"); + btnSaveToFile.addActionListener( + actionEvent -> { + if (SAVE_TO_FILE) { + FileWriter w; + try { + w = new FileWriter(RECORD_FILE_PATH); + + for (HTTPReqRes actual : defaultSession.messages) { + Gson geson = new Gson(); + String serialized = geson.toJson(actual); + w.write(serialized + "\n"); + } + w.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + }); + gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(0, 0, 0, 0); + gbc.gridx = 6; + gbc.gridy = 3; + trackContainer.add(btnSaveToFile, gbc); + } + + private JPanel setup_tab_input_json(GridBagLayout bottom_layout) { + inputContainer = new JPanel(); + inputContainer.setLayout(bottom_layout); + + lblInfo = new JLabel(" "); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.insets = new Insets(10, 10, 10, 10); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.gridwidth = 2; + inputContainer.add(lblInfo, gbc); + + lblOutput = new JLabel(" "); + gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.insets = new Insets(10, 10, 10, 10); + gbc.gridx = 0; + gbc.gridy = 4; + gbc.gridwidth = 2; + inputContainer.add(lblOutput, gbc); + + txtSearch = new JTextArea(); + + JScrollPane scrollPane2 = + new JScrollPane( + txtSearch, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.BOTH; + gbc.insets = new Insets(10, 10, 10, 10); + gbc.gridx = 0; + gbc.gridy = 1; + gbc.gridwidth = 2; + gbc.gridheight = 3; + inputContainer.add(scrollPane2, gbc); + + JButton btnReadJSON = new JButton("Read JSON"); + gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(10, 10, 10, 10); + gbc.gridx = 2; + gbc.gridy = 2; + inputContainer.add(btnReadJSON, gbc); + + btnReadJSON.addActionListener( + e -> { + testSuite = new TestSuite(); + + readMsgDefFile(); // Updates the Message Definitions + readJSONinput(txtSearch.getText()); + + try { + updateTxtSessionConfig(); + } catch (ParsingException exc) { + setJSONError(true, "error in updating the session config"); + } + + lblOutput.setText("Number of Tests: " + testSuite.getTests().size()); + + if (testSuite.getTests().size() > 0) { + btnExecuteSuite.setEnabled(true); + } + }); + + JButton btnStop = new JButton("Stop"); + gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(10, 10, 10, 10); + gbc.gridx = 2; + gbc.gridy = 1; + inputContainer.add(btnStop, gbc); + + btnStop.addActionListener( + e -> { + if (active_ex != null) { + active_ex.interrupt(); + INTERCEPT_ENABLED = false; + active_ex.interrupt(); + } + }); + + btnExecuteSuite = new JButton("Execute Test Suite"); + btnExecuteSuite.setEnabled(false); + + btnExecuteSuite.addActionListener( + e -> { + executeSuite(); + }); + + gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.SOUTHWEST; + gbc.insets = new Insets(10, 10, 10, 10); + gbc.gridx = 2; + gbc.gridy = 3; + inputContainer.add(btnExecuteSuite, gbc); + return inputContainer; + } + + private JScrollPane setup_tab_suite_result(GridBagLayout bottom_layout) { + resultTableModel = + new DefaultTableModel(foundData, testSuiteColNames) { + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + }; + + resultTable = + new JTable(resultTableModel) { + // Implement table cell tool tips. + public String getToolTipText(MouseEvent e) { + String tip = null; + Point p = e.getPoint(); + int rowIndex = rowAtPoint(p); + int colIndex = columnAtPoint(p); + + try { + tip = getValueAt(rowIndex, colIndex).toString(); + } catch (RuntimeException e1) { + // catch null pointer exception if mouse is over an empty line + } + return tip; + } + }; + + resultTable.setDefaultRenderer( + Object.class, + new DefaultTableCellRenderer() { + @Override + public Component getTableCellRendererComponent( + JTable table, + Object value, + boolean isSelected, + boolean hasFocus, + int row, + int column) { + final Component c = + super.getTableCellRendererComponent( + table, value, isSelected, hasFocus, row, column); + if (value == null) return c; + if (value.equals("failed")) { + c.setBackground(Color.RED); + } else { + c.setBackground(Color.WHITE); + } + return c; + } + }); + + JScrollPane scrollPane = + new JScrollPane( + resultTable, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + + resultTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + + // Adds all the test result to the result table + resultTable + .getSelectionModel() + .addListSelectionListener( + event -> { + if (resultTable.getSelectedRow() > -1) { + + int row = resultTable.getSelectedRow(); + // BurpSuite.getTests.get(row).getTable(); + + DefaultTableModel dm = (DefaultTableModel) testTable.getModel(); + dm.getDataVector().removeAllElements(); + dm.fireTableDataChanged(); + + for (String[] act : testSuite.getTests().get(row).getRows()) { + + ((DefaultTableModel) testTable.getModel()).addRow(act); + } + } + }); + return scrollPane; + } + + private JSplitPane setup_tab_test_result(GridBagLayout bottom_layout) { + // Test Result Tab + testTableModel = + new DefaultTableModel(foundData, foundTableColNames) { + @Override + public boolean isCellEditable(int row, int column) { + return false; + } + }; + + testTable = new JTable(testTableModel); + // Add all the operations result to the table + testTable + .getSelectionModel() + .addListSelectionListener( + listSelectionEvent -> { + if (!listSelectionEvent.getValueIsAdjusting()) { + if (testTable.getSelectedRow() == -1) return; + int index = + Integer.parseInt( + (String) + testTable + .getModel() + .getValueAt( + testTable.getSelectedRow(), + 4)); + int op_index = + Integer.parseInt( + (String) + testTable + .getModel() + .getValueAt( + testTable.getSelectedRow(), + 0)); + + Operation op = + testSuite + .tests + .get(resultTable.getSelectedRow()) + .operations + .get(op_index); + + MessageType msg_type; + try { + msg_type = + MessageType.getFromList( + messageTypes, op.getMessageType()); + } catch (ParsingException e) { + throw new RuntimeException(e); + } + + for (HTTPReqRes m : op.matchedMessages) { + if (m.index == index) { + if (msg_type.msg_to_process_is_request) { + try { + messageViewer.setMessage(m, true); + } catch (HttpMalformedHeaderException e) { + throw new RuntimeException(e); + } + } else { + try { + messageViewer.setMessage(m, false); + } catch (HttpMalformedHeaderException e) { + throw new RuntimeException(e); + } + } + break; + } + } + } + }); + + JScrollPane scrollPane3 = + new JScrollPane( + testTable, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + + splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT); + + splitPane.setLeftComponent(scrollPane3); + + // controller = new IMessageEditorController() { + // @Override + // public IHttpService getHttpService() { + // return new IHttpService() { + // @Override + // public String getHost() { + // return null; + // } + // + // @Override + // public int getPort() { + // return 0; + // } + // + // @Override + // public String getProtocol() { + // return null; + // } + // }; + // } + // + // @Override + // public byte[] getRequest() { + // return viewedMessage.getRequest(); + // } + // + // @Override + // public byte[] getResponse() { + // return viewedMessage.getResponse(); + // } + // }; + return splitPane; + } + + private JPanel setup_tab_session_config(GridBagLayout bottom_layout) { + JPanel sessionConfig = new JPanel(); + sessionConfig.setLayout(bottom_layout); + + txtSessionConfig = new JTextArea(); + JScrollPane scrollPane5 = + new JScrollPane( + txtSessionConfig, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.BOTH; + gbc.insets = new Insets(10, 10, 10, 10); + gbc.gridx = 0; + gbc.gridy = 1; + gbc.gridwidth = 2; + gbc.gridheight = 3; + sessionConfig.add(scrollPane5, gbc); + + JButton btnSetSessionConfig = new JButton("save"); + btnSetSessionConfig.addActionListener( + actionEvent -> { + try { + updateTxtSessionConfig(); + } catch (ParsingException e) { + e.printStackTrace(); + } + }); + + gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.BOTH; + gbc.insets = new Insets(10, 10, 10, 10); + gbc.gridx = 2; + gbc.gridy = 3; + gbc.gridwidth = 1; + gbc.gridheight = 1; + sessionConfig.add(btnSetSessionConfig, gbc); + + return sessionConfig; + } + + private JPanel setub_tab_debug(GridBagLayout bottom_layout) { + // Debug Tab + JPanel debugPanel = new JPanel(); + + debugPanel.setLayout(bottom_layout); + + txt_out_debug_tab.setEditable(false); + JScrollPane scrollPane6 = + new JScrollPane( + txt_out_debug_tab, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + GridBagConstraints constraints1 = new GridBagConstraints(); + constraints1.gridx = 0; + constraints1.gridy = 1; + constraints1.gridwidth = 1; // One cell wide + constraints1.gridheight = 3; // One cell high + constraints1.fill = GridBagConstraints.BOTH; + constraints1.weightx = 0.5; // Half of the horizontal space + constraints1.weighty = 0.5; // Half of the vertical space + debugPanel.add(scrollPane6, constraints1); + + txt_err_debug_tab.setEditable(false); + JScrollPane scrollPane7 = + new JScrollPane( + txt_err_debug_tab, + JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, + JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); + GridBagConstraints constraints2 = new GridBagConstraints(); + constraints2.gridx = 1; + constraints2.gridy = 1; + constraints2.gridwidth = 1; // One cell wide + constraints2.gridheight = 3; // One cell high + constraints2.fill = GridBagConstraints.BOTH; + constraints2.weightx = 0.5; // Half of the horizontal space + constraints2.weighty = 0.5; // Half of the vertical space + debugPanel.add(scrollPane7, constraints2); + + JButton btn_clear = new JButton("clear"); + GridBagConstraints gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.insets = new Insets(10, 10, 10, 10); + gbc.gridx = 2; + gbc.gridy = 1; + debugPanel.add(btn_clear, gbc); + + btn_clear.addActionListener( + e -> { + txt_out_debug_tab.setText(""); + txt_err_debug_tab.setText(""); + }); + + JLabel lbl_left = new JLabel("Output log"); + gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.insets = new Insets(10, 10, 10, 10); + gbc.gridx = 0; + gbc.gridy = 0; + gbc.gridwidth = 2; + debugPanel.add(lbl_left, gbc); + + JLabel lbl_right = new JLabel("Error log"); + gbc = new GridBagConstraints(); + gbc.anchor = GridBagConstraints.WEST; + gbc.fill = GridBagConstraints.HORIZONTAL; + gbc.insets = new Insets(10, 10, 10, 10); + gbc.gridx = 1; + gbc.gridy = 0; + gbc.gridwidth = 2; + debugPanel.add(lbl_right, gbc); + + return debugPanel; + } +} diff --git a/tool/src/main/java/migt/HTTPReqRes.java b/tool/src/main/java/org/zaproxy/addon/migt/HTTPReqRes.java similarity index 67% rename from tool/src/main/java/migt/HTTPReqRes.java rename to tool/src/main/java/org/zaproxy/addon/migt/HTTPReqRes.java index f170777..ec5b0f7 100644 --- a/tool/src/main/java/migt/HTTPReqRes.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/HTTPReqRes.java @@ -1,16 +1,23 @@ -package migt; - -import burp.IExtensionHelpers; -import burp.IHttpRequestResponse; -import burp.IHttpRequestResponsePersisted; -import burp.IHttpService; -import org.apache.http.NameValuePair; -import org.apache.http.client.utils.URLEncodedUtils; -import org.apache.http.message.BasicNameValuePair; -import org.apache.http.message.ParserCursor; -import org.apache.http.message.TokenParser; -import org.apache.http.util.Args; -import org.apache.http.util.CharArrayBuffer; +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; import java.net.MalformedURLException; import java.net.URI; @@ -24,17 +31,31 @@ import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.apache.http.NameValuePair; +import org.apache.http.client.utils.URLEncodedUtils; +import org.apache.http.message.BasicNameValuePair; +import org.apache.http.message.ParserCursor; +import org.apache.http.message.TokenParser; +import org.apache.http.util.Args; +import org.apache.http.util.CharArrayBuffer; +import org.parosproxy.paros.db.DatabaseException; +import org.parosproxy.paros.model.HistoryReference; +import org.parosproxy.paros.network.HttpHeaderField; +import org.parosproxy.paros.network.HttpMalformedHeaderException; +import org.parosproxy.paros.network.HttpMessage; +import org.parosproxy.paros.network.HttpRequestHeader; /** - * Class which is intended to substitute the IHTTPRequestResponse one, because of serialization support + * Class which is intended to substitute the IHTTPRequestResponse one, because of + * serialization support */ public class HTTPReqRes implements Cloneable { - static public int instances; + public static int instances; public Integer index = -1; // index of the message wrt the burp proxy public boolean isRequest = false; public boolean isResponse = false; public int body_offset_req; // identifies the index where the body ends in the request - public int body_offset_resp; // the index where teh body of the response starts + public int body_offset_resp; // the index where the body of the response starts byte[] body_req = null; // the body of the request message byte[] body_resp = null; // the body of the response message // host data @@ -48,14 +69,51 @@ public class HTTPReqRes implements Cloneable { private List headers_req; // the headers of the request private List headers_resp; // the headers of the response + public String Res_header; + + public String Res_body; + + public String Req_header; + + public String Req_body; + + /** + * In OWASP ZAP there is no method that returns header + body at the same time this function + * concatenates the two elements, if desired we could replace it with one that after passing an + * HttpMessage the concatenation returns directly but I didn't want to alter too much the + * starting code + * + * @param Fheader header del messaggio + * @param Fbody body del messaggio + */ + private byte[] concat_Request(byte[] Fheader, byte[] Fbody) { + Req_header = new String(Fheader); + Req_body = new String(Fbody); + byte[] result = new byte[Fheader.length + Fbody.length]; + System.arraycopy(Fheader, 0, result, 0, Fheader.length); + System.arraycopy(Fbody, 0, result, Fheader.length, Fbody.length); + + return result; + } + + private byte[] concat_Response(byte[] Fheader, byte[] Fbody) { + Res_header = new String(Fheader); + Res_body = new String(Fbody); + byte[] result = new byte[Fheader.length + Fbody.length]; + System.arraycopy(Fheader, 0, result, 0, Fheader.length); + System.arraycopy(Fbody, 0, result, Fheader.length, Fbody.length); + + return result; + } + /** * Instantiate an HTTPReqRes element * - * @param request the request in byte[] form + * @param request the request in byte[] form * @param response the response in byte[] form */ public HTTPReqRes(byte[] request, byte[] response) { - //TODO: make method to work as with helpers + // TODO: make method to work as with helpers this.isRequest = true; this.isResponse = true; this.setRequest(request); @@ -66,67 +124,116 @@ public HTTPReqRes(byte[] request, byte[] response) { /** * Instantiate an HTTPReqRes element from a IHttpRequestResponsePersisted message * - * @param message the message to be created from - * @param helpers the helpers + *

--> now changed to create it from a HistoryReference + * + * @param hmsg the history reference to access the message */ - public HTTPReqRes(IHttpRequestResponsePersisted message, IExtensionHelpers helpers) { + public HTTPReqRes(HistoryReference hmsg) + throws MalformedURLException, HttpMalformedHeaderException, DatabaseException { + HttpMessage message = hmsg.getHttpMessage(); this.isRequest = true; this.isResponse = true; - this.setRequest(message.getRequest()); - this.setResponse(message.getResponse()); - this.setRequest_url(helpers.analyzeRequest(message).getUrl().toString()); - IHttpService service = message.getHttpService(); - this.setHost(service.getHost()); - this.setPort(service.getPort()); - this.setProtocol(service.getProtocol()); - this.body_offset_req = helpers.analyzeRequest(message.getRequest()).getBodyOffset(); - this.body_offset_resp = helpers.analyzeResponse(message.getResponse()).getBodyOffset(); - this.headers_req = helpers.analyzeRequest(message.getRequest()).getHeaders(); - this.headers_resp = helpers.analyzeResponse(message.getResponse()).getHeaders(); + + // --------------------------------------------------------------------------------------------// + this.setRequest( + concat_Request( + message.getRequestHeader().toString().getBytes(), + message.getRequestBody().getBytes())); + this.setResponse( + concat_Response( + message.getResponseHeader().toString().getBytes(), + message.getResponseBody().getBytes())); + + // Qua dovrei prendere un URL, più passaggi per convertire da java.net.URI a + // org.apache.commons.httpclient.URI + String readURI = message.getRequestHeader().getURI().toString(); + URL url = new URL(readURI); + this.setRequest_url(url.toString()); + + // utilizzo HttpRequestHeader perchè in teoria è solo per le richieste + HttpRequestHeader service = message.getRequestHeader(); + this.setHost(service.getHostName()); + this.setPort(service.getHostPort()); + if (service.isSecure()) { + this.setProtocol("https"); + } else { + this.setProtocol("http"); + } + + // Verificare che sia ciò che voglio + this.body_offset_req = message.getRequestHeader().toString().length(); + this.body_offset_resp = message.getResponseHeader().toString().length(); + + // da ottimizzare ma per il momento ok + + this.headers_req = toStringList(message.getRequestHeader().getHeaders()); + this.headers_resp = toStringList(message.getResponseHeader().getHeaders()); instances++; } + /** Converts a List of HttpHeaderField to a List of String since the Burp code used that one */ + private List toStringList(List lista) { + String buf; + List toReturn = new ArrayList<>(); + for (HttpHeaderField item : lista) { + buf = item.toString(); + toReturn.add(buf); + } + return toReturn; + } + /** * Instantiate an HTTPReqRes element. If a message is a request it does not gather the response * - * @param message an IHTTPRequestResponse message - * @param helpers an istance of the IExtensionHelpers + * @param message an IHTTPRequestResponse message * @param isRequest true if the message is a request, false otherwise + * @param index indice */ - public HTTPReqRes(IHttpRequestResponse message, IExtensionHelpers helpers, boolean isRequest, int index) { + public HTTPReqRes(HttpMessage message, boolean isRequest, int index) { if (!isRequest) { this.isResponse = true; - this.setResponse(message.getResponse()); - this.headers_resp = helpers.analyzeResponse(message.getResponse()).getHeaders(); - this.body_offset_resp = helpers.analyzeRequest(message.getResponse()).getBodyOffset(); + this.setResponse( + concat_Response( + message.getResponseHeader().toString().getBytes(), + message.getResponseBody().getBytes())); + this.headers_resp = toStringList(message.getResponseHeader().getHeaders()); + this.body_offset_resp = message.getResponseHeader().toString().length(); } this.index = index; - // the request is always present in a IHTTPRequestResponse + // TODO the request is always present in a IHTTPRequestResponse this.isRequest = true; - this.setRequest(message.getRequest()); - this.setRequest_url(helpers.analyzeRequest(message).getUrl().toString()); - this.headers_req = helpers.analyzeRequest(message.getRequest()).getHeaders(); - this.request_url = helpers.analyzeRequest(message).getUrl().toString(); - this.body_offset_req = helpers.analyzeRequest(message.getRequest()).getBodyOffset(); - - // set host info - IHttpService service = message.getHttpService(); - this.setHost(service.getHost()); - this.setPort(service.getPort()); - this.setProtocol(service.getProtocol()); + this.setRequest( + concat_Request( + message.getRequestHeader().toString().getBytes(), + message.getRequestBody().getBytes())); + this.setRequest_url(message.getRequestHeader().getURI().toString()); + + this.headers_req = toStringList(message.getRequestHeader().getHeaders()); + this.request_url = message.getRequestHeader().getURI().toString(); + this.body_offset_req = message.getRequestHeader().toString().length(); + + // set host infogetHttpService + HttpRequestHeader service = message.getRequestHeader(); + this.setHost(service.getHostName()); + this.setPort(service.getHostPort()); + if (service.isSecure()) { + this.setProtocol("https"); + } else { + this.setProtocol("http"); + } instances++; } /** - * Function taken from URLEncodedUtils and edited - * Returns a list of {@link NameValuePair}s parameters. + * Function taken from URLEncodedUtils and edited Returns a list of {@link NameValuePair}s + * parameters. * - * @param buf text to parse. - * @param charset Encoding to use when decoding the parameters. + * @param buf text to parse. + * @param charset Encoding to use when decoding the parameters. * @param separators element separators. * @return a list of {@link NameValuePair} as built from the URI's query portion. * @since 4.4 @@ -157,54 +264,68 @@ public static List parse_url_query_no_decoding( } } if (!name.isEmpty()) { - list.add(new BasicNameValuePair( - name, - value)); + list.add(new BasicNameValuePair(name, value)); } } return list; } /** - * Function used to replace an IHttpRequestResponse message with the values contained in this object + * Function used to replace an IHttpRequestResponse message with the values contained in this + * object * - * @param message the message to be replaced - * @param helpers the burp helpers - * @return the edited message with the request and/or response replaced + *

// @param message the message to be replaced // @param helpers the burp helpers // @return + * the edited message with the request and/or response replaced */ - public IHttpRequestResponse replaceBurpMessage(IHttpRequestResponse message, IExtensionHelpers helpers) { + + /* + public HttpMessage replaceBurpMessage(HttpMessage message) throws HttpMalformedHeaderException, URIException { if (isRequest) { - message.setRequest(request); + message.setRequestHeader(Req_header); + message.setRequestBody(Req_body); + } if (isResponse) { - message.setResponse(response); + message.setResponseHeader(Res_header); + message.setResponseBody(Res_body); } if (host != null && port != 0 && protocol != null) { - message.setHttpService( - this.getHttpService(helpers) + if (protocol == "https"){ + message.getRequestHeader().setSecure(true); + } else { + message.getRequestHeader().setSecure(false); + } + + //TODO: verificare che le modiche all'URI in questo modo vadano bene + org.apache.commons.httpclient.URI origialURI = new org.apache.commons.httpclient.URI(request_url, true); + + org.apache.commons.httpclient.URI newURI = new org.apache.commons.httpclient.URI( + origialURI.getScheme(), + null, + host, + port, + origialURI.getPath(), + origialURI.getQuery(), + origialURI.getFragment() ); + + //imposto i valori di host e port cambiando l'URI + message.getRequestHeader().setURI(newURI); + + } return message; } - - public IHttpService getHttpService(IExtensionHelpers helpers) { - return helpers.buildHttpService( - host, - port, - protocol - ); - } + */ public String getUrlHeader() { - if (!isRequest) - throw new RuntimeException("called getUrlHeader on a response message"); + if (!isRequest) throw new RuntimeException("called getUrlHeader on a response message"); return this.headers_req.get(0); } public void setUrlHeader(String url_header) { - if (!isRequest) - throw new RuntimeException("called setUrlHeader on a response message"); + if (!isRequest) throw new RuntimeException("called setUrlHeader on a response message"); this.headers_req.set(0, url_header); } @@ -223,21 +344,26 @@ public boolean hasBody(boolean isRequest) { } public byte[] getBody(boolean isRequest) { - if (isRequest && (!this.hasBody(isRequest) | this.request == null | this.request.length == 0)) { + if (isRequest + && (!this.hasBody(isRequest) | this.request == null | this.request.length == 0)) { throw new RuntimeException("called getBody, but class is not properly initialized"); } - if (!isRequest && (!this.hasBody(isRequest) | this.response == null | this.response.length == 0)) { + if (!isRequest + && (!this.hasBody(isRequest) | this.response == null | this.response.length == 0)) { throw new RuntimeException("called getBody, but class is not properly initialized"); } if (isRequest) { // if asking for the first time, take the body from the message if (this.body_req == null) - this.body_req = Arrays.copyOfRange(this.request, this.body_offset_req, this.request.length); + this.body_req = + Arrays.copyOfRange(this.request, this.body_offset_req, this.request.length); return this.body_req; } else { if (this.body_resp == null) - this.body_resp = Arrays.copyOfRange(this.response, this.body_offset_resp, this.response.length); + this.body_resp = + Arrays.copyOfRange( + this.response, this.body_offset_resp, this.response.length); return this.body_resp; } } @@ -250,7 +376,6 @@ public List getHeaders(boolean isRequest) { * Builds the message taking the headers and the body, without using the burp's helpers. * * @param isRequest true if message is a request message - * @return */ public byte[] build_message(boolean isRequest) { String builded = ""; @@ -264,7 +389,7 @@ public byte[] build_message(boolean isRequest) { if (header.contains("Content-Length:")) { content_hearder_found = true; if (body.length == 0) - continue;// if Content-Length header found, but message has no body, remove. + continue; // if Content-Length header found, but message has no body, remove. builded += content_header; } else { builded += header; @@ -278,8 +403,7 @@ public byte[] build_message(boolean isRequest) { builded += "\r\n"; // last row of header before body - if (body.length != 0) - builded += new String(body); + if (body.length != 0) builded += new String(body); return builded.getBytes(StandardCharsets.UTF_8); } @@ -338,11 +462,7 @@ public void setBody(boolean isRequest, String body) { setBody(isRequest, body.getBytes()); } - /** - * Get the original un-edited request message - * - * @return - */ + /** Get the original un-edited request message */ public byte[] getRequest() { return request; } @@ -351,11 +471,7 @@ public void setRequest(byte[] request) { this.request = request; } - /** - * Get the original un-edited response message - * - * @return - */ + /** Get the original un-edited response message */ public byte[] getResponse() { return response; } @@ -453,8 +569,9 @@ public String getUrlParam(String param) { /** * Get the given parameter value from the url of the request messsage. * - * @param disable_url_encode Set to true to get the value of the parameter without URL decoding it - * @param param the parameter name to be searched + * @param disable_url_encode Set to true to get the value of the parameter without URL decoding + * it + * @param param the parameter name to be searched * @return the value of the parameter */ public String getUrlParam(String param, boolean disable_url_encode) { @@ -499,7 +616,7 @@ public String getUrlRegex(String regex) { /** * Edit this message's URL with a regex, everything matched will be replaced by new_value * - * @param regex the regex to execute + * @param regex the regex to execute * @param new_value the value to substitute to matched content */ public void editUrlRegex(String regex, String new_value) { @@ -524,9 +641,7 @@ public void editUrlParam(String param, String value) throws ParsingException { List params = new ArrayList<>(); try { - params = URLEncodedUtils.parse( - new URI(request_url), StandardCharsets.UTF_8 - ); + params = URLEncodedUtils.parse(new URI(request_url), StandardCharsets.UTF_8); } catch (URISyntaxException e) { throw new RuntimeException(e); } @@ -555,9 +670,10 @@ public void editUrlParam(String param, String value) throws ParsingException { throw new RuntimeException(e); } - request_url = request_url.replaceAll( - "\\Q" + java.util.regex.Matcher.quoteReplacement(url.getQuery()) + "\\E", - new_query); + request_url = + request_url.replaceAll( + "\\Q" + java.util.regex.Matcher.quoteReplacement(url.getQuery()) + "\\E", + new_query); updateHeadersWHurl(); } @@ -575,9 +691,7 @@ public void removeUrlParam(String name) throws ParsingException { List params = new ArrayList<>(); try { - params = URLEncodedUtils.parse( - new URI(request_url), StandardCharsets.UTF_8 - ); + params = URLEncodedUtils.parse(new URI(request_url), StandardCharsets.UTF_8); } catch (URISyntaxException e) { throw new RuntimeException(e); } @@ -606,17 +720,19 @@ public void removeUrlParam(String name) throws ParsingException { throw new RuntimeException(e); } - request_url = request_url.replaceAll( - "\\Q" + java.util.regex.Matcher.quoteReplacement(url.getQuery()) + "\\E", - new_query); + request_url = + request_url.replaceAll( + "\\Q" + java.util.regex.Matcher.quoteReplacement(url.getQuery()) + "\\E", + new_query); updateHeadersWHurl(); } /** - * Adds an url query parameter to the request url. If parameter already present, concatenate new value to old. + * Adds an url query parameter to the request url. If parameter already present, concatenate new + * value to old. * - * @param name the name of the new parameter + * @param name the name of the new parameter * @param value the value of the new parameter */ public void addUrlParam(String name, String value) { @@ -627,9 +743,7 @@ public void addUrlParam(String name, String value) { List params = new ArrayList<>(); try { - params = URLEncodedUtils.parse( - new URI(request_url), StandardCharsets.UTF_8 - ); + params = URLEncodedUtils.parse(new URI(request_url), StandardCharsets.UTF_8); } catch (URISyntaxException e) { throw new RuntimeException(e); } @@ -661,9 +775,10 @@ public void addUrlParam(String name, String value) { throw new RuntimeException(e); } - request_url = request_url.replaceAll( - "\\Q" + java.util.regex.Matcher.quoteReplacement(url.getQuery()) + "\\E", - new_query); + request_url = + request_url.replaceAll( + "\\Q" + java.util.regex.Matcher.quoteReplacement(url.getQuery()) + "\\E", + new_query); updateHeadersWHurl(); } @@ -672,7 +787,7 @@ public void addUrlParam(String name, String value) { * Get the given parameter value from the head * * @param isRequest if the message is a request - * @param param the parameter name to be searched + * @param param the parameter name to be searched * @return the value of the parameter */ public String getHeadParam(boolean isRequest, String param) { @@ -711,7 +826,7 @@ public String getHeadRegex(boolean isRequest, String regex) { * Edits the Header of the given message * * @param isRequest select the request or teh response - * @param param the name of the header + * @param param the name of the header * @param new_value the new value */ public void editHeadParam(boolean isRequest, String param, String new_value) { @@ -737,7 +852,7 @@ public void editHeadParam(boolean isRequest, String param, String new_value) { * Edit the header of the message with a regex * * @param isRequest select the request or response message - * @param regex the regex to execute + * @param regex the regex to execute * @param new_value the new value to substitute */ public void editHeadRegex(boolean isRequest, String regex, String new_value) { @@ -745,20 +860,22 @@ public void editHeadRegex(boolean isRequest, String regex, String new_value) { throw new RuntimeException("tried to edit headers of response not yet received"); } - getHeaders(isRequest).replaceAll(header -> { - Pattern p = Pattern.compile(regex); - Matcher m = p.matcher(header); - header = m.replaceAll(new_value); - return header; - }); + getHeaders(isRequest) + .replaceAll( + header -> { + Pattern p = Pattern.compile(regex); + Matcher m = p.matcher(header); + header = m.replaceAll(new_value); + return header; + }); } /** * Adds a Header to the given message * * @param isRequest if the message to edit is the request or the response - * @param name the name of the new header - * @param value the value of the new header + * @param name the name of the new header + * @param value the value of the new header */ public void addHeadParameter(boolean isRequest, String name, String value) { List headers = isRequest ? this.headers_req : this.headers_resp; @@ -787,7 +904,7 @@ public void addHeadParameter(boolean isRequest, String name, String value) { * Removes the header from the given message * * @param isRequest select the request or the response - * @param name the name of the header + * @param name the name of the header */ public void removeHeadParameter(boolean isRequest, String name) { List headers = isRequest ? this.headers_req : this.headers_resp; @@ -807,11 +924,12 @@ public void removeHeadParameter(boolean isRequest, String name) { } /** - * Given a message, get the given parameter value from the body, note that it accepts a regular expression, and - * everything matched will be returned as a value + * Given a message, get the given parameter value from the body, note that it accepts a regular + * expression, and everything matched will be returned as a value * * @param isRequest if the message is a request - * @param regex the parameter to be searched as a regex, everything matched by this will be returned as a value + * @param regex the parameter to be searched as a regex, everything matched by this will be + * returned as a value * @return the value of the parameter */ public String getBodyRegex(boolean isRequest, String regex) { @@ -830,7 +948,7 @@ public String getBodyRegex(boolean isRequest, String regex) { * Edit the body of the message. Replaces the matched content of the regex with the new value. * * @param isRequest select the request or response message - * @param regex the regex to execute + * @param regex the regex to execute * @param new_value the new value to substitute */ public void editBodyRegex(boolean isRequest, String regex, String new_value) { @@ -842,7 +960,8 @@ public void editBodyRegex(boolean isRequest, String regex, String new_value) { } /** - * Append to the body of the message the given value. If the message doesn't have a body it creates it. + * Append to the body of the message the given value. If the message doesn't have a body it + * creates it. * * @param isRequest select the request or response message * @param new_value the value to append @@ -863,9 +982,7 @@ public void addBody(boolean isRequest, String new_value) { } } - /** - * Updates the headers in this request message with the acctual url value - */ + /** Updates the headers in this request message with the acctual url value */ public void updateHeadersWHurl() throws RuntimeException { if (!isRequest || request_url == null) { throw new RuntimeException("Trying to access the url of a response message"); @@ -889,9 +1006,9 @@ public void updateHeadersWHurl() throws RuntimeException { new_header_0 += "?" + url.getQuery(); } - //if (url.getRef() != null) { + // if (url.getRef() != null) { // new_header_0 += "#" + url.getRef(); - //} + // } new_header_0 += " " + url.getProtocol().toUpperCase(); new_header_0 += "/" + header_0[2].split("/")[1]; @@ -915,7 +1032,7 @@ public void updateHeadersWHurl() throws RuntimeException { /** * Function to check if the given message matches a message_type * - * @param msg_type the message type to check against it + * @param msg_type the message type to check against it * @param is_request tells whether the message you are checking is a request or a response * @return true or false, if matched or not respectively */ @@ -927,30 +1044,29 @@ public boolean matches_msg_type(MessageType msg_type, boolean is_request) { */ if (msg_type.getByResponse) { if (!isResponse) return false; // both request and response have to be present - matchedMessage = Tools.executeChecks( - msg_type.checks, - this, - true, - new ArrayList<>() // TODO: fix - ); + matchedMessage = + Tools.executeChecks( + msg_type.checks, this, true, new ArrayList<>() // TODO: fix + ); } else if (msg_type.getByRequest) { if (!isResponse) return false; // both request and response have to be present - matchedMessage = Tools.executeChecks( - msg_type.checks, - this, - false, - new ArrayList<>() // TODO: fix - ); + matchedMessage = + Tools.executeChecks( + msg_type.checks, this, false, new ArrayList<>() // TODO: fix + ); } else { - // this check is done to avoid matching request messages when intercepting a response + // this check is done to avoid matching request messages when intercepting a + // response if (is_request != msg_type.msg_to_process_is_request) return false; - if (!msg_type.isRequest && !isResponse) return false; // this message is not containing a response - matchedMessage = Tools.executeChecks( - msg_type.checks, - this, - msg_type.isRequest, - new ArrayList<>() // TODO: fix - ); + if (!msg_type.isRequest && !isResponse) + return false; // this message is not containing a response + matchedMessage = + Tools.executeChecks( + msg_type.checks, + this, + msg_type.isRequest, + new ArrayList<>() // TODO: fix + ); } } catch (Exception e) { e.printStackTrace(); @@ -962,16 +1078,13 @@ public boolean matches_msg_type(MessageType msg_type, boolean is_request) { * Returns a string representation of all the headers of the message * * @param isRequest select the request or the response - * @return */ public String getHeadersString(boolean isRequest) { List headers_string = getHeaders(isRequest); return String.join("\r\n", headers_string); } - /** - * An enum representing the possible message sections - */ + /** An enum representing the possible message sections */ public enum MessageSection { HEAD, BODY, @@ -979,11 +1092,13 @@ public enum MessageSection { RAW; /** - * Function that given a message section in form of String, returns the corresponding MessageSection enum value + * Function that given a message section in form of String, returns the corresponding + * MessageSection enum value * * @param input the input string * @return the MessageSection enum value - * @throws ParsingException if the input does not correspond to any of the possible messagesections + * @throws ParsingException if the input does not correspond to any of the possible + * messagesections */ public static MessageSection fromString(String input) throws ParsingException { if (input != null) { diff --git a/tool/src/main/java/migt/JWT.java b/tool/src/main/java/org/zaproxy/addon/migt/JWT.java similarity index 77% rename from tool/src/main/java/migt/JWT.java rename to tool/src/main/java/org/zaproxy/addon/migt/JWT.java index f72739a..111a732 100644 --- a/tool/src/main/java/migt/JWT.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/JWT.java @@ -1,21 +1,49 @@ -package migt; - -import com.nimbusds.jose.*; -import com.nimbusds.jose.crypto.*; +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; + +import static org.zaproxy.addon.migt.Tools.check_json_strings_equals; + +import com.nimbusds.jose.Header; +import com.nimbusds.jose.JOSEException; +import com.nimbusds.jose.JOSEObject; +import com.nimbusds.jose.JWEHeader; +import com.nimbusds.jose.JWEObject; +import com.nimbusds.jose.JWSHeader; +import com.nimbusds.jose.JWSObject; +import com.nimbusds.jose.JWSSigner; +import com.nimbusds.jose.JWSVerifier; +import com.nimbusds.jose.Payload; +import com.nimbusds.jose.crypto.ECDHDecrypter; +import com.nimbusds.jose.crypto.ECDHEncrypter; +import com.nimbusds.jose.crypto.RSADecrypter; +import com.nimbusds.jose.crypto.RSAEncrypter; +import com.nimbusds.jose.crypto.RSASSASigner; +import com.nimbusds.jose.crypto.RSASSAVerifier; import com.nimbusds.jose.jwk.JWK; import com.nimbusds.jwt.SignedJWT; -import org.json.JSONException; - import java.text.ParseException; import java.util.Base64; +import org.json.JSONException; -import static migt.Tools.check_json_strings_equals; - - -/** - * Class to manage JWT tokens - * Uses https://connect2id.com/products/nimbus-jose-jwt - */ +/** Class to manage JWT tokens Uses https://connect2id.com/products/nimbus-jose-jwt */ public class JWT { public String header; public String header_original; @@ -33,9 +61,7 @@ public class JWT { EncryptingAlg e_alg; SigningAlgs signing_alg; - /** - * Constructor that instantiate a JWT object - */ + /** Constructor that instantiate a JWT object */ public JWT() { raw = ""; signature = ""; @@ -52,7 +78,8 @@ public JWT() { } /** - * Parse a JWT token from a string and stores header, payload, and signature string values inside the JWT object + * Parse a JWT token from a string and stores header, payload, and signature string values + * inside the JWT object * * @param raw_jwt the raw JWT in string format * @throws ParsingException if there are problems in the parsing @@ -90,7 +117,7 @@ public void parse(String raw_jwt) throws ParsingException { try { if (!decrypt) // otherwise it is already parsed - parsed_jwt = SignedJWT.parse(raw_jwt); + parsed_jwt = SignedJWT.parse(raw_jwt); if (parsed_jwt instanceof JWSObject) { Header header = parsed_jwt.getHeader(); signing_alg = SigningAlgs.fromString(header.getAlgorithm().getName()); @@ -103,8 +130,10 @@ public void parse(String raw_jwt) throws ParsingException { header = parsed_jwt.getHeader().toString(); header_original = parsed_jwt.getParsedParts()[0].toString(); payload = parsed_jwt.getPayload().toString(); - signature = parsed_jwt instanceof JWSObject ? - ((JWSObject) parsed_jwt).getSignature().toString() : null; + signature = + parsed_jwt instanceof JWSObject + ? ((JWSObject) parsed_jwt).getSignature().toString() + : null; } catch (JSONException e) { throw new ParsingException("Error parsing JWT tokens"); } @@ -166,7 +195,8 @@ public String build() throws ParsingException { if (this.header.equals("") || this.payload.equals("")) throw new ParsingException("error in building jwt, empty values"); - if (check_json_strings_equals(header, new String(Base64.getDecoder().decode(header_original)))) { + if (check_json_strings_equals( + header, new String(Base64.getDecoder().decode(header_original)))) { res += header_original; } else { res += Base64.getEncoder().encodeToString(header.getBytes()); @@ -224,26 +254,28 @@ public String build() throws ParsingException { if (public_key_pem_enc.length() != 0) { // if the JWE has been decrypted, now it needs to be re-encrypted try { - JWEObject editedJWE = new JWEObject( - JWEHeader.parse(header), - new Payload(payload) - ); + JWEObject editedJWE = + new JWEObject(JWEHeader.parse(header), new Payload(payload)); switch (e_alg) { case RSA_OAEP: case RSA_OAEP_256: editedJWE.encrypt( - new RSAEncrypter(JWK.parseFromPEMEncodedObjects(public_key_pem_enc).toRSAKey())); + new RSAEncrypter( + JWK.parseFromPEMEncodedObjects(public_key_pem_enc) + .toRSAKey())); break; case ECDH_ES_A128KW: case ECDH_ES_A256KW: editedJWE.encrypt( - new ECDHEncrypter(JWK.parseFromPEMEncodedObjects(public_key_pem_enc).toECKey())); + new ECDHEncrypter( + JWK.parseFromPEMEncodedObjects(public_key_pem_enc) + .toECKey())); break; } res = editedJWE.serialize(); - } catch (JOSEException | java.text.ParseException e) { + } catch (JOSEException | ParseException e) { throw new ParsingException("Unable to encrypt JWE " + e); } } else { @@ -256,9 +288,7 @@ public String build() throws ParsingException { return res; } - /** - * All JWS signing algs supported - */ + /** All JWS signing algs supported */ public enum SigningAlgs { RS256, RS512; @@ -275,9 +305,7 @@ public static SigningAlgs fromString(String algStr) throws ParsingException { } } - /** - * All JWE encrypting algs supported - */ + /** All JWE encrypting algs supported */ public enum EncryptingAlg { RSA_OAEP, RSA_OAEP_256, diff --git a/tool/src/main/java/org/zaproxy/addon/migt/Marker.java b/tool/src/main/java/org/zaproxy/addon/migt/Marker.java new file mode 100644 index 0000000..1ff86f6 --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/Marker.java @@ -0,0 +1,50 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; + +import java.util.Objects; + +/** Class used to mark User Actions to be managed by session actions */ +public class Marker { + String name; + + public int hashCode() { + return Objects.hash(name); + } + + /** + * Constructor to instantiate a new marker object + * + * @param _name name of the marker + */ + public Marker(String _name) { + name = _name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Marker marker = (Marker) o; + + return Objects.equals(name, marker.name); + } +} diff --git a/tool/src/main/java/org/zaproxy/addon/migt/MessageOperation.java b/tool/src/main/java/org/zaproxy/addon/migt/MessageOperation.java new file mode 100644 index 0000000..21504ed --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/MessageOperation.java @@ -0,0 +1,569 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; + +import static org.zaproxy.addon.migt.Tools.getVariableByName; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.json.JSONObject; + +/** The class storing a MessageOperation object */ +public class MessageOperation extends Module { + HTTPReqRes.MessageSection from; + String what; + String to; + MessageOperationActions action; + String save_as; // The name of the variable to save the parameter's value + String use; + MessageOpType type; + boolean url_decode = true; // enable or disable url decoding of url parameters + + // GENERATE POC + String template; + String output_path; + + /** Used to Instantiate the class */ + public MessageOperation() { + init(); + } + + public MessageOperation(JSONObject message_op_json) throws ParsingException { + init(); + + java.util.Iterator keys = message_op_json.keys(); + while (keys.hasNext()) { + String key = keys.next(); + + switch (key) { + case "from": + from = HTTPReqRes.MessageSection.fromString(message_op_json.getString("from")); + break; + case "remove parameter": + what = message_op_json.getString("remove parameter"); + action = MessageOperationActions.REMOVE_PARAMETER; + break; + case "remove match word": + what = message_op_json.getString("remove match word"); + action = MessageOperationActions.REMOVE_MATCH_WORD; + break; + case "edit": + what = message_op_json.getString("edit"); + action = MessageOperationActions.EDIT; + break; + case "edit regex": + what = message_op_json.getString("edit regex"); + action = MessageOperationActions.EDIT_REGEX; + break; + case "in": + to = message_op_json.getString("in"); + break; + case "add": + what = message_op_json.getString("add"); + action = MessageOperationActions.ADD; + break; + case "this": + to = message_op_json.getString("this"); + break; + case "save": + what = message_op_json.getString("save"); + action = MessageOperationActions.SAVE; + break; + case "save match": + what = message_op_json.getString("save match"); + action = MessageOperationActions.SAVE_MATCH; + break; + case "as": + save_as = message_op_json.getString("as"); + break; + case "use": + use = message_op_json.getString("use"); + break; + case "type": + type = MessageOpType.fromString(message_op_json.getString("type")); + break; + case "template": + template = message_op_json.getString("template"); + break; + case "output_path": + output_path = message_op_json.getString("output_path"); + break; + case "url decode": + url_decode = message_op_json.getBoolean("url decode"); + break; + default: + throw new ParsingException("Message operation key \" " + key + "\" not valid"); + } + } + } + + /** + * Returns the adding of a message operation, decides if the value to be inserted/edited should + * be a variable or a typed value and return it + * + * @param m the message operation which has to be examined + * @return the adding to be used in add/edit + * @throws ParsingException if the variable name is not valid or the variable has not been + * initiated + */ + public static String getAdding(MessageOperation m, List vars) throws ParsingException { + if (!m.use.isEmpty()) { + Var v = getVariableByName(m.use, vars); + return v.get_value_string(); + } else { + + return m.to; + } + } + + public void init() { + this.what = ""; + this.to = ""; + this.save_as = ""; + this.use = ""; + this.type = MessageOpType.HTTP; + this.template = ""; + this.output_path = ""; + } + + public void loader(Operation_API api) { + this.imported_api = api; + } + + public Operation_API exporter() { + if (imported_api instanceof Operation_API) { + return (Operation_API) this.imported_api; + } + return null; + } + + /** + * Given an operation, and a message, execute the Message operations contained in the operation + * + * @return the updated Operation with the result + * @throws ParsingException if parsing of names is not successfull + */ + public void execute() { + Pattern pattern; + Matcher matcher; + try { + if (type == MessageOpType.GENERATE_POC) { + if (!((Operation_API) imported_api).is_request) { + throw new ParsingException( + "Invalid POC generation, message should be a request"); + } + + if (!template.equals("csrf")) { + System.out.println("CSRF template not supported"); + return; // other templates not supported yet + } + + String poc = Tools.generate_CSRF_POC(((Operation_API) imported_api).message); + + try { + File myObj = new File(output_path); + myObj.createNewFile(); + } catch (IOException e) { + throw new ParsingException( + "Invalid POC generation output path: " + + output_path + + " " + + e.getMessage()); + } + try { + FileWriter myWriter = new FileWriter(output_path); + myWriter.write(poc); + myWriter.close(); + } catch (IOException e) { + throw new ParsingException( + "Something went wrong while writing output file for POC generator: " + + output_path + + " " + + e.getMessage()); + } + } else { + if (action == null) { + throw new ParsingException("Invalid action in message operation"); + } + + switch (action) { + case REMOVE_PARAMETER: + switch (from) { + case URL: + if (!((Operation_API) imported_api).is_request) { + throw new ParsingException("Searching URL in response"); + } + String url_header = + ((Operation_API) imported_api).message.getUrlHeader(); + pattern = + Pattern.compile( + "&?" + + Pattern.quote(what) + + "=[^& ]*((?=&)|(?= ))"); + matcher = pattern.matcher(url_header); + String new_url = matcher.replaceFirst(""); + ((Operation_API) imported_api).message.setUrlHeader(new_url); + break; + + case HEAD: + ((Operation_API) imported_api) + .message.removeHeadParameter( + ((Operation_API) imported_api).is_request, what); + break; + + case BODY: + String body = + new String( + ((Operation_API) imported_api) + .message.getBody( + ((Operation_API) imported_api) + .is_request)); + pattern = Pattern.compile(Pattern.quote(what)); + matcher = pattern.matcher(body); + ((Operation_API) imported_api) + .message.setBody( + ((Operation_API) imported_api).is_request, + matcher.replaceAll("")); + break; + } + break; + + case ADD: + if (getAdding(this, ((Operation_API) imported_api).vars) == null + | getAdding(this, ((Operation_API) imported_api).vars).isEmpty()) { + // TODO: should raise exception or set operation not applicable? + break; + } + switch (from) { + case HEAD: + { + ((Operation_API) imported_api) + .message.addHeadParameter( + ((Operation_API) imported_api).is_request, + what, + getAdding( + this, + ((Operation_API) imported_api).vars)); + break; + } + case BODY: + { + String tmp = + new String( + ((Operation_API) imported_api) + .message.getBody( + ((Operation_API) imported_api) + .is_request)); + tmp = + tmp + + getAdding( + this, + ((Operation_API) imported_api).vars); + ((Operation_API) imported_api) + .message.setBody( + ((Operation_API) imported_api).is_request, tmp); + break; + } + case URL: + if (!((Operation_API) imported_api).is_request) { + throw new ParsingException("Searching URL in response"); + } + String header_0 = + ((Operation_API) imported_api).message.getUrlHeader(); + + pattern = + Pattern.compile( + "&?" + + Pattern.quote(what) + + "=[^& ]*((?=&)|(?= ))"); + matcher = pattern.matcher(header_0); + + String newHeader_0 = ""; + boolean found = false; + while (matcher.find() & !found) { + String before = header_0.substring(0, matcher.end()); + String after = header_0.substring(matcher.end()); + newHeader_0 = + before + + getAdding( + this, + ((Operation_API) imported_api).vars) + + after; + found = true; + } + ((Operation_API) imported_api).message.setUrlHeader(newHeader_0); + break; + } + break; + + case EDIT: + byte[] msg = + Tools.editMessageParam( + what, + from, + ((Operation_API) imported_api).message, + ((Operation_API) imported_api).is_request, + getAdding(this, ((Operation_API) imported_api).vars), + true); + + if (((Operation_API) imported_api).message.isRequest) { + ((Operation_API) imported_api).message.setRequest(msg); + } else { + ((Operation_API) imported_api).message.setResponse(msg); + } + break; + + case EDIT_REGEX: + msg = + Tools.editMessage( + what, + this, + ((Operation_API) imported_api).message, + ((Operation_API) imported_api).is_request, + getAdding(this, ((Operation_API) imported_api).vars)); + + if (((Operation_API) imported_api).message.isRequest) { + ((Operation_API) imported_api).message.setRequest(msg); + } else { + ((Operation_API) imported_api).message.setResponse(msg); + } + break; + + case REMOVE_MATCH_WORD: + switch (from) { + case HEAD: + { + List headers = + ((Operation_API) imported_api) + .message.getHeaders( + ((Operation_API) imported_api) + .is_request); + pattern = Pattern.compile(Pattern.quote(what)); + List new_headers = new ArrayList<>(); + + for (String header : headers) { + matcher = pattern.matcher(header); + new_headers.add(matcher.replaceAll("")); + } + + ((Operation_API) imported_api) + .message.setHeaders( + ((Operation_API) imported_api).is_request, + new_headers); + break; + } + case BODY: + { + pattern = Pattern.compile(Pattern.quote(what)); + matcher = + pattern.matcher( + new String( + ((Operation_API) imported_api) + .message.getBody( + ((Operation_API) + imported_api) + .is_request))); + ((Operation_API) imported_api) + .message.setBody( + ((Operation_API) imported_api).is_request, + matcher.replaceAll("")); + break; + } + case URL: + if (!((Operation_API) imported_api).is_request) { + throw new ParsingException("Searching URL in response"); + } + String header_0 = + ((Operation_API) imported_api).message.getUrlHeader(); + + pattern = Pattern.compile(what); + matcher = pattern.matcher(header_0); + String newHeader_0 = matcher.replaceFirst(""); + + ((Operation_API) imported_api).message.setUrlHeader(newHeader_0); + break; + } + break; + + case SAVE: + case SAVE_MATCH: + switch (from) { + case HEAD: + { + String value = + action == MessageOperationActions.SAVE + ? ((Operation_API) imported_api) + .message.getHeadParam( + ((Operation_API) imported_api) + .is_request, + what) + : ((Operation_API) imported_api) + .message.getHeadRegex( + ((Operation_API) imported_api) + .is_request, + what); + + if (value.isEmpty()) { + System.out.println( + "Warning: saved head parameter \"" + + what + + "\" that has an empty value"); + } + + Var v = new Var(save_as, value); + ((Operation_API) imported_api).vars.add(v); + } + break; + case BODY: + { + String value = + ((Operation_API) imported_api) + .message.getBodyRegex( + ((Operation_API) imported_api) + .is_request, + what); + + if (value.isEmpty()) { + System.out.println( + "Warning: saved body regex \"" + + what + + "\" that matched an empty value"); + } + + Var v = new Var(save_as, value); + ((Operation_API) imported_api).vars.add(v); + } + break; + case URL: + { + if (!((Operation_API) imported_api).is_request) { + throw new ParsingException( + "Trying to acces the url of a response message"); + } + + String value = + action == MessageOperationActions.SAVE + ? ((Operation_API) imported_api) + .message.getUrlParam(what, !url_decode) + : ((Operation_API) imported_api) + .message.getUrlRegex(what); + + if (value.isEmpty()) { + System.out.println( + "Warning: saved URL parameter \"" + + what + + "\" that has an empty value"); + } + + Var v = new Var(save_as, value); + ((Operation_API) imported_api).vars.add(v); + } + break; + } + break; + } + } + applicable = true; + } catch (StackOverflowError | ParsingException e) { + e.printStackTrace(); + // applicable is already false + } + } + + /** All the possible actions of a MessageOperation */ + public enum MessageOperationActions { + REMOVE_PARAMETER, + REMOVE_MATCH_WORD, + EDIT, + EDIT_REGEX, + ADD, + SAVE, + SAVE_MATCH, + ENCODE; + + /** + * From a string get the corresponding enum value + * + * @param input the string + * @return the enum value + * @throws ParsingException if the input is malformed + */ + public static MessageOperationActions fromString(String input) throws ParsingException { + if (input != null) { + switch (input) { + case "remove parameter": + return REMOVE_PARAMETER; + case "remove match word": + return REMOVE_MATCH_WORD; + case "edit": + return EDIT; + case "edit regex": + return EDIT_REGEX; + case "add": + return ADD; + case "save": + return SAVE; + case "save match": + return SAVE_MATCH; + case "encode": + return ENCODE; + default: + throw new ParsingException( + "invalid Message operation action \"" + input + "\""); + } + } else { + throw new NullPointerException(); + } + } + } + + /** The possible types of messageOps */ + public enum MessageOpType { + HTTP, + GENERATE_POC; + + /** + * From a string get the corresponding enum value + * + * @param input the string + * @return the enum value + * @throws ParsingException if the input is malformed + */ + public static MessageOpType fromString(String input) throws ParsingException { + if (input != null) { + switch (input) { + case "http": + return HTTP; + case "generate_poc": + return GENERATE_POC; + default: + throw new ParsingException("invalid message Op Type"); + } + } else { + throw new NullPointerException(); + } + } + } +} diff --git a/tool/src/main/java/migt/MessageType.java b/tool/src/main/java/org/zaproxy/addon/migt/MessageType.java similarity index 66% rename from tool/src/main/java/migt/MessageType.java rename to tool/src/main/java/org/zaproxy/addon/migt/MessageType.java index 4c9b5d8..a54c0b8 100644 --- a/tool/src/main/java/migt/MessageType.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/MessageType.java @@ -1,14 +1,32 @@ -package migt; +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; import java.util.ArrayList; import java.util.List; -/** - * Class storing a MessageType - */ +/** Class storing a MessageType */ public class MessageType implements Cloneable { String name; - Boolean isRequest; // this tells if the message where the checks are executed is a request or a response + Boolean isRequest; // this tells if the message where the checks are executed is a request or a + // response List checks; String responseName; String requestName; @@ -16,12 +34,14 @@ public class MessageType implements Cloneable { Boolean getByResponse; // this tells if you are getting a response by checking a request Boolean getByRequest; // this tells if you are getting a request by checking a response - boolean msg_to_process_is_request; // this tells whether the message to be intercepted is a request or a response + boolean msg_to_process_is_request; // this tells whether the message to be intercepted is a + + // request or a response /** * Instantiate a MessageType * - * @param name the name of that message + * @param name the name of that message * @param isRequest if the message is a request */ public MessageType(String name, Boolean isRequest) { @@ -32,7 +52,8 @@ public MessageType(String name, Boolean isRequest) { this.requestName = ""; this.getByResponse = false; this.getByRequest = false; - this.msg_to_process_is_request = isRequest; // init with this, if getByResponse or getByRequest, then change + this.msg_to_process_is_request = + isRequest; // init with this, if getByResponse or getByRequest, then change // accordingly } @@ -44,7 +65,8 @@ public MessageType(String name, Boolean isRequest) { * @return the corresponding MessageType (if found) * @throws Exception if the MessageType can not be found */ - public static MessageType getFromList(List list, String name) throws ParsingException { + public static MessageType getFromList(List list, String name) + throws ParsingException { for (MessageType act : list) { try { if (act.name.equals(name)) { diff --git a/tool/src/main/java/migt/Module.java b/tool/src/main/java/org/zaproxy/addon/migt/Module.java similarity index 57% rename from tool/src/main/java/migt/Module.java rename to tool/src/main/java/org/zaproxy/addon/migt/Module.java index e393bc0..f1cd191 100644 --- a/tool/src/main/java/migt/Module.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/Module.java @@ -1,12 +1,30 @@ -package migt; +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; +import javax.swing.JPanel; import org.json.JSONObject; -import javax.swing.*; - /** - * This class is the Parent class inherited by all modules. It provides some methods and parameters to be - * used by other classes + * This class is the Parent class inherited by all modules. It provides some methods and parameters + * to be used by other classes */ public class Module { // These variables should be present in each module @@ -15,16 +33,12 @@ public class Module { API api; // the api of this module API imported_api; // the api imported from a previous module - /** - * When the module doesn't require any input - */ - public Module() { - - } + /** When the module doesn't require any input */ + public Module() {} /** - * Used when the module requires inputs from the JSON language. - * Instantiate the module by parsing a JSONObject which contains the JSON inputs. + * Used when the module requires inputs from the JSON language. Instantiate the module by + * parsing a JSONObject which contains the JSON inputs. * * @param json_module the json input for this module */ @@ -33,16 +47,13 @@ public Module(JSONObject json_module) { } /** - * This function should be called to check that after an initialization of a module all the necessary parameters - * are set correctly. And the JSON has been parsed correctly with all the required tags present. + * This function should be called to check that after an initialization of a module all the + * necessary parameters are set correctly. And the JSON has been parsed correctly with all the + * required tags present. */ - public void validate() throws ParsingException { + public void validate() throws ParsingException {} - } - - /** - * Method used to get the API object of this module to be used in other modules. - */ + /** Method used to get the API object of this module to be used in other modules. */ public T getAPI() { return null; } @@ -50,13 +61,11 @@ public T getAPI() { /** * Method used to set the API object of this module, when it is edited or simply to initiate it. */ - public void setAPI(API api) { - - } + public void setAPI(API api) {} /** - * Placeholder of a loader method for the API. This method should load all the things needed by the module - * from the previous module where the API is imported from. + * Placeholder of a loader method for the API. This method should load all the things needed by + * the module from the previous module where the API is imported from. * * @param api the imported API */ @@ -68,8 +77,9 @@ public void loader(API api) { } /** - * Placeholder of a exporter function. This function should return the API object to where it has been loaded after - * it has been edited. There is no need to call the exporter if the API is not edited. + * Placeholder of a exporter function. This function should return the API object to where it + * has been loaded after it has been edited. There is no need to call the exporter if the API is + * not edited. * * @return the edited API * @throws ParsingException @@ -79,10 +89,11 @@ public API exporter() throws ParsingException { } /** - * Update the result of this module from a child module. For convenience, returns the result of the module + * Update the result of this module from a child module. For convenience, returns the result of + * the module * * @param module the module to save the result from - * @param The module class + * @param The module class */ public boolean setResult(T module) { if (!module.applicable) { @@ -121,9 +132,9 @@ public void execute() { } /** - * This method should return a graphical representation of the module, used to visualize the module from the GUI. - * The representation should include useful informations about what the module did, or which errors were found - * during execution + * This method should return a graphical representation of the module, used to visualize the + * module from the GUI. The representation should include useful informations about what the + * module did, or which errors were found during execution * * @return a JPanel object containing the graphical interface of this module */ diff --git a/tool/src/main/java/migt/Operation.java b/tool/src/main/java/org/zaproxy/addon/migt/Operation.java similarity index 77% rename from tool/src/main/java/migt/Operation.java rename to tool/src/main/java/org/zaproxy/addon/migt/Operation.java index 4d38b55..5c0b8fb 100644 --- a/tool/src/main/java/migt/Operation.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/Operation.java @@ -1,19 +1,42 @@ -package migt; +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; -import burp.IInterceptedProxyMessage; -import org.json.JSONArray; -import org.json.JSONObject; +import static org.zaproxy.addon.migt.Tools.buildStringWithVars; +import static org.zaproxy.addon.migt.Tools.executeChecks; +import static org.zaproxy.addon.migt.Tools.executeDecodeOps; +import static org.zaproxy.addon.migt.Tools.executeEditOps; +import static org.zaproxy.addon.migt.Tools.executeMessageOperations; +import static org.zaproxy.addon.migt.Tools.findParentDiv; +import static org.zaproxy.addon.migt.Tools.getVariableByName; import java.util.ArrayList; import java.util.List; import java.util.regex.PatternSyntaxException; +import org.json.JSONArray; +import org.json.JSONObject; +import org.parosproxy.paros.network.HttpMessage; -import static migt.Tools.*; - -/** - * Class storing an Operation in a Test - */ +/** Class storing an Operation in a Test */ public class Operation extends Module { + private String session; public List messageOperations; public String from_session; public String to_session; @@ -26,7 +49,7 @@ public class Operation extends Module { public boolean isSessionOp = false; public List matchedMessages; public byte[] processed_message; - public List log_messages; + public List log_messages; public List session_operations; // Decode operations public List decodeOperations; @@ -37,15 +60,12 @@ public class Operation extends Module { private List checks; private String messageType; private Action action; - private String session; private SessionOperation.SessionAction sessionAction; // submodules private boolean at_hash_verify; private At_Hash_update at_hash_update; - /** - * Instantiate an operation - */ + /** Instantiate an operation */ public Operation() { init(); } @@ -54,13 +74,12 @@ public Operation() { * Instantiate an Operation parsing a JSON object * * @param operation_json the operation defined in MIG-L as JSONObject - * @param isActive if the operation is used inside an active or passive test - * @param messageTypes All the message types imported + * @param isActive if the operation is used inside an active or passive test + * @param messageTypes All the message types imported * @throws Exception */ - public Operation(JSONObject operation_json, - boolean isActive, - List messageTypes) throws Exception { + public Operation(JSONObject operation_json, boolean isActive, List messageTypes) + throws Exception { init(); if (isActive) { @@ -146,10 +165,11 @@ public Operation(JSONObject operation_json, // Edit operations if (operation_json.has("edit operations")) { - editOperations = Tools.parseEditsFromJSON(operation_json.getJSONArray("edit operations")); + editOperations = + Tools.parseEditsFromJSON(operation_json.getJSONArray("edit operations")); } - //Other modules + // Other modules if (operation_json.has("at_hash_verify")) { at_hash_verify = operation_json.getBoolean("at_hash_verify"); } @@ -188,7 +208,7 @@ public String getMessageType() { * Set the message type of the message the operation needs to deal with * * @param messageType the name of the message type - * @param msg_types the list of message types + * @param msg_types the list of message types * @throws Exception Thrown if the message type is not found */ public void setMessageType(String messageType, List msg_types) throws Exception { @@ -255,21 +275,24 @@ public void setDecodeOperations(List decodeOperations) { /** * Used to process session operations of a given operation * - * @return An array of Object elements, the first is the edited operation, the second is the updated variables + * @return An array of Object elements, the first is the edited operation, the second is the + * updated variables */ - public List executeSessionOps(Test t, //TODO add this to the input api of Operation - List vars) throws ParsingException { + public List executeSessionOps( + Test t, // TODO add this to the input api of Operation + List vars) + throws ParsingException { List updated_vars = vars; for (SessionOperation sop : this.session_operations) { -/* - List vars_new = eal.onBeforeExSessionOps(); - - for (Var v : vars_new) { - if (!updated_vars.contains(v)) { - updated_vars.inse - } - } - */ + /* + List vars_new = eal.onBeforeExSessionOps(); + + for (Var v : vars_new) { + if (!updated_vars.contains(v)) { + updated_vars.inse + } + } + */ Session session = t.getSession(sop.from_session); Track track = session.track; @@ -278,8 +301,14 @@ public List executeSessionOps(Test t, //TODO add this to the input api of O String value = ""; switch (sop.target) { case TRACK: - for (SessionTrackAction sa : t.getSession(sop.from_session).track - .getStasFromMarkers(sop.at, sop.to, sop.is_from_included, sop.is_to_included)) { + for (SessionTrackAction sa : + t.getSession(sop.from_session) + .track + .getStasFromMarkers( + sop.at, + sop.to, + sop.is_from_included, + sop.is_to_included)) { value += sa.toString() + "\n"; } break; @@ -311,7 +340,8 @@ public List executeSessionOps(Test t, //TODO add this to the input api of O value = session.last_url; break; case ALL_ASSERT: - for (SessionTrackAction sa : t.getSession(sop.from_session).track.getTrack()) { + for (SessionTrackAction sa : + t.getSession(sop.from_session).track.getTrack()) { if (sa.isAssert) { value += sa + "\n"; } @@ -342,7 +372,8 @@ public List executeSessionOps(Test t, //TODO add this to the input api of O track.mark(session.last_open, sop.mark_name); break; case ALL_ASSERT: - for (SessionTrackAction sa : t.getSession(sop.from_session).track.getTrack()) { + for (SessionTrackAction sa : + t.getSession(sop.from_session).track.getTrack()) { if (sa.isAssert) { track.mark(sa, sop.mark_name); } @@ -350,21 +381,32 @@ public List executeSessionOps(Test t, //TODO add this to the input api of O break; case TRACK: case LAST_URL: - throw new ParsingException("Invalid session operation target: " + sop.target); + throw new ParsingException( + "Invalid session operation target: " + sop.target); default: throw new ParsingException("Invalid session operation target"); } break; case REMOVE: if (sop.to != null && !sop.to.equals("")) { - // TODO: remove interval of indices instead of using the remove construct of lists, because it + // TODO: remove interval of indices instead of using the remove construct of + // lists, because it // removes duplicated things - int[] range = t.getSession(sop.from_session).track. - getStasIndexFromRange(sop.at, sop.to, sop.is_from_included, sop.is_to_included); - - - t.getSession(sop.from_session).track.getTrack().subList(range[0], range[1] + 1).clear(); + int[] range = + t.getSession(sop.from_session) + .track + .getStasIndexFromRange( + sop.at, + sop.to, + sop.is_from_included, + sop.is_to_included); + + t.getSession(sop.from_session) + .track + .getTrack() + .subList(range[0], range[1] + 1) + .clear(); } else { track.remove(new Marker(sop.at)); } @@ -375,6 +417,7 @@ public List executeSessionOps(Test t, //TODO add this to the input api of O return updated_vars; } + @SuppressWarnings("unchecked") public Operation_API getAPI() { return this.api; // TODO: check if the api should be updated with the processed message before returning it @@ -382,7 +425,8 @@ public Operation_API getAPI() { } /** - * Sets the api of this Operation with the given api. Note that the variables are added, not substituted + * Sets the api of this Operation with the given api. Note that the variables are added, not + * substituted * * @param api the new api to be set */ @@ -403,7 +447,8 @@ public void setAPI(Operation_API api) { // add the intercepted message to the matched messages to be displayed if (!matchedMessages.contains(api.message)) { - // it could be added multiple times because this method is called by other Modules that returns this api + // it could be added multiple times because this method is called by other Modules that + // returns this api // edited matchedMessages.add(api.message); } @@ -415,12 +460,8 @@ public void setAPI(Operation_API api) { public void execute() { if (!preconditions.isEmpty()) { try { - applicable = Tools.executeChecks( - preconditions, - api.message, - api.is_request, - api.vars - ); + applicable = + Tools.executeChecks(preconditions, api.message, api.is_request, api.vars); if (!applicable) return; } catch (ParsingException e) { applicable = false; @@ -461,17 +502,13 @@ public void execute() { try { applicable = true; executeMessageOperations(this); - if (!applicable | !result) - return; + if (!applicable | !result) return; executeEditOps(this, api.vars); - if (!applicable | !result) - return; + if (!applicable | !result) return; executeDecodeOps(this, api.vars); - if (!applicable | !result) - return; + if (!applicable | !result) return; executeChecks(this, api.vars); - if (!applicable | !result) - return; + if (!applicable | !result) return; if (at_hash_verify) { At_Hash_check at = new At_Hash_check(); @@ -494,8 +531,8 @@ public void execute() { } // TODO: move this here instead of Execute Actives - //executeSessionOps(, api.vars); - //if (!applicable | !result) + // executeSessionOps(, api.vars); + // if (!applicable | !result) // return; } catch (ParsingException | PatternSyntaxException e) { @@ -505,17 +542,15 @@ public void execute() { } if (!save_name.equals("")) { - Var v = new Var( - save_name, - api.is_request ? api.message.getRequest() : api.message.getResponse() - ); + Var v = + new Var( + save_name, + api.is_request ? api.message.getRequest() : api.message.getResponse()); api.vars.add(v); } } - /** - * Enum containing all the possible Active operation actions - */ + /** Enum containing all the possible Active operation actions */ public enum Action { INTERCEPT; @@ -538,9 +573,7 @@ public static Action fromString(String input) throws ParsingException { } } - /** - * Enum that contains all the possible action to do after a message is received - */ + /** Enum that contains all the possible action to do after a message is received */ public enum Then { FORWARD, DROP; @@ -568,4 +601,3 @@ public static Then fromString(String input) throws ParsingException { } } } - diff --git a/tool/src/main/java/org/zaproxy/addon/migt/Operation_API.java b/tool/src/main/java/org/zaproxy/addon/migt/Operation_API.java new file mode 100644 index 0000000..d2f9032 --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/Operation_API.java @@ -0,0 +1,40 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; + +import java.util.ArrayList; +import java.util.List; + +/** This class provides an API for an Operation module, to be used by other modules. */ +public class Operation_API extends API { + public HTTPReqRes message; + public List vars; + boolean is_request; + + public Operation_API(HTTPReqRes message, boolean is_request) { + this.message = message; + this.is_request = is_request; + this.vars = new ArrayList<>(); + } + + public Operation_API(List vars) { + this.vars = vars; + } +} diff --git a/tool/src/main/java/org/zaproxy/addon/migt/ParsingException.java b/tool/src/main/java/org/zaproxy/addon/migt/ParsingException.java new file mode 100644 index 0000000..83a4c76 --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/ParsingException.java @@ -0,0 +1,34 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; + +/** Exception raised when the parsing of the language fails */ +public class ParsingException extends Exception { + private static final long serialVersionUID = 1L; + + /** + * Raised when there is a problem in the parsing of the json + * + * @param errorMessage the error message to be displayed + */ + public ParsingException(String errorMessage) { + super(errorMessage); + } +} diff --git a/tool/src/main/java/org/zaproxy/addon/migt/ReqResPanel.java b/tool/src/main/java/org/zaproxy/addon/migt/ReqResPanel.java new file mode 100644 index 0000000..47731c6 --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/ReqResPanel.java @@ -0,0 +1,60 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; + +import java.awt.BorderLayout; +import org.parosproxy.paros.extension.AbstractPanel; +import org.parosproxy.paros.network.HttpMalformedHeaderException; +import org.parosproxy.paros.network.HttpMessage; +import org.zaproxy.zap.extension.httppanel.HttpPanelRequest; +import org.zaproxy.zap.extension.httppanel.HttpPanelResponse; + +public class ReqResPanel extends AbstractPanel { + private static final long serialVersionUID = 1L; + private HttpPanelRequest httpPanelRequest; + private HttpPanelResponse httpPanelResponse; + + public ReqResPanel() { + this.setName("ReqResPanel"); + this.setLayout(new BorderLayout()); + + httpPanelRequest = new HttpPanelRequest(false, "Request Panel"); + // httpPanelRequest.loadConfig(new ZapXmlConfiguration()); + + httpPanelResponse = new HttpPanelResponse(false, "Response Panel"); + // httpPanelResponse.loadConfig(new ZapXmlConfiguration()); + + this.add(httpPanelRequest, BorderLayout.NORTH); + this.add(httpPanelResponse, BorderLayout.SOUTH); + } + + public void setMessage(HTTPReqRes msg, boolean isRequest) throws HttpMalformedHeaderException { + HttpMessage httpm = new HttpMessage(); + if (isRequest) { + httpm.setRequestHeader(msg.Req_header); + httpm.setRequestBody(msg.Req_body); + httpPanelRequest.setMessage(httpm); + } else { + httpm.setResponseHeader(msg.Res_header); + httpm.setResponseBody(msg.Res_body); + httpPanelResponse.setMessage(httpm); + } + } +} diff --git a/tool/src/main/java/migt/Session.java b/tool/src/main/java/org/zaproxy/addon/migt/Session.java similarity index 54% rename from tool/src/main/java/migt/Session.java rename to tool/src/main/java/org/zaproxy/addon/migt/Session.java index 0b5ee2f..818a41f 100644 --- a/tool/src/main/java/migt/Session.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/Session.java @@ -1,16 +1,37 @@ -package migt; - -import burp.IExtensionHelpers; -import burp.IHttpRequestResponsePersisted; +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.parosproxy.paros.db.DatabaseException; +import org.parosproxy.paros.model.HistoryReference; +import org.parosproxy.paros.network.HttpMalformedHeaderException; +import org.parosproxy.paros.network.HttpMessage; -/** - * Class to manage Sessions - */ +/** Class to manage Sessions */ public class Session { // Session actions public SessionTrackAction last_action; @@ -22,12 +43,11 @@ public class Session { Track track; int index = 0; List messages; + boolean isOffline = false; ExecuteTrack ex; - /** - * Instantiate the session - */ + /** Instantiate the session */ public Session() { this.messages = new ArrayList<>(); this.name = ""; @@ -86,9 +106,8 @@ public boolean hasNext() { } /** - * Gets the next track element. - * Note that the function hasNext() could be called before the execution of this function - * track + * Gets the next track element. Note that the function hasNext() could be called + * before the execution of this function track * * @return the next track element */ @@ -97,31 +116,38 @@ public SessionTrackAction next() { } /** - * Adds a message to the list of messages in this session. if the param filter is set to true the message is checked - * against some common file extension and if it is matched it is discarded + * Adds a message to the list of messages in this session. if the param filter is set to true + * the message is checked against some common file extension and if it is matched it is + * discarded * - * @param message the message to be added - * @param helpers an IExtensionHelpers istance - * @param filter specify if filtering is enabled + * @param hmsg the message to be added + * @param filter specify if filtering is enabled * @return the added message */ - public HTTPReqRes addMessage(IHttpRequestResponsePersisted message, IExtensionHelpers helpers, boolean filter) { + public HTTPReqRes addMessage(HistoryReference hmsg, boolean filter) + throws URISyntaxException, + MalformedURLException, + HttpMalformedHeaderException, + DatabaseException { HTTPReqRes res = null; + HttpMessage message = hmsg.getHttpMessage(); if (filter) { - String url = helpers.analyzeRequest(message.getRequest()).getHeaders().get(0); + String forURI = message.getRequestHeader().getURI().toString(); + URI uri = new URI(forURI); + String url = uri.toURL().toString(); url = url.split("\\sHTTP")[0]; - Pattern pattern = Pattern.compile("\\.gif$|\\.jpg$|\\.jpeg$|\\.svg$|\\.png$|\\.css$|\\.js$|\\.webp$|\\.ico$|" + - "\\.tiff$|\\.bpm$|\\.ttf$|\\.otf$|\\.woff$|\\.woff2$|\\.eot$|\\.txt$"); + Pattern pattern = + Pattern.compile( + "\\.gif$|\\.jpg$|\\.jpeg$|\\.svg$|\\.png$|\\.css$|\\.js$|\\.webp$|\\.ico$|" + + "\\.tiff$|\\.bpm$|\\.ttf$|\\.otf$|\\.woff$|\\.woff2$|\\.eot$|\\.txt$"); Matcher matcher = pattern.matcher(url); if (!matcher.find()) { - // - res = new HTTPReqRes(message, helpers); + res = new HTTPReqRes(hmsg); messages.add(res); - } } else { - res = new HTTPReqRes(message, helpers); + res = new HTTPReqRes(hmsg); messages.add(res); } return res; diff --git a/tool/src/main/java/migt/SessionOperation.java b/tool/src/main/java/org/zaproxy/addon/migt/SessionOperation.java similarity index 83% rename from tool/src/main/java/migt/SessionOperation.java rename to tool/src/main/java/org/zaproxy/addon/migt/SessionOperation.java index 28f582c..8b97f9a 100644 --- a/tool/src/main/java/migt/SessionOperation.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/SessionOperation.java @@ -1,17 +1,33 @@ -package migt; - -import org.json.JSONArray; -import org.json.JSONObject; +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.json.JSONArray; +import org.json.JSONObject; -/** - * Class containing a session Operation - */ +/** Class containing a session Operation */ public class SessionOperation { public String from_session; public SessOperationAction action; @@ -31,7 +47,8 @@ public class SessionOperation { * @return the list of tession operations * @throws ParsingException if the parsing goes wrong */ - public static List parseFromJson(JSONObject act_operation) throws ParsingException { + public static List parseFromJson(JSONObject act_operation) + throws ParsingException { List lsop = new ArrayList<>(); if (act_operation.has("session operations")) { JSONArray session_ops = act_operation.getJSONArray("session operations"); @@ -48,8 +65,9 @@ public static List parseFromJson(JSONObject act_operation) thr break; case "save": sop.action = SessOperationAction.SAVE; - sop.target = SessOperationTarget - .getFromString(act_session_op.getString("save")); + sop.target = + SessOperationTarget.getFromString( + act_session_op.getString("save")); break; case "as": sop.as = act_session_op.getString("as"); @@ -63,12 +81,13 @@ public static List parseFromJson(JSONObject act_operation) thr break; case "mark": sop.action = SessOperationAction.MARKER; - sop.target = SessOperationTarget - .getFromString(act_session_op.getString("mark")); + sop.target = + SessOperationTarget.getFromString( + act_session_op.getString("mark")); sop.mark_name = act_session_op.getString("name"); break; case "name": - //Already processed in mark + // Already processed in mark break; case "remove": sop.action = SessOperationAction.REMOVE; @@ -86,7 +105,8 @@ public static List parseFromJson(JSONObject act_operation) thr break; default: - throw new ParsingException("Unexpected value: " + key + " in session operation"); + throw new ParsingException( + "Unexpected value: " + key + " in session operation"); } } lsop.add(sop); @@ -97,17 +117,16 @@ public static List parseFromJson(JSONObject act_operation) thr } /** - * Parse a string containing a range in the form of [number, number] or (number,number], based on the type of - * parenthesis you can say that the rance is included [ or excluded ( + * Parse a string containing a range in the form of [number, number] or (number,number], based + * on the type of parenthesis you can say that the rance is included [ or excluded ( * * @param range The string containing the range to parse - * @return Position 0, true if from included. - * Position 1: true if to included. - * Position 2: from, - * Position 3: to + * @return Position 0, true if from included. Position 1: true if to included. Position 2: from, + * Position 3: to */ public static List parseRange(String range) throws ParsingException { - Pattern p = Pattern.compile("^(\\(|\\[)\\s*([^\\[\\],]*)\\s*,\\s*([^\\[\\],]*)\\s*(\\)|\\])$"); + Pattern p = + Pattern.compile("^(\\(|\\[)\\s*([^\\[\\],]*)\\s*,\\s*([^\\[\\],]*)\\s*(\\)|\\])$"); Matcher m = p.matcher(range); List l = new ArrayList<>(); @@ -137,9 +156,7 @@ public static List parseRange(String range) throws ParsingException { return l; } - /** - * Enum containing all the possible session operation actions - */ + /** Enum containing all the possible session operation actions */ public enum SessionAction { START, PAUSE, @@ -176,9 +193,7 @@ public static SessionAction fromString(String input) throws ParsingException { } } - /** - * Defines the action of a session action - */ + /** Defines the action of a session action */ public enum SessAction { CLICK, OPEN, @@ -258,9 +273,7 @@ public static SessAction getFromString(String s) throws ParsingException { } } - /** - * Defines the action of a session operation - */ + /** Defines the action of a session operation */ public enum SessOperationAction { SAVE, INSERT, @@ -269,8 +282,8 @@ public enum SessOperationAction { } /** - * Defines the target of a session operation. - * Is it better to use js or just build a form? if a form is used, body has to be interpreted + * Defines the target of a session operation. Is it better to use js or just build a form? if a + * form is used, body has to be interpreted */ public enum SessOperationTarget { LAST_ACTION, @@ -289,7 +302,8 @@ public enum SessOperationTarget { * Parse a string containing a session operation target * * @param s the string to parse - * @throws ParsingException if the string is malformed, or no session operation target is found + * @throws ParsingException if the string is malformed, or no session operation target is + * found */ public static SessOperationTarget getFromString(String s) throws ParsingException { diff --git a/tool/src/main/java/migt/SessionTrackAction.java b/tool/src/main/java/org/zaproxy/addon/migt/SessionTrackAction.java similarity index 80% rename from tool/src/main/java/migt/SessionTrackAction.java rename to tool/src/main/java/org/zaproxy/addon/migt/SessionTrackAction.java index 34e36f7..9a2eec2 100644 --- a/tool/src/main/java/migt/SessionTrackAction.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/SessionTrackAction.java @@ -1,12 +1,29 @@ -package migt; +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; import java.util.ArrayList; import java.util.List; import java.util.Objects; -/** - * This class represents an user action in a session - */ +/** This class represents an user action in a session */ public class SessionTrackAction { public SessionOperation.SessAction action; public String elem_type; @@ -15,11 +32,14 @@ public class SessionTrackAction { public String content; public List markers; public boolean isAssert; + // "action | elem_type=elem_source | content" - /** - * Constuctor used to instantiate the class - */ + public int hashCode() { + return Objects.hash(elem, elem_type, elem_source, content); + } + + /** Constuctor used to instantiate the class */ public SessionTrackAction() { action = null; elem_type = ""; @@ -62,10 +82,10 @@ public void parse_raw_action(String raw_action) throws ParsingException { if (action == SessionOperation.SessAction.CLEAR_COOKIES) return; elem = splitted[1].trim(); - if (!(action == SessionOperation.SessAction.OPEN) && - action != SessionOperation.SessAction.WAIT && - action != SessionOperation.SessAction.ALERT && - action != SessionOperation.SessAction.SET_VAR) { + if (!(action == SessionOperation.SessAction.OPEN) + && action != SessionOperation.SessAction.WAIT + && action != SessionOperation.SessAction.ALERT + && action != SessionOperation.SessAction.SET_VAR) { String[] tmp = elem.split("="); elem_type = tmp[0].trim(); elem_source = tmp[1].trim(); @@ -175,4 +195,4 @@ public boolean equals(Object o) { if (!Objects.equals(content, that.content)) return false; return Objects.equals(markers, that.markers); } -} \ No newline at end of file +} diff --git a/tool/src/main/java/migt/Test.java b/tool/src/main/java/org/zaproxy/addon/migt/Test.java similarity index 77% rename from tool/src/main/java/migt/Test.java rename to tool/src/main/java/org/zaproxy/addon/migt/Test.java index 9854fdf..8ab9b43 100644 --- a/tool/src/main/java/migt/Test.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/Test.java @@ -1,8 +1,23 @@ -package migt; - -import burp.IInterceptedProxyMessage; -import org.json.JSONArray; -import org.json.JSONObject; +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; import java.io.BufferedWriter; import java.io.File; @@ -14,10 +29,11 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; +import org.json.JSONArray; +import org.json.JSONObject; +import org.parosproxy.paros.network.HttpMessage; -/** - * Class to store a test - */ +/** Class to store a test */ public class Test { public ResultType result; public String resultSession; @@ -38,18 +54,21 @@ public class Test { String description; List mandatory_keys = new ArrayList<>(); - /** - * Empty constructor for tests - */ + private byte[] concat(byte[] Fheader, byte[] Fbody) { + byte[] result = new byte[Fheader.length + Fbody.length]; + System.arraycopy(Fheader, 0, result, 0, Fheader.length); + System.arraycopy(Fbody, 0, result, Fheader.length, Fbody.length); + + return result; + } + + /** Empty constructor for tests */ public Test() { init(); } - /** - * Instantiate a test - */ - public Test(JSONObject test_json, - List messageTypes) throws Exception { + /** Instantiate a test */ + public Test(JSONObject test_json, List messageTypes) throws Exception { init(); description = test_json.getString("description"); @@ -122,10 +141,10 @@ public Test(JSONObject test_json, throw new ParsingException("session tag is missing"); } - //Array of Operations + // Array of Operations JSONArray arrOps = test_json.getJSONArray("operations"); - //Reads all the operations + // Reads all the operations for (int j = 0; j < arrOps.length(); j++) { JSONObject act_operation = arrOps.getJSONObject(j); @@ -151,7 +170,7 @@ public void init() { violated_properties = ""; mitigations = ""; - //mandatory_keys.add("result") // for manuals is not required + // mandatory_keys.add("result") // for manuals is not required mandatory_keys.add("name"); mandatory_keys.add("type"); mandatory_keys.add("sessions"); @@ -178,8 +197,8 @@ public void setDescription(String description) { } /** - * This function returns a list of String arrays, containing all the informations of all the matched/unmatched - * messages during the execution of the operations in the test + * This function returns a list of String arrays, containing all the informations of all the + * matched/unmatched messages during the execution of the operations in the test * * @return a list of String array to be put on a table with 5 columns */ @@ -189,13 +208,15 @@ public List getRows() { int count = 0; for (Operation op : operations) { for (HTTPReqRes msg : op.matchedMessages) { - String[] tmp = new String[]{ - String.valueOf(count), - String.valueOf(op.getMessageType()), - "", - op.getChecks().toString(), - msg.index.toString(), - "-"}; // TODO: somehow put if the message made the test fail + String[] tmp = + new String[] { + String.valueOf(count), + String.valueOf(op.getMessageType()), + "", + op.getChecks().toString(), + msg.index.toString(), + "-" + }; // TODO: somehow put if the message made the test fail res.add(tmp); } count++; @@ -207,7 +228,7 @@ public List getRows() { * Set the type of the test * * @param type a String that can be either "passive" or "active" - * @throws ParsingException if the param type is not recognized + * @throws ParsingException if the param type is not recognized * @throws NullPointerException if type is null */ public void setType(String type) throws ParsingException, NullPointerException { @@ -240,8 +261,8 @@ public Session getSession(String session_name) throws ParsingException { } /** - * Function used to logs informations about the test, such as matched messages, all the messages intercepted, - * and sessions + * Function used to logs informations about the test, such as matched messages, all the messages + * intercepted, and sessions * * @param log_folder The folder containing all the mig-t logs */ @@ -328,10 +349,7 @@ public void logTest(String log_folder) { header += "=\t" + "Intercepted from session: " + o.from_session + "\n"; header += "=\t" + "Message name: " + o.getMessageType() + "\n"; header += "==========================================================\n"; - String base_path = matched_folder + - "/operation_" + - op_count + - "_" + o.getMessageType(); + String base_path = matched_folder + "/operation_" + op_count + "_" + o.getMessageType(); for (HTTPReqRes m : o.matchedMessages) { if (m != null) { if (m.getRequest() != null) { @@ -365,17 +383,23 @@ public void logTest(String log_folder) { HashSet logged_requests = new HashSet(); + // TODO HistoryID dovrebbe funzionare ma bisogna controllare + // Save all messages seen by this operation if (o.log_messages != null) { - for (IInterceptedProxyMessage m : o.log_messages) { - if (!logged_requests.contains(m.getMessageReference())) { - byte[] request = m.getMessageInfo().getRequest(); + for (HttpMessage m : o.log_messages) { + if (!logged_requests.contains(m.getHistoryRef().getHistoryId())) { + byte[] request = + concat( + m.getRequestHeader().toString().getBytes(), + m.getResponseBody().getBytes()); if (request != null) { - //log request - File log_message = new File( - all_path - + m.getMessageReference() - + "_request.raw"); + // log request + File log_message = + new File( + all_path + + m.getHistoryRef().getHistoryId() + + "_request.raw"); try { FileWriter fw = new FileWriter(log_message.getAbsoluteFile()); BufferedWriter bw = new BufferedWriter(fw); @@ -385,16 +409,20 @@ public void logTest(String log_folder) { e.printStackTrace(); } } - logged_requests.add(m.getMessageReference()); + logged_requests.add(m.getHistoryRef().getHistoryId()); } - byte[] response = m.getMessageInfo().getResponse(); + byte[] response = + concat( + m.getResponseHeader().toString().getBytes(), + m.getResponseBody().getBytes()); if (response != null) { - //log response - File log_message = new File( - all_path - + m.getMessageReference() - + "_response.raw"); + // log response + File log_message = + new File( + all_path + + m.getHistoryRef().getHistoryId() + + "_response.raw"); try { FileWriter fw = new FileWriter(log_message.getAbsoluteFile()); BufferedWriter bw = new BufferedWriter(fw); @@ -429,11 +457,11 @@ public void logTest(String log_folder) { * Function that execute the given passive test. * * @param messageList a list of HTTPReqRes messages - * @param msg_types the message types used by the test + * @param msg_types the message types used by the test * @return true if a test is passed, false otherwise */ - public boolean execute(List messageList, - List msg_types) throws ParsingException { + public boolean execute(List messageList, List msg_types) + throws ParsingException { int i, j; boolean res = true; @@ -441,7 +469,8 @@ public boolean execute(List messageList, j = 0; while (j < operations.size() && res) { Operation currentOP = operations.get(j); - MessageType msg_type = MessageType.getFromList(msg_types, currentOP.getMessageType()); + MessageType msg_type = + MessageType.getFromList(msg_types, currentOP.getMessageType()); if (currentOP.api == null) { currentOP.api = new Operation_API(vars); @@ -449,8 +478,12 @@ public boolean execute(List messageList, currentOP.api.vars = vars; } - if (messageList.get(i).matches_msg_type(msg_type, msg_type.msg_to_process_is_request)) { - currentOP.setAPI(new Operation_API(messageList.get(i), msg_type.msg_to_process_is_request)); + if (messageList + .get(i) + .matches_msg_type(msg_type, msg_type.msg_to_process_is_request)) { + currentOP.setAPI( + new Operation_API( + messageList.get(i), msg_type.msg_to_process_is_request)); currentOP.execute(); res = currentOP.getResult(); } @@ -471,9 +504,7 @@ public boolean execute(List messageList, return res; } - /** - * The result type of (also the oracle) of an Active test - */ + /** The result type of (also the oracle) of an Active test */ public enum ResultType { CORRECT_FLOW, INCORRECT_FLOW, diff --git a/tool/src/main/java/migt/TestSuite.java b/tool/src/main/java/org/zaproxy/addon/migt/TestSuite.java similarity index 60% rename from tool/src/main/java/migt/TestSuite.java rename to tool/src/main/java/org/zaproxy/addon/migt/TestSuite.java index f290fae..5e4a5bb 100644 --- a/tool/src/main/java/migt/TestSuite.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/TestSuite.java @@ -1,6 +1,23 @@ -package migt; - -import org.apache.commons.text.StringEscapeUtils; +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; import java.io.BufferedWriter; import java.io.File; @@ -9,18 +26,15 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; +import org.apache.commons.text.StringEscapeUtils; -/** - * Class to store a TestSuite - */ +/** Class to store a TestSuite */ public class TestSuite { String name; String description; List tests; - /** - * Instantiate the TestSuite - */ + /** Instantiate the TestSuite */ public TestSuite() { this.name = ""; this.description = ""; @@ -30,9 +44,9 @@ public TestSuite() { /** * Instantiate a TestSuite * - * @param name the name of the test suite + * @param name the name of the test suite * @param description the description of the test suite - * @param tests the list of the tests + * @param tests the list of the tests */ public TestSuite(String name, String description, List tests) { this.name = name; @@ -61,11 +75,18 @@ public void log_test_suite(String log_folder_path) { log_content += "|-----------|-------------|------|--------|------------|\n"; for (Test t : tests) { - log_content += "|" + t.name + - "|" + t.description + - "|" + (t.isActive ? "active" : "passive") + - "|" + t.success + - "|" + t.applicable + "|\n"; + log_content += + "|" + + t.name + + "|" + + t.description + + "|" + + (t.isActive ? "active" : "passive") + + "|" + + t.success + + "|" + + t.applicable + + "|\n"; t.logTest(log_folder_path); } @@ -84,11 +105,16 @@ public void log_test_suite(String log_folder_path) { log_content_csv += "name,description,type,result,applicable\n"; for (Test t : tests) { log_content_csv += - StringEscapeUtils.escapeJava(t.name.replaceAll(",", "")) + "," + - StringEscapeUtils.escapeJava(t.description.replaceAll(",", "")) + "," + - (t.isActive ? "active" : "passive") + "," + - t.success + "," + - t.applicable + "\n"; + StringEscapeUtils.escapeJava(t.name.replaceAll(",", "")) + + "," + + StringEscapeUtils.escapeJava(t.description.replaceAll(",", "")) + + "," + + (t.isActive ? "active" : "passive") + + "," + + t.success + + "," + + t.applicable + + "\n"; } File log_suite_csv = new File(test_log_folder + "results.csv"); @@ -102,7 +128,6 @@ public void log_test_suite(String log_folder_path) { } } - public List getTests() { return tests; } diff --git a/tool/src/main/java/migt/Tools.java b/tool/src/main/java/org/zaproxy/addon/migt/Tools.java similarity index 65% rename from tool/src/main/java/migt/Tools.java rename to tool/src/main/java/org/zaproxy/addon/migt/Tools.java index a80f67e..a74de71 100644 --- a/tool/src/main/java/migt/Tools.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/Tools.java @@ -1,14 +1,29 @@ -package migt; +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; import com.google.gson.JsonElement; import com.google.gson.JsonParser; import com.jayway.jsonpath.Configuration; import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.PathNotFoundException; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; @@ -16,23 +31,24 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; -/** - * Class with methods to process messages and execute tests - */ +/** Class with methods to process messages and execute tests */ public class Tools { /** - * This function execute a list of checks over a message, returning true if all the checks are successful + * This function execute a list of checks over a message, returning true if all the checks are + * successful * - * @param checks a List of checks - * @param message the message to be checked + * @param checks a List of checks + * @param message the message to be checked * @param isRequest set true if the request has to be checked, false for the response * @return returns the result of the checks (true if all the tests are successful) */ - public static boolean executeChecks(List checks, - HTTPReqRes message, - boolean isRequest, - List vars) throws ParsingException { + public static boolean executeChecks( + List checks, HTTPReqRes message, boolean isRequest, List vars) + throws ParsingException { for (Check c : checks) { if (!c.execute(message, isRequest, vars)) { return false; @@ -52,8 +68,7 @@ public static Operation executeChecks(Operation op, List vars) throws Parsi for (Check c : op.getChecks()) { c.loader(op.api); c.execute(vars); - if (!op.setResult(c)) - break; + if (!op.setResult(c)) break; } return op; } @@ -65,13 +80,11 @@ public static Operation executeChecks(Operation op, List vars) throws Parsi * @return The operation (edited) * @throws ParsingException if something goes wrong */ - public static Operation executeDecodeOps(Operation op, - List vars) throws ParsingException { + public static Operation executeDecodeOps(Operation op, List vars) throws ParsingException { for (DecodeOperation dop : op.getDecodeOperations()) { dop.loader(op.getAPI()); dop.execute(vars); - if (!op.setResult(dop)) - break; + if (!op.setResult(dop)) break; op.setAPI((Operation_API) dop.exporter()); } @@ -85,13 +98,12 @@ public static Operation executeDecodeOps(Operation op, * @return The operation (edited) * @throws ParsingException if something goes wrong */ - public static DecodeOperation executeDecodeOps(DecodeOperation op, - List vars) throws ParsingException { + public static DecodeOperation executeDecodeOps(DecodeOperation op, List vars) + throws ParsingException { for (DecodeOperation dop : op.decodeOperations) { dop.loader(op.getAPI()); dop.execute(vars); - if (!op.setResult(dop)) - break; + if (!op.setResult(dop)) break; op.setAPI((DecodeOperation_API) dop.exporter()); } @@ -105,13 +117,12 @@ public static DecodeOperation executeDecodeOps(DecodeOperation op, * @return the Decode operation (edited) * @throws ParsingException if something goes wrong */ - public static DecodeOperation executeEditOps(DecodeOperation op, - List vars) throws ParsingException { + public static DecodeOperation executeEditOps(DecodeOperation op, List vars) + throws ParsingException { for (EditOperation eop : op.editOperations) { eop.loader(op.getAPI()); eop.execute(vars); - if (!op.setResult(eop)) - break; + if (!op.setResult(eop)) break; op.setAPI(eop.exporter()); } @@ -129,8 +140,7 @@ public static Operation executeEditOps(Operation op, List vars) throws Pars for (EditOperation eop : op.editOperations) { eop.loader(op.getAPI()); eop.execute(vars); - if (!op.setResult(eop)) - break; + if (!op.setResult(eop)) break; op.setAPI((Operation_API) eop.exporter()); } @@ -142,8 +152,7 @@ public static Operation executeMessageOperations(Operation op) throws ParsingExc mop.loader(op.api); mop.execute(); op.setAPI(mop.exporter()); - if (!op.setResult(mop)) - break; + if (!op.setResult(mop)) break; } return op; } @@ -176,7 +185,8 @@ public static List parseChecksFromJSON(JSONArray checks_array) throws Par * @return the parsed list of Edit operations * @throws ParsingException if there are problems parsing the JSON array */ - public static List parseEditsFromJSON(JSONArray edits_array) throws ParsingException { + public static List parseEditsFromJSON(JSONArray edits_array) + throws ParsingException { List res = new ArrayList<>(); for (int i = 0; i < edits_array.length(); i++) { JSONObject act_edit = edits_array.getJSONObject(i); @@ -223,7 +233,8 @@ public static List readMsgTypesFromJson(String input) throws Parsin if (act_msg_type.has("checks")) { msg_obj.checks = parseChecksFromJSON(act_msg_type.getJSONArray("checks")); } else { - throw new ParsingException("message type definition is invalid, no checks or regex found"); + throw new ParsingException( + "message type definition is invalid, no checks or regex found"); } msg_types.add(msg_obj); } @@ -232,78 +243,79 @@ public static List readMsgTypesFromJson(String input) throws Parsin } /** - * Returns the default string that contains the default message types that fill a msg_def.json file + * Returns the default string that contains the default message types that fill a msg_def.json + * file * * @return the string */ public static String getDefaultJSONMsgType() { - return "{\n" + - " \"message_types\": [\n" + - " {\n" + - " \"name\": \"authorization request\",\n" + - " \"is request\": true,\n" + - " \"response name\": \"authorization response\",\n" + - " \"checks\": [\n" + - " {\n" + - " \"in\": \"url\",\n" + - " \"check param\": \"response_type\",\n" + - " \"is present\": \"true\"\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"name\": \"token request\",\n" + - " \"is request\": true,\n" + - " \"response name\": \"token response\",\n" + - " \"checks\": [\n" + - " {\n" + - " \"in\": \"url\",\n" + - " \"check param\": \"code\",\n" + - " \"is present\": \"true\"\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"name\": \"coda landing request\",\n" + - " \"is request\": true,\n" + - " \"response name\": \"coda landing response\",\n" + - " \"checks\": [\n" + - " {\n" + - " \"in\": \"url\",\n" + - " \"check\": \"/welcome\",\n" + - " \"is present\": \"true\"\n" + - " },\n" + - " {\n" + - " \"in\": \"head\",\n" + - " \"check\": \"Host\",\n" + - " \"is\": \"coda.io\"\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"name\": \"saml request\",\n" + - " \"is request\": true,\n" + - " \"checks\": [\n" + - " {\n" + - " \"in\": \"url\",\n" + - " \"check param\": \"SAMLRequest\",\n" + - " \"is present\": true\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"name\": \"saml response\",\n" + - " \"is request\": true,\n" + - " \"checks\": [\n" + - " {\n" + - " \"in\": \"body\",\n" + - " \"check param\": \"SAMLResponse\",\n" + - " \"is present\": true\n" + - " }\n" + - " ]\n" + - " }\n" + - " ]\n" + - "}"; + return "{\n" + + " \"message_types\": [\n" + + " {\n" + + " \"name\": \"authorization request\",\n" + + " \"is request\": true,\n" + + " \"response name\": \"authorization response\",\n" + + " \"checks\": [\n" + + " {\n" + + " \"in\": \"url\",\n" + + " \"check param\": \"response_type\",\n" + + " \"is present\": \"true\"\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"name\": \"token request\",\n" + + " \"is request\": true,\n" + + " \"response name\": \"token response\",\n" + + " \"checks\": [\n" + + " {\n" + + " \"in\": \"url\",\n" + + " \"check param\": \"code\",\n" + + " \"is present\": \"true\"\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"name\": \"coda landing request\",\n" + + " \"is request\": true,\n" + + " \"response name\": \"coda landing response\",\n" + + " \"checks\": [\n" + + " {\n" + + " \"in\": \"url\",\n" + + " \"check\": \"/welcome\",\n" + + " \"is present\": \"true\"\n" + + " },\n" + + " {\n" + + " \"in\": \"head\",\n" + + " \"check\": \"Host\",\n" + + " \"is\": \"coda.io\"\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"name\": \"saml request\",\n" + + " \"is request\": true,\n" + + " \"checks\": [\n" + + " {\n" + + " \"in\": \"url\",\n" + + " \"check param\": \"SAMLRequest\",\n" + + " \"is present\": true\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"name\": \"saml response\",\n" + + " \"is request\": true,\n" + + " \"checks\": [\n" + + " {\n" + + " \"in\": \"body\",\n" + + " \"check param\": \"SAMLResponse\",\n" + + " \"is present\": true\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + "}"; } /** @@ -312,11 +324,11 @@ public static String getDefaultJSONMsgType() { * @return the string */ public static String getDefaultJSONConfig() { - return "{\n" + - " \"last_driver_path\":\"\",\n" + - " \"last_browser_used\": \"\"\n" + - " \"default_port\":8080\n" + - "}"; + return "{\n" + + " \"last_driver_path\":\"\",\n" + + " \"last_browser_used\": \"\"\n" + + " \"default_port\":8080\n" + + "}"; } /** @@ -336,7 +348,7 @@ public static String removeNewline(String input) { * Builds a string, substituting variables names with values * * @param vars the list of variables to use - * @param s the string + * @param s the string * @return the builded string * @throws ParsingException if a variable is not found */ @@ -390,39 +402,44 @@ public static Var getVariableByName(String name, List vars) throws ParsingE */ public static String generate_CSRF_POC(HTTPReqRes message) { - String CSFR_TEMPLATE = "\n" + - "\n" + - " \n" + - "

Attack Page

\n" + - "

Service Provider (SP) is your service.

\n" + - "

Identity Provider (IdP) is the provider with which the SP allows to associate the account.

\n" + - "

These are the steps to reproduce the attack:

\n" + - "

1. The victim clicks on button to initiate force-login to IdP and victim logs in as the attacker because IdP suffering of Pre-Authentication Login CSRF. To simulate this step, the victim logs in with the attacker's IdP credentials.

\n" + - "

2. The victim logins at SP with victim credentials.

\n" + - "

3. The victim clicks on following link which suffers of CSRF, to associate attacker IdP account with the Victim SP account.

\n" + - " $INSERT_HERE$\n" + - "
\n" + - "

4. If the IdP attacker account has been associated with the victim SP account then the vulnerability has been properly exploited.

\n" + - " \n" + - ""; + String CSFR_TEMPLATE = + "\n" + + "\n" + + " \n" + + "

Attack Page

\n" + + "

Service Provider (SP) is your service.

\n" + + "

Identity Provider (IdP) is the provider with which the SP allows to associate the account.

\n" + + "

These are the steps to reproduce the attack:

\n" + + "

1. The victim clicks on button to initiate force-login to IdP and victim logs in as the attacker because IdP suffering of Pre-Authentication Login CSRF. To simulate this step, the victim logs in with the attacker's IdP credentials.

\n" + + "

2. The victim logins at SP with victim credentials.

\n" + + "

3. The victim clicks on following link which suffers of CSRF, to associate attacker IdP account with the Victim SP account.

\n" + + " $INSERT_HERE$\n" + + "
\n" + + "

4. If the IdP attacker account has been associated with the victim SP account then the vulnerability has been properly exploited.

\n" + + " \n" + + ""; String POST_TEMPLATE = - "
\n" + - " \n" + - " $BODY_PARAMETERS$\n" + - "
\n" + - " \n" + - "
\n"; - - String TEMPLATE_BODY_PARAMS = " \n" + - " $PARAM_NAME$\n" + - " \n" + - " \n" + - " \n" + - " "; + "
\n" + + " \n" + + " $BODY_PARAMETERS$\n" + + "
\n" + + " \n" + + "
\n"; + + String TEMPLATE_BODY_PARAMS = + " \n" + + " $PARAM_NAME$\n" + + " \n" + + " \n" + + " \n" + + " "; String encoding = message.getHeadParam(true, "Content-Type").strip(); - String body = new String(message.getBody(true), StandardCharsets.UTF_8); // splitMessage(message, helpers, true).get(2); + String body = + new String( + message.getBody(true), + StandardCharsets.UTF_8); // splitMessage(message, helpers, true).get(2); String url = message.getUrl(); String method = message.getUrlHeader().split(" ")[0]; @@ -451,8 +468,7 @@ public static String generate_CSRF_POC(HTTPReqRes message) { String name = m.group(1); String value = m.group(2); if (name.length() != 0) { - body_params.put(name, - value.length() != 0 ? value : ""); + body_params.put(name, value.length() != 0 ? value : ""); } } for (String key : body_params.keySet()) { @@ -532,10 +548,11 @@ public static String generate_CSRF_POC(HTTPReqRes message) { /** * Create batches of passive tests, grouping them by the session they need to execute. * - * @return An HashMap object having as keys, Strings representing the sessions names, and as value a list of tests - * that need to execute that session + * @return An HashMap object having as keys, Strings representing the sessions names, and as + * value a list of tests that need to execute that session */ - public static HashMap> batchPassivesFromSession(List testList) throws ParsingException { + public static HashMap> batchPassivesFromSession(List testList) + throws ParsingException { HashMap> batch = new HashMap<>(); for (Test t : testList) { if (t.sessions.isEmpty()) { @@ -574,20 +591,22 @@ public static List debatchPassive(HashMap> batch) { /** * Edit a message treating it as a string using a regex * - * @param regex the regex used to match the things to change - * @param mop the message operation containing information about the section to match the regex + * @param regex the regex used to match the things to change + * @param mop the message operation containing information about the section to match the regex * @param messageInfo the message as IHttpRequestResponse object - * @param isRequest specify if the message to consider is the request or response - * @param new_value the new value to substitute to the message section + * @param isRequest specify if the message to consider is the request or response + * @param new_value the new value to substitute to the message section * @return the edited message as byte array * @throws ParsingException if problems are encountered in editing the message */ - public static byte[] editMessage(String regex, - MessageOperation mop, - HTTPReqRes messageInfo, - boolean isRequest, - String new_value) throws ParsingException { - //TODO: remove in future versions + public static byte[] editMessage( + String regex, + MessageOperation mop, + HTTPReqRes messageInfo, + boolean isRequest, + String new_value) + throws ParsingException { + // TODO: remove in future versions Pattern pattern = null; Matcher matcher = null; switch (mop.from) { @@ -608,7 +627,7 @@ public static byte[] editMessage(String regex, matcher = pattern.matcher(new String(messageInfo.getBody(isRequest))); messageInfo.setBody(isRequest, matcher.replaceAll(new_value)); - //Automatically update content-lenght + // Automatically update content-lenght return messageInfo.getMessage(isRequest); case URL: @@ -629,30 +648,34 @@ public static byte[] editMessage(String regex, /** * Edit a message parameter * - * @param param_name the name of the parameter to edit + * @param param_name the name of the parameter to edit * @param message_section the message section to edit - * @param messageInfo the message as IHttpRequestResponse object - * @param isRequest specify if the message to consider is the request or response - * @param new_value the new value of the parameter - * @param isBodyRegex when the section is body, set it to true if you want to use a regex to substitute the value, - * otherwise a parameter param=... is searched + * @param messageInfo the message as IHttpRequestResponse object + * @param isRequest specify if the message to consider is the request or response + * @param new_value the new value of the parameter + * @param isBodyRegex when the section is body, set it to true if you want to use a regex to + * substitute the value, otherwise a parameter param=... is searched * @return the edited message as byte array * @throws ParsingException if problems are encountered in editing the message */ - public static byte[] editMessageParam(String param_name, - HTTPReqRes.MessageSection message_section, - HTTPReqRes messageInfo, - boolean isRequest, - String new_value, - boolean isBodyRegex) throws ParsingException { - //TODO: remove in future versions + public static byte[] editMessageParam( + String param_name, + HTTPReqRes.MessageSection message_section, + HTTPReqRes messageInfo, + boolean isRequest, + String new_value, + boolean isBodyRegex) + throws ParsingException { + // TODO: remove in future versions Pattern pattern = null; Matcher matcher = null; switch (message_section) { case HEAD: messageInfo.editHeadParam(isRequest, param_name, new_value); byte[] message = messageInfo.getMessage(isRequest); - messageInfo.setHost(new_value); // this should be set when the message is converted to the burp class + messageInfo.setHost( + new_value); // this should be set when the message is converted to the burp + // class return message; case BODY: @@ -665,7 +688,7 @@ public static byte[] editMessageParam(String param_name, matcher = pattern.matcher(new String(messageInfo.getBody(isRequest))); String new_body = matcher.replaceFirst(new_value); messageInfo.setBody(isRequest, new_body); - //Automatically update content-lenght + // Automatically update content-lenght return messageInfo.getMessage(isRequest); case URL: @@ -677,7 +700,8 @@ public static byte[] editMessageParam(String param_name, pattern = Pattern.compile(Pattern.quote(param_name) + "=[^& ]*((?=&)|(?= ))"); matcher = pattern.matcher(url_header); - messageInfo.setUrlHeader(matcher.replaceAll(param_name + "=" + new_value)); // problema + messageInfo.setUrlHeader( + matcher.replaceAll(param_name + "=" + new_value)); // problema return messageInfo.getMessage(isRequest); } @@ -703,7 +727,8 @@ public static String findParentDiv(String in) throws ParsingException { int cut_indx = -1; - //-2 because if the last element is a div i don't take it, otherwise is not a div, so i don't take it + // -2 because if the last element is a div i don't take it, otherwise is not a div, so i + // don't take it for (int i = split.length - 2; i > 0; i--) { if (split[i].contains("div")) { cut_indx = i; @@ -728,21 +753,23 @@ public static String findParentDiv(String in) throws ParsingException { /** * Given a json string and a json path, edit the json. * - * @param action the action to do, (edit, remove, add, or save) + * @param action the action to do, (edit, remove, add, or save) * @param content the json content as string - * @param j_path the json path as string + * @param j_path the json path as string * @param save_as the name of the variable if the action is save * @return the edited json * @throws PathNotFoundException if the path in the json is not found */ - public static String editJson(EditOperation.Jwt_action action, - String content, - String j_path, - List vars, - String save_as, - String newValue, - String newKey) throws PathNotFoundException { - //TODO: remove in future versions + public static String editJson( + EditOperation.Jwt_action action, + String content, + String j_path, + List vars, + String save_as, + String newValue, + String newKey) + throws PathNotFoundException { + // TODO: remove in future versions Object document = Configuration.defaultConfiguration().jsonProvider().parse(content); JsonPath jsonPath = JsonPath.compile(j_path); @@ -754,11 +781,12 @@ public static String editJson(EditOperation.Jwt_action action, document = jsonPath.set(document, newValue, Configuration.defaultConfiguration()); break; case ADD: - document = jsonPath.put( - document, - newKey, - newValue.isEmpty() ? null : newValue, - Configuration.defaultConfiguration()); + document = + jsonPath.put( + document, + newKey, + newValue.isEmpty() ? null : newValue, + Configuration.defaultConfiguration()); break; case SAVE: Object to_save = JsonPath.read(content, j_path); @@ -780,18 +808,21 @@ public static String editJson(EditOperation.Jwt_action action, break; } - // This difference has been added because the parsed documed removed backslashes from the content even if it + // This difference has been added because the parsed documed removed backslashes from the + // content even if it // was not modified (like if it was just a save) if (!(action == EditOperation.Jwt_action.SAVE)) { - return Configuration.defaultConfiguration().jsonProvider().toJson(document); //basically converts to string + return Configuration.defaultConfiguration() + .jsonProvider() + .toJson(document); // basically converts to string } else { return content; } } /** - * Checks that the json resulting by parsing the two strings is equals. Equals in this case means that the same keys - * and values are present, but the order is ignored + * Checks that the json resulting by parsing the two strings is equals. Equals in this case + * means that the same keys and values are present, but the order is ignored * * @param s1 the string 1 * @param s2 the string 2 diff --git a/tool/src/main/java/migt/Track.java b/tool/src/main/java/org/zaproxy/addon/migt/Track.java similarity index 69% rename from tool/src/main/java/migt/Track.java rename to tool/src/main/java/org/zaproxy/addon/migt/Track.java index 2c8a9ce..e21638b 100644 --- a/tool/src/main/java/migt/Track.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/Track.java @@ -1,15 +1,40 @@ -package migt; +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; +import java.util.Objects; /** - * This class represents the track of a session. The track is a list of all the user actions to be done during a test + * This class represents the track of a session. The track is a list of all the user actions to be + * done during a test */ public class Track { private final List track; + public int hashCode() { + return Objects.hash(track); + } + /** * Instantiate a new Track object, starting from a raw session track * @@ -31,8 +56,8 @@ public Track(String raw) throws ParsingException { } /** - * Updates the indexes of the first and last element of the track. This function has to be called each time the - * list is updated. + * Updates the indexes of the first and last element of the track. This function has to be + * called each time the list is updated. */ private void updateIndexes() { for (SessionTrackAction sta : track) { @@ -46,7 +71,6 @@ private void updateIndexes() { track.get(track.size() - 1).markers.add(new Marker("ML")); } - public List getTrack() { return track; } @@ -54,7 +78,7 @@ public List getTrack() { /** * Get the index of the first User Action having the given marker. * - * @param marker_name The name of the marker to search + * @param marker_name The name of the marker to search * @param start_from_last true if you want to start searching from the last element * @return the index */ @@ -92,26 +116,28 @@ public int indexOfStaFromMarker(String marker_name, boolean start_from_last) { } /** - * Return the index of the first user action which has the markerFrom, and the index of the first user action having - * the marker to (searched from the end of the track) + * Return the index of the first user action which has the markerFrom, and the index of the + * first user action having the marker to (searched from the end of the track) * - * @param markerFrom The left range marker to search for - * @param markerTo The right range marker to search for + * @param markerFrom The left range marker to search for + * @param markerTo The right range marker to search for * @param is_from_included true if the left range element is included - * @param is_to_included true if the right range element is included - * @return an array of int of length 2, containing the left index in position 0, and right index in position 1 + * @param is_to_included true if the right range element is included + * @return an array of int of length 2, containing the left index in position 0, and right index + * in position 1 * @throws ParsingException If the markers are not found */ - public int[] getStasIndexFromRange(String markerFrom, - String markerTo, - boolean is_from_included, - boolean is_to_included) throws ParsingException { - int indx_from = is_from_included ? - indexOfStaFromMarker(markerFrom, false) : - indexOfStaFromMarker(markerFrom, false) + 1; - int indx_to = is_to_included ? - indexOfStaFromMarker(markerTo, true) : - indexOfStaFromMarker(markerTo, true) - 1; + public int[] getStasIndexFromRange( + String markerFrom, String markerTo, boolean is_from_included, boolean is_to_included) + throws ParsingException { + int indx_from = + is_from_included + ? indexOfStaFromMarker(markerFrom, false) + : indexOfStaFromMarker(markerFrom, false) + 1; + int indx_to = + is_to_included + ? indexOfStaFromMarker(markerTo, true) + : indexOfStaFromMarker(markerTo, true) - 1; if (indx_from == -1) throw new ParsingException("Invalid from marker"); @@ -123,19 +149,20 @@ public int[] getStasIndexFromRange(String markerFrom, } /** - * Given a range represented by two markers, return the User actions in that range from the track. + * Given a range represented by two markers, return the User actions in that range from the + * track. * - * @param markerFrom the left range marker to search for - * @param markerTo the right range marker to search for (starting from the end of the track backward) + * @param markerFrom the left range marker to search for + * @param markerTo the right range marker to search for (starting from the end of the track + * backward) * @param is_from_included true if the left range element should be included - * @param is_to_included true if the right range element should be included + * @param is_to_included true if the right range element should be included * @return a list of User Actions in that range * @throws ParsingException if markers are not found */ - public List getStasFromMarkers(String markerFrom, - String markerTo, - boolean is_from_included, - boolean is_to_included) throws ParsingException { + public List getStasFromMarkers( + String markerFrom, String markerTo, boolean is_from_included, boolean is_to_included) + throws ParsingException { int[] range = getStasIndexFromRange(markerFrom, markerTo, is_from_included, is_to_included); @@ -157,11 +184,10 @@ public List getStasFromMarkers(String markerFrom, * Mark a given user action with a marker * * @param to_be_marked the User Action to be marked - * @param marker_name the name of the marker + * @param marker_name the name of the marker * @throws ParsingException If the User Action is not found in the track */ - public void mark(SessionTrackAction to_be_marked, - String marker_name) throws ParsingException { + public void mark(SessionTrackAction to_be_marked, String marker_name) throws ParsingException { int indx = track.indexOf(to_be_marked); if (indx == -1) throw new ParsingException("Cannot find previous action, maybe not yet occurred?"); @@ -174,15 +200,14 @@ public void mark(SessionTrackAction to_be_marked, /** * Insert an User Action in a given position of the track * - * @param at the marker used as a reference to insert the user action to + * @param at the marker used as a reference to insert the user action to * @param to_be_inserted the User Action to insert in string format * @throws ParsingException if marker non present in track or action malformed */ public void insert(Marker at, String to_be_inserted) throws ParsingException { int indx = indexOfStaFromMarker(at.name, false) + 1; - if (at.name.equals("M0")) - indx = 0; + if (at.name.equals("M0")) indx = 0; if (to_be_inserted.contains("\n")) { // If there are multiple actions in the String diff --git a/tool/src/main/java/migt/Var.java b/tool/src/main/java/org/zaproxy/addon/migt/Var.java similarity index 71% rename from tool/src/main/java/migt/Var.java rename to tool/src/main/java/org/zaproxy/addon/migt/Var.java index 4eab5db..44e08ff 100644 --- a/tool/src/main/java/migt/Var.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/Var.java @@ -1,15 +1,30 @@ -package migt; - -/** - * The class storing the variables used in the test and sessions +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. */ +package org.zaproxy.addon.migt; + +/** The class storing the variables used in the test and sessions */ public class Var { public String name; public Object value; - /** - * Instantiate a Var object - */ + /** Instantiate a Var object */ public Var() { this.name = ""; this.value = ""; @@ -18,7 +33,7 @@ public Var() { /** * Constructor for variables that have a String Value * - * @param name the name of the var + * @param name the name of the var * @param value the String value of the var */ public Var(String name, String value) { @@ -29,7 +44,7 @@ public Var(String name, String value) { /** * Constructor for variables that have a message value * - * @param name the name of the variable + * @param name the name of the variable * @param message the value of the message as String */ public Var(String name, byte[] message) { @@ -40,7 +55,7 @@ public Var(String name, byte[] message) { /** * Constructor for variable that have a JSON array value * - * @param name the name of the variable + * @param name the name of the variable * @param value the value of the JSON array */ public Var(String name, Object[] value) { @@ -91,7 +106,8 @@ public String get_value_string() throws ParsingException { } /** - * Use this function to get the value of this variable expecting that it is storing an HTTP message + * Use this function to get the value of this variable expecting that it is storing an HTTP + * message * * @return the http message in byte[] format * @throws ParsingException if the value of the variable is not a message @@ -104,9 +120,7 @@ public byte[] get_value_message() throws ParsingException { return (byte[]) value; } - /** - * Enum containing all the possible types of variables - */ + /** Enum containing all the possible types of variables */ public enum VarType { STRING, MESSAGE, diff --git a/tool/src/main/java/migt/XML.java b/tool/src/main/java/org/zaproxy/addon/migt/XML.java similarity index 71% rename from tool/src/main/java/migt/XML.java rename to tool/src/main/java/org/zaproxy/addon/migt/XML.java index 464d7bc..33bd512 100644 --- a/tool/src/main/java/migt/XML.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/XML.java @@ -1,11 +1,27 @@ -package migt; - -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; @@ -14,13 +30,13 @@ import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; -import java.io.IOException; -import java.io.StringReader; -import java.io.StringWriter; +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; -/** - * Class used to parse and edit xml strings - */ +/** Class used to parse and edit xml strings */ public class XML { /** @@ -29,10 +45,11 @@ public class XML { * @param input the xml string * @return the Document object * @throws ParserConfigurationException if something goes wrong - * @throws IOException if something goes wrong - * @throws SAXException if something goes wrong + * @throws IOException if something goes wrong + * @throws SAXException if something goes wrong */ - public static Document documentFromString(String input) throws ParserConfigurationException, IOException, SAXException { + public static Document documentFromString(String input) + throws ParserConfigurationException, IOException, SAXException { DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); Document doc = docBuilder.parse(new InputSource(new StringReader(input))); @@ -46,7 +63,7 @@ public static Document documentFromString(String input) throws ParserConfigurati * @return the xml string */ public static String stringFromDocument(Document doc) { - //https://stackoverflow.com/questions/2567416/xml-document-to-string + // https://stackoverflow.com/questions/2567416/xml-document-to-string try { StringWriter sw = new StringWriter(); TransformerFactory tf = TransformerFactory.newInstance(); @@ -64,23 +81,30 @@ public static String stringFromDocument(Document doc) { } /** - * This function edits the given tag attribute from an xml string into a new value, and returns the xml string + * This function edits the given tag attribute from an xml string into a new value, and returns + * the xml string * - * @param input the xml string - * @param tag the tag of the attribute + * @param input the xml string + * @param tag the tag of the attribute * @param attribute the attribute to be edited - * @param newValue the new value of the attribute - * @param occurency tells the index of the matched occurency to do the thing on, set it to -1 to do for all occurencies + * @param newValue the new value of the attribute + * @param occurency tells the index of the matched occurency to do the thing on, set it to -1 to + * do for all occurencies * @return the edited xml */ - public static String editTagAttributes(String input, String tag, String attribute, String newValue, Integer occurency) throws ParsingException { + public static String editTagAttributes( + String input, String tag, String attribute, String newValue, Integer occurency) + throws ParsingException { try { Document doc = documentFromString(input); NodeList matched = doc.getElementsByTagName(tag); if (occurency != -1) { - matched.item(occurency).getAttributes().getNamedItem(attribute).setTextContent(newValue); + matched.item(occurency) + .getAttributes() + .getNamedItem(attribute) + .setTextContent(newValue); } else { for (int i = 0; i < matched.getLength(); i++) { Node act_node = matched.item(i); @@ -99,15 +123,18 @@ public static String editTagAttributes(String input, String tag, String attribut } /** - * This function edits the given tag value from an xml string into a new value and returns the edited xml string + * This function edits the given tag value from an xml string into a new value and returns the + * edited xml string * - * @param input the xml string - * @param tag the tag to be edited - * @param newValue the new value of the tag - * @param occurency tells the index of the matched occurency to do the thing on, set it to -1 to do for all occurencies + * @param input the xml string + * @param tag the tag to be edited + * @param newValue the new value of the tag + * @param occurency tells the index of the matched occurency to do the thing on, set it to -1 to + * do for all occurencies * @return the edited xml */ - public static String editTagValue(String input, String tag, String newValue, Integer occurency) throws ParsingException { + public static String editTagValue(String input, String tag, String newValue, Integer occurency) + throws ParsingException { try { Document doc = documentFromString(input); @@ -135,12 +162,14 @@ public static String editTagValue(String input, String tag, String newValue, Int /** * This function removes a tag from an xml string * - * @param input the input xml string - * @param tag the tag to be removed - * @param occurency tells the index of the matched occurency to do the thing on, set it to -1 to do for all occurencies + * @param input the input xml string + * @param tag the tag to be removed + * @param occurency tells the index of the matched occurency to do the thing on, set it to -1 to + * do for all occurencies * @return the xml result */ - public static String removeTag(String input, String tag, Integer occurency) throws ParsingException { + public static String removeTag(String input, String tag, Integer occurency) + throws ParsingException { try { Document doc = documentFromString(input); @@ -168,13 +197,15 @@ public static String removeTag(String input, String tag, Integer occurency) thro /** * This function removes a tag's attribute from an xml string * - * @param input the xml string - * @param tag the tag in which contains the attribute + * @param input the xml string + * @param tag the tag in which contains the attribute * @param attribute the attribute to be removed - * @param occurency tells the index of the matched occurency to do the thing on, set it to -1 to do for all occurencies + * @param occurency tells the index of the matched occurency to do the thing on, set it to -1 to + * do for all occurencies * @return the edited xml string */ - public static String removeTagAttribute(String input, String tag, String attribute, Integer occurency) throws ParsingException { + public static String removeTagAttribute( + String input, String tag, String attribute, Integer occurency) throws ParsingException { try { Document doc = documentFromString(input); @@ -202,14 +233,17 @@ public static String removeTagAttribute(String input, String tag, String attribu /** * This function adds an attribute in a tag from an xml string * - * @param input the xml string - * @param tag the tag to add the attribute - * @param attribute the attribute name to be added - * @param value the attribute value to be added - * @param occurrency tells the index of the matched occurency to do the thing on, set it to -1 to do for all occurencies + * @param input the xml string + * @param tag the tag to add the attribute + * @param attribute the attribute name to be added + * @param value the attribute value to be added + * @param occurrency tells the index of the matched occurency to do the thing on, set it to -1 + * to do for all occurencies * @return the edited xml string */ - public static String addTagAttribute(String input, String tag, String attribute, String value, Integer occurrency) throws ParsingException { + public static String addTagAttribute( + String input, String tag, String attribute, String value, Integer occurrency) + throws ParsingException { try { Document doc = documentFromString(input); @@ -236,20 +270,22 @@ public static String addTagAttribute(String input, String tag, String attribute, } catch (NullPointerException ne) { throw new ParsingException("invalid occurency"); } - } /** * This function adds a tag specifing it's parent * - * @param input the xml string - * @param parent the parent tag to add the new tag as a child - * @param tag the new tag name - * @param value the new tag value - * @param occurency tells the index of the matched occurency to do the thing on, set it to -1 to do for all occurencies + * @param input the xml string + * @param parent the parent tag to add the new tag as a child + * @param tag the new tag name + * @param value the new tag value + * @param occurency tells the index of the matched occurency to do the thing on, set it to -1 to + * do for all occurencies * @return the edited xml string */ - public static String addTag(String input, String parent, String tag, String value, Integer occurency) throws ParsingException { + public static String addTag( + String input, String parent, String tag, String value, Integer occurency) + throws ParsingException { try { Document doc = documentFromString(input); @@ -281,13 +317,15 @@ public static String addTag(String input, String parent, String tag, String valu /** * This function returns a value of a given tag's attribute * - * @param input the input xml string - * @param tag the tag containing the attribute + * @param input the input xml string + * @param tag the tag containing the attribute * @param attribute the attribute - * @param occurency tells the index of the matched occurency to do the thing on, set it to -1 to do for all occurencies + * @param occurency tells the index of the matched occurency to do the thing on, set it to -1 to + * do for all occurencies * @return the attribute value */ - public static String getTagAttributeValue(String input, String tag, String attribute, Integer occurency) throws ParsingException { + public static String getTagAttributeValue( + String input, String tag, String attribute, Integer occurency) throws ParsingException { try { Document doc = documentFromString(input); Node matched = null; @@ -310,12 +348,14 @@ public static String getTagAttributeValue(String input, String tag, String attri /** * This fucntion returns the value of a given tag * - * @param input the input xml string - * @param tag the tag name - * @param occurency tells the index of the matched occurency to do the thing on, set it to -1 to do for all occurencies + * @param input the input xml string + * @param tag the tag name + * @param occurency tells the index of the matched occurency to do the thing on, set it to -1 to + * do for all occurencies * @return the tag value */ - public static String getTagValaue(String input, String tag, Integer occurency) throws ParsingException { + public static String getTagValaue(String input, String tag, Integer occurency) + throws ParsingException { try { Document doc = documentFromString(input); Node matched = null; diff --git a/tool/src/main/java/org/zaproxy/addon/migt/ZAPPrincipale.java b/tool/src/main/java/org/zaproxy/addon/migt/ZAPPrincipale.java new file mode 100644 index 0000000..bb3f243 --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/ZAPPrincipale.java @@ -0,0 +1,432 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2024 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt; + +import java.awt.BorderLayout; +import java.awt.GridLayout; +import java.io.PrintStream; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.Objects; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.parosproxy.paros.core.proxy.ProxyListener; +import org.parosproxy.paros.db.DatabaseException; +import org.parosproxy.paros.extension.AbstractPanel; +import org.parosproxy.paros.extension.ExtensionAdaptor; +import org.parosproxy.paros.extension.ExtensionHook; +import org.parosproxy.paros.model.HistoryReference; +import org.parosproxy.paros.model.Model; +import org.parosproxy.paros.network.HttpMalformedHeaderException; +import org.parosproxy.paros.network.HttpMessage; + +/** Main class executed by ZAP */ +public class ZAPPrincipale extends ExtensionAdaptor implements ProxyListener { + + public static PrintStream printStream; + public static PrintStream errorStream; + private GUIclass mainPane; // The GUI + private AbstractPanel statusPanel = null; // wrap per OWASP ZAP + + public static final String NAME = "MIGT"; + protected static final String PREFIX = "migt"; + + private static final Logger LOGGER = LogManager.getLogger(ZAPPrincipale.class); + + public ZAPPrincipale() { + super(NAME); + setI18nPrefix(PREFIX); + } + + /** Main function creating the extension */ + @Override + public void hook(ExtensionHook extensionHook) { + System.out.println("Initializing extension"); + super.hook(extensionHook); + + extensionHook.addProxyListener(this); + mainPane = new GUIclass(); + + // As long as we're not running as a daemon + if (hasView()) { + extensionHook.getHookView().addStatusPanel(getStatusPanel(mainPane)); + } + } + + private AbstractPanel getStatusPanel(GUIclass _mainPane_) { + if (statusPanel == null) { + SwingUtilities.invokeLater( + () -> { + statusPanel = new AbstractPanel(); + statusPanel.setLayout(new BorderLayout()); + statusPanel.setName("MIG-T"); + statusPanel.setIcon( + new ImageIcon(getClass().getResource("resources/logo.png"))); + + // //setup output stream in Burp + // OutputStream stdOut = callbacks.getStdout(); + // OutputStream stdErr = callbacks.getStderr(); + // printStream = new PrintStream(stdOut); + // errorStream = new PrintStream(stdErr); + + // questo dovreppe permettere di testare il funzionamento ma potrebbe + // implicare il reindirizzamento + // di tutto lo stderr e stdout di ZAP al nostro pannello + + // OutputStream stdOut = System.out; + // OutputStream stdErr = System.err; + // printStream = new PrintStream(stdOut); + // errorStream = new PrintStream(stdErr); + + // TODO: controllare questo codice, prima creava un istanza a parte + mainPane = new GUIclass(); + + _mainPane_.messageViewer = new ReqResPanel(); + _mainPane_.splitPane.setRightComponent(mainPane.messageViewer); + + /* dovrei aver sostituito questi elementi nel metodo hook + callbacks.registerProxyListener(BurpExtender.this); + callbacks.registerHttpListener(BurpExtender.this); + */ + statusPanel.add(_mainPane_); + + }); + } + return statusPanel; + } + + // Questo è il codice Burp di partenza, sostituito dalla funzione hook + + // public void registerExtenderCallbacks(IBurpExtenderCallbacks callbacks) { + // System.out.println("Initializing extension"); + // + // this.callbacks = callbacks; + // helpers = callbacks.getHelpers(); + // + // callbacks.setExtensionName("MIG Testing tool"); + // + // //The UI is created + // SwingUtilities.invokeLater(() -> { + // // setup output stream + // OutputStream stdOut = callbacks.getStdout(); + // OutputStream stdErr = callbacks.getStderr(); + // + // printStream = new PrintStream(stdOut); + // errorStream = new PrintStream(stdErr); + // + // mainPane = new Main(); + // mainPane.callbacks = callbacks; + // mainPane.messageViewer = callbacks.createMessageEditor(mainPane.controller, + // false); + // mainPane.splitPane.setRightComponent(mainPane.messageViewer.getComponent()); + // + // + // // add the custom tab to Burp's UI + // callbacks.addSuiteTab(/*BurpExtender.*/this); + // + // // register ourselves as an HTTP listener + // callbacks.registerProxyListener(/*BurpExtender.*/this); + // //callbacks.registerHttpListener(BurpExtender.this); + // }); + // + // + // } + + @Override + public boolean canUnload() { + // The extension can be dynamically unloaded, all resources used/added can be freed/removed + // from core. + return true; + } + + @Override + public void unload() { + super.unload(); + } + + @Override + public String getDescription() { + return "This add-on provides custom security checks for web applications"; + } + + @Override + public boolean onHttpRequestSend(HttpMessage msg) { + boolean messageIsRequest; + if (msg.getRequestHeader().isEmpty()) { + messageIsRequest = false; + } else { + messageIsRequest = true; + } + + HTTPReqRes message = + new HTTPReqRes( + // messageInfo, + msg, + messageIsRequest, + // proxy_message.getMessageReference() + msg.getHistoryRef().getHistoryId()); + + if (mainPane.INTERCEPT_ENABLED) { + + // //la porta viene prelevata dall'InterceptedProxyMessage, la porta che + // ottengo da un HttpMessage potrebbe andare bene? + // + // + // /* Check at which port of the proxy the message has been received + // if it is different from the one of the session avoid message*/ + // if (!port.equals(mainPane.actual_operation.session_port)) { + // return; + // } + + // Log the received message by adding it to the list of received messages + log_message(messageIsRequest, msg); + + MessageType msg_type = null; + try { + msg_type = + MessageType.getFromList( + mainPane.messageTypes, mainPane.actual_operation.getMessageType()); + } catch (Exception e) { + e.printStackTrace(); + mainPane.actual_operation.applicable = false; + } + + // Check that the given message matches the message type specified in the test + boolean matchMessage = message.matches_msg_type(msg_type, messageIsRequest); + + if (matchMessage) { + // If the operation's action is an intercept + if (Objects.requireNonNull(mainPane.actual_operation.getAction()) + == Operation.Action.INTERCEPT) { + try { + processMatchedMsg(msg_type, /*messageInfo,*/ message); + if (mainPane.actual_operation.then != null + & mainPane.actual_operation.then == Operation.Then.DROP) { + return false; // IN ZAP A BOOL IS RETURNED STATING IF THE MESSAGE HAVE + // TO BE SENT + } + } catch (Exception e) { + e.printStackTrace(); + mainPane.actual_operation.applicable = false; + } + } + } + } + if (mainPane.recording) { + if (!messageIsRequest) { // do not remove + synchronized (mainPane.interceptedMessages) { + try { + HistoryReference historyRef = + new HistoryReference( + Model.getSingleton().getSession(), + HistoryReference.TYPE_TEMPORARY, + msg); + mainPane.interceptedMessages.add(new HTTPReqRes(historyRef)); + + if (mainPane.defaultSession != null) { + mainPane.defaultSession.addMessage(historyRef, mainPane.FILTERING); + } + + } catch (HttpMalformedHeaderException e) { + throw new RuntimeException(e); + } catch (DatabaseException e) { + throw new RuntimeException(e); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + } + } + + /* This is the original code for the saveBuffer + if (mainPane.recording) { + if (!messageIsRequest) { // do not remove + synchronized (mainPane.interceptedMessages) { + IHttpRequestResponsePersisted actual = + callbacks.saveBuffersToTempFiles(messageInfo); + mainPane.interceptedMessages.add( + new HTTPReqRes(actual) + ); + if (mainPane.defaultSession != null) { + mainPane.defaultSession.addMessage(actual, + mainPane.FILTERING); + } + } + } + } + */ + return true; + } + + @Override + public boolean onHttpResponseReceive(HttpMessage msg) { + + boolean messageIsRequest; + if (msg.getRequestHeader().isEmpty()) { + messageIsRequest = false; + } else { + messageIsRequest = true; + } + + HTTPReqRes message = + new HTTPReqRes( + // messageInfo, + msg, + messageIsRequest, + // proxy_message.getMessageReference() + msg.getHistoryRef().getHistoryId()); + + if (mainPane.INTERCEPT_ENABLED) { + // TODO aggiungere controllo porta per separare sessioni, controllare se il + // funzionamento è corretto, return false dovrebbe impedire l'inoltro + // per il momento evitiamo in controllo della porta, quindi possiamo gestire + // solo una sessione + // + // + // /* Check at which port of the proxy the message has been received + // if it is different from the one of the session avoid message*/ + // if (!port.equals(mainPane.actual_operation.session_port)) { + // return; + // } + + // Log the received message by adding it to the list of received messages + log_message(messageIsRequest, msg); + + MessageType msg_type = null; + try { + msg_type = + MessageType.getFromList( + mainPane.messageTypes, mainPane.actual_operation.getMessageType()); + } catch (Exception e) { + e.printStackTrace(); + mainPane.actual_operation.applicable = false; + } + + // Check that the given message matches the message type specified in the test + boolean matchMessage = message.matches_msg_type(msg_type, messageIsRequest); + + if (matchMessage) { + // If the operation's action is an intercept + if (Objects.requireNonNull(mainPane.actual_operation.getAction()) + == Operation.Action.INTERCEPT) { + try { + processMatchedMsg(msg_type, /*messageInfo,*/ message); + if (mainPane.actual_operation.then != null + & mainPane.actual_operation.then == Operation.Then.DROP) { + return false; // IN ZAP A BOOL IS RETURNED STATING IF THE MESSAGE HAVE + // TO BE SENT + } + } catch (Exception e) { + e.printStackTrace(); + mainPane.actual_operation.applicable = false; + } + } + } + } + if (mainPane.recording) { + if (!messageIsRequest) { // do not remove + synchronized (mainPane.interceptedMessages) { + try { + HistoryReference historyRef = + new HistoryReference( + Model.getSingleton().getSession(), + HistoryReference.TYPE_TEMPORARY, + msg); + mainPane.interceptedMessages.add(new HTTPReqRes(historyRef)); + + if (mainPane.defaultSession != null) { + mainPane.defaultSession.addMessage(historyRef, mainPane.FILTERING); + } + + } catch (HttpMalformedHeaderException e) { + throw new RuntimeException(e); + } catch (DatabaseException e) { + throw new RuntimeException(e); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + } + } + } + return true; + } + + @Override + public int getArrangeableListenerOrder() { + return 0; + } + + /** + * @param msg_type the message type to be used // @param messageInfo the original intercepted + * messageInfo to being able to edit the message + * @param messageInfo a custom parsed message to be used in opeations + */ + private void processMatchedMsg(MessageType msg_type, HTTPReqRes messageInfo) { + // TODO sistemare messageInfo.setHighlight("red"); + + mainPane.actual_operation.setAPI( + new Operation_API(messageInfo, msg_type.msg_to_process_is_request)); + mainPane.actual_operation.execute(); + + // if message has been edited inside operation update the value + try { + if (mainPane.actual_operation.processed_message != null) { + if (msg_type.msg_to_process_is_request) { + if (!Arrays.equals( + messageInfo.getRequest(), + mainPane.actual_operation.processed_message)) { + messageInfo.setRequest(mainPane.actual_operation.processed_message); + } + } else { + if (!Arrays.equals( + messageInfo.getResponse(), + mainPane.actual_operation.processed_message)) { + messageInfo.setResponse(mainPane.actual_operation.processed_message); + } + } + } + } catch (UnsupportedOperationException e) { + // This is thrown when an already issued request is being substituted + System.err.println("Warning, edited message that has already been sent"); + } + resume(); + } + + /** Tells the lock on the Execute Actives process to resume the execution */ + private void resume() { + // Resume the execution thread + synchronized (mainPane.waiting) { + mainPane.waiting.notify(); + } + } + + private void log_message(boolean isRequest, HttpMessage message) { + mainPane.actual_operation.log_messages.add(message); + } +} diff --git a/tool/src/main/java/samlraider/application/BurpCertificateBuilder.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/application/BurpCertificateBuilder.java similarity index 71% rename from tool/src/main/java/samlraider/application/BurpCertificateBuilder.java rename to tool/src/main/java/org/zaproxy/addon/migt/samlraider/application/BurpCertificateBuilder.java index 8eec80b..73a2592 100644 --- a/tool/src/main/java/samlraider/application/BurpCertificateBuilder.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/application/BurpCertificateBuilder.java @@ -1,57 +1,72 @@ /* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) + * Zed Attack Proxy (ZAP) and its related class files. * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss + * ZAP is an HTTP/HTTPS proxy for assessing web application security. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * Copyright 2015 The ZAP Development Team * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * 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 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ +package org.zaproxy.addon.migt.samlraider.application; -package samlraider.application; - -import samlraider.helpers.CertificateHelper; -import samlraider.model.BurpCertificate; -import samlraider.model.BurpCertificateExtension; -import samlraider.model.ObjectIdentifier; -import org.bouncycastle.asn1.ASN1Encodable; -import org.bouncycastle.asn1.ASN1EncodableVector; -import org.bouncycastle.asn1.DERSequence; -import org.bouncycastle.asn1.x509.*; -import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; -import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.x509.X509V3CertificateGenerator; -import org.bouncycastle.x509.extension.X509ExtensionUtil; - -import javax.security.auth.x500.X500Principal; import java.io.IOException; import java.math.BigInteger; -import java.security.*; +import java.security.InvalidKeyException; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Security; +import java.security.SignatureException; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.text.ParseException; -import java.util.*; +import java.util.Calendar; +import java.util.Date; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.security.auth.x500.X500Principal; +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; +import org.bouncycastle.asn1.x509.KeyPurposeId; +import org.bouncycastle.asn1.x509.KeyUsage; +import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; +import org.bouncycastle.asn1.x509.X509Extensions; +import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.x509.X509V3CertificateGenerator; +import org.bouncycastle.x509.extension.X509ExtensionUtil; +import org.zaproxy.addon.migt.samlraider.helpers.CertificateHelper; +import org.zaproxy.addon.migt.samlraider.model.BurpCertificate; +import org.zaproxy.addon.migt.samlraider.model.BurpCertificateExtension; +import org.zaproxy.addon.migt.samlraider.model.ObjectIdentifier; @SuppressWarnings("deprecation") public class BurpCertificateBuilder { + private final List issuerAlternativeName; private final List subjectAlternativeName; private final List burpCertificateExtensions; @@ -103,9 +118,8 @@ public BurpCertificateBuilder(String subject) { } /** - * Generates a new certificate and sets the fields Private/Public Key and - * Source of this object. The certificate is signed with the private key of - * the given issuer. + * Generates a new certificate and sets the fields Private/Public Key and Source of this object. + * The certificate is signed with the private key of the given issuer. * * @param issuer The Private Key of this issuer is used for signing * @return New certificate object for our plugin @@ -118,8 +132,15 @@ public BurpCertificateBuilder(String subject) { * @throws IOException * @throws InvalidKeySpecException */ - public BurpCertificate generateCertificate(BurpCertificate issuer) throws CertificateEncodingException, InvalidKeyException, IllegalStateException, NoSuchAlgorithmException, SignatureException, - NoSuchProviderException, IOException, InvalidKeySpecException { + public BurpCertificate generateCertificate(BurpCertificate issuer) + throws CertificateEncodingException, + InvalidKeyException, + IllegalStateException, + NoSuchAlgorithmException, + SignatureException, + NoSuchProviderException, + IOException, + InvalidKeySpecException { if (privateKey == null || publicKey == null) { generateKeyPair(); } @@ -131,9 +152,8 @@ public BurpCertificate generateCertificate(BurpCertificate issuer) throws Certif } /** - * Generates a new certificate and sets the fields Private/Public Key and - * Source of this object. The certificate is signed with the private key of - * "this" object. + * Generates a new certificate and sets the fields Private/Public Key and Source of this object. + * The certificate is signed with the private key of "this" object. * * @return BurpCertificate which is self-signed. * @throws CertificateEncodingException @@ -145,8 +165,15 @@ public BurpCertificate generateCertificate(BurpCertificate issuer) throws Certif * @throws InvalidKeySpecException * @throws IOException */ - public BurpCertificate generateSelfSignedCertificate() throws CertificateEncodingException, InvalidKeyException, IllegalStateException, NoSuchAlgorithmException, SignatureException, - NoSuchProviderException, InvalidKeySpecException, IOException { + public BurpCertificate generateSelfSignedCertificate() + throws CertificateEncodingException, + InvalidKeyException, + IllegalStateException, + NoSuchAlgorithmException, + SignatureException, + NoSuchProviderException, + InvalidKeySpecException, + IOException { if (privateKey == null || publicKey == null) { generateKeyPair(); } @@ -158,8 +185,7 @@ public BurpCertificate generateSelfSignedCertificate() throws CertificateEncodin } /** - * Creates a X.509v3 Certificate. The values of "this" object are used for - * the building process. + * Creates a X.509v3 Certificate. The values of "this" object are used for the building process. * * @param privateKey which signes the certificates * @return certificate object @@ -170,8 +196,13 @@ public BurpCertificate generateSelfSignedCertificate() throws CertificateEncodin * @throws SignatureException * @throws IOException */ - private X509Certificate generateX509Certificate(PrivateKey privateKey) throws CertificateEncodingException, InvalidKeyException, IllegalStateException, NoSuchAlgorithmException, - SignatureException, IOException { + private X509Certificate generateX509Certificate(PrivateKey privateKey) + throws CertificateEncodingException, + InvalidKeyException, + IllegalStateException, + NoSuchAlgorithmException, + SignatureException, + IOException { // X.509v3 General @@ -191,11 +222,14 @@ private X509Certificate generateX509Certificate(PrivateKey privateKey) throws Ce if (hasBasicConstraints) { if (isCA && hasNoPathLimit) { - certificateGenerator.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(true)); + certificateGenerator.addExtension( + X509Extensions.BasicConstraints, true, new BasicConstraints(true)); } else if (isCA && !hasNoPathLimit) { - certificateGenerator.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(pathLimit)); + certificateGenerator.addExtension( + X509Extensions.BasicConstraints, true, new BasicConstraints(pathLimit)); } else { - certificateGenerator.addExtension(X509Extensions.BasicConstraints, true, new BasicConstraints(false)); + certificateGenerator.addExtension( + X509Extensions.BasicConstraints, true, new BasicConstraints(false)); } } @@ -204,7 +238,8 @@ private X509Certificate generateX509Certificate(PrivateKey privateKey) throws Ce for (int i : keyUsage) { allKeyUsages |= i; } - certificateGenerator.addExtension(X509Extensions.KeyUsage, true, new KeyUsage(allKeyUsages)); + certificateGenerator.addExtension( + X509Extensions.KeyUsage, true, new KeyUsage(allKeyUsages)); } if (extendedKeyUsage.size() > 0) { @@ -212,17 +247,25 @@ private X509Certificate generateX509Certificate(PrivateKey privateKey) throws Ce for (KeyPurposeId i : extendedKeyUsage) { allExtendedKeyUsages.add(i); } - certificateGenerator.addExtension(X509Extensions.ExtendedKeyUsage, false, new DERSequence(allExtendedKeyUsages)); + certificateGenerator.addExtension( + X509Extensions.ExtendedKeyUsage, false, new DERSequence(allExtendedKeyUsages)); } if (subjectAlternativeName.size() > 0) { - GeneralNames generalNames = new GeneralNames(subjectAlternativeName.toArray(new GeneralName[subjectAlternativeName.size()])); - certificateGenerator.addExtension(X509Extensions.SubjectAlternativeName, true, generalNames); + GeneralNames generalNames = + new GeneralNames( + subjectAlternativeName.toArray( + new GeneralName[subjectAlternativeName.size()])); + certificateGenerator.addExtension( + X509Extensions.SubjectAlternativeName, true, generalNames); } if (setSubjectKeyIdentifier == true) { JcaX509ExtensionUtils j = new JcaX509ExtensionUtils(); - certificateGenerator.addExtension(X509Extensions.SubjectKeyIdentifier, false, j.createSubjectKeyIdentifier(publicKey)); + certificateGenerator.addExtension( + X509Extensions.SubjectKeyIdentifier, + false, + j.createSubjectKeyIdentifier(publicKey)); } if (!subjectKeyIdentifier.isEmpty() && setSubjectKeyIdentifier == false) { @@ -232,13 +275,20 @@ private X509Certificate generateX509Certificate(PrivateKey privateKey) throws Ce } if (issuerAlternativeName.size() > 0) { - GeneralNames generalNames = new GeneralNames(issuerAlternativeName.toArray(new GeneralName[issuerAlternativeName.size()])); - certificateGenerator.addExtension(X509Extensions.IssuerAlternativeName, true, generalNames); + GeneralNames generalNames = + new GeneralNames( + issuerAlternativeName.toArray( + new GeneralName[issuerAlternativeName.size()])); + certificateGenerator.addExtension( + X509Extensions.IssuerAlternativeName, true, generalNames); } if (setAuthorityKeyIdentifier == true && issuerCertificate != null) { JcaX509ExtensionUtils j = new JcaX509ExtensionUtils(); - certificateGenerator.addExtension(X509Extensions.AuthorityKeyIdentifier, true, j.createAuthorityKeyIdentifier(issuerCertificate)); + certificateGenerator.addExtension( + X509Extensions.AuthorityKeyIdentifier, + true, + j.createAuthorityKeyIdentifier(issuerCertificate)); } if (!authorityKeyIdentifier.isEmpty() && setAuthorityKeyIdentifier == false) { @@ -249,7 +299,8 @@ private X509Certificate generateX509Certificate(PrivateKey privateKey) throws Ce for (BurpCertificateExtension e : burpCertificateExtensions) { // http://bouncycastle.sourcearchive.com/documentation/1.43/classorg_1_1bouncycastle_1_1x509_1_1X509V3CertificateGenerator_fd5118a4eaa4870e5fbf6efc02f10c00.html#fd5118a4eaa4870e5fbf6efc02f10c00 - ASN1Encodable extension = X509ExtensionUtil.fromExtensionValue(e.getExtensionValue()); // Finally!!! + ASN1Encodable extension = + X509ExtensionUtil.fromExtensionValue(e.getExtensionValue()); // Finally!!! certificateGenerator.addExtension(e.getOid(), e.isCritical(), extension); } @@ -257,14 +308,15 @@ private X509Certificate generateX509Certificate(PrivateKey privateKey) throws Ce } /** - * Generates a Public and Private Key with the minimum size of 512 Bytes and - * set the variables of this object. + * Generates a Public and Private Key with the minimum size of 512 Bytes and set the variables + * of this object. * * @throws NoSuchAlgorithmException * @throws NoSuchProviderException * @throws InvalidKeySpecException */ - public void generateKeyPair() throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException { + public void generateKeyPair() + throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException { KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA", "BC"); // Minimum Key Size if (keySize < 512) { @@ -296,6 +348,7 @@ public void setSerial(BigInteger serial) { this.serial = serial; } + // public void setNotBefore(Date notBefore) { this.notBefore = notBefore; } @@ -404,7 +457,8 @@ public void addKeyUsage(String keyUsage) { } public void addSubjectAlternativeName(String type, String name) { - subjectAlternativeName.add(new GeneralName(ObjectIdentifier.getX509SubjectAlternativeNames(type), name)); + subjectAlternativeName.add( + new GeneralName(ObjectIdentifier.getX509SubjectAlternativeNames(type), name)); } public void addSubjectAlternativeName(String subjectAlternativeName) { @@ -416,7 +470,8 @@ public void addSubjectAlternativeName(String subjectAlternativeName) { } public void addIssuerAlternativeName(String type, String name) { - issuerAlternativeName.add(new GeneralName(ObjectIdentifier.getX509SubjectAlternativeNames(type), name)); + issuerAlternativeName.add( + new GeneralName(ObjectIdentifier.getX509SubjectAlternativeNames(type), name)); } public void addIssuerAlternativeName(String issuerAlternativeName) { @@ -481,5 +536,4 @@ public void setAuthorityKeyIdentifier(String authorityKeyIdentifier) { public void setIssuserCertificate(X509Certificate issuerCertificate) { this.issuerCertificate = issuerCertificate; } - -} \ No newline at end of file +} diff --git a/tool/src/main/java/samlraider/application/CertificateTabController.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/application/CertificateTabController.java similarity index 70% rename from tool/src/main/java/samlraider/application/CertificateTabController.java rename to tool/src/main/java/org/zaproxy/addon/migt/samlraider/application/CertificateTabController.java index 3de803f..0a5cb4d 100644 --- a/tool/src/main/java/samlraider/application/CertificateTabController.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/application/CertificateTabController.java @@ -1,84 +1,125 @@ /* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) + * Zed Attack Proxy (ZAP) and its related class files. * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss + * ZAP is an HTTP/HTTPS proxy for assessing web application security. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * Copyright 2015 The ZAP Development Team * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * 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 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ - -package samlraider.application; - -import burp.ITab; -import com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException; -import com.sun.org.apache.xml.internal.security.utils.Base64; -import samlraider.gui.CertificateTab; -import samlraider.helpers.FileHelper; -import samlraider.helpers.Flags; -import samlraider.model.BurpCertificate; -import samlraider.model.BurpCertificateExtension; -import samlraider.model.BurpCertificateStore; -import samlraider.model.ObjectIdentifier; -import org.bouncycastle.openssl.PEMKeyPair; -import org.bouncycastle.openssl.PEMParser; -import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; - -import java.awt.*; -import java.io.*; -import java.security.*; +package org.zaproxy.addon.migt.samlraider.application; + +// import burp.ITab; + +// sostituito le due classi con java.util.Base64 + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileReader; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.SignatureException; import java.security.cert.Certificate; -import java.security.cert.*; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.text.ParseException; +import java.util.Base64; import java.util.Collections; import java.util.LinkedList; import java.util.List; -import java.util.Observable; - -public class CertificateTabController extends Observable implements ITab { - - private final CertificateTab certificateTab; - private final BurpCertificateStore burpCertificateStore; - private final FileHelper fileHelper; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; +import org.zaproxy.addon.migt.samlraider.gui.CertificateTab; +import org.zaproxy.addon.migt.samlraider.helpers.FileHelper; +import org.zaproxy.addon.migt.samlraider.helpers.Flags; +import org.zaproxy.addon.migt.samlraider.model.BurpCertificate; +import org.zaproxy.addon.migt.samlraider.model.BurpCertificateExtension; +import org.zaproxy.addon.migt.samlraider.model.BurpCertificateStore; +import org.zaproxy.addon.migt.samlraider.model.ObjectIdentifier; + +public class CertificateTabController /*extends Observable implements ITab*/ { + // + private final CertificateTab certificateTab = new CertificateTab(); + private final BurpCertificateStore burpCertificateStore = new BurpCertificateStore(); + private final FileHelper fileHelper = new FileHelper(); + + private final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this); + + // Metodo per aggiungere un osservatore + public void addObserver(PropertyChangeListener listener) { + changeSupport.addPropertyChangeListener(listener); + } - public CertificateTabController(CertificateTab certificateTab) { - this.certificateTab = certificateTab; - burpCertificateStore = new BurpCertificateStore(); - fileHelper = new FileHelper(); - if (Flags.DEBUG) { - importExampleCertificates(); - setCertificateTree(); - } + // Metodo per rimuovere un osservatore + public void removeObserver(PropertyChangeListener listener) { + changeSupport.removePropertyChangeListener(listener); } - /* - * Control GUI - */ + /** Update top view with all certificates. */ + public void setCertificateTree() { + certificateTab.setCertificateRootNode(burpCertificateStore.getRootNode()); + changeSupport.firePropertyChange( + "certificateTreeUpdated", null, burpCertificateStore.getRootNode()); + } - /** - * Import preloaded certificates as examples - */ - public static BurpCertificate importExampleCertificates_edit(String path_certificate, String path_private) { - BurpCertificate c1 = importCertificate_edit(path_certificate); //"src/main/resources/examples/certificate.pem"); - importPrivateKey_edit(c1, path_private); //"src/main/resources/examples/private_key_rsa.pem" - //importCertificateChain_edit("src/main/resources/examples/example.org_chain.pem"); + // /** + // * Original, Observable is deprecated + // */ + // public void setCertificateTree() { + // certificateTab.setCertificateRootNode(burpCertificateStore.getRootNode()); + // setChanged(); + // notifyObservers(); + // } + + // public CertificateTabController(CertificateTab certificateTab) { + // this.certificateTab = certificateTab; + // burpCertificateStore = new BurpCertificateStore(); + // fileHelper = new FileHelper(); + // if (Flags.DEBUG) { + // importExampleCertificates(); + // setCertificateTree(); + // } + // } + // + // /* + // * Control GUI + // */ + // + + /** Import preloaded certificates as examples */ + public static BurpCertificate importExampleCertificates_edit( + String path_certificate, String path_private) { + BurpCertificate c1 = + importCertificate_edit( + path_certificate); // "src/main/resources/examples/certificate.pem"); + importPrivateKey_edit( + c1, path_private); // "src/main/resources/examples/private_key_rsa.pem" + // importCertificateChain_edit("src/main/resources/examples/example.org_chain.pem"); return c1; } @@ -97,16 +138,17 @@ public static BurpCertificate importCertificate_edit(String filename) { ByteArrayInputStream bais = new ByteArrayInputStream(value); fis.close(); CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); - X509Certificate x509certificate = (X509Certificate) certFactory.generateCertificate(bais); + X509Certificate x509certificate = + (X509Certificate) certFactory.generateCertificate(bais); BurpCertificate certificate = new BurpCertificate(x509certificate); certificate.setPublicKey(x509certificate.getPublicKey()); certificate.setSource("Imported"); return certificate; } catch (IOException | CertificateException e) { - //setStatus("Error reading file. (" + e.getMessage() + ")"); + // setStatus("Error reading file. (" + e.getMessage() + ")"); e.printStackTrace(); } catch (Exception e) { - //setStatus("Error (" + e.getMessage() + ")"); + // setStatus("Error (" + e.getMessage() + ")"); e.printStackTrace(); } return null; @@ -122,14 +164,16 @@ public static BurpCertificate importCertificateFromString_edit(String inputStrin CertificateFactory certFactory; try { certFactory = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(inputString)); - X509Certificate x509certificate = (X509Certificate) certFactory.generateCertificate(bais); + ByteArrayInputStream bais = + new ByteArrayInputStream(Base64.getDecoder().decode(inputString)); + X509Certificate x509certificate = + (X509Certificate) certFactory.generateCertificate(bais); BurpCertificate certificate = new BurpCertificate(x509certificate); certificate.setPublicKey(x509certificate.getPublicKey()); certificate.setSource("Imported"); return certificate; - } catch (CertificateException | Base64DecodingException e) { + } catch (CertificateException | IllegalArgumentException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); @@ -137,15 +181,16 @@ public static BurpCertificate importCertificateFromString_edit(String inputStrin return null; } - /* - * Import - */ - + // + // /* + // * Import + // */ + // /** * Read and import an X.509v3 certificate chain. * - * @param filename X.509v3 certificate chain (get with - * openssl s_client -connect example.org -showcerts) + * @param filename X.509v3 certificate chain (get with + * openssl s_client -connect example.org -showcerts) * @return List with all certificates in chain */ public static List importCertificateChain_edit(String filename) { @@ -170,21 +215,20 @@ public static List importCertificateChain_edit(String filename) return certificateChain; } catch (IOException | CertificateException e) { - //setStatus("Error reading certificate chain. (" + e.getMessage() + ")"); + // setStatus("Error reading certificate chain. (" + e.getMessage() + ")"); e.printStackTrace(); } catch (Exception e) { - //setStatus("Error (" + e.getMessage() + ")"); + // setStatus("Error (" + e.getMessage() + ")"); e.printStackTrace(); } return null; } /** - * Import a private RSA key in PEM format from a file and add it to the - * selected certificate. + * Import a private RSA key in PEM format from a file and add it to the selected certificate. * * @param certificate which the private key is for. - * @param filename of the private RSA key in PEM format + * @param filename of the private RSA key in PEM format */ public static void importPrivateKey_edit(BurpCertificate certificate, String filename) { BufferedReader br; @@ -196,22 +240,23 @@ public static void importPrivateKey_edit(BurpCertificate certificate, String fil pp.close(); certificate.setPrivateKey(kp.getPrivate()); } catch (IOException e) { - //setStatus("Error importing private key. (" + e.getMessage() + ")"); + // setStatus("Error importing private key. (" + e.getMessage() + ")"); e.printStackTrace(); } catch (Exception e) { - //setStatus("Error (" + e.getMessage() + ")"); + // setStatus("Error (" + e.getMessage() + ")"); e.printStackTrace(); } } /** - * Copy all X.509v3 general information and all extensions 1:1 from one - * source certificat to one destination certificate. + * Copy all X.509v3 general information and all extensions 1:1 from one source certificat to one + * destination certificate. * - * @param certificate with the original information + * @param certificate with the original information * @param burpCertificateBuilder for generating the destination certificate */ - private static void cloneProperties(BurpCertificate certificate, BurpCertificateBuilder burpCertificateBuilder) { + private static void cloneProperties( + BurpCertificate certificate, BurpCertificateBuilder burpCertificateBuilder) { burpCertificateBuilder.setVersion(certificate.getVersionNumber()); burpCertificateBuilder.setSerial(certificate.getSerialNumberBigInteger()); if (certificate.getPublicKeyAlgorithm().equals("RSA")) { @@ -229,6 +274,7 @@ private static void cloneProperties(BurpCertificate certificate, BurpCertificate } } + // /** * Clone a certificate and sign it with another private key from an issuer. * @@ -236,7 +282,8 @@ private static void cloneProperties(BurpCertificate certificate, BurpCertificate * @return cloned certificate */ public static BurpCertificate cloneAndSignCertificate_edit(BurpCertificate certificate) { - BurpCertificateBuilder burpCertificateBuilder = new BurpCertificateBuilder(certificate.getSubject()); + BurpCertificateBuilder burpCertificateBuilder = + new BurpCertificateBuilder(certificate.getSubject()); cloneProperties(certificate, burpCertificateBuilder); BurpCertificate burpCertificate; @@ -251,17 +298,7 @@ public static BurpCertificate cloneAndSignCertificate_edit(BurpCertificate certi } /** - * Update top view with all certificates. - */ - public void setCertificateTree() { - certificateTab.setCertificateRootNode(burpCertificateStore.getRootNode()); - setChanged(); - notifyObservers(); - } - - /** - * Setting the status line in GUI or print to STDOUT if no GUI and DEBUG - * Flag is set. + * Setting the status line in GUI or print to STDOUT if no GUI and DEBUG Flag is set. * * @param status message to display */ @@ -273,9 +310,7 @@ private void setStatus(String status) { } } - /** - * Updates the bottom view with detailed certificate information - */ + /** Updates the bottom view with detailed certificate information */ public void setCertificateDetails(BurpCertificate burpCertificate) { // Plugin Specific certificateTab.setTxtSource(burpCertificate.getSource()); @@ -354,7 +389,8 @@ public void setCertificateDetails(BurpCertificate burpCertificate) { for (BurpCertificateExtension extension : burpCertificate.getAllExtensions()) { if (!ObjectIdentifier.extensionsIsSupported(extension.getOid())) { if (ObjectIdentifier.getExtension(extension.getOid()) != null) { - unsupportedExtensions.add(ObjectIdentifier.getExtension(extension.getOid())); + unsupportedExtensions.add( + ObjectIdentifier.getExtension(extension.getOid())); } else { // display OID number if extension name is unknown unsupportedExtensions.add(extension.getOid()); @@ -365,15 +401,17 @@ public void setCertificateDetails(BurpCertificate burpCertificate) { } } - /** - * Import preloaded certificates as examples - */ + /** Import preloaded certificates as examples */ private void importExampleCertificates() { if (fileHelper.startedFromJar()) { try { - BurpCertificate c1 = importCertificate(fileHelper.exportRessourceFromJar("examples/certificate.pem")); - importPrivateKey(c1, fileHelper.exportRessourceFromJar("examples/private_key_rsa.pem")); - importCertificateChain(fileHelper.exportRessourceFromJar("examples/example.org_chain.pem")); + BurpCertificate c1 = + importCertificate( + fileHelper.exportRessourceFromJar("examples/certificate.pem")); + importPrivateKey( + c1, fileHelper.exportRessourceFromJar("examples/private_key_rsa.pem")); + importCertificateChain( + fileHelper.exportRessourceFromJar("examples/example.org_chain.pem")); setCertificateDetails(c1); } catch (IOException e) { setStatus("Error importing example certificates (" + e.getMessage() + ")"); @@ -404,7 +442,8 @@ public BurpCertificate importCertificate(String filename) { ByteArrayInputStream bais = new ByteArrayInputStream(value); fis.close(); CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); - X509Certificate x509certificate = (X509Certificate) certFactory.generateCertificate(bais); + X509Certificate x509certificate = + (X509Certificate) certFactory.generateCertificate(bais); BurpCertificate certificate = new BurpCertificate(x509certificate); certificate.setPublicKey(x509certificate.getPublicKey()); certificate.setSource("Imported"); @@ -423,41 +462,42 @@ public BurpCertificate importCertificate(String filename) { return null; } - /** - * Read an PEM encoded X.509v3 certificate - * - * @param inputString PEM encoded X.509 certificate - * @return certificate - */ - public BurpCertificate importCertificateFromString(String inputString) { - setStatus("Importing certificate..."); - CertificateFactory certFactory; - try { - certFactory = CertificateFactory.getInstance("X.509"); - ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(inputString)); - X509Certificate x509certificate = (X509Certificate) certFactory.generateCertificate(bais); - BurpCertificate certificate = new BurpCertificate(x509certificate); - certificate.setPublicKey(x509certificate.getPublicKey()); - certificate.setSource("Imported"); - burpCertificateStore.addCertificate(certificate); - setCertificateTree(); - setStatus("Certificate imported"); - return certificate; - } catch (CertificateException | Base64DecodingException e) { - setStatus("Error reading input certificate. (" + e.getMessage() + ")"); - e.printStackTrace(); - } catch (Exception e) { - setStatus("Error. (" + e.getMessage() + ")"); - e.printStackTrace(); - } - return null; - } - + // /** + // * Read an PEM encoded X.509v3 certificate + // * + // * @param inputString PEM encoded X.509 certificate + // * @return certificate + // */ + // public BurpCertificate importCertificateFromString(String inputString) { + // setStatus("Importing certificate..."); + // CertificateFactory certFactory; + // try { + // certFactory = CertificateFactory.getInstance("X.509"); + // ByteArrayInputStream bais = new ByteArrayInputStream(Base64.decode(inputString)); + // X509Certificate x509certificate = (X509Certificate) + // certFactory.generateCertificate(bais); + // BurpCertificate certificate = new BurpCertificate(x509certificate); + // certificate.setPublicKey(x509certificate.getPublicKey()); + // certificate.setSource("Imported"); + // burpCertificateStore.addCertificate(certificate); + // setCertificateTree(); + // setStatus("Certificate imported"); + // return certificate; + // } catch (CertificateException | Base64DecodingException e) { + // setStatus("Error reading input certificate. (" + e.getMessage() + ")"); + // e.printStackTrace(); + // } catch (Exception e) { + // setStatus("Error. (" + e.getMessage() + ")"); + // e.printStackTrace(); + // } + // return null; + // } + // /** * Read and import an X.509v3 certificate chain. * - * @param filename X.509v3 certificate chain (get with - * openssl s_client -connect example.org -showcerts) + * @param filename X.509v3 certificate chain (get with + * openssl s_client -connect example.org -showcerts) * @return List with all certificates in chain */ public List importCertificateChain(String filename) { @@ -494,16 +534,15 @@ public List importCertificateChain(String filename) { return null; } - /* - * Export - */ - + // /* + // * Export + // */ + // /** - * Import a private RSA key in PEM format from a file and add it to the - * selected certificate. + * Import a private RSA key in PEM format from a file and add it to the selected certificate. * * @param certificate which the private key is for. - * @param filename of the private RSA key in PEM format + * @param filename of the private RSA key in PEM format */ public void importPrivateKey(BurpCertificate certificate, String filename) { setStatus("Importing private key..."); @@ -528,10 +567,10 @@ public void importPrivateKey(BurpCertificate certificate, String filename) { /** * Import a private Key in PKCS8 format in DER format. * - * @param certificate which the private key is for. Possible way to convert to - * PKCS8: - * openssl pkcs8 -topk8 -inform PEM -outform DER -in privatekey.pem -out private_key_pkcs8.pem -nocrypt - * @param filename of the PKCS8 key + * @param certificate which the private key is for. Possible way to convert to PKCS8: + * openssl pkcs8 -topk8 -inform PEM -outform DER -in privatekey.pem -out private_key_pkcs8.pem -nocrypt + * + * @param filename of the PKCS8 key */ public void importPKCS8(BurpCertificate certificate, String filename) { setStatus("Importing private key..."); @@ -559,15 +598,15 @@ public void importPKCS8(BurpCertificate certificate, String filename) { } } - /* - * Clone - */ - + // /* + // * Clone + // */ + // /** * Export the certificate to a file. * * @param certificate to export - * @param filename for the exported certificate + * @param filename for the exported certificate */ public void exportCertificate(BurpCertificate certificate, String filename) { try { @@ -585,7 +624,7 @@ public void exportCertificate(BurpCertificate certificate, String filename) { * Export Private RSA Key in PEM format. * * @param certificate to export - * @param filename for the exported private RSA key + * @param filename for the exported private RSA key */ public void exportPrivateKey(BurpCertificate certificate, String filename) { setStatus("Exporting private key..."); @@ -601,14 +640,14 @@ public void exportPrivateKey(BurpCertificate certificate, String filename) { } /** - * Clone a certificate from one source certificate. New private key material - * is generated. + * Clone a certificate from one source certificate. New private key material is generated. * - * @param certificate to clone + * @param certificate to clone * @param burpCertificateBuilder for generating the new cloned certificate * @return cloned certificate */ - public BurpCertificate cloneCertificate(BurpCertificate certificate, BurpCertificateBuilder burpCertificateBuilder) { + public BurpCertificate cloneCertificate( + BurpCertificate certificate, BurpCertificateBuilder burpCertificateBuilder) { cloneProperties(certificate, burpCertificateBuilder); setStatus("Cloning certificate..."); @@ -650,12 +689,15 @@ public BurpCertificate cloneCertificate(BurpCertificate certificate, BurpCertifi /** * Clone a certificate and sign it with another private key from an issuer. * - * @param certificate to clone + * @param certificate to clone * @param burpCertificateBuilder for the cloned certificate - * @param issuerCertificate for signing the new certificate + * @param issuerCertificate for signing the new certificate * @return cloned certificate */ - public BurpCertificate cloneAndSignCertificate(BurpCertificate certificate, BurpCertificateBuilder burpCertificateBuilder, BurpCertificate issuerCertificate) { + public BurpCertificate cloneAndSignCertificate( + BurpCertificate certificate, + BurpCertificateBuilder burpCertificateBuilder, + BurpCertificate issuerCertificate) { cloneProperties(certificate, burpCertificateBuilder); try { @@ -670,12 +712,19 @@ public BurpCertificate cloneAndSignCertificate(BurpCertificate certificate, Burp BurpCertificate burpCertificate; try { burpCertificate = burpCertificateBuilder.generateCertificate(issuerCertificate); - burpCertificate.setSource("Cloned and signed by cloned " + issuerCertificate.getSubject()); + burpCertificate.setSource( + "Cloned and signed by cloned " + issuerCertificate.getSubject()); burpCertificateStore.addCertificate(burpCertificate); setCertificateTree(); setStatus("Certificate cloned and signed."); return burpCertificate; - } catch (CertificateEncodingException | InvalidKeyException | IllegalStateException | NoSuchAlgorithmException | SignatureException | NoSuchProviderException | InvalidKeySpecException + } catch (CertificateEncodingException + | InvalidKeyException + | IllegalStateException + | NoSuchAlgorithmException + | SignatureException + | NoSuchProviderException + | InvalidKeySpecException | IOException e) { setStatus("Error cloning certificate. (" + e.getMessage() + ")"); e.printStackTrace(); @@ -689,8 +738,8 @@ public BurpCertificate cloneAndSignCertificate(BurpCertificate certificate, Burp * Clone whole certificate chain * * @param certificateChain to clone - * @return List of cloned certificates. According to RFC 5246: Next - * Certificate must sign previous. + * @return List of cloned certificates. According to RFC 5246: Next Certificate must sign + * previous. */ public List cloneCertificateChain(List certificateChain) { List certificates = new LinkedList<>(); @@ -701,9 +750,12 @@ public List cloneCertificateChain(List certifi BurpCertificate previousCertificate = null; for (BurpCertificate c : certificateChain) { if (previousCertificate == null) { // self-sign - currentCertificate = cloneCertificate(c, new BurpCertificateBuilder(c.getSubject())); + currentCertificate = + cloneCertificate(c, new BurpCertificateBuilder(c.getSubject())); } else { - currentCertificate = cloneAndSignCertificate(c, new BurpCertificateBuilder(c.getSubject()), previousCertificate); + currentCertificate = + cloneAndSignCertificate( + c, new BurpCertificateBuilder(c.getSubject()), previousCertificate); } // remove b/c already added in called methods above burpCertificateStore.removeCertificate(currentCertificate); @@ -717,14 +769,14 @@ public List cloneCertificateChain(List certifi return certificates; } - /* - * Create new - */ - + // /* + // * Create new + // */ + // /** - * Create a new X.509v3 certificate from certificate tab form. All entered - * fields are applied and the unsupported extensions are cloned from - * burpCertificate parameter if this option is activated. + * Create a new X.509v3 certificate from certificate tab form. All entered fields are applied + * and the unsupported extensions are cloned from burpCertificate parameter if this option is + * activated. * * @param burpCertificate Unsupported extensions to clone. */ @@ -734,7 +786,8 @@ public void createBurpCertificate(BurpCertificate burpCertificate) { // X.509 General - BurpCertificateBuilder burpCertificateBuilder = new BurpCertificateBuilder(certificateTab.getTxtSubject()); + BurpCertificateBuilder burpCertificateBuilder = + new BurpCertificateBuilder(certificateTab.getTxtSubject()); burpCertificateBuilder.setVersion(3); burpCertificateBuilder.setSerial(certificateTab.getTxtSerialNumber()); burpCertificateBuilder.setSignatureAlgorithm(certificateTab.getTxtSignatureAlgorithm()); @@ -784,13 +837,15 @@ public void createBurpCertificate(BurpCertificate burpCertificate) { if (certificateTab.isAutoSubjectKeyIdentifier()) { burpCertificateBuilder.setSubjectKeyIdentifier(true); } else if (certificateTab.getSubjectKeyIdentifier().length() > 0) { - burpCertificateBuilder.setSubjectKeyIdentifier(certificateTab.getSubjectKeyIdentifier()); + burpCertificateBuilder.setSubjectKeyIdentifier( + certificateTab.getSubjectKeyIdentifier()); } if (certificateTab.isAutoAuthorityKeyIdentifier()) { burpCertificateBuilder.setAuthorityKeyIdentifier(true); } else if (certificateTab.getAuthorityKeyIdentifier().length() > 0) { - burpCertificateBuilder.setAuthorityKeyIdentifier(certificateTab.getAuthorityKeyIdentifier()); + burpCertificateBuilder.setAuthorityKeyIdentifier( + certificateTab.getAuthorityKeyIdentifier()); } // Unsupported Extensions - copy only unsupported ones which are not @@ -841,23 +896,24 @@ public void createBurpCertificate(BurpCertificate burpCertificate) { } } - /* - * Read - */ - - /** - * Get all certificates which have a private key. - * - * @return List of certificates with private key. - */ - public List getCertificatesWithPrivateKey() { - return burpCertificateStore.getBurpCertificatesWithPrivateKey(); - } - - /* - * Remove - */ - + // + // /* + // * Read + // */ + // + // /** + // * Get all certificates which have a private key. + // * + // * @return List of certificates with private key. + // */ + // public List getCertificatesWithPrivateKey() { + // return burpCertificateStore.getBurpCertificatesWithPrivateKey(); + // } + // + // /* + // * Remove + // */ + // /** * Removes a certifiate from the certificate tree. * @@ -868,26 +924,26 @@ public void removeBurpCertificate(BurpCertificate burpCertificate) { setStatus("Certificate removed."); setCertificateTree(); } - - /* - * Implement ITab for BurpExtender - */ - - @Override - public String getTabCaption() { - return "SAML Raider Certificates"; - } - - @Override - public Component getUiComponent() { - return certificateTab; - } - - public String getSamlRequestParameterName() { - return certificateTab.getSamlRequestParameterName(); - } - - public String getSamlResponseParameterName() { - return certificateTab.getSamlResponseParameterName(); - } -} \ No newline at end of file + // + // /* + // * Implement ITab for BurpExtender + // */ + // + // @Override + // public String getTabCaption() { + // return "SAML Raider Certificates"; + // } + // + // @Override + // public Component getUiComponent() { + // return certificateTab; + // } + // + // public String getSamlRequestParameterName() { + // return certificateTab.getSamlRequestParameterName(); + // } + // + // public String getSamlResponseParameterName() { + // return certificateTab.getSamlResponseParameterName(); + // } +} diff --git a/tool/src/main/java/org/zaproxy/addon/migt/samlraider/application/SamlTabController.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/application/SamlTabController.java new file mode 100644 index 0000000..bf7d185 --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/application/SamlTabController.java @@ -0,0 +1,806 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2015 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt.samlraider.application; + +import java.io.IOException; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; +import org.zaproxy.addon.migt.samlraider.helpers.XMLHelpers; +import org.zaproxy.addon.migt.samlraider.model.BurpCertificate; + +public class SamlTabController /*implements IMessageEditorTab, Observer*/ { + + private static final String XML_CERTIFICATE_NOT_FOUND = "X509 Certificate not found"; + private static final String XSW_ATTACK_APPLIED = "XSW Attack applied"; + private static final String XXE_CONTENT_APPLIED = "XXE content applied"; + private static final String XML_NOT_SUITABLE_FOR_XXE = + "This XML Message is not suitable for this particular XXE attack"; + private static final String XSLT_CONTENT_APPLIED = "XSLT content applied"; + private static final String XML_NOT_SUITABLE_FOR_XLST = + "This XML Message is not suitable for this particular XLST attack"; + private static final String XML_COULD_NOT_SIGN = "Could not sign XML"; + private static final String XML_COULD_NOT_SERIALIZE = "Could not serialize XML"; + private static final String XML_NOT_WELL_FORMED = + "XML isn't well formed or binding is not supported"; + private static final String XML_NOT_SUITABLE_FOR_XSW = + "This XML Message is not suitable for this particular XSW, is there a signature?"; + private static final String NO_BROWSER = + "Could not open diff in Browser. Path to file was copied to clipboard"; + private static final String NO_DIFF_TEMP_FILE = "Could not create diff temp file."; + + /* + private final IExtensionHelpers helpers; + private final IBurpExtenderCallbacks callbacks; + private final XMLHelpers xmlHelpers; + private final ITextEditor textArea; + private final ITextEditor textEditorInformation; + private final SamlMain samlGUI; + private final boolean editable; + private final CertificateTabController certificateTabController; + private final XSWHelpers xswHelpers; + private final HTTPHelpers httpHelpers; + private byte[] message; + private String orgSAMLMessage; + private String SAMLMessage; + private boolean isInflated = true; + private boolean isGZip = false; + private boolean isWSSUrlEncoded = false; + private boolean isSOAPMessage; + private boolean isWSSMessage; + private boolean isSAMLRequest; // otherwise it's a SAMLResponse + private String httpMethod; // So URI and POST Binding is supported + private boolean isEdited = false; + private boolean isRawMode = false; + + */ + + // public SamlTabController(IBurpExtenderCallbacks callbacks, boolean editable, + // CertificateTabController certificateTabController) { + // this.editable = editable; + // this.callbacks = callbacks; + // this.helpers = callbacks.getHelpers(); + // samlGUI = new SamlMain(this); + // textArea = samlGUI.getTextEditorAction(); + // textArea.setEditable(editable); + // textEditorInformation = samlGUI.getTextEditorInformation(); + // textEditorInformation.setEditable(false); + // xmlHelpers = new XMLHelpers(); + // xswHelpers = new XSWHelpers(); + // httpHelpers = new HTTPHelpers(); + // this.certificateTabController = certificateTabController; + // this.certificateTabController.addObserver(this); + // } + + public static String removeSignature_edit(String input) { + XMLHelpers xmlHelpers = new XMLHelpers(); + String res = ""; + try { + Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(input); + + int sign = xmlHelpers.removeAllSignatures(document); + if (sign > 0) { + + res = xmlHelpers.getStringOfDocument(document, 2, true); + } else { + // setInfoMessageText("No Signatures available to remove"); + } + + } catch (SAXException e1) { + e1.printStackTrace(); + // setInfoMessageText(XML_NOT_WELL_FORMED); + } catch (IOException e) { + e.printStackTrace(); + // setInfoMessageText(XML_COULD_NOT_SERIALIZE); + } + return res; + } + + public static String resignAssertion_edit(String input, String input_cert) { + XMLHelpers xmlHelpers = new XMLHelpers(); + try { + BurpCertificate original_cert = + CertificateTabController.importCertificateFromString_edit(input_cert); + + BurpCertificate cert = + CertificateTabController.cloneAndSignCertificate_edit(original_cert); + + if (cert != null) { + Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(input); + NodeList assertions = xmlHelpers.getAssertions(document); + String signAlgorithm = xmlHelpers.getSignatureAlgorithm(assertions.item(0)); + String digestAlgorithm = xmlHelpers.getDigestAlgorithm(assertions.item(0)); + + xmlHelpers.removeAllSignatures(document); + String string = xmlHelpers.getString(document); + Document doc = xmlHelpers.getXMLDocumentOfSAMLMessage(string); + xmlHelpers.removeEmptyTags(doc); + xmlHelpers.signAssertion( + doc, + signAlgorithm, + digestAlgorithm, + cert.getCertificate(), + cert.getPrivateKey()); + return xmlHelpers.getStringOfDocument(doc, 2, true); + } + } catch (SAXException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + // + // public static String resignMessage_edit(String input, String input_cert) { + // try { + // //if (isWSSMessage) { + // //setInfoMessageText("Message signing is not possible with WS-Security messages"); + // //} else { + // + // BurpCertificate original_cert = + // CertificateTabController.importCertificateFromString_edit(input_cert); + // + // BurpCertificate cert = + // CertificateTabController.cloneAndSignCertificate_edit(original_cert); + // + // XMLHelpers xmlHelpers = new XMLHelpers(); + // + // if (cert != null) { + // Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(input); + // NodeList responses = xmlHelpers.getResponse(document); + // String signAlgorithm = xmlHelpers.getSignatureAlgorithm(responses.item(0)); + // String digestAlgorithm = xmlHelpers.getDigestAlgorithm(responses.item(0)); + // + // xmlHelpers.removeOnlyMessageSignature(document); + // xmlHelpers.signMessage(document, signAlgorithm, digestAlgorithm, + // cert.getCertificate(), + // cert.getPrivateKey()); + // String res = xmlHelpers.getStringOfDocument(document, 2, true); + // return res; + // } + // //} + // } catch (IOException e) { + // //setInfoMessageText(XML_COULD_NOT_SERIALIZE); + // } catch (SAXException e) { + // //setInfoMessageText(XML_NOT_WELL_FORMED); + // } catch (CertificateException e) { + // //setInfoMessageText(XML_COULD_NOT_SIGN); + // } catch (NoSuchAlgorithmException e) { + // //setInfoMessageText(XML_COULD_NOT_SIGN + ", no such algorithm"); + // } catch (InvalidKeySpecException e) { + // //setInfoMessageText(XML_COULD_NOT_SIGN + ", invalid private key"); + // } catch (MarshalException e) { + // //setInfoMessageText(XML_COULD_NOT_SERIALIZE); + // } catch (XMLSignatureException e) { + // //setInfoMessageText(XML_COULD_NOT_SIGN); + // } + // return null; + // } + // + // @Override + // public byte[] getMessage() { + // byte[] byteMessage = message; + // if (isModified()) { + // if (isSOAPMessage) { + // try { + // // TODO Only working with getString for both documents, + // // otherwise namespaces and attributes are emptied -.- + // IResponseInfo responseInfo = helpers.analyzeResponse(byteMessage); + // int bodyOffset = responseInfo.getBodyOffset(); + // String HTTPHeader = new String(byteMessage, 0, bodyOffset, + // StandardCharsets.UTF_8); + // + // String soapMessage = new String(byteMessage, bodyOffset, + // byteMessage.length - bodyOffset, StandardCharsets.UTF_8); + // Document soapDocument = + // xmlHelpers.getXMLDocumentOfSAMLMessage(soapMessage); + // Element soapBody = xmlHelpers.getSOAPBody(soapDocument); + // xmlHelpers.getString(soapDocument); + // Document samlDocumentEdited = + // xmlHelpers.getXMLDocumentOfSAMLMessage(SAMLMessage); + // xmlHelpers.getString(samlDocumentEdited); + // Element samlResponse = (Element) samlDocumentEdited.getFirstChild(); + // soapDocument.adoptNode(samlResponse); + // Element soapFirstChildOfBody = (Element) soapBody.getFirstChild(); + // soapBody.replaceChild(samlResponse, soapFirstChildOfBody); + // String wholeMessage = HTTPHeader + xmlHelpers.getString(soapDocument); + // byteMessage = wholeMessage.getBytes(StandardCharsets.UTF_8); + // } catch (IOException e) { + // } catch (SAXException e) { + // setInfoMessageText(XML_NOT_WELL_FORMED); + // } + // } else { + // String textMessage = null; + // + // if (isRawMode) { + // textMessage = new String(textArea.getText()); + // } else { + // try { + // textMessage = xmlHelpers + // + // .getStringOfDocument(xmlHelpers.getXMLDocumentOfSAMLMessage(new String(textArea.getText())), + // 0, true); + // } catch (IOException e) { + // setInfoMessageText(XML_COULD_NOT_SERIALIZE); + // } catch (SAXException e) { + // setInfoMessageText(XML_NOT_WELL_FORMED); + // } + // } + // + // String parameterToUpdate; + // if (isSAMLRequest) { + // parameterToUpdate = + // certificateTabController.getSamlRequestParameterName(); + // } else { + // parameterToUpdate = + // certificateTabController.getSamlResponseParameterName(); + // } + // + // if (isWSSMessage) { + // parameterToUpdate = "wresult"; + // } + // + // byte parameterType; + // if (httpMethod.equals("GET")) { + // parameterType = IParameter.PARAM_URL; + // } else { + // parameterType = IParameter.PARAM_BODY; + // } + // IParameter newParameter = helpers.buildParameter(parameterToUpdate, + // getEncodedSAMLMessage(textMessage), + // parameterType); + // + // byteMessage = helpers.updateParameter(byteMessage, newParameter); + // } + // } + // return byteMessage; + // } + // + // @Override + // public byte[] getSelectedData() { + // return textArea.getSelectedText(); + // } + // + // @Override + // public String getTabCaption() { + // return "SAML Raider"; + // } + // + // @Override + // public Component getUiComponent() { + // return samlGUI; + // } + // + // @Override + // public boolean isEnabled(byte[] content, boolean isRequest) { + // return isRequest && isSAMLMessage(content); + // } + // + // private boolean isSAMLMessage(byte[] content) { + // IRequestInfo info = helpers.analyzeRequest(content); + // httpMethod = helpers.analyzeRequest(content).getMethod(); + // if (info.getContentType() == IRequestInfo.CONTENT_TYPE_XML) { + // isSOAPMessage = true; + // try { + // IRequestInfo requestInfo = helpers.analyzeRequest(content); + // int bodyOffset = requestInfo.getBodyOffset(); + // String soapMessage = new String(content, bodyOffset, content.length - + // bodyOffset, StandardCharsets.UTF_8); + // Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(soapMessage); + // return xmlHelpers.getAssertions(document).getLength() != 0 + // || xmlHelpers.getEncryptedAssertions(document).getLength() != 0; + // } catch (SAXException e) { + // e.printStackTrace(); + // return false; + // } + // } + // // WSS Security + // else if (null != helpers.getRequestParameter(content, "wresult")) { + // try { + // IRequestInfo requestInfo = helpers.analyzeRequest(content); + // isWSSUrlEncoded = requestInfo.getContentType() == + // IRequestInfo.CONTENT_TYPE_URL_ENCODED; + // isWSSMessage = true; + // IParameter parameter = helpers.getRequestParameter(content, "wresult"); + // String wssMessage = getDecodedSAMLMessage(parameter.getValue()); + // Document document; + // document = xmlHelpers.getXMLDocumentOfSAMLMessage(wssMessage); + // return xmlHelpers.getAssertions(document).getLength() != 0 + // || xmlHelpers.getEncryptedAssertions(document).getLength() != 0; + // } catch (SAXException e) { + // e.printStackTrace(); + // return false; + // } + // } else { + // isWSSMessage = false; + // isSOAPMessage = false; + // + // IParameter requestParameter; + // requestParameter = helpers.getRequestParameter(content, + // certificateTabController.getSamlResponseParameterName()); + // if (requestParameter != null) { + // isSAMLRequest = false; + // return true; + // } + // requestParameter = helpers.getRequestParameter(content, + // certificateTabController.getSamlRequestParameterName()); + // if (requestParameter != null) { + // isSAMLRequest = true; + // return true; + // } + // } + // return false; + // } + // + // @Override + // public boolean isModified() { + // return textArea.isTextModified() || isEdited; + // } + // + // @Override + // public void setMessage(byte[] content, boolean isRequest) { + // resetInfoMessageText(); + // isEdited = false; + // if (content == null) { + // textArea.setText(null); + // textArea.setEditable(false); + // setGUIEditable(false); + // resetInformationDisplay(); + // } else { + // message = content; + // try { + // if (isSOAPMessage) { + // IResponseInfo responseInfo = helpers.analyzeResponse(content); + // int bodyOffset = responseInfo.getBodyOffset(); + // String soapMessage = new String(content, bodyOffset, content.length - + // bodyOffset, StandardCharsets.UTF_8); + // Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(soapMessage); + // Document documentSAML = xmlHelpers.getSAMLResponseOfSOAP(document); + // SAMLMessage = xmlHelpers.getStringOfDocument(documentSAML, 0, false); + // } else if (isWSSMessage) { + // IParameter parameter = helpers.getRequestParameter(content, "wresult"); + // SAMLMessage = getDecodedSAMLMessage(parameter.getValue()); + // } else { + // IParameter parameter; + // + // if (isSAMLRequest) { + // parameter = helpers.getRequestParameter(content, + // certificateTabController.getSamlRequestParameterName()); + // } else { + // parameter = helpers.getRequestParameter(content, + // certificateTabController.getSamlResponseParameterName()); + // } + // + // SAMLMessage = getDecodedSAMLMessage(parameter.getValue()); + // } + // + // } catch (IOException e) { + // e.printStackTrace(); + // setInfoMessageText(XML_COULD_NOT_SERIALIZE); + // } catch (SAXException e) { + // e.printStackTrace(); + // setInfoMessageText(XML_NOT_WELL_FORMED); + // SAMLMessage = "" + XML_NOT_WELL_FORMED + ""; + // } catch (ParserConfigurationException e) { + // e.printStackTrace(); + // } + // + // setInformationDisplay(); + // updateCertificateList(); + // updateXSWList(); + // orgSAMLMessage = SAMLMessage; + // textArea.setText(SAMLMessage.getBytes()); + // textArea.setEditable(editable); + // + // setGUIEditable(editable); + // } + // } + // + // private void setInformationDisplay() { + // SamlPanelInfo infoPanel = samlGUI.getInfoPanel(); + // infoPanel.clearAll(); + // + // try { + // Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(SAMLMessage); + // NodeList assertions = xmlHelpers.getAssertions(document); + // if (assertions.getLength() > 0) { + // Node assertion = assertions.item(0); + // infoPanel.setIssuer(xmlHelpers.getIssuer(document)); + // infoPanel.setConditionNotBefore(xmlHelpers.getConditionNotBefore(assertion)); + // infoPanel.setConditionNotAfter(xmlHelpers.getConditionNotAfter(assertion)); + // + // infoPanel.setSubjectConfNotBefore(xmlHelpers.getSubjectConfNotBefore(assertion)); + // + // infoPanel.setSubjectConfNotAfter(xmlHelpers.getSubjectConfNotAfter(assertion)); + // infoPanel.setSignatureAlgorithm(xmlHelpers.getSignatureAlgorithm(assertion)); + // infoPanel.setDigestAlgorithm(xmlHelpers.getDigestAlgorithm(assertion)); + // + // textEditorInformation.setText(xmlHelpers.getStringOfDocument(xmlHelpers.getXMLDocumentOfSAMLMessage(SAMLMessage), 2, true).getBytes()); + // } else { + // assertions = xmlHelpers.getEncryptedAssertions(document); + // Node assertion = assertions.item(0); + // infoPanel.setEncryptionAlgorithm(xmlHelpers.getEncryptionMethod(assertion)); + // } + // } catch (SAXException | IOException e) { + // setInfoMessageText(XML_NOT_WELL_FORMED); + // } + // } + // + // private void resetInformationDisplay() { + // SamlPanelInfo infoPanel = samlGUI.getInfoPanel(); + // infoPanel.setIssuer(""); + // infoPanel.setConditionNotBefore(""); + // infoPanel.setConditionNotAfter(""); + // infoPanel.setSubjectConfNotBefore(""); + // infoPanel.setSubjectConfNotAfter(""); + // infoPanel.setSignatureAlgorithm(""); + // infoPanel.setDigestAlgorithm(""); + // infoPanel.setEncryptionAlgorithm(""); + // textEditorInformation.setText("".getBytes()); + // } + // + // public String getEncodedSAMLMessage(String message) { + // byte[] byteMessage; + // if (isWSSMessage) { + // if (isWSSUrlEncoded) { + // return URLEncoder.encode(message, StandardCharsets.UTF_8); + // } else { + // return message; + // } + // } + // byteMessage = message.getBytes(StandardCharsets.UTF_8); + // if (isInflated) { + // try { + // byteMessage = httpHelpers.compress(byteMessage, isGZip); + // } catch (IOException e) { + // } + // } + // String base64Encoded = helpers.base64Encode(byteMessage); + // return URLEncoder.encode(base64Encoded, StandardCharsets.UTF_8); + // } + // + // public String getDecodedSAMLMessage(String message) { + // + // if (isWSSMessage) { + // if (isWSSUrlEncoded) { + // return helpers.urlDecode(message); + // } else { + // return message; + // } + // } + // + // String urlDecoded = helpers.urlDecode(message); + // byte[] base64Decoded = helpers.base64Decode(urlDecoded); + // + // isInflated = true; + // isGZip = true; + // + // // try normal Zip Inflate + // try { + // byte[] inflated = httpHelpers.decompress(base64Decoded, true); + // return new String(inflated, StandardCharsets.UTF_8); + // } catch (IOException e) { + // } catch (DataFormatException e) { + // isGZip = false; + // } + // + // // try Gzip Inflate + // try { + // byte[] inflated = httpHelpers.decompress(base64Decoded, false); + // return new String(inflated, StandardCharsets.UTF_8); + // } catch (IOException e) { + // } catch (DataFormatException e) { + // isInflated = false; + // } + // + // return new String(base64Decoded, StandardCharsets.UTF_8); + // } + // + // public void removeSignature() { + // resetInfoMessageText(); + // try { + // Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(new + // String(textArea.getText())); + // if (xmlHelpers.removeAllSignatures(document) > 0) { + // SAMLMessage = xmlHelpers.getStringOfDocument(document, 2, true); + // textArea.setText(SAMLMessage.getBytes()); + // isEdited = true; + // setRawMode(false); + // setInfoMessageText("Message signature successful removed"); + // } else { + // setInfoMessageText("No Signatures available to remove"); + // } + // } catch (SAXException e1) { + // setInfoMessageText(XML_NOT_WELL_FORMED); + // } catch (IOException e) { + // setInfoMessageText(XML_COULD_NOT_SERIALIZE); + // } + // } + // + // public void resetMessage() { + // if (isRawMode) { + // SAMLMessage = orgSAMLMessage; + // } + // textArea.setText(SAMLMessage.getBytes()); + // isEdited = false; + // } + // + // public void setRawMode(boolean rawModeEnabled) { + // isRawMode = rawModeEnabled; + // isEdited = true; + // samlGUI.getActionPanel().setRawModeEnabled(rawModeEnabled); + // } + // + // public void resignAssertion() { + // try { + // resetInfoMessageText(); + // BurpCertificate cert = samlGUI.getActionPanel().getSelectedCertificate(); + // if (cert != null) { + // setInfoMessageText("Signing..."); + // Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(new + // String(textArea.getText())); + // NodeList assertions = xmlHelpers.getAssertions(document); + // String signAlgorithm = xmlHelpers.getSignatureAlgorithm(assertions.item(0)); + // String digestAlgorithm = xmlHelpers.getDigestAlgorithm(assertions.item(0)); + // + // xmlHelpers.removeAllSignatures(document); + // String string = xmlHelpers.getString(document); + // Document doc = xmlHelpers.getXMLDocumentOfSAMLMessage(string); + // xmlHelpers.removeEmptyTags(doc); + // xmlHelpers.signAssertion(doc, signAlgorithm, digestAlgorithm, + // cert.getCertificate(), + // cert.getPrivateKey()); + // SAMLMessage = xmlHelpers.getStringOfDocument(doc, 2, true); + // textArea.setText(SAMLMessage.getBytes()); + // isEdited = true; + // setRawMode(false); + // setInfoMessageText("Assertions successfully signed"); + // } else { + // setInfoMessageText("no certificate chosen to sign"); + // } + // } catch (SAXException e) { + // setInfoMessageText(XML_NOT_WELL_FORMED); + // } catch (IOException e) { + // setInfoMessageText(XML_COULD_NOT_SERIALIZE); + // } catch (Exception e) { + // setInfoMessageText(XML_COULD_NOT_SIGN); + // } + // } + // + // public void resignMessage() { + // try { + // resetInfoMessageText(); + // if (isWSSMessage) { + // setInfoMessageText("Message signing is not possible with WS-Security + // messages"); + // } else { + // setInfoMessageText("Signing..."); + // BurpCertificate cert = samlGUI.getActionPanel().getSelectedCertificate(); + // if (cert != null) { + // Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(new + // String(textArea.getText())); + // NodeList responses = xmlHelpers.getResponse(document); + // String signAlgorithm = + // xmlHelpers.getSignatureAlgorithm(responses.item(0)); + // String digestAlgorithm = xmlHelpers.getDigestAlgorithm(responses.item(0)); + // + // xmlHelpers.removeOnlyMessageSignature(document); + // xmlHelpers.signMessage(document, signAlgorithm, digestAlgorithm, + // cert.getCertificate(), + // cert.getPrivateKey()); + // SAMLMessage = xmlHelpers.getStringOfDocument(document, 2, true); + // textArea.setText(SAMLMessage.getBytes()); + // isEdited = true; + // setRawMode(false); + // setInfoMessageText("Message successfully signed"); + // } else { + // setInfoMessageText("no certificate chosen to sign"); + // } + // } + // } catch (IOException e) { + // setInfoMessageText(XML_COULD_NOT_SERIALIZE); + // } catch (SAXException e) { + // setInfoMessageText(XML_NOT_WELL_FORMED); + // } catch (CertificateException e) { + // setInfoMessageText(XML_COULD_NOT_SIGN); + // } catch (NoSuchAlgorithmException e) { + // setInfoMessageText(XML_COULD_NOT_SIGN + ", no such algorithm"); + // } catch (InvalidKeySpecException e) { + // setInfoMessageText(XML_COULD_NOT_SIGN + ", invalid private key"); + // } catch (MarshalException e) { + // setInfoMessageText(XML_COULD_NOT_SERIALIZE); + // } catch (XMLSignatureException e) { + // setInfoMessageText(XML_COULD_NOT_SIGN); + // } + // } + // + // private void setInfoMessageText(String infoMessage) { + // samlGUI.getActionPanel().getInfoMessageLabel().setText(infoMessage); + // } + // + // private void resetInfoMessageText() { + // samlGUI.getActionPanel().getInfoMessageLabel().setText(""); + // } + // + // private void updateCertificateList() { + // List list = certificateTabController.getCertificatesWithPrivateKey(); + // samlGUI.getActionPanel().setCertificateList(list); + // } + // + // private void updateXSWList() { + // samlGUI.getActionPanel().setXSWList(XSWHelpers.xswTypes); + // } + // + // public void sendToCertificatesTab() { + // try { + // Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(new + // String(textArea.getText())); + // String cert = xmlHelpers.getCertificate(document.getDocumentElement()); + // if (cert != null) { + // certificateTabController.importCertificateFromString(cert); + // } else { + // setInfoMessageText(XML_CERTIFICATE_NOT_FOUND); + // } + // } catch (SAXException e) { + // setInfoMessageText(XML_NOT_WELL_FORMED); + // } + // } + // + // public void showXSWPreview() { + // try { + // Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(orgSAMLMessage); + // xswHelpers.applyXSW(samlGUI.getActionPanel().getSelectedXSW(), document); + // String after = xmlHelpers.getStringOfDocument(document, 2, true); + // String diff = xswHelpers.diffLineMode(orgSAMLMessage, after); + // + // File file = File.createTempFile("tmp", ".html", null); + // FileOutputStream fileOutputStream = new FileOutputStream(file); + // file.deleteOnExit(); + // fileOutputStream.write(diff.getBytes(StandardCharsets.UTF_8)); + // fileOutputStream.flush(); + // fileOutputStream.close(); + // + // URI uri = new URL("file://" + file.getAbsolutePath()).toURI(); + // + // Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null; + // if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) { + // desktop.browse(uri); + // } else { + // StringSelection stringSelection = new StringSelection(uri.toString()); + // Clipboard clpbrd = Toolkit.getDefaultToolkit().getSystemClipboard(); + // clpbrd.setContents(stringSelection, null); + // setInfoMessageText(NO_BROWSER); + // } + // + // } catch (SAXException e) { + // setInfoMessageText(XML_NOT_WELL_FORMED); + // } catch (DOMException e) { + // setInfoMessageText(XML_NOT_SUITABLE_FOR_XSW); + // } catch (MalformedURLException e) { + // } catch (URISyntaxException e) { + // } catch (IOException e) { + // setInfoMessageText(NO_DIFF_TEMP_FILE); + // } + // } + // + // public void applyXSW() { + // Document document; + // try { + // document = xmlHelpers.getXMLDocumentOfSAMLMessage(orgSAMLMessage); + // xswHelpers.applyXSW(samlGUI.getActionPanel().getSelectedXSW(), document); + // SAMLMessage = xmlHelpers.getStringOfDocument(document, 2, true); + // textArea.setText(SAMLMessage.getBytes()); + // isEdited = true; + // setRawMode(false); + // setInfoMessageText(XSW_ATTACK_APPLIED); + // } catch (SAXException e) { + // setInfoMessageText(XML_NOT_WELL_FORMED); + // } catch (IOException e) { + // setInfoMessageText(XML_COULD_NOT_SERIALIZE); + // } catch (DOMException | NullPointerException e) { + // setInfoMessageText(XML_NOT_SUITABLE_FOR_XSW); + // } + // } + // + // public void applyXXE(String collabUrl) { + // String xxePayload = " + // %xxe; ]>\n"; + // String[] splitMsg = orgSAMLMessage.split("\\?>"); + // if (splitMsg.length == 2) { + // SAMLMessage = splitMsg[0] + "?>" + xxePayload + splitMsg[1]; + // } else { + // String xmlDeclaration = "\n"; + // SAMLMessage = xmlDeclaration + xxePayload + orgSAMLMessage; + // } + // textArea.setText(SAMLMessage.getBytes()); + // isEdited = true; + // setRawMode(true); + // setInfoMessageText(XXE_CONTENT_APPLIED); + // } + // + // public void applyXSLT(String collabUrl) { + // String xslt = "\n" + + // "\n" + + // " \n" + + // " \n" + + // " \n" + + // " \n" + // + + // " \n" + + // " \n" + + // " \n" + + // " \n" + + // " \n" + + // ""; + // String transformString = ""; + // int index = orgSAMLMessage.indexOf(transformString); + // + // if (index == -1) { + // setInfoMessageText(XML_NOT_SUITABLE_FOR_XLST); + // } else { + // int substringIndex = index + transformString.length(); + // String firstPart = orgSAMLMessage.substring(0, substringIndex); + // String secondPart = orgSAMLMessage.substring(substringIndex); + // SAMLMessage = firstPart + xslt + secondPart; + // textArea.setText(SAMLMessage.getBytes()); + // isEdited = true; + // setRawMode(true); + // setInfoMessageText(XSLT_CONTENT_APPLIED); + // } + // } + // + // public synchronized void addMatchAndReplace(String match, String replace) { + // XSWHelpers.MATCH_AND_REPLACE_MAP.put(match, replace); + // } + // + // public synchronized HashMap getMatchAndReplaceMap() { + // return XSWHelpers.MATCH_AND_REPLACE_MAP; + // } + // + // public void setGUIEditable(boolean editable) { + // if (editable) { + // samlGUI.getActionPanel().enableControls(); + // } else { + // samlGUI.getActionPanel().disableControls(); + // } + // } + // + // public void showSignatureHelp() { + // SignatureHelpWindow window = new SignatureHelpWindow(); + // window.setVisible(true); + // } + // + // public void showXSWHelp() { + // XSWHelpWindow window = new XSWHelpWindow(); + // window.setVisible(true); + // } + // + // @Override + // public void update(Observable arg0, Object arg1) { + // updateCertificateList(); + // } + // + // public IBurpExtenderCallbacks getCallbacks() { + // return callbacks; + // } +} diff --git a/tool/src/main/java/samlraider/gui/CertificateTab.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/CertificateTab.java similarity index 75% rename from tool/src/main/java/samlraider/gui/CertificateTab.java rename to tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/CertificateTab.java index f4c1ddf..c6d33ed 100644 --- a/tool/src/main/java/samlraider/gui/CertificateTab.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/CertificateTab.java @@ -1,41 +1,30 @@ /* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) + * Zed Attack Proxy (ZAP) and its related class files. * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss + * ZAP is an HTTP/HTTPS proxy for assessing web application security. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * Copyright 2015 The ZAP Development Team * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * 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 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ - -package samlraider.gui; - -import samlraider.application.BurpCertificateBuilder; -import samlraider.application.CertificateTabController; -import samlraider.model.BurpCertificate; -import samlraider.model.ObjectIdentifier; - -import javax.swing.*; -import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.TreeSelectionListener; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeModel; -import java.awt.*; +package org.zaproxy.addon.migt.samlraider.gui; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; @@ -43,14 +32,38 @@ import java.util.Collections; import java.util.LinkedList; import java.util.List; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.DefaultListModel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JFileChooser; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSeparator; +import javax.swing.JSplitPane; +import javax.swing.JTextField; +import javax.swing.JTree; +import javax.swing.SwingUtilities; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import org.zaproxy.addon.migt.samlraider.application.BurpCertificateBuilder; +import org.zaproxy.addon.migt.samlraider.application.CertificateTabController; +import org.zaproxy.addon.migt.samlraider.model.BurpCertificate; +import org.zaproxy.addon.migt.samlraider.model.ObjectIdentifier; public class CertificateTab extends JPanel { private static final long serialVersionUID = 1L; final JFileChooser fc = new JFileChooser(); - private CertificateTabController certificateTabController; + private transient CertificateTabController certificateTabController; private JTree certificateTree; private DefaultTreeModel certificateTreeModel; - private BurpCertificate selectedBurpCertificate; + private transient BurpCertificate selectedBurpCertificate; // Plugin Specific private JTextField txtSource; @@ -73,7 +86,7 @@ public class CertificateTab extends JPanel { private JTextField txtStatus; private JTextField txtSignature; - //Parameter Names + // Parameter Names private JTextField txtSamlRequestParamName; private JTextField txtSamlResponseParamName; @@ -82,8 +95,8 @@ public class CertificateTab extends JPanel { private JCheckBox chckbxCa; private JCheckBox chckbxNoPathLimit; private JTextField txtPathLimit; - private List jbxKeyUsages; - private List jbxExtendedKeyUsages; + private transient List jbxKeyUsages; + private transient List jbxExtendedKeyUsages; private JList lstSubjectAlternativeNames; private DefaultListModel lstSubjectAlternativeNamesModel; private JList lstIssuerAlternativeNames; @@ -106,12 +119,13 @@ public CertificateTab() { // Wait b/c initialize text Fields try { - SwingUtilities.invokeAndWait(new Runnable() { - @Override - public void run() { - initializeGui(); - } - }); + SwingUtilities.invokeAndWait( + new Runnable() { + @Override + public void run() { + initializeGui(); + } + }); } catch (InvocationTargetException | InterruptedException e) { e.printStackTrace(); } @@ -128,10 +142,10 @@ private void initializeGui() { JPanel panelTop = new JPanel(); splitPane.setLeftComponent(panelTop); GridBagLayout gbl_panelTop = new GridBagLayout(); - gbl_panelTop.columnWidths = new int[]{200, 903}; - gbl_panelTop.rowHeights = new int[]{15, 192, 19, 0}; - gbl_panelTop.columnWeights = new double[]{0.0, Double.MIN_VALUE}; - gbl_panelTop.rowWeights = new double[]{0.0, 1.0, 0.0, Double.MIN_VALUE}; + gbl_panelTop.columnWidths = new int[] {200, 903}; + gbl_panelTop.rowHeights = new int[] {15, 192, 19, 0}; + gbl_panelTop.columnWeights = new double[] {0.0, Double.MIN_VALUE}; + gbl_panelTop.rowWeights = new double[] {0.0, 1.0, 0.0, Double.MIN_VALUE}; panelTop.setLayout(gbl_panelTop); JLabel lblTitle = new JLabel("SAML Certificates"); @@ -163,50 +177,57 @@ private void initializeGui() { verticalBox.add(btnImport); JButton btnImportCertificateChain = new JButton("Import Chain ..."); - btnImportCertificateChain.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - int returnVal = fc.showOpenDialog(CertificateTab.this); - if (returnVal == JFileChooser.APPROVE_OPTION) { - File file = fc.getSelectedFile(); - certificateTabController.importCertificateChain(file.getAbsolutePath()); - } else { - - } - } - }); + btnImportCertificateChain.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + int returnVal = fc.showOpenDialog(CertificateTab.this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = fc.getSelectedFile(); + certificateTabController.importCertificateChain(file.getAbsolutePath()); + } else { + + } + } + }); verticalBox.add(btnImportCertificateChain); JButton btnExport = new JButton("Export..."); - btnExport.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - int returnVal = fc.showOpenDialog(CertificateTab.this); - if (returnVal == JFileChooser.APPROVE_OPTION) { - File file = fc.getSelectedFile(); - certificateTabController.exportCertificate(selectedBurpCertificate, file.getAbsolutePath()); - } else { - System.out.println("Cancelled by user"); - } - } - }); + btnExport.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + int returnVal = fc.showOpenDialog(CertificateTab.this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = fc.getSelectedFile(); + certificateTabController.exportCertificate( + selectedBurpCertificate, file.getAbsolutePath()); + } else { + System.out.println("Cancelled by user"); + } + } + }); verticalBox.add(btnExport); JButton btnClone = new JButton("Clone"); - btnClone.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - certificateTabController.cloneCertificate(selectedBurpCertificate, new BurpCertificateBuilder(selectedBurpCertificate.getSubject())); - } - }); + btnClone.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + certificateTabController.cloneCertificate( + selectedBurpCertificate, + new BurpCertificateBuilder(selectedBurpCertificate.getSubject())); + } + }); Component verticalStrut = Box.createVerticalStrut(20); verticalStrut.setPreferredSize(new Dimension(0, 10)); verticalBox.add(verticalStrut); JButton btnDelete = new JButton("Delete"); - btnDelete.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - certificateTabController.removeBurpCertificate(selectedBurpCertificate); - } - }); + btnDelete.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + certificateTabController.removeBurpCertificate(selectedBurpCertificate); + } + }); verticalBox.add(btnDelete); Component verticalStrut_1 = Box.createVerticalStrut(20); @@ -215,33 +236,37 @@ public void actionPerformed(ActionEvent e) { verticalBox.add(btnClone); JButton btnCloneChain = new JButton("Clone Chain"); - btnCloneChain.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - List toClone = new LinkedList<>(); - DefaultMutableTreeNode node = (DefaultMutableTreeNode) certificateTree.getLastSelectedPathComponent(); - certificateTreeModel.getPathToRoot(node); - - for (Object n : node.getUserObjectPath()) { - if (n instanceof BurpCertificate) { - toClone.add((BurpCertificate) n); + btnCloneChain.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + List toClone = new LinkedList<>(); + DefaultMutableTreeNode node = + (DefaultMutableTreeNode) + certificateTree.getLastSelectedPathComponent(); + certificateTreeModel.getPathToRoot(node); + + for (Object n : node.getUserObjectPath()) { + if (n instanceof BurpCertificate) { + toClone.add((BurpCertificate) n); + } + } + Collections.reverse(toClone); + certificateTabController.cloneCertificateChain(toClone); } - } - Collections.reverse(toClone); - certificateTabController.cloneCertificateChain(toClone); - } - }); + }); verticalBox.add(btnCloneChain); - btnImport.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - int returnVal = fc.showOpenDialog(CertificateTab.this); - if (returnVal == JFileChooser.APPROVE_OPTION) { - File file = fc.getSelectedFile(); - certificateTabController.importCertificate(file.getAbsolutePath()); - } else { - System.out.println("Cancelled by user"); - } - } - }); + btnImport.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + int returnVal = fc.showOpenDialog(CertificateTab.this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = fc.getSelectedFile(); + certificateTabController.importCertificate(file.getAbsolutePath()); + } else { + System.out.println("Cancelled by user"); + } + } + }); JScrollPane scrollPane_2 = new JScrollPane(); scrollPane_2.setAlignmentX(Component.LEFT_ALIGNMENT); @@ -259,16 +284,19 @@ public void actionPerformed(ActionEvent e) { certificateTree.setAlignmentY(Component.TOP_ALIGNMENT); certificateTree.setAlignmentX(Component.LEFT_ALIGNMENT); scrollPane_2.setViewportView(certificateTree); - certificateTree.addTreeSelectionListener(new TreeSelectionListener() { - public void valueChanged(TreeSelectionEvent e) { - DefaultMutableTreeNode node = (DefaultMutableTreeNode) certificateTree.getLastSelectedPathComponent(); - if (node == null || node.getUserObject() instanceof String) { - return; - } - BurpCertificate burpCertificate = (BurpCertificate) node.getUserObject(); - certificateTabController.setCertificateDetails(burpCertificate); - } - }); + certificateTree.addTreeSelectionListener( + new TreeSelectionListener() { + public void valueChanged(TreeSelectionEvent e) { + DefaultMutableTreeNode node = + (DefaultMutableTreeNode) + certificateTree.getLastSelectedPathComponent(); + if (node == null || node.getUserObject() instanceof String) { + return; + } + BurpCertificate burpCertificate = (BurpCertificate) node.getUserObject(); + certificateTabController.setCertificateDetails(burpCertificate); + } + }); txtStatus = new JTextField(); txtStatus.setEditable(false); @@ -310,11 +338,48 @@ public void valueChanged(TreeSelectionEvent e) { JScrollPane bottomScrollPane = new JScrollPane(panelBottom); splitPane.setRightComponent(bottomScrollPane); GridBagLayout gbl_panelBottom = new GridBagLayout(); - gbl_panelBottom.columnWidths = new int[]{200, 1180}; - gbl_panelBottom.rowHeights = new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 39, 0}; - gbl_panelBottom.columnWeights = new double[]{0.0, 1.0}; - gbl_panelBottom.rowWeights = new double[]{1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, - 0.0, 1.0, Double.MIN_VALUE}; + gbl_panelBottom.columnWidths = new int[] {200, 1180}; + gbl_panelBottom.rowHeights = + new int[] { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 39, 0 + }; + gbl_panelBottom.columnWeights = new double[] {0.0, 1.0}; + gbl_panelBottom.rowWeights = + new double[] { + 1.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 1.0, + Double.MIN_VALUE + }; panelBottom.setLayout(gbl_panelBottom); JLabel lblPluginSpecific = new JLabel("Plugin Specific"); @@ -364,17 +429,19 @@ public void valueChanged(TreeSelectionEvent e) { hbPrivateKey.add(chckbxPrivateKey); JButton btnImportPrivateKeyPKCS8 = new JButton("PKCS#8 DER..."); - btnImportPrivateKeyPKCS8.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - int returnVal = fc.showOpenDialog(CertificateTab.this); - if (returnVal == JFileChooser.APPROVE_OPTION) { - File file = fc.getSelectedFile(); - certificateTabController.importPKCS8(selectedBurpCertificate, file.getAbsolutePath()); - } else { - System.out.println("Cancelled by user"); - } - } - }); + btnImportPrivateKeyPKCS8.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + int returnVal = fc.showOpenDialog(CertificateTab.this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = fc.getSelectedFile(); + certificateTabController.importPKCS8( + selectedBurpCertificate, file.getAbsolutePath()); + } else { + System.out.println("Cancelled by user"); + } + } + }); Component horizontalStrut_5 = Box.createHorizontalStrut(20); hbPrivateKey.add(horizontalStrut_5); @@ -387,30 +454,34 @@ public void actionPerformed(ActionEvent e) { hbPrivateKey.add(btnImportPrivateKeyPKCS8); btnExportPrivateKeyRSA = new JButton("Traditional RSA PEM..."); - btnExportPrivateKeyRSA.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - int returnVal = fc.showOpenDialog(CertificateTab.this); - if (returnVal == JFileChooser.APPROVE_OPTION) { - File file = fc.getSelectedFile(); - certificateTabController.exportPrivateKey(selectedBurpCertificate, file.getAbsolutePath()); - } else { - System.out.println("Cancelled by user"); - } - } - }); + btnExportPrivateKeyRSA.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + int returnVal = fc.showOpenDialog(CertificateTab.this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = fc.getSelectedFile(); + certificateTabController.exportPrivateKey( + selectedBurpCertificate, file.getAbsolutePath()); + } else { + System.out.println("Cancelled by user"); + } + } + }); JButton btnImportPrivateKeyRSA = new JButton("Traditional RSA PEM..."); - btnImportPrivateKeyRSA.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - int returnVal = fc.showOpenDialog(CertificateTab.this); - if (returnVal == JFileChooser.APPROVE_OPTION) { - File file = fc.getSelectedFile(); - certificateTabController.importPrivateKey(selectedBurpCertificate, file.getAbsolutePath()); - } else { - System.out.println("Cancelled by user"); - } - } - }); + btnImportPrivateKeyRSA.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + int returnVal = fc.showOpenDialog(CertificateTab.this); + if (returnVal == JFileChooser.APPROVE_OPTION) { + File file = fc.getSelectedFile(); + certificateTabController.importPrivateKey( + selectedBurpCertificate, file.getAbsolutePath()); + } else { + System.out.println("Cancelled by user"); + } + } + }); Component horizontalStrut_12 = Box.createHorizontalStrut(20); hbPrivateKey.add(horizontalStrut_12); @@ -443,11 +514,12 @@ public void actionPerformed(ActionEvent e) { panelBottom.add(hbEdit, gbc_hbEdit); JButton btnSaveAndSelfsign = new JButton("Save and Self-Sign"); - btnSaveAndSelfsign.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - certificateTabController.createBurpCertificate(selectedBurpCertificate); - } - }); + btnSaveAndSelfsign.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + certificateTabController.createBurpCertificate(selectedBurpCertificate); + } + }); hbEdit.add(btnSaveAndSelfsign); Component horizontalStrut_8 = Box.createHorizontalStrut(20); @@ -508,7 +580,9 @@ public void actionPerformed(ActionEvent e) { gbc_lblSignatureAlgorithm.gridy = 9; panelBottom.add(lblSignatureAlgorithm, gbc_lblSignatureAlgorithm); - txtSignatureAlgorithm = new JComboBox(ObjectIdentifier.getAllSignatureAlgorithms().toArray(new String[0])); + txtSignatureAlgorithm = + new JComboBox( + ObjectIdentifier.getAllSignatureAlgorithms().toArray(new String[0])); txtSignatureAlgorithm.setSelectedIndex(-1); txtSignatureAlgorithm.setEditable(true); GridBagConstraints gbc_txtSignaturealgorithm = new GridBagConstraints(); @@ -544,7 +618,8 @@ public void actionPerformed(ActionEvent e) { panelBottom.add(lblNotBefore, gbc_lblNotBefore); txtNotBefore = new JTextField(); - txtNotBefore.setToolTipText("Format: \"May 23 23:05:42 2005 GMT\" or \"Mon May 23 23:05:42 CET 2005\""); + txtNotBefore.setToolTipText( + "Format: \"May 23 23:05:42 2005 GMT\" or \"Mon May 23 23:05:42 CET 2005\""); GridBagConstraints gbc_txtNotbefore = new GridBagConstraints(); gbc_txtNotbefore.anchor = GridBagConstraints.WEST; gbc_txtNotbefore.insets = new Insets(0, 0, 5, 0); @@ -595,7 +670,9 @@ public void actionPerformed(ActionEvent e) { gbc_lblPublicKeyAlgorithm.gridy = 14; panelBottom.add(lblPublicKeyAlgorithm, gbc_lblPublicKeyAlgorithm); - txtPublicKeyAlgorithm = new JComboBox(ObjectIdentifier.getAllPublicKeyAlgorithms().toArray(new String[0])); + txtPublicKeyAlgorithm = + new JComboBox( + ObjectIdentifier.getAllPublicKeyAlgorithms().toArray(new String[0])); txtPublicKeyAlgorithm.setSelectedIndex(-1); txtPublicKeyAlgorithm.setEditable(true); GridBagConstraints gbc_txtPublickezalgorithm = new GridBagConstraints(); @@ -701,12 +778,14 @@ public void actionPerformed(ActionEvent e) { panelBottom.add(horizontalBox, gbc_horizontalBox); chckbxCa = new JCheckBox("CA"); - chckbxCa.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - txtPathLimit.setEnabled(chckbxCa.isSelected() && !chckbxNoPathLimit.isSelected()); - chckbxNoPathLimit.setEnabled(chckbxCa.isSelected()); - } - }); + chckbxCa.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + txtPathLimit.setEnabled( + chckbxCa.isSelected() && !chckbxNoPathLimit.isSelected()); + chckbxNoPathLimit.setEnabled(chckbxCa.isSelected()); + } + }); horizontalBox.add(chckbxCa); Component horizontalStrut_4 = Box.createHorizontalStrut(20); @@ -720,30 +799,32 @@ public void actionPerformed(ActionEvent e) { txtPathLimit.setColumns(10); chckbxNoPathLimit = new JCheckBox("No Path Limit"); - chckbxNoPathLimit.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - txtPathLimit.setEnabled(!chckbxNoPathLimit.isSelected()); - txtPathLimit.setText(""); - } - }); + chckbxNoPathLimit.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + txtPathLimit.setEnabled(!chckbxNoPathLimit.isSelected()); + txtPathLimit.setText(""); + } + }); horizontalBox.add(chckbxNoPathLimit); Component horizontalStrut_10 = Box.createHorizontalStrut(20); horizontalBox.add(horizontalStrut_10); chckbxIgnoreBasicConstraints = new JCheckBox("Don't copy."); - chckbxIgnoreBasicConstraints.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - boolean isSelected = chckbxIgnoreBasicConstraints.isSelected(); - chckbxCa.setEnabled(!isSelected); - chckbxNoPathLimit.setEnabled(!isSelected); - if (isSelected) { - txtPathLimit.setEnabled(false); - } else { - txtPathLimit.setEnabled(!chckbxNoPathLimit.isSelected()); - } - } - }); + chckbxIgnoreBasicConstraints.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + boolean isSelected = chckbxIgnoreBasicConstraints.isSelected(); + chckbxCa.setEnabled(!isSelected); + chckbxNoPathLimit.setEnabled(!isSelected); + if (isSelected) { + txtPathLimit.setEnabled(false); + } else { + txtPathLimit.setEnabled(!chckbxNoPathLimit.isSelected()); + } + } + }); horizontalBox.add(chckbxIgnoreBasicConstraints); JLabel lblKeyusage = new JLabel("Key Usage"); @@ -825,20 +906,23 @@ public void actionPerformed(ActionEvent e) { JButton btnDeletesubjectalternativename = new JButton("Delete"); btnDeletesubjectalternativename.setAlignmentY(Component.TOP_ALIGNMENT); - btnDeletesubjectalternativename.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - int selectedIndex = lstSubjectAlternativeNames.getSelectedIndex(); - if (selectedIndex != -1) { - lstSubjectAlternativeNamesModel.remove(selectedIndex); - } - } - }); + btnDeletesubjectalternativename.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + int selectedIndex = lstSubjectAlternativeNames.getSelectedIndex(); + if (selectedIndex != -1) { + lstSubjectAlternativeNamesModel.remove(selectedIndex); + } + } + }); hbSubjectAlternativeName.add(btnDeletesubjectalternativename); Component horizontalStrut_1 = Box.createHorizontalStrut(20); hbSubjectAlternativeName.add(horizontalStrut_1); - cbbSubjectAlternativeNameType = new JComboBox(ObjectIdentifier.getAllSubjectAlternativeNames().toArray(new String[0])); + cbbSubjectAlternativeNameType = + new JComboBox( + ObjectIdentifier.getAllSubjectAlternativeNames().toArray(new String[0])); cbbSubjectAlternativeNameType.setMaximumSize(new Dimension(0, 25)); cbbSubjectAlternativeNameType.setMinimumSize(new Dimension(100, 25)); cbbSubjectAlternativeNameType.setAlignmentX(Component.LEFT_ALIGNMENT); @@ -853,12 +937,17 @@ public void actionPerformed(ActionEvent e) { txtSubjectAlternativeNameName.setColumns(20); JButton tbnAddSubjectAlternativeName = new JButton("Add"); - tbnAddSubjectAlternativeName.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - - addSubjectAlternativeNames(txtSubjectAlternativeNameName.getText() + " (" + cbbSubjectAlternativeNameType.getSelectedItem().toString() + ")"); - } - }); + tbnAddSubjectAlternativeName.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + + addSubjectAlternativeNames( + txtSubjectAlternativeNameName.getText() + + " (" + + cbbSubjectAlternativeNameType.getSelectedItem().toString() + + ")"); + } + }); tbnAddSubjectAlternativeName.setAlignmentY(Component.TOP_ALIGNMENT); hbSubjectAlternativeName.add(tbnAddSubjectAlternativeName); @@ -896,20 +985,23 @@ public void actionPerformed(ActionEvent e) { JButton btnBtndeleteissueralternativename = new JButton("Delete"); btnBtndeleteissueralternativename.setAlignmentY(Component.TOP_ALIGNMENT); - btnBtndeleteissueralternativename.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - int selectedIndex = lstIssuerAlternativeNames.getSelectedIndex(); - if (selectedIndex != -1) { - lstIssuerAlternativeNamesModel.remove(selectedIndex); - } - } - }); + btnBtndeleteissueralternativename.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + int selectedIndex = lstIssuerAlternativeNames.getSelectedIndex(); + if (selectedIndex != -1) { + lstIssuerAlternativeNamesModel.remove(selectedIndex); + } + } + }); hbIssuerAlternativeName.add(btnBtndeleteissueralternativename); Component horizontalStrut_2 = Box.createHorizontalStrut(20); hbIssuerAlternativeName.add(horizontalStrut_2); - cbbIssuerAlternativeNameType = new JComboBox(ObjectIdentifier.getAllSubjectAlternativeNames().toArray(new String[0])); + cbbIssuerAlternativeNameType = + new JComboBox( + ObjectIdentifier.getAllSubjectAlternativeNames().toArray(new String[0])); cbbIssuerAlternativeNameType.setMaximumSize(new Dimension(0, 25)); cbbIssuerAlternativeNameType.setAlignmentX(Component.LEFT_ALIGNMENT); cbbIssuerAlternativeNameType.setAlignmentY(Component.TOP_ALIGNMENT); @@ -925,11 +1017,16 @@ public void actionPerformed(ActionEvent e) { JButton btnAddissueralternativename = new JButton("Add"); btnAddissueralternativename.setMaximumSize(new Dimension(0, 25)); btnAddissueralternativename.setAlignmentY(Component.TOP_ALIGNMENT); - btnAddissueralternativename.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - addIssuerAlternativeNames(txtIssuerAlternativeNameName.getText() + " (" + cbbIssuerAlternativeNameType.getSelectedItem().toString() + ")"); - } - }); + btnAddissueralternativename.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + addIssuerAlternativeNames( + txtIssuerAlternativeNameName.getText() + + " (" + + cbbIssuerAlternativeNameType.getSelectedItem().toString() + + ")"); + } + }); hbIssuerAlternativeName.add(btnAddissueralternativename); JLabel lblSubjectKeyIdentifier = new JLabel("Subject Key Identifier"); @@ -953,11 +1050,13 @@ public void actionPerformed(ActionEvent e) { txtSubjectkeyidentifier.setColumns(40); chckbxAutosubjectkeyidentifier = new JCheckBox("Auto generate from Public Key"); - chckbxAutosubjectkeyidentifier.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - txtSubjectkeyidentifier.setEnabled(!chckbxAutosubjectkeyidentifier.isSelected()); - } - }); + chckbxAutosubjectkeyidentifier.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + txtSubjectkeyidentifier.setEnabled( + !chckbxAutosubjectkeyidentifier.isSelected()); + } + }); hbSubjectKeyIdentifier.add(chckbxAutosubjectkeyidentifier); JLabel lblAuthorityKeyIdentifier = new JLabel("Authority Key Identifier"); @@ -981,11 +1080,13 @@ public void actionPerformed(ActionEvent e) { txtAuthoritykeyidentifier.setColumns(40); chckbxAutoauthoritykeyidetifier = new JCheckBox("Auto generate from Issuer Public Key"); - chckbxAutoauthoritykeyidetifier.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - txtAuthoritykeyidentifier.setEnabled(!chckbxAutoauthoritykeyidetifier.isSelected()); - } - }); + chckbxAutoauthoritykeyidetifier.addActionListener( + new ActionListener() { + public void actionPerformed(ActionEvent e) { + txtAuthoritykeyidentifier.setEnabled( + !chckbxAutoauthoritykeyidetifier.isSelected()); + } + }); hbAuthorityKeyIdentifier.add(chckbxAutoauthoritykeyidetifier); JLabel lblUnsupportedExtensions = new JLabel("Unsupported Extensions"); diff --git a/tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/ImagePanel.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/ImagePanel.java new file mode 100644 index 0000000..740cd9a --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/ImagePanel.java @@ -0,0 +1,79 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2015 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt.samlraider.gui; + +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import javax.imageio.ImageIO; +import javax.swing.JPanel; + +public class ImagePanel extends JPanel { + + private static final long serialVersionUID = 1L; + private final int preferredHeight = 1200; + private final int preferredWidth = 800; + private transient BufferedImage image; + + public ImagePanel(String path) { + try { + image = ImageIO.read(new File(path)); + } catch (IOException ex) { + try { + image = ImageIO.read(getClass().getResourceAsStream("/" + path)); + } catch (IOException e) { + e.printStackTrace(); + } + } + + BufferedImage resized = new BufferedImage(preferredWidth, preferredHeight, image.getType()); + Graphics2D g = resized.createGraphics(); + g.setRenderingHint( + RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + g.drawImage( + image, + 0, + 0, + preferredWidth, + preferredHeight, + 0, + 0, + image.getWidth(), + image.getHeight(), + null); + g.dispose(); + image = resized; + } + + @Override + protected void paintComponent(Graphics g) { + super.paintComponent(g); + g.drawImage(image, 0, 0, null); + } + + @Override + public Dimension getPreferredSize() { + return new Dimension(preferredWidth, preferredHeight); + } +} diff --git a/tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/SamlMain.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/SamlMain.java new file mode 100644 index 0000000..e86de81 --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/SamlMain.java @@ -0,0 +1,111 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2015 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt.samlraider.gui; + +import javax.swing.JPanel; +import org.zaproxy.addon.migt.samlraider.application.SamlTabController; + +public class SamlMain extends JPanel { + + private static final long serialVersionUID = 1L; + // private ITextEditor textEditorAction; + // private ITextEditor textEditorInformation; + private transient SamlTabController controller; + private SamlPanelAction panelAction; + private SamlPanelInfo panelInformation; + + // public SamlMain() { + // super(); + // initializeUI(); + // } + // + // public SamlMain(SamlTabController controller) { + // super(); + // this.controller = controller; + // initializeUI(); + // } + // + // private void initializeUI() { + // setLayout(new BorderLayout(0, 0)); + // + // JSplitPane splitPaneAction = new JSplitPane(); + // splitPaneAction.setOrientation(JSplitPane.VERTICAL_SPLIT); + // splitPaneAction.setDividerSize(5); + // add(splitPaneAction, BorderLayout.CENTER); + // + // JPanel panelActionTop = new JPanel(); + // splitPaneAction.setLeftComponent(panelActionTop); + // panelActionTop.setLayout(new BorderLayout(0, 0)); + // panelAction = new SamlPanelAction(controller); + // panelActionTop.add(panelAction); + // + // JPanel panelActionBottom = new JPanel(); + // splitPaneAction.setRightComponent(panelActionBottom); + // panelActionBottom.setLayout(new BorderLayout(0, 0)); + // textEditorAction = controller.getCallbacks().createTextEditor(); + // + // textEditorAction.setText("".getBytes()); + // panelActionBottom.add(textEditorAction.getComponent(), BorderLayout.CENTER); + // + // JSplitPane splitPaneInformation = new JSplitPane(); + // splitPaneInformation.setOrientation(JSplitPane.VERTICAL_SPLIT); + // splitPaneAction.setDividerSize(5); + // add(splitPaneInformation, BorderLayout.CENTER); + // + // JPanel panelInformationTop = new JPanel(); + // splitPaneInformation.setLeftComponent((panelInformationTop)); + // panelInformationTop.setLayout(new BorderLayout(0, 0)); + // panelInformation = new SamlPanelInfo(); + // panelInformationTop.add(panelInformation); + // + // JPanel panelInformationBottom = new JPanel(); + // splitPaneInformation.setRightComponent(panelInformationBottom); + // panelInformationBottom.setLayout(new BorderLayout(0, 0)); + // textEditorInformation = controller.getCallbacks().createTextEditor(); + // textEditorInformation.setText("".getBytes()); + // textEditorAction.setEditable(false); + // panelInformationBottom.add(textEditorInformation.getComponent(), BorderLayout.CENTER); + // + // JTabbedPane tabbedPane = new JTabbedPane(); + // add(tabbedPane); + // tabbedPane.addTab("SAML Attacks", null, splitPaneAction, "SAML Attacks"); + // tabbedPane.addTab("SAML Message Info", null, splitPaneInformation, "SAML Message + // Info"); + // + // this.invalidate(); + // this.updateUI(); + // } + // + // public ITextEditor getTextEditorAction() { + // return textEditorAction; + // } + // + // public ITextEditor getTextEditorInformation() { + // return textEditorInformation; + // } + + public SamlPanelAction getActionPanel() { + return panelAction; + } + + public SamlPanelInfo getInfoPanel() { + return panelInformation; + } +} diff --git a/tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/SamlPanelAction.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/SamlPanelAction.java new file mode 100644 index 0000000..a2d4cc4 --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/SamlPanelAction.java @@ -0,0 +1,474 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2015 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt.samlraider.gui; + +import java.util.List; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import org.zaproxy.addon.migt.samlraider.application.SamlTabController; +import org.zaproxy.addon.migt.samlraider.model.BurpCertificate; + +public class SamlPanelAction extends JPanel { + + private static final long serialVersionUID = 1L; + private transient SamlTabController controller; + private JLabel lblMessage; + private JComboBox cmbboxCertificate; + private JComboBox cmbboxXSW; + private JButton btnXSWHelp; + private JButton btnXSWPreview; + private JButton btnSignatureReset; + private JButton btnXSWApply; + private JButton btnMatchAndReplace; + private JButton btnTestXXE; + private JButton btnTestXSLT; + private JButton btnSignatureHelp; + private JButton btnSignatureRemove; + private JButton btnSignatureReplace; + private JButton btnSendCertificate; + private JButton btnSignatureAdd; + private JTextField txtSearch; + private JCheckBox chkRawMode; + + // public SamlPanelAction() { + // initialize(); + // } + // + // public SamlPanelAction(SamlTabController controller) { + // this.controller = controller; + // initialize(); + // } + + // private void initialize() { + // GridBagLayout gridBagLayout = new GridBagLayout(); + // gridBagLayout.columnWidths = new int[]{0, 0, 0}; + // gridBagLayout.rowHeights = new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + // gridBagLayout.columnWeights = new double[]{0.0, 0.0, 0.0, Double.MIN_VALUE}; + // gridBagLayout.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + // 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE}; + // setLayout(gridBagLayout); + // + // + // JLabel lblSamlMsgTitle = new JLabel("SAML Message"); + // GridBagConstraints gbc_lblSamlMsgTitle = new GridBagConstraints(); + // gbc_lblSamlMsgTitle.insets = new Insets(10, 5, 5, 0); + // gbc_lblSamlMsgTitle.anchor = GridBagConstraints.WEST; + // gbc_lblSamlMsgTitle.gridx = 0; + // gbc_lblSamlMsgTitle.gridy = 0; + // gbc_lblSamlMsgTitle.gridwidth = 3; + // add(lblSamlMsgTitle, gbc_lblSamlMsgTitle); + // + // btnSignatureReset = new JButton("Reset Message"); + // GridBagConstraints gbc_btnSignatureReset = new GridBagConstraints(); + // gbc_btnSignatureReset.anchor = GridBagConstraints.WEST; + // gbc_btnSignatureReset.insets = new Insets(5, 5, 0, 0); + // gbc_btnSignatureReset.gridx = 1; + // gbc_btnSignatureReset.gridy = 1; + // add(btnSignatureReset, gbc_btnSignatureReset); + // btnSignatureReset.addActionListener(new ActionListener() { + // public void actionPerformed(ActionEvent e) { + // controller.resetMessage(); + // } + // }); + // + // chkRawMode = new JCheckBox("Raw Mode (don't parse XML before sending)"); + // GridBagConstraints gbc_chkRawMode = new GridBagConstraints(); + // gbc_chkRawMode.anchor = GridBagConstraints.WEST; + // gbc_chkRawMode.insets = new Insets(5, 5, 0, 0); + // gbc_chkRawMode.gridx = 2; + // gbc_chkRawMode.gridy = 1; + // gbc_chkRawMode.gridwidth = 2; + // add(chkRawMode, gbc_chkRawMode); + // chkRawMode.addActionListener(new ActionListener() { + // public void actionPerformed(ActionEvent e) { + // controller.setRawMode(chkRawMode.isSelected()); + // } + // }); + // + // JLabel lblXSWTitle = new JLabel("XSW Attacks"); + // GridBagConstraints gbc_lblXSWTitle = new GridBagConstraints(); + // gbc_lblXSWTitle.insets = new Insets(10, 5, 5, 0); + // gbc_lblXSWTitle.anchor = GridBagConstraints.WEST; + // gbc_lblXSWTitle.gridx = 0; + // gbc_lblXSWTitle.gridy = 3; + // gbc_lblXSWTitle.gridwidth = 3; + // add(lblXSWTitle, gbc_lblXSWTitle); + // + // btnXSWHelp = new JButton("?"); + // GridBagConstraints gbc_btnXSWHelp = new GridBagConstraints(); + // gbc_btnXSWHelp.insets = new Insets(5, 5, 0, 0); + // gbc_btnXSWHelp.gridx = 0; + // gbc_btnXSWHelp.gridy = 4; + // btnXSWHelp.addActionListener(new ActionListener() { + // public void actionPerformed(ActionEvent e) { + // controller.showXSWHelp(); + // } + // }); + // add(btnXSWHelp, gbc_btnXSWHelp); + // + // cmbboxXSW = new JComboBox(); + // GridBagConstraints gbc_cmbboxXSW = new GridBagConstraints(); + // gbc_cmbboxXSW.insets = new Insets(5, 5, 0, 0); + // gbc_cmbboxXSW.anchor = GridBagConstraints.WEST; + // gbc_cmbboxXSW.gridx = 1; + // gbc_cmbboxXSW.gridy = 4; + // gbc_cmbboxXSW.gridwidth = 3; + // add(cmbboxXSW, gbc_cmbboxXSW); + // + // btnXSWPreview = new JButton("Preview in Browser..."); + // GridBagConstraints gbc_btnXSWPreview = new GridBagConstraints(); + // gbc_btnXSWPreview.anchor = GridBagConstraints.WEST; + // gbc_btnXSWPreview.insets = new Insets(5, 5, 0, 0); + // gbc_btnXSWPreview.gridx = 1; + // gbc_btnXSWPreview.gridy = 5; + // btnXSWPreview.addActionListener(new ActionListener() { + // public void actionPerformed(ActionEvent e) { + // controller.showXSWPreview(); + // } + // }); + // add(btnXSWPreview, gbc_btnXSWPreview); + // + // btnXSWApply = new JButton("Apply XSW"); + // GridBagConstraints gbc_btnXSWApply = new GridBagConstraints(); + // gbc_btnXSWApply.insets = new Insets(5, 5, 0, 0); + // gbc_btnXSWApply.anchor = GridBagConstraints.WEST; + // gbc_btnXSWApply.gridx = 3; + // gbc_btnXSWApply.gridy = 5; + // btnXSWApply.addActionListener(new ActionListener() { + // public void actionPerformed(ActionEvent e) { + // controller.applyXSW(); + // } + // }); + // add(btnXSWApply, gbc_btnXSWApply); + // + // btnMatchAndReplace = new JButton("Match and Replace"); + // GridBagConstraints gbc_btnMatchAndReplace = new GridBagConstraints(); + // gbc_btnMatchAndReplace.insets = new Insets(5, 5, 0, 0); + // gbc_btnMatchAndReplace.anchor = GridBagConstraints.WEST; + // gbc_btnMatchAndReplace.gridx = 2; + // gbc_btnMatchAndReplace.gridy = 5; + // btnMatchAndReplace.addActionListener(new ActionListener() { + // public void actionPerformed(ActionEvent e) { + // showMatchAndReplaceDialog(); + // } + // }); + // add(btnMatchAndReplace, gbc_btnMatchAndReplace); + // + // JLabel lblXmlAttacksTitle = new JLabel("XML Attacks"); + // GridBagConstraints gbc_lblXmlAttacksTitle = new GridBagConstraints(); + // gbc_lblXmlAttacksTitle.anchor = GridBagConstraints.WEST; + // gbc_lblXmlAttacksTitle.insets = new Insets(10, 5, 5, 0); + // gbc_lblXmlAttacksTitle.gridx = 0; + // gbc_lblXmlAttacksTitle.gridy = 7; + // gbc_lblXmlAttacksTitle.gridwidth = 3; + // add(lblXmlAttacksTitle, gbc_lblXmlAttacksTitle); + // + // btnTestXXE = new JButton("Test XXE"); + // GridBagConstraints gbc_btnTestXXE = new GridBagConstraints(); + // gbc_btnTestXXE.insets = new Insets(5, 5, 0, 0); + // gbc_btnTestXXE.anchor = GridBagConstraints.WEST; + // gbc_btnTestXXE.gridx = 1; + // gbc_btnTestXXE.gridy = 8; + // btnTestXXE.addActionListener(new ActionListener() { + // public void actionPerformed(ActionEvent e) { + // String collabUrl = JOptionPane.showInputDialog(btnXSWApply, + // "Enter Burp Collaborator URL (e.g. + // https://xyz.burpcollaborator.net)"); + // if (collabUrl != null) { + // controller.applyXXE(collabUrl); + // } + // } + // }); + // add(btnTestXXE, gbc_btnTestXXE); + // + // btnTestXSLT = new JButton("Test XSLT"); + // GridBagConstraints gbc_btnTestXSLT = new GridBagConstraints(); + // gbc_btnTestXSLT.insets = new Insets(5, 5, 0, 0); + // gbc_btnTestXSLT.anchor = GridBagConstraints.WEST; + // gbc_btnTestXSLT.gridx = 2; + // gbc_btnTestXSLT.gridy = 8; + // btnTestXSLT.addActionListener(new ActionListener() { + // public void actionPerformed(ActionEvent e) { + // String collabUrl = JOptionPane.showInputDialog(btnXSWApply, + // "Enter Burp Collaborator URL (e.g. + // https://xyz.burpcollaborator.net)"); + // if (collabUrl != null) { + // controller.applyXSLT(collabUrl); + // } + // } + // }); + // add(btnTestXSLT, gbc_btnTestXSLT); + // + // JLabel lblSignatureTitle = new JLabel("XML Signature Attacks"); + // GridBagConstraints gbc_lblSignatureTitle = new GridBagConstraints(); + // gbc_lblSignatureTitle.anchor = GridBagConstraints.WEST; + // gbc_lblSignatureTitle.insets = new Insets(10, 5, 5, 0); + // gbc_lblSignatureTitle.gridx = 0; + // gbc_lblSignatureTitle.gridy = 10; + // gbc_lblSignatureTitle.gridwidth = 3; + // add(lblSignatureTitle, gbc_lblSignatureTitle); + // + // btnSignatureHelp = new JButton("?"); + // GridBagConstraints gbc_btnSignatureHelp = new GridBagConstraints(); + // gbc_btnSignatureHelp.insets = new Insets(5, 5, 0, 0); + // gbc_btnSignatureHelp.gridx = 0; + // gbc_btnSignatureHelp.gridy = 11; + // btnSignatureHelp.addActionListener(new ActionListener() { + // public void actionPerformed(ActionEvent e) { + // controller.showSignatureHelp(); + // } + // }); + // add(btnSignatureHelp, gbc_btnSignatureHelp); + // + // cmbboxCertificate = new JComboBox(); + // GridBagConstraints gbc_cmbboxCertificate = new GridBagConstraints(); + // gbc_cmbboxCertificate.insets = new Insets(5, 5, 0, 0); + // gbc_cmbboxCertificate.anchor = GridBagConstraints.WEST; + // gbc_cmbboxCertificate.gridx = 1; + // gbc_cmbboxCertificate.gridy = 12; + // gbc_cmbboxCertificate.gridwidth = 3; + // add(cmbboxCertificate, gbc_cmbboxCertificate); + // + // btnSignatureRemove = new JButton("Remove Signatures"); + // btnSignatureRemove.addActionListener(new ActionListener() { + // public void actionPerformed(ActionEvent e) { + // controller.removeSignature(); + // } + // }); + // GridBagConstraints gbc_btnSignatureRemove = new GridBagConstraints(); + // gbc_btnSignatureRemove.anchor = GridBagConstraints.WEST; + // gbc_btnSignatureRemove.insets = new Insets(5, 5, 0, 0); + // gbc_btnSignatureRemove.gridx = 1; + // gbc_btnSignatureRemove.gridy = 11; + // add(btnSignatureRemove, gbc_btnSignatureRemove); + // + // btnSignatureReplace = new JButton("(Re-)Sign Assertion"); + // btnSignatureReplace.addActionListener(new ActionListener() { + // public void actionPerformed(ActionEvent e) { + // controller.resignAssertion(); + // } + // }); + // GridBagConstraints gbc_btnSignatureReplace = new GridBagConstraints(); + // gbc_btnSignatureReplace.anchor = GridBagConstraints.WEST; + // gbc_btnSignatureReplace.insets = new Insets(5, 5, 0, 0); + // gbc_btnSignatureReplace.gridx = 1; + // gbc_btnSignatureReplace.gridy = 13; + // add(btnSignatureReplace, gbc_btnSignatureReplace); + // + // + // btnSendCertificate = new JButton("Send Certificate to SAML Raider Certificates"); + // GridBagConstraints gbc_btnSendCertificate = new GridBagConstraints(); + // gbc_btnSendCertificate.anchor = GridBagConstraints.WEST; + // gbc_btnSendCertificate.insets = new Insets(5, 5, 0, 0); + // gbc_btnSendCertificate.gridx = 2; + // gbc_btnSendCertificate.gridy = 11; + // gbc_btnSendCertificate.gridwidth = 2; + // btnSendCertificate.addActionListener(new ActionListener() { + // public void actionPerformed(ActionEvent e) { + // controller.sendToCertificatesTab(); + // } + // }); + // add(btnSendCertificate, gbc_btnSendCertificate); + // + // btnSignatureAdd = new JButton("(Re-)Sign Message"); + // GridBagConstraints gbc_btnSignatureAdd = new GridBagConstraints(); + // gbc_btnSignatureAdd.anchor = GridBagConstraints.WEST; + // btnSignatureAdd.addActionListener(new ActionListener() { + // public void actionPerformed(ActionEvent e) { + // controller.resignMessage(); + // } + // }); + // gbc_btnSignatureAdd.insets = new Insets(5, 5, 0, 0); + // gbc_btnSignatureAdd.gridx = 2; + // gbc_btnSignatureAdd.gridy = 13; + // add(btnSignatureAdd, gbc_btnSignatureAdd); + // + // + // lblMessage = new JLabel(""); + // lblMessage.setBackground(new Color(255, 250, 205)); + // lblMessage.setForeground(new Color(255, 140, 0)); + // GridBagConstraints gbc_lblMessage = new GridBagConstraints(); + // gbc_lblMessage.anchor = GridBagConstraints.WEST; + // gbc_lblMessage.gridwidth = 3; + // gbc_lblMessage.insets = new Insets(20, 5, 10, 0); + // gbc_lblMessage.gridx = 0; + // gbc_lblMessage.gridy = 14; + // add(lblMessage, gbc_lblMessage); + // } + + public JLabel getInfoMessageLabel() { + return lblMessage; + } + + public void setCertificateList(List list) { + DefaultComboBoxModel model = new DefaultComboBoxModel(); + + for (BurpCertificate cert : list) { + model.addElement(cert); + } + cmbboxCertificate.setModel(model); + } + + public BurpCertificate getSelectedCertificate() { + return (BurpCertificate) cmbboxCertificate.getSelectedItem(); + } + + public void setXSWList(String[] xswTypes) { + DefaultComboBoxModel model = new DefaultComboBoxModel(xswTypes); + cmbboxXSW.setModel(model); + } + + public String getSelectedXSW() { + return (String) cmbboxXSW.getSelectedItem(); + } + + public String getSearchText() { + return txtSearch.getText(); + } + + public boolean isRawModeEnabled() { + return chkRawMode.isSelected(); + } + + public void setRawModeEnabled(boolean rawModeEnabled) { + chkRawMode.setSelected(rawModeEnabled); + } + + public void disableControls() { + cmbboxCertificate.setEnabled(false); + cmbboxXSW.setEnabled(false); + btnXSWHelp.setEnabled(false); + btnXSWPreview.setEnabled(false); + btnSignatureReset.setEnabled(false); + btnXSWApply.setEnabled(false); + btnSignatureHelp.setEnabled(false); + btnSignatureRemove.setEnabled(false); + btnSignatureReplace.setEnabled(false); + btnSendCertificate.setEnabled(false); + btnSignatureAdd.setEnabled(false); + btnMatchAndReplace.setEnabled(false); + btnTestXXE.setEnabled(false); + btnTestXSLT.setEnabled(false); + chkRawMode.setEnabled(false); + this.revalidate(); + } + + public void enableControls() { + cmbboxCertificate.setEnabled(true); + cmbboxXSW.setEnabled(true); + btnXSWHelp.setEnabled(true); + btnXSWPreview.setEnabled(true); + btnSignatureReset.setEnabled(true); + btnXSWApply.setEnabled(true); + btnSignatureHelp.setEnabled(true); + btnSignatureRemove.setEnabled(true); + btnSignatureReplace.setEnabled(true); + btnSendCertificate.setEnabled(true); + btnSignatureAdd.setEnabled(true); + btnMatchAndReplace.setEnabled(true); + btnTestXXE.setEnabled(true); + btnTestXSLT.setEnabled(true); + chkRawMode.setEnabled(true); + this.revalidate(); + } + // + // private void showMatchAndReplaceDialog() { + // HashMap matchAndReplaceMap = controller.getMatchAndReplaceMap(); + // + // JPanel dialogPanel = new JPanel(); + // dialogPanel.setLayout(new BorderLayout()); + // dialogPanel.add(new JLabel("Match and replace rules takes effect after apply XSW"), + // BorderLayout.NORTH); + // + // JPanel listPanel = new JPanel(); + // JTextField matchInputText = new JTextField(); + // JTextField replaceInputText = new JTextField(); + // + // JButton addEntryButton = new JButton("\u2795"); + // addEntryButton.addActionListener(new ActionListener() { + // + // @Override + // public void actionPerformed(ActionEvent e) { + // if (matchInputText.getText() != "" && replaceInputText.getText() != "") { + // matchAndReplaceMap.put(matchInputText.getText(), + // replaceInputText.getText()); + // updateMatchAndReplaceList(listPanel, matchInputText, replaceInputText, + // addEntryButton); + // SwingUtilities.getWindowAncestor((Component) e.getSource()).pack(); + // } + // } + // }); + // + // updateMatchAndReplaceList(listPanel, matchInputText, replaceInputText, + // addEntryButton); + // JOptionPane.showMessageDialog(this, listPanel, "Apply XSW - Match and Replace", + // JOptionPane.PLAIN_MESSAGE); + // } + // + // private void updateMatchAndReplaceList(JPanel listPanel, JTextField matchInputText, + // JTextField replaceInputText, JButton addEntryButton) { + // HashMap matchAndReplaceMap = controller.getMatchAndReplaceMap(); + // listPanel.setLayout(new GridBagLayout()); + // listPanel.removeAll(); + // GridBagConstraints c = new GridBagConstraints(); + // c.fill = GridBagConstraints.HORIZONTAL; + // c.gridx = 0; + // c.gridy = 0; + // listPanel.add(new JLabel("Match: "), c); + // c.gridx = 1; + // listPanel.add(new JLabel("Replace: "), c); + // c.gridx = 0; + // c.gridy = 1; + // listPanel.add(matchInputText, c); + // c.gridx = 1; + // listPanel.add(replaceInputText, c); + // c.gridx = 2; + // listPanel.add(addEntryButton, c); + // + // c.gridy = 2; + // for (String matchRule : matchAndReplaceMap.keySet()) { + // c.gridx = 0; + // listPanel.add(new JLabel(matchRule), c); + // + // c.gridx = 1; + // listPanel.add(new JLabel(matchAndReplaceMap.get(matchRule)), c); + // JButton deleteEntryBtn = new JButton("\u2796"); + // deleteEntryBtn.addActionListener(new ActionListener() { + // + // @Override + // public void actionPerformed(ActionEvent e) { + // matchAndReplaceMap.remove(matchRule); + // updateMatchAndReplaceList(listPanel, matchInputText, replaceInputText, + // addEntryButton); + // SwingUtilities.getWindowAncestor((Component) e.getSource()).pack(); + // } + // }); + // c.gridx = 2; + // listPanel.add(deleteEntryBtn, c); + // c.gridy++; + // } + // listPanel.revalidate(); + // } +} diff --git a/tool/src/main/java/samlraider/gui/SamlPanelInfo.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/SamlPanelInfo.java similarity index 86% rename from tool/src/main/java/samlraider/gui/SamlPanelInfo.java rename to tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/SamlPanelInfo.java index fa4cb00..a85282d 100644 --- a/tool/src/main/java/samlraider/gui/SamlPanelInfo.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/SamlPanelInfo.java @@ -1,32 +1,29 @@ /* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) + * Zed Attack Proxy (ZAP) and its related class files. * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss + * ZAP is an HTTP/HTTPS proxy for assessing web application security. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * Copyright 2015 The ZAP Development Team * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * 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 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ +package org.zaproxy.addon.migt.samlraider.gui; -package samlraider.gui; - -import javax.swing.*; -import java.awt.*; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.JLabel; +import javax.swing.JPanel; public class SamlPanelInfo extends JPanel { @@ -54,10 +51,27 @@ public SamlPanelInfo() { private void initialize() { GridBagLayout gridBagLayout = new GridBagLayout(); - gridBagLayout.columnWidths = new int[]{0, 0}; - gridBagLayout.rowHeights = new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - gridBagLayout.columnWeights = new double[]{0.0, Double.MIN_VALUE}; - gridBagLayout.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE}; + gridBagLayout.columnWidths = new int[] {0, 0}; + gridBagLayout.rowHeights = new int[] {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + gridBagLayout.columnWeights = new double[] {0.0, Double.MIN_VALUE}; + gridBagLayout.rowWeights = + new double[] { + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + Double.MIN_VALUE + }; setLayout(gridBagLayout); lblAssertionTitle = new JLabel("Assertion Information"); @@ -277,4 +291,4 @@ public void clearAll() { setDigestAlgorithm(""); setEncryptionAlgorithm(""); } -} \ No newline at end of file +} diff --git a/tool/src/main/java/samlraider/gui/SignatureHelpWindow.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/SignatureHelpWindow.java similarity index 56% rename from tool/src/main/java/samlraider/gui/SignatureHelpWindow.java rename to tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/SignatureHelpWindow.java index 372c0a6..764259f 100644 --- a/tool/src/main/java/samlraider/gui/SignatureHelpWindow.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/SignatureHelpWindow.java @@ -1,37 +1,36 @@ /* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) + * Zed Attack Proxy (ZAP) and its related class files. * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss + * ZAP is an HTTP/HTTPS proxy for assessing web application security. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * Copyright 2015 The ZAP Development Team * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * 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 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ +package org.zaproxy.addon.migt.samlraider.gui; -package samlraider.gui; - -import javax.swing.*; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; import javax.swing.border.EmptyBorder; -import java.awt.*; public class SignatureHelpWindow extends JFrame { - private static final long serialVersionUID = 1L; private final JPanel contentPane; @@ -44,10 +43,10 @@ public SignatureHelpWindow() { contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane); GridBagLayout gbl_contentPane = new GridBagLayout(); - gbl_contentPane.columnWidths = new int[]{224, 0, 0}; - gbl_contentPane.rowHeights = new int[]{0, 0, 62, 0, 0, 0, 0}; - gbl_contentPane.columnWeights = new double[]{0.0, 1.0, Double.MIN_VALUE}; - gbl_contentPane.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE}; + gbl_contentPane.columnWidths = new int[] {224, 0, 0}; + gbl_contentPane.rowHeights = new int[] {0, 0, 62, 0, 0, 0, 0}; + gbl_contentPane.columnWeights = new double[] {0.0, 1.0, Double.MIN_VALUE}; + gbl_contentPane.rowWeights = new double[] {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE}; contentPane.setLayout(gbl_contentPane); JLabel lblSamlSignaturesHelp = new JLabel("SAML Signatures Help"); @@ -67,9 +66,10 @@ public SignatureHelpWindow() { gbc_lblNewLabel.gridy = 2; contentPane.add(lblNewLabel, gbc_lblNewLabel); - JLabel lblChooseACertificate = new JLabel( - "Choose a certificate of this list to sign the message or the assertion. You can " - + "manage the SAML Certificates in the SAML Certificates Tab."); + JLabel lblChooseACertificate = + new JLabel( + "Choose a certificate of this list to sign the message or the assertion. You can " + + "manage the SAML Certificates in the SAML Certificates Tab."); GridBagConstraints gbc_lblChooseACertificate = new GridBagConstraints(); gbc_lblChooseACertificate.anchor = GridBagConstraints.NORTH; gbc_lblChooseACertificate.fill = GridBagConstraints.HORIZONTAL; @@ -86,10 +86,11 @@ public SignatureHelpWindow() { gbc_lblResignMessage.gridy = 4; contentPane.add(lblResignMessage, gbc_lblResignMessage); - JLabel lblWithTheChosen = new JLabel( - " With the chosen certificate the message or " - + "the assertion is signed. If the message or assertion was signed, the signature is replaced.
" - + "If you choose to sign the assertion, the message signature is removed, because the signature gets invalid."); + JLabel lblWithTheChosen = + new JLabel( + " With the chosen certificate the message or " + + "the assertion is signed. If the message or assertion was signed, the signature is replaced.
" + + "If you choose to sign the assertion, the message signature is removed, because the signature gets invalid."); GridBagConstraints gbc_lblWithTheChosen = new GridBagConstraints(); gbc_lblWithTheChosen.fill = GridBagConstraints.HORIZONTAL; gbc_lblWithTheChosen.insets = new Insets(0, 0, 5, 0); @@ -97,5 +98,4 @@ public SignatureHelpWindow() { gbc_lblWithTheChosen.gridy = 4; contentPane.add(lblWithTheChosen, gbc_lblWithTheChosen); } - } diff --git a/tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/XSWHelpWindow.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/XSWHelpWindow.java new file mode 100644 index 0000000..10c79f5 --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/gui/XSWHelpWindow.java @@ -0,0 +1,75 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2015 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt.samlraider.gui; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ScrollPaneConstants; +import javax.swing.border.EmptyBorder; + +public class XSWHelpWindow extends JFrame { + + private static final long serialVersionUID = 1L; + private final JPanel contentPane; + + public XSWHelpWindow() { + setTitle("XML Signature Wrapping Help"); + setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + setBounds(100, 100, 600, 400); + setMinimumSize(new Dimension(600, 400)); + contentPane = new JPanel(); + contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); + setContentPane(contentPane); + contentPane.setLayout(new BorderLayout(0, 0)); + + JLabel lblDescription = + new JLabel( + "With xml wrapping attacks you try to trick the xml signature validator into validating an " + + "signature of an element while evaluating an other element. The XSWs in the image are supported." + + "
The blue element represents the signature." + + "
The green one represents the original element, which is correctly signed. " + + "
The red one represents the falsly evaluated element, if the validating is not correctly implemented." + + "
Mind that the first two XSWs can be used for signed responses only whereas the other ones can be used for signed assertions only." + + "
These XSW are taken from this paper:
Somorovsky, Juraj, et al. \"On Breaking SAML: Be Whoever You Want to Be.\" USENIX Security Symposium. 2012." + + "
Please check out this paper for further information." + + ""); + contentPane.add(lblDescription, BorderLayout.NORTH); + + JScrollPane scrollPane = new JScrollPane(); + scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); + contentPane.add(scrollPane, BorderLayout.CENTER); + + ImagePanel panel; + String className = getClass().getName().replace('.', '/'); + String classJar = getClass().getResource("/" + className + ".class").toString(); + if (classJar.startsWith("jar:")) { + panel = new ImagePanel("xswlist.png"); + } else { + panel = new ImagePanel("src/main/resources/xswlist.png"); + } + + scrollPane.setViewportView(panel); + } +} diff --git a/tool/src/main/java/samlraider/helpers/CertificateHelper.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/CertificateHelper.java similarity index 65% rename from tool/src/main/java/samlraider/helpers/CertificateHelper.java rename to tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/CertificateHelper.java index 631be0f..724d7f5 100644 --- a/tool/src/main/java/samlraider/helpers/CertificateHelper.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/CertificateHelper.java @@ -1,32 +1,23 @@ /* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) + * Zed Attack Proxy (ZAP) and its related class files. * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss + * ZAP is an HTTP/HTTPS proxy for assessing web application security. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * Copyright 2015 The ZAP Development Team * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * 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 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ - -package samlraider.helpers; - -import org.bouncycastle.cert.X509CertificateHolder; -import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +package org.zaproxy.addon.migt.samlraider.helpers; import java.math.BigInteger; import java.security.cert.CertificateException; @@ -36,6 +27,8 @@ import java.util.Date; import java.util.Locale; import java.util.regex.Pattern; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; public final class CertificateHelper { @@ -48,7 +41,10 @@ public static byte[] hexStringToByteArray(String s) { int len = s.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { - data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + Character.digit(s.charAt(i + 1), 16)); + data[i / 2] = + (byte) + ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i + 1), 16)); } return data; } @@ -68,12 +64,10 @@ public static BigInteger hexStringToBigInteger(String hexString) { /** * Reads a Date as a String and returns a Date object. * - * @param date Date as a String. Supported formats are - * "MMM d HH:mm:ss yyy ZZZ" (Eg. Aug 15 10:56:49 2012 GMT) and - * "EEE MMM d HH:mm:ss ZZZ yyy" (Eg. Thu Mar 26 01:00:00 CET - * 2015) + * @param date Date as a String. Supported formats are "MMM d HH:mm:ss yyy ZZZ" (Eg. Aug 15 + * 10:56:49 2012 GMT) and "EEE MMM d HH:mm:ss ZZZ yyy" (Eg. Thu Mar 26 01:00:00 CET 2015) * @return Date object - * @throws ParseException if input date cannot be parsed + * @throws ParseException if input date cannot be parsed * @throws IllegalArgumentException If input date format is not supported */ public static Date stringToDate(String date) throws ParseException, IllegalArgumentException { @@ -83,13 +77,17 @@ public static Date stringToDate(String date) throws ParseException, IllegalArgum Locale.setDefault(Locale.US); // The input date is in english // Format: MMM d HH:mm:ss yyy ZZZ (Eg. Aug 15 10:56:49 2012 GMT) - pattern = Pattern.compile("[A-Z][a-z]{2} [0-9]{1,2} ([0-9]{2}:){1,2}[0-9]{2} [0-9]{4} [A-Z]{3}"); + pattern = + Pattern.compile( + "[A-Z][a-z]{2} [0-9]{1,2} ([0-9]{2}:){1,2}[0-9]{2} [0-9]{4} [A-Z]{3}"); if (pattern.matcher(date).matches()) { simpleDateFormat = new SimpleDateFormat("MMM d HH:mm:ss yyy ZZZ"); return simpleDateFormat.parse(date); } // Format: EEE MMM d HH:mm:ss ZZZ yyy (Eg. Thu Mar 26 01:00:00 CET 2015) - pattern = Pattern.compile("[A-Z][a-z]{2} [A-Z][a-z]{2} [0-9]{1,2} ([0-9]{2}:){1,2}[0-9]{2} [A-Z]{3,4} [0-9]{4}"); + pattern = + Pattern.compile( + "[A-Z][a-z]{2} [A-Z][a-z]{2} [0-9]{1,2} ([0-9]{2}:){1,2}[0-9]{2} [A-Z]{3,4} [0-9]{4}"); if (pattern.matcher(date).matches()) { simpleDateFormat = new SimpleDateFormat("EEE MMM d HH:mm:ss ZZZ yyyy"); return simpleDateFormat.parse(date); @@ -117,16 +115,17 @@ public static String byteArrayToHex(byte[] bytes) { } /** - * Converts the Object x509CertificateHolder to X509Certificate using the - * Bouncy Castle library. + * Converts the Object x509CertificateHolder to X509Certificate using the Bouncy Castle library. * * @param x509CertificateHolder Certificate to convert. * @return Converted certificate * @throws CertificateException if the certifiate cannot be converted */ - public static X509Certificate x509CertificateHolderToX509Certificate(X509CertificateHolder x509CertificateHolder) throws CertificateException { - return new JcaX509CertificateConverter().setProvider("BC").getCertificate(x509CertificateHolder); - + public static X509Certificate x509CertificateHolderToX509Certificate( + X509CertificateHolder x509CertificateHolder) throws CertificateException { + return new JcaX509CertificateConverter() + .setProvider("BC") + .getCertificate(x509CertificateHolder); } /** @@ -153,5 +152,4 @@ public static String hexBytesToString(String hexString) { public static String byteArrayToString(byte[] bytes) { return hexBytesToString(byteArrayToHex(bytes)); } - -} \ No newline at end of file +} diff --git a/tool/src/main/java/samlraider/helpers/FileHelper.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/FileHelper.java similarity index 52% rename from tool/src/main/java/samlraider/helpers/FileHelper.java rename to tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/FileHelper.java index 4f37564..375e005 100644 --- a/tool/src/main/java/samlraider/helpers/FileHelper.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/FileHelper.java @@ -1,34 +1,31 @@ /* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) + * Zed Attack Proxy (ZAP) and its related class files. * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss + * ZAP is an HTTP/HTTPS proxy for assessing web application security. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * Copyright 2015 The ZAP Development Team * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * 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 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ +package org.zaproxy.addon.migt.samlraider.helpers; -package samlraider.helpers; - -import org.bouncycastle.openssl.jcajce.JcaPEMWriter; - -import java.io.*; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.Writer; import java.nio.file.Files; +import org.bouncycastle.openssl.jcajce.JcaPEMWriter; public class FileHelper { @@ -42,14 +39,17 @@ public String exportRessourceFromJar(String filename) throws IOException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream(filename); File outputFile = File.createTempFile(filename, ""); outputFile.deleteOnExit(); - Files.copy(inputStream, outputFile.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING); + Files.copy( + inputStream, + outputFile.toPath(), + java.nio.file.StandardCopyOption.REPLACE_EXISTING); return outputFile.getAbsolutePath(); } /** * Helper method for exporting PEM object. * - * @param object to export in PEM format. + * @param pemObject to export in PEM format. * @param filename for the file to export. */ public void exportPEMObject(Object pemObject, String filename) throws IOException { diff --git a/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/Flags.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/Flags.java new file mode 100644 index 0000000..eaea807 --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/Flags.java @@ -0,0 +1,26 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2015 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt.samlraider.helpers; + +public class Flags { + + // Write SAMLRaiderDebug.log and load example certificates + public static final boolean DEBUG = false; +} diff --git a/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/HTTPHelpers.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/HTTPHelpers.java new file mode 100644 index 0000000..f82a762 --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/HTTPHelpers.java @@ -0,0 +1,68 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2015 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt.samlraider.helpers; + +public class HTTPHelpers { + // + // // Source: + // // http://qupera.blogspot.ch/2013/02/howto-compress-and-uncompress-java-byte.html + // public byte[] decompress(byte[] data, boolean gzip) throws IOException, + // DataFormatException { + // Inflater inflater = new Inflater(true); + // inflater.setInput(data); + // + // ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length); + // byte[] buffer = new byte[1024]; + // while (!inflater.finished()) { + // int count = inflater.inflate(buffer); + // outputStream.write(buffer, 0, count); + // } + // outputStream.close(); + // byte[] output = outputStream.toByteArray(); + // + // inflater.end(); + // + // return output; + // } + // + // // Source: + // // http://qupera.blogspot.ch/2013/02/howto-compress-and-uncompress-java-byte.html + // public byte[] compress(byte[] data, boolean gzip) throws IOException { + // Deflater deflater = new Deflater(5, gzip); + // deflater.setInput(data); + // + // ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length); + // + // deflater.finish(); + // byte[] buffer = new byte[1024]; + // while (!deflater.finished()) { + // int count = deflater.deflate(buffer); + // outputStream.write(buffer, 0, count); + // } + // outputStream.close(); + // byte[] output = outputStream.toByteArray(); + // + // deflater.end(); + // + // return output; + // } + // + +} diff --git a/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/X509KeySelector.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/X509KeySelector.java new file mode 100644 index 0000000..0d4a27d --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/X509KeySelector.java @@ -0,0 +1,69 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2015 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt.samlraider.helpers; + +// import com.sun.org.apache.xml.internal.security.signature.XMLSignature; + +// Source: +/*http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html*/ +// for Validation purposes only +// public class X509KeySelector extends KeySelector { +// static boolean algEquals(String algURI, String algName) { +// return (algName.equalsIgnoreCase("DSA") && +// algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1)) || +// (algName.equalsIgnoreCase("RSA") && +// algURI.equalsIgnoreCase(SignatureMethod.RSA_SHA1)) || +// (algName.equalsIgnoreCase("RSA") && +// algURI.equalsIgnoreCase(XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256)); +// } +// +// public KeySelectorResult select(KeyInfo keyInfo, +// KeySelector.Purpose purpose, +// AlgorithmMethod method, +// XMLCryptoContext context) +// throws KeySelectorException { +// @SuppressWarnings("rawtypes") +// Iterator ki = keyInfo.getContent().iterator(); +// while (ki.hasNext()) { +// XMLStructure info = (XMLStructure) ki.next(); +// if (!(info instanceof X509Data)) +// continue; +// X509Data x509Data = (X509Data) info; +// @SuppressWarnings("rawtypes") +// Iterator xi = x509Data.getContent().iterator(); +// while (xi.hasNext()) { +// Object o = xi.next(); +// if (!(o instanceof X509Certificate)) +// continue; +// final PublicKey key = ((X509Certificate) o).getPublicKey(); +// // Make sure the algorithm is compatible +// // with the method. +// if (algEquals(method.getAlgorithm(), key.getAlgorithm())) { +// return new KeySelectorResult() { +// public Key getKey() { +// return key; +// } +// }; +// } +// } +// } +// throw new KeySelectorException("No key found!"); +// } +// } diff --git a/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/XMLHelpers.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/XMLHelpers.java new file mode 100644 index 0000000..4d6a107 --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/XMLHelpers.java @@ -0,0 +1,770 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2015 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt.samlraider.helpers; + +import java.io.IOException; +import java.io.StringReader; +import java.io.StringWriter; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import javax.xml.crypto.MarshalException; +import javax.xml.crypto.XMLStructure; +import javax.xml.crypto.dsig.CanonicalizationMethod; +import javax.xml.crypto.dsig.DigestMethod; +import javax.xml.crypto.dsig.Reference; +import javax.xml.crypto.dsig.SignedInfo; +import javax.xml.crypto.dsig.Transform; +import javax.xml.crypto.dsig.XMLSignatureException; +import javax.xml.crypto.dsig.XMLSignatureFactory; +import javax.xml.crypto.dsig.dom.DOMSignContext; +import javax.xml.crypto.dsig.keyinfo.KeyInfo; +import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory; +import javax.xml.crypto.dsig.keyinfo.X509Data; +import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import org.apache.xml.security.Init; +import org.apache.xml.security.signature.XMLSignature; +import org.apache.xml.security.transforms.Transforms; +import org.w3c.dom.Attr; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +public class XMLHelpers { + + // + /** + * Returns a namespace aware document builder factory. + * + * @return DocumentBuilderFactory NamespaceAware + */ + public DocumentBuilderFactory getDBF() { + try { + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setFeature( + "http://xml.org/sax/features/external-general-entities", false); + documentBuilderFactory.setFeature( + "http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + documentBuilderFactory.setFeature( + "http://xml.org/sax/features/external-parameter-entities", false); + documentBuilderFactory.setFeature( + javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true); + documentBuilderFactory.setNamespaceAware(true); + return documentBuilderFactory; + } catch (ParserConfigurationException e) { + e.printStackTrace(); + } + return null; + } + + /** + * Returns a string serialization of a string + * + * @param document document which should be converted to a string + * @return string of document + * @throws IOException If an error in serialization occurred + */ + public String getString(Document document) throws IOException { + return getString(document, false, 0); + } + + public String getString(Document doc, boolean indenting, int indent) throws IOException { + // https://stackoverflow.com/a/2567428 Taken from here + try { + StringWriter sw = new StringWriter(); + TransformerFactory tf = TransformerFactory.newInstance(); + Transformer transformer = tf.newTransformer(); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); + transformer.setOutputProperty(OutputKeys.METHOD, "xml"); + transformer.setOutputProperty(OutputKeys.INDENT, "no"); + transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); + + transformer.transform(new DOMSource(doc), new StreamResult(sw)); + return sw.toString(); + } catch (Exception ex) { + throw new RuntimeException("Error converting to String", ex); + } + } + + /** + * Returns a string serialization of a string, use indent and linebreaks = true to pretty print + * a document + * + * @param document document which should be converted to a string + * @param indent amount of indent + * @param linebreaks if line breaks should be inserted + * @return string of document, pretty or linearized + * @throws IOException if an Serializer error occures + */ + public String getStringOfDocument(Document document, int indent, boolean linebreaks) + throws IOException { + document.normalize(); + removeEmptyTags(document); + return getString(document, linebreaks, indent); + } + + /** + * Converts a string representation of a XML document in a document Object + * + * @param message String representation of a XML document + * @return Document of XML string + * @throws SAXException If any parse errors occur. + */ + public Document getXMLDocumentOfSAMLMessage(String message) throws SAXException { + try { + DocumentBuilderFactory documentBuilderFactory = getDBF(); + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + Document document = documentBuilder.parse(new InputSource(new StringReader(message))); + return document; + } catch (ParserConfigurationException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } + + // + // + /** + * Returns all Signatures of the given Document + * + * @param document document with signatures + * @return NodeList with signatures + */ + public NodeList getSignatures(Document document) { + NodeList nl = document.getElementsByTagNameNS("*", "Signature"); + return nl; + } + + /** + * Removes empty tags, spaces between XML tags + * + * @param document document in which the empty tags should be removed + */ + public void removeEmptyTags(Document document) { + NodeList nl = null; + try { + if (Thread.currentThread().getContextClassLoader() == null) { + Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + } + XPath xPath = XPathFactory.newInstance().newXPath(); + nl = + (NodeList) + xPath.evaluate( + "//text()[normalize-space(.)='']", + document, + XPathConstants.NODESET); + + for (int i = 0; i < nl.getLength(); ++i) { + Node node = nl.item(i); + node.getParentNode().removeChild(node); + } + + } catch (XPathExpressionException e) { + e.printStackTrace(); + } + } + + /** + * Removes all signatures in a given XML document + * + * @param document document in which the signature should be removed + * @return number of removed signatures + */ + public int removeAllSignatures(Document document) { + NodeList nl = getSignatures(document); + int nrSig = nl.getLength(); + + for (int i = 0; i < nrSig; i++) { + Node parent = nl.item(0).getParentNode(); + parent.removeChild(nl.item(0)); + } + removeEmptyTags(document); + document.normalize(); + return nrSig; + } + + // + // /** + // * Removes a signature in a given XML document + // * + // * @param document document in which the signature should be removed + // * @return number of removed signatures + // */ + // public int removeOnlyMessageSignature(Document document) { + // try { + // if (Thread.currentThread().getContextClassLoader() == null) { + // Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + // } + // setIDAttribute(document); + // XPath xpath = XPathFactory.newInstance().newXPath(); + // XPathExpression expr = + // xpath.compile("//*[local-name()='Response']/*[local-name()='Signature']"); + // NodeList nl = (NodeList) expr.evaluate(document, XPathConstants.NODESET); + // + // int nrSig = nl.getLength(); + // + // for (int i = 0; i < nrSig; i++) { + // Node parent = nl.item(0).getParentNode(); + // parent.removeChild(nl.item(0)); + // } + // removeEmptyTags(document); + // document.normalize(); + // return nrSig; + // } catch (XPathExpressionException e) { + // e.printStackTrace(); + // } + // return 0; + // } + // + /** + * Returns a NodeList with assertions of the given XML document + * + * @param document document with the assertions + * @return NodeList with assertions + */ + public NodeList getAssertions(Document document) { + return document.getElementsByTagNameNS("*", "Assertion"); + } + + // + // + // /** + // * Returns a NodeList with encrypted assertions of the given XML document + // * + // * @param document document with the encrypted assertions + // * @return NodeList with encrypted assertions + // */ + // public NodeList getEncryptedAssertions(Document document) { + // return document.getElementsByTagNameNS("*", "EncryptedAssertion"); + // } + // + // /** + // * Returns SOAP Body as an Element + // * + // * @param document document with SOAP body + // * @return Element SOAP Body Element or null if no body found + // */ + // public Element getSOAPBody(Document document) { + // try { + // if (Thread.currentThread().getContextClassLoader() == null) { + // Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + // } + // XPath xpath = XPathFactory.newInstance().newXPath(); + // XPathExpression expr = + // xpath.compile("//*[local-name()='Envelope']/*[local-name()='Body']"); + // NodeList elements = (NodeList) expr.evaluate(document, XPathConstants.NODESET); + // if (elements.getLength() > 0) { + // return (Element) elements.item(0); + // } + // } catch (XPathExpressionException e) { + // e.printStackTrace(); + // } + // return null; + // } + // + // /** + // * Returns SAML Response out of SOAP Body as an Element + // * + // * @param document document with SOAP envelope + // * @return Document SAML Response + // */ + // public Document getSAMLResponseOfSOAP(Document document) throws + // ParserConfigurationException { + // Element body = getSOAPBody(document); + // DocumentBuilderFactory documentBuilderFactory = getDBF(); + // DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + // Document documentSAML = documentBuilder.newDocument(); + // Element SAMLresponseOld = (Element) body.getFirstChild(); + // Element SAMLresponse = (Element) documentSAML.adoptNode(SAMLresponseOld); + // documentSAML.appendChild(SAMLresponse); + // return documentSAML; + // } + // + // /** + // * Returns a NodeList response Element in it + // * + // * @param document document with the response + // * @return NodeList with response element + // */ + // public NodeList getResponse(Document document) { + // return document.getElementsByTagNameNS("*", "Response"); + // } + // + /** + * Returns the attribute value of a XML tag + * + * @param element DOM element which contains the attribute + * @param attributeName name of the Attribute + * @return attribute if found attribute value otherwise an empty string + */ + private String getAttributeValueByName(Element element, String attributeName) { + if (element == null) { + return ""; + } + Attr attribute = (Attr) element.getAttributes().getNamedItem(attributeName); + if (attribute != null) { + return attribute.getNodeValue(); + } + return ""; + } + + // + // /** + // * Returns the issuer of an SAML Message + // * + // * @param document Document which contains the issuer + // * @return Issuer of message / first Assertion if found, else empty string + // */ + // public String getIssuer(Document document) { + // NodeList nl = document.getElementsByTagNameNS("*", "Issuer"); + // if (nl.getLength() > 0) { + // return nl.item(0).getTextContent(); + // } + // return ""; + // } + // + // /** + // * Returns NotBefore Date Attribute of Condition Element + // * + // * @param assertion Assertion with Condition tag + // * @return NotBefore date Attribute of Condition Element if found, else + // * empty string + // */ + // public String getConditionNotBefore(Node assertion) { + // if (assertion == null || !assertion.getLocalName().equals("Assertion")) { + // return "no assertion"; + // } + // Element conditions = (Element) ((Element) assertion).getElementsByTagNameNS("*", + // "Conditions").item(0); + // return getAttributeValueByName(conditions, "NotBefore"); + // } + // + // /** + // * Returns NotOnOrAfter Date Attribute of Condition Element + // * + // * @param assertion Assertion with Condition tag + // * @return NotOnOrAfter Date Attribute of Condition Element if found, else + // * empty string + // */ + // public String getConditionNotAfter(Node assertion) { + // if (assertion == null || !assertion.getLocalName().equals("Assertion")) { + // return "no assertion"; + // } + // Element conditions = (Element) ((Element) assertion).getElementsByTagNameNS("*", + // "Conditions").item(0); + // return getAttributeValueByName(conditions, "NotOnOrAfter"); + // } + // + // /** + // * Returns NotBefore Date Attribute of SubjectConfirmation Element + // * + // * @param assertion Assertion with SubjectConfirmation tag + // * @return NotBefore Date Attribute of SubjectConfirmation Element if found, + // * else empty string + // */ + // public String getSubjectConfNotBefore(Node assertion) { + // if (assertion == null || !assertion.getLocalName().equals("Assertion")) { + // return "no assertion"; + // } + // Element subjConfirmation = (Element) ((Element) assertion).getElementsByTagNameNS("*", + // "SubjectConfirmationData").item(0); + // return getAttributeValueByName(subjConfirmation, "NotBefore"); + // } + // + // /** + // * Returns NotOnOrAfter Date Attribute of SubjectConfirmation Element + // * + // * @param assertion Assertion with SubjectConfirmation tag + // * @return NotOnOrAfter Date Attribute of SubjectConfirmation Element if + // * found, else empty string + // */ + // public String getSubjectConfNotAfter(Node assertion) { + // if (assertion == null || !assertion.getLocalName().equals("Assertion")) { + // return "no assertion"; + // } + // Element subjConfirmation = (Element) ((Element) assertion).getElementsByTagNameNS("*", + // "SubjectConfirmationData").item(0); + // return getAttributeValueByName(subjConfirmation, "NotOnOrAfter"); + // } + // + /** + * Returns Signature Algorithm of Node which is signed + * + * @param node node with Signature + * @return Signature Algorithm of Node which is signed + */ + public String getSignatureAlgorithm(Node node) { + if (node == null) { + return "no element"; + } + Element signatureMethod = + (Element) ((Element) node).getElementsByTagNameNS("*", "SignatureMethod").item(0); + return getAttributeValueByName(signatureMethod, "Algorithm"); + } + + /** + * Returns Digest Algorithm of Node which is signed + * + * @param node node with Signature + * @return Digest Algorithm of Node which is signed + */ + public String getDigestAlgorithm(Node node) { + if (node == null) { + return "no element"; + } + Element digestMethod = + (Element) ((Element) node).getElementsByTagNameNS("*", "DigestMethod").item(0); + return getAttributeValueByName(digestMethod, "Algorithm"); + } + + // + // /** + // * Returns encryption algorithm of encrypted assertion + // * + // * @param assertion encrypted assertion node + // * @return encryption algorithm of encrypted assertion + // */ + // public String getEncryptionMethod(Node assertion) { + // if (assertion == null || !assertion.getLocalName().equals("EncryptedAssertion")) { + // return "no encryption"; + // } + // Element encryptionMethod = (Element) ((Element) assertion).getElementsByTagNameNS("*", + // "EncryptionMethod") + // .item(0); + // return getAttributeValueByName(encryptionMethod, "Algorithm"); + // } + // + /** + * Returns embedded x509 certificate of signature + * + * @param node node with embedded x509 certificate, no matter how deeply nested + * @return first embedded x509 certificate of signature or null if not found + */ + public String getCertificate(Node node) { + NodeList certificates = ((Element) node).getElementsByTagNameNS("*", "X509Certificate"); + + if (certificates.getLength() > 0) { + Element certificate = (Element) certificates.item(0); + return certificate.getTextContent(); + } + return null; + } + + /** + * Set the ID Attribute in an XML Document so that java recognises the ID Attribute as a real id + * + * @param document Document to set the ids + */ + public void setIDAttribute(Document document) { + try { + if (Thread.currentThread().getContextClassLoader() == null) { + Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + } + XPath xpath = XPathFactory.newInstance().newXPath(); + XPathExpression expr = xpath.compile("//*[@ID]"); + NodeList nodeList = (NodeList) expr.evaluate(document, XPathConstants.NODESET); + for (int i = 0; i < nodeList.getLength(); i++) { + Element elem = (Element) nodeList.item(i); + Attr attr = (Attr) elem.getAttributes().getNamedItem("ID"); + elem.setIdAttributeNode(attr, true); + } + } catch (XPathExpressionException e) { + e.printStackTrace(); + } + } + + /** + * Sign assertions in SAML message + * + * @param document Document in assertions should be signed + * @param signAlgorithm Signature algorithm in uri form, default if an unknown algorithm is + * provided: http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 + * @param digestAlgorithm Digest algorithm in uri form, default if an unknown algorithm is + * provided: http://www.w3.org/2001/04/xmlenc#sha256 + */ + public void signAssertion( + Document document, + String signAlgorithm, + String digestAlgorithm, + X509Certificate cert, + PrivateKey key) + throws CertificateException, + NoSuchAlgorithmException, + InvalidKeySpecException, + MarshalException, + XMLSignatureException, + IOException { + try { + if (Thread.currentThread().getContextClassLoader() == null) { + Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + } + setIDAttribute(document); + XPath xpath = XPathFactory.newInstance().newXPath(); + XPathExpression expr = xpath.compile("//*[local-name()='Assertion']/@ID"); + NodeList nlURIs = (NodeList) expr.evaluate(document, XPathConstants.NODESET); + + String[] sigIDs = new String[nlURIs.getLength()]; + + for (int i = 0; i < nlURIs.getLength(); i++) { + sigIDs[i] = nlURIs.item(i).getNodeValue(); + } + + Init.init(); + for (String id : sigIDs) { + signElement(document, id, cert, key, signAlgorithm, digestAlgorithm); + } + } catch (XPathExpressionException e) { + e.printStackTrace(); + } + } + + // + // /** + // * Sign whole SAML Message + // * + // * @param document Document with the response to sign + // * @param signAlgorithm Signature algorithm in uri form, default if an unknown + // * algorithm is provided: + // * http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 + // * @param digestAlgorithm Digest algorithm in uri form, default if an unknown algorithm + // * is provided: http://www.w3.org/2001/04/xmlenc#sha256 + // */ + // public void signMessage(Document document, String signAlgorithm, String digestAlgorithm, + // X509Certificate cert, PrivateKey key) + // throws CertificateException, NoSuchAlgorithmException, InvalidKeySpecException, + // MarshalException, XMLSignatureException, IOException { + // try { + // if (Thread.currentThread().getContextClassLoader() == null) { + // Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); + // } + // setIDAttribute(document); + // XPath xpath = XPathFactory.newInstance().newXPath(); + // XPathExpression expr = xpath.compile("//*[local-name()='Response']/@ID"); + // NodeList nlURIs = (NodeList) expr.evaluate(document, XPathConstants.NODESET); + // + // String[] sigIDs = new String[nlURIs.getLength()]; + // + // for (int i = 0; i < nlURIs.getLength(); i++) { + // sigIDs[i] = nlURIs.item(i).getNodeValue(); + // } + // + // Init.init(); + // for (String id : sigIDs) { + // signElement(document, id, cert, key, signAlgorithm, digestAlgorithm); + // } + // } catch (XPathExpressionException e) { + // e.printStackTrace(); + // } + // } + // + // + /** + * Sign the assertion with the given id + * + * @param doc Document in which the assertion with the given id should be signed + * @param id the signature algorithm + * @param key the private key to sign the assertion + * @param cert the certificate which should be included in the assertions signed info + * @param signAlgorithm Signature algorithm in uri form, default if an unknown algorithm is + * provided: http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 + * @param digestAlgorithm Digest algorithm in uri form, default if an unknown algorithm is + * provided: http://www.w3.org/2001/04/xmlenc#sha256 + */ + private Document signElement( + Document doc, + String id, + X509Certificate cert, + PrivateKey key, + String signAlgorithm, + String digestAlgorithm) + throws MarshalException, XMLSignatureException { + + try { + XMLSignatureFactory xmlSignatureFactory = + XMLSignatureFactory.getInstance( + "DOM", new org.jcp.xml.dsig.internal.dom.XMLDSigRI()); + List transforms = new ArrayList(); + Transform enveloped = + xmlSignatureFactory.newTransform( + Transforms.TRANSFORM_ENVELOPED_SIGNATURE, (XMLStructure) null); + transforms.add(enveloped); + Transform c14n = + xmlSignatureFactory.newTransform( + Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS, (XMLStructure) null); + transforms.add(c14n); + + Reference ref; + try { + ref = + xmlSignatureFactory.newReference( + "#" + id, + xmlSignatureFactory.newDigestMethod(digestAlgorithm, null), + transforms, + null, + null); + } catch (NoSuchAlgorithmException e) { + ref = + xmlSignatureFactory.newReference( + "#" + id, + xmlSignatureFactory.newDigestMethod(DigestMethod.SHA256, null), + transforms, + null, + null); + } + + SignedInfo signedInfo; + try { + signedInfo = + xmlSignatureFactory.newSignedInfo( + xmlSignatureFactory.newCanonicalizationMethod( + CanonicalizationMethod.EXCLUSIVE, + (C14NMethodParameterSpec) null), + xmlSignatureFactory.newSignatureMethod(signAlgorithm, null), + Collections.singletonList(ref)); + } catch (NoSuchAlgorithmException e) { + signedInfo = + xmlSignatureFactory.newSignedInfo( + xmlSignatureFactory.newCanonicalizationMethod( + CanonicalizationMethod.EXCLUSIVE, + (C14NMethodParameterSpec) null), + xmlSignatureFactory.newSignatureMethod( + XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256, null), + Collections.singletonList(ref)); + } + + KeyInfoFactory keyInfoFactory = xmlSignatureFactory.getKeyInfoFactory(); + List x509Content = new ArrayList<>(); + x509Content.add(cert); + X509Data x509Data = keyInfoFactory.newX509Data(x509Content); + KeyInfo keyInfo = keyInfoFactory.newKeyInfo(Collections.singletonList(x509Data)); + + Element elementToSign = doc.getElementById(id); + NodeList issuerList = elementToSign.getElementsByTagNameNS("*", "Issuer"); + Element elementBeforeSignature; + + if (issuerList.getLength() > 0) { + elementBeforeSignature = (Element) issuerList.item(0); + } else { + elementBeforeSignature = elementToSign; + } + + // find next sibling node of Element type + Node nextNodeAfterIssuer = elementBeforeSignature.getNextSibling(); + while (nextNodeAfterIssuer != null + && nextNodeAfterIssuer.getNodeType() != Node.ELEMENT_NODE) { + nextNodeAfterIssuer = nextNodeAfterIssuer.getNextSibling(); + } + Element nextElementAfterIssuer = (Element) nextNodeAfterIssuer; + + DOMSignContext domSignContext = new DOMSignContext(key, elementToSign); + domSignContext.setDefaultNamespacePrefix("ds"); + domSignContext.setNextSibling(nextElementAfterIssuer); + + javax.xml.crypto.dsig.XMLSignature signature = + xmlSignatureFactory.newXMLSignature(signedInfo, keyInfo); + signature.sign(domSignContext); + + return doc; + } catch (InvalidAlgorithmParameterException e) { + e.printStackTrace(); + } catch (NoSuchAlgorithmException e1) { + e1.printStackTrace(); + } + return null; + } + // + // /*------------ + // //Source: http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html + // ------------*/ + // + // /** + // * Validates if the first XML Signature of the given document is valid + // * Only used for test purposes + // * + // * @param document Document with signature to validate + // * @return true if valid, else false + // */ + // public boolean validateSignature(Document document) throws Exception { + // + // setIDAttribute(document); + // XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM"); + // + // // Find Signature element. + // NodeList nl = + // document.getElementsByTagNameNS(javax.xml.crypto.dsig.XMLSignature.XMLNS, "Signature"); + // if (nl.getLength() == 0) { + // throw new Exception("Cannot find Signature element"); + // } + // + // // Create a DOMValidateContext and specify a KeySelector + // // and document context. + // DOMValidateContext valContext = new DOMValidateContext(new X509KeySelector(), + // nl.item(0)); + // + // // Unmarshal the XMLSignature + // javax.xml.crypto.dsig.XMLSignature signature = fac.unmarshalXMLSignature(valContext); + // + // // Validate the XMLSignature. + // boolean coreValidity = signature.validate(valContext); + // + // // Check core validation status. + // if (coreValidity == false) { + // boolean sv = signature.getSignatureValue().validate(valContext); + // if (sv == false) { + // if (Flags.DEBUG) { + // // Check the validation status of each Reference. + // @SuppressWarnings("rawtypes") + // Iterator i = signature.getSignedInfo().getReferences().iterator(); + // for (int j = 0; i.hasNext(); j++) { + // boolean refValid = ((Reference) i.next()).validate(valContext); + // System.out.println("ref[" + j + "] validity status: " + refValid); + // } + // } + // } + // } + // return coreValidity; + // } + // + +} diff --git a/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/XSWHelpers.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/XSWHelpers.java new file mode 100644 index 0000000..39ea45b --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/XSWHelpers.java @@ -0,0 +1,316 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2015 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt.samlraider.helpers; + +public class XSWHelpers { + // + // // XSW9 was removed b/c it does not work. Code is still there if you want to have a look + // :) + // public final static String[] xswTypes = {"XSW1", "XSW2", "XSW3", "XSW4", "XSW5", "XSW6", + // "XSW7", "XSW8"}; + // + // public static final HashMap MATCH_AND_REPLACE_MAP = new HashMap<>(); + // + // /* + // * Following are the 8 common XML Signature Wrapping attacks implemented, which + // * were found in a paper called "On Breaking SAML: Be Whoever You Want to Be" We + // * have also documented these attacks in our product documentation for further + // * information + // * + // */ + // + // public void applyXSW(String xswType, Document document) { + // switch (xswType) { + // case "XSW1": + // applyXSW1(document); + // break; + // case "XSW2": + // applyXSW2(document); + // break; + // case "XSW3": + // applyXSW3(document); + // break; + // case "XSW4": + // applyXSW4(document); + // break; + // case "XSW5": + // applyXSW5(document); + // break; + // case "XSW6": + // applyXSW6(document); + // break; + // case "XSW7": + // applyXSW7(document); + // break; + // case "XSW8": + // applyXSW8(document); + // break; + // case "XSW9": + // applyXSW9(document); + // break; + // } + // } + // + // // Fixed - clonedSignature was not found in clonedResponse + // public void applyXSW1(Document document) { + // Element response = (Element) document.getElementsByTagNameNS("*", "Response").item(0); + // Element clonedResponse = (Element) response.cloneNode(true); + // // The Original response will be the evil one + // applyMatchAndReplaceValues(response); + // Element clonedSignature = (Element) clonedResponse.getElementsByTagNameNS("*", + // "Signature").item(0); + // clonedSignature.getParentNode().removeChild(clonedSignature); + // Element signature = (Element) response.getElementsByTagNameNS("*", + // "Signature").item(0); + // signature.appendChild(clonedResponse); + // response.setAttribute("ID", "_evil_response_ID"); + // } + // + // // Fixed - clonedSignature was not found in clonedResponse + // public void applyXSW2(Document document) { + // Element response = (Element) document.getElementsByTagNameNS("*", "Response").item(0); + // Element clonedResponse = (Element) response.cloneNode(true); + // // The Original response will be the evil one + // applyMatchAndReplaceValues(response); + // Element clonedSignature = (Element) clonedResponse.getElementsByTagNameNS("*", + // "Signature").item(0); + // clonedSignature.getParentNode().removeChild(clonedSignature); + // Element signature = (Element) response.getElementsByTagNameNS("*", + // "Signature").item(0); + // signature.getParentNode().insertBefore(clonedResponse, signature); + // response.setAttribute("ID", "_evil_response_ID"); + // } + // + // public void applyXSW3(Document document) { + // Element assertion = (Element) document.getElementsByTagNameNS("*", + // "Assertion").item(0); + // Element evilAssertion = (Element) assertion.cloneNode(true); + // applyMatchAndReplaceValues(evilAssertion); + // Element copiedSignature = (Element) evilAssertion.getElementsByTagNameNS("*", + // "Signature").item(0); + // evilAssertion.setAttribute("ID", "_evil_assertion_ID"); + // evilAssertion.removeChild(copiedSignature); + // document.getDocumentElement().insertBefore(evilAssertion, assertion); + // } + // + // public void applyXSW4(Document document) { + // Element assertion = (Element) document.getElementsByTagNameNS("*", + // "Assertion").item(0); + // Element evilAssertion = (Element) assertion.cloneNode(true); + // applyMatchAndReplaceValues(evilAssertion); + // Element copiedSignature = (Element) evilAssertion.getElementsByTagNameNS("*", + // "Signature").item(0); + // evilAssertion.setAttribute("ID", "_evil_assertion_ID"); + // evilAssertion.removeChild(copiedSignature); + // document.getDocumentElement().appendChild(evilAssertion); + // evilAssertion.appendChild(assertion); + // } + // + // public void applyXSW5(Document document) { + // Element evilAssertion = (Element) document.getElementsByTagNameNS("*", + // "Assertion").item(0); + // Element assertion = (Element) evilAssertion.cloneNode(true); + // applyMatchAndReplaceValues(evilAssertion); + // Element copiedSignature = (Element) assertion.getElementsByTagNameNS("*", + // "Signature").item(0); + // assertion.removeChild(copiedSignature); + // document.getDocumentElement().appendChild(assertion); + // evilAssertion.setAttribute("ID", "_evil_assertion_ID"); + // } + // + // public void applyXSW6(Document document) { + // Element evilAssertion = (Element) document.getElementsByTagNameNS("*", + // "Assertion").item(0); + // Element originalSignature = (Element) evilAssertion.getElementsByTagNameNS("*", + // "Signature").item(0); + // Element assertion = (Element) evilAssertion.cloneNode(true); + // applyMatchAndReplaceValues(evilAssertion); + // Element copiedSignature = (Element) assertion.getElementsByTagNameNS("*", + // "Signature").item(0); + // assertion.removeChild(copiedSignature); + // originalSignature.appendChild(assertion); + // evilAssertion.setAttribute("ID", "_evil_assertion_ID"); + // } + // + // public void applyXSW7(Document document) { + // Element assertion = (Element) document.getElementsByTagNameNS("*", + // "Assertion").item(0); + // Element extensions = document.createElement("Extensions"); + // document.getDocumentElement().insertBefore(extensions, assertion); + // Element evilAssertion = (Element) assertion.cloneNode(true); + // applyMatchAndReplaceValues(evilAssertion); + // Element copiedSignature = (Element) evilAssertion.getElementsByTagNameNS("*", + // "Signature").item(0); + // evilAssertion.removeChild(copiedSignature); + // extensions.appendChild(evilAssertion); + // } + // + // public void applyXSW8(Document document) { + // Element evilAssertion = (Element) document.getElementsByTagNameNS("*", + // "Assertion").item(0); + // Element originalSignature = (Element) evilAssertion.getElementsByTagNameNS("*", + // "Signature").item(0); + // Element assertion = (Element) evilAssertion.cloneNode(true); + // applyMatchAndReplaceValues(evilAssertion); + // Element copiedSignature = (Element) assertion.getElementsByTagNameNS("*", + // "Signature").item(0); + // assertion.removeChild(copiedSignature); + // Element object = document.createElement("Object"); + // originalSignature.appendChild(object); + // object.appendChild(assertion); + // } + // + // /* + // * CVE-2019-3465 + // * XMLSecLibs <= 31.4.2, 2.1.0 & 3.0.3 + // * SimpleSAMLphp <= 1.17.6 + // * Duo Authentication Gateway (DAG) <= 1.5.9 (https://duo.com/labs/psa/duo-psa-2019-002) + // * Moodle auth_saml2 < 2019110701 + // (https://twitter.com/Catalyst_IT_AU/status/1192353402753208320) + // * Advisory: https://simplesamlphp.org/security/201911-01 + // * Vuln. discovery and write up by Hackmanit: + // * + // https://www.hackmanit.de/en/blog-en/82-xml-signature-validation-bypass-in-simplesamlphp-and-xmlseclibs + // * Tweet by @jurajsomorovsky: + // https://twitter.com/jurajsomorovsky/status/1192452032835325952 + // * + // * TODO: This does not yet work :/ + // */ + // public void applyXSW9(Document document) { + // + // try { + // XMLHelpers xmlHelpers = new XMLHelpers(); + // + // // Calculate new digest by signing the document + // Document documentToSign = + // xmlHelpers.getXMLDocumentOfSAMLMessage(xmlHelpers.getStringOfDocument(document, 2, true)); + // Element evilAssertion = (Element) documentToSign.getElementsByTagNameNS("*", + // "Assertion").item(0); + // evilAssertion.setAttribute("ID", "_evil_assertion_ID"); + // applyMatchAndReplaceValues(evilAssertion); + // Document documentNewDigest = selfSignAssertion(documentToSign); + // + // // Remove new SignatureValue and KeyInfo + // Element newAssertion = (Element) documentNewDigest.getElementsByTagNameNS("*", + // "Assertion").item(0); + // Element signatureToModify = (Element) newAssertion.getElementsByTagNameNS("*", + // "Signature").item(0); + // Element signatureValueToRemove = (Element) + // newAssertion.getElementsByTagNameNS("*", "SignatureValue").item(0); + // signatureToModify.removeChild(signatureValueToRemove); + // Element keyInfoToRemove = (Element) newAssertion.getElementsByTagNameNS("*", + // "KeyInfo").item(0); + // signatureToModify.removeChild(keyInfoToRemove); + // + // // Read original SignatureValue, KeyInfo & SignedInfo + // Element originalAssertion = (Element) document.getElementsByTagNameNS("*", + // "Assertion").item(0); + // Element originalSignatureValue = (Element) + // originalAssertion.getElementsByTagNameNS("*", "SignatureValue").item(0); + // Element originalKeyInfo = (Element) originalAssertion.getElementsByTagNameNS("*", + // "KeyInfo").item(0); + // Element originalSignedInfo = (Element) + // originalAssertion.getElementsByTagNameNS("*", "SignedInfo").item(0); + // + // // Add original SignatureValue and KeyInfo to new document + // Element fakeSignedInfo = (Element) newAssertion.getElementsByTagNameNS("*", + // "SignedInfo").item(0); + // + // signatureToModify.insertBefore(documentNewDigest.adoptNode(originalSignedInfo.cloneNode(true)), fakeSignedInfo); + // + // signatureToModify.insertBefore(documentNewDigest.adoptNode(originalSignatureValue.cloneNode(true)), fakeSignedInfo); + // // + // signatureToModify.insertBefore(documentNewDigest.adoptNode(originalKeyInfo.cloneNode(true)), + // fakeSignedInfo); + // + // // Add wrapper element and original assertion to the end + // Element wrapper = documentNewDigest.createElement("Wrapper"); + // newAssertion.getParentNode().appendChild(wrapper); + // + // wrapper.appendChild(documentNewDigest.adoptNode(originalAssertion.cloneNode(true))); + // + // // Print for testing... + // + // + // } catch (IOException | SAXException e) { + // e.printStackTrace(); + // } + // } + // + // // Used for XSW9 + // private Document selfSignAssertion(Document document) { + // + // try { + // BurpCertificateBuilder burpCertificateBuilder = new + // BurpCertificateBuilder("CN=samlraider-temporary-cert.example.net"); + // BurpCertificate burpCertificate = + // burpCertificateBuilder.generateSelfSignedCertificate(); + // XMLHelpers xmlHelpers = new XMLHelpers(); + // + // NodeList assertions = xmlHelpers.getAssertions(document); + // String signAlgorithm = xmlHelpers.getSignatureAlgorithm(assertions.item(0)); + // String digestAlgorithm = xmlHelpers.getDigestAlgorithm(assertions.item(0)); + // xmlHelpers.removeAllSignatures(document); + // + // String string = xmlHelpers.getString(document); + // Document documentToSign = xmlHelpers.getXMLDocumentOfSAMLMessage(string); + // xmlHelpers.removeEmptyTags(documentToSign); + // xmlHelpers.signAssertion(documentToSign, signAlgorithm, digestAlgorithm, + // burpCertificate.getCertificate(), burpCertificate.getPrivateKey()); + // return documentToSign; + // + // } catch (InvalidKeyException | NoSuchAlgorithmException | SignatureException | + // NoSuchProviderException | + // InvalidKeySpecException | IOException | CertificateException | + // XMLSignatureException | SAXException | + // MarshalException e) { + // e.printStackTrace(); + // } + // return null; + // } + // + // public String diffLineMode(String text1, String text2) { + // diff_match_patch differ = new diff_match_patch(); + // differ.Diff_Timeout = 5; + // diff_match_patch.LinesToCharsResult result = differ.diff_linesToChars(text1, text2); + // + // LinkedList diffs = differ.diff_main(result.chars1, + // result.chars2, false); + // differ.diff_charsToLines(diffs, result.lineArray); + // return differ.diff_prettyHtml(diffs); + // } + // + // public void applyMatchAndReplaceValues(Node elem) { + // for (int i = 0; i < elem.getChildNodes().getLength(); i++) { + // Node currentNode = elem.getChildNodes().item(i); + // if (currentNode.getNodeType() == Node.ELEMENT_NODE) { + // applyMatchAndReplaceValues(currentNode); + // } else { + // if (!currentNode.getNodeValue().trim().equals("")) { + // for (String matchString : MATCH_AND_REPLACE_MAP.keySet()) { + // if (currentNode.getNodeValue().equals(matchString)) { + // currentNode.setNodeValue(MATCH_AND_REPLACE_MAP.get(matchString)); + // } + // } + // } + // } + // } + // } +} diff --git a/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/diff_match_patch.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/diff_match_patch.java new file mode 100644 index 0000000..d554654 --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/diff_match_patch.java @@ -0,0 +1,2474 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2015 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt.samlraider.helpers; + +/* + * Functions for diff, match and patch. + * Computes the difference between two texts to create a patch. + * Applies the patch onto another text, allowing for errors. + * + * @author fraser@google.com (Neil Fraser) + */ + +/** Class containing the diff, match and patch methods. Also contains the behaviour settings. */ +public class diff_match_patch { + + // // Defaults. + // // Set these on your diff_match_patch instance to override the defaults. + // + // /** + // * The number of bits in an int. + // */ + // private final short Match_MaxBits = 32; + // // Define some regex patterns for matching boundaries. + // private final Pattern BLANKLINEEND + // = Pattern.compile("\\n\\r?\\n\\Z", Pattern.DOTALL); + // private final Pattern BLANKLINESTART + // = Pattern.compile("\\A\\r?\\n\\r?\\n", Pattern.DOTALL); + // /** + // * Number of seconds to map a diff before giving up (0 for infinity). + // */ + // public float Diff_Timeout = 1.0f; + // /** + // * Cost of an empty edit operation in terms of edit characters. + // */ + // public short Diff_EditCost = 4; + // /** + // * At what point is no match declared (0.0 = perfection, 1.0 = very loose). + // */ + // public float Match_Threshold = 0.5f; + // /** + // * How far to search for a match (0 = exact location, 1000+ = broad match). + // * A match this many characters away from the expected location will add + // * 1.0 to the score (0.0 is a perfect match). + // */ + // public int Match_Distance = 1000; + // /** + // * When deleting a large block of text (over ~64 characters), how close do + // * the contents have to be to match the expected contents. (0.0 = perfection, + // * 1.0 = very loose). Note that Match_Threshold controls how closely the + // * end points of a delete need to match. + // */ + // public float Patch_DeleteThreshold = 0.5f; + // + // + // // DIFF FUNCTIONS + // /** + // * Chunk size for context length. + // */ + // public short Patch_Margin = 4; + // + // /** + // * Unescape selected chars for compatability with JavaScript's encodeURI. + // * In speed critical applications this could be dropped since the + // * receiving application will certainly decode these fine. + // * Note that this function is case-sensitive. Thus "%3f" would not be + // * unescaped. But this is ok because it is only called with the output of + // * URLEncoder.encode which returns uppercase hex. + // *

+ // * Example: "%3F" -> "?", "%24" -> "$", etc. + // * + // * @param str The string to escape. + // * @return The escaped string. + // */ + // private static String unescapeForEncodeUriCompatability(String str) { + // return str.replace("%21", "!").replace("%7E", "~") + // .replace("%27", "'").replace("%28", "(").replace("%29", ")") + // .replace("%3B", ";").replace("%2F", "/").replace("%3F", "?") + // .replace("%3A", ":").replace("%40", "@").replace("%26", "&") + // .replace("%3D", "=").replace("%2B", "+").replace("%24", "$") + // .replace("%2C", ",").replace("%23", "#"); + // } + // + // /** + // * Find the differences between two texts. + // * Run a faster, slightly less optimal diff. + // * This method allows the 'checklines' of diff_main() to be optional. + // * Most of the time checklines is wanted, so default to true. + // * + // * @param text1 Old string to be diffed. + // * @param text2 New string to be diffed. + // * @return Linked List of Diff objects. + // */ + // public LinkedList diff_main(String text1, String text2) { + // return diff_main(text1, text2, true); + // } + // + // /** + // * Find the differences between two texts. + // * + // * @param text1 Old string to be diffed. + // * @param text2 New string to be diffed. + // * @param checklines Speedup flag. If false, then don't run a + // * line-level diff first to identify the changed areas. + // * If true, then run a faster slightly less optimal diff. + // * @return Linked List of Diff objects. + // */ + // public LinkedList diff_main(String text1, String text2, + // boolean checklines) { + // // Set a deadline by which time the diff must be complete. + // long deadline; + // if (Diff_Timeout <= 0) { + // deadline = Long.MAX_VALUE; + // } else { + // deadline = System.currentTimeMillis() + (long) (Diff_Timeout * 1000); + // } + // return diff_main(text1, text2, checklines, deadline); + // } + // + // /** + // * Find the differences between two texts. Simplifies the problem by + // * stripping any common prefix or suffix off the texts before diffing. + // * + // * @param text1 Old string to be diffed. + // * @param text2 New string to be diffed. + // * @param checklines Speedup flag. If false, then don't run a + // * line-level diff first to identify the changed areas. + // * If true, then run a faster slightly less optimal diff. + // * @param deadline Time when the diff should be complete by. Used + // * internally for recursive calls. Users should set DiffTimeout + // instead. + // * @return Linked List of Diff objects. + // */ + // private LinkedList diff_main(String text1, String text2, + // boolean checklines, long deadline) { + // // Check for null inputs. + // if (text1 == null || text2 == null) { + // throw new IllegalArgumentException("Null inputs. (diff_main)"); + // } + // + // // Check for equality (speedup). + // LinkedList diffs; + // if (text1.equals(text2)) { + // diffs = new LinkedList(); + // if (text1.length() != 0) { + // diffs.add(new Diff(Operation.EQUAL, text1)); + // } + // return diffs; + // } + // + // // Trim off common prefix (speedup). + // int commonlength = diff_commonPrefix(text1, text2); + // String commonprefix = text1.substring(0, commonlength); + // text1 = text1.substring(commonlength); + // text2 = text2.substring(commonlength); + // + // // Trim off common suffix (speedup). + // commonlength = diff_commonSuffix(text1, text2); + // String commonsuffix = text1.substring(text1.length() - commonlength); + // text1 = text1.substring(0, text1.length() - commonlength); + // text2 = text2.substring(0, text2.length() - commonlength); + // + // // Compute the diff on the middle block. + // diffs = diff_compute(text1, text2, checklines, deadline); + // + // // Restore the prefix and suffix. + // if (commonprefix.length() != 0) { + // diffs.addFirst(new Diff(Operation.EQUAL, commonprefix)); + // } + // if (commonsuffix.length() != 0) { + // diffs.addLast(new Diff(Operation.EQUAL, commonsuffix)); + // } + // + // diff_cleanupMerge(diffs); + // return diffs; + // } + // + // /** + // * Find the differences between two texts. Assumes that the texts do not + // * have any common prefix or suffix. + // * + // * @param text1 Old string to be diffed. + // * @param text2 New string to be diffed. + // * @param checklines Speedup flag. If false, then don't run a + // * line-level diff first to identify the changed areas. + // * If true, then run a faster slightly less optimal diff. + // * @param deadline Time when the diff should be complete by. + // * @return Linked List of Diff objects. + // */ + // private LinkedList diff_compute(String text1, String text2, + // boolean checklines, long deadline) { + // LinkedList diffs = new LinkedList(); + // + // if (text1.length() == 0) { + // // Just add some text (speedup). + // diffs.add(new Diff(Operation.INSERT, text2)); + // return diffs; + // } + // + // if (text2.length() == 0) { + // // Just delete some text (speedup). + // diffs.add(new Diff(Operation.DELETE, text1)); + // return diffs; + // } + // + // String longtext = text1.length() > text2.length() ? text1 : text2; + // String shorttext = text1.length() > text2.length() ? text2 : text1; + // int i = longtext.indexOf(shorttext); + // if (i != -1) { + // // Shorter text is inside the longer text (speedup). + // Operation op = (text1.length() > text2.length()) ? + // Operation.DELETE : Operation.INSERT; + // diffs.add(new Diff(op, longtext.substring(0, i))); + // diffs.add(new Diff(Operation.EQUAL, shorttext)); + // diffs.add(new Diff(op, longtext.substring(i + shorttext.length()))); + // return diffs; + // } + // + // if (shorttext.length() == 1) { + // // Single character string. + // // After the previous speedup, the character can't be an equality. + // diffs.add(new Diff(Operation.DELETE, text1)); + // diffs.add(new Diff(Operation.INSERT, text2)); + // return diffs; + // } + // + // // Check to see if the problem can be split in two. + // String[] hm = diff_halfMatch(text1, text2); + // if (hm != null) { + // // A half-match was found, sort out the return data. + // String text1_a = hm[0]; + // String text1_b = hm[1]; + // String text2_a = hm[2]; + // String text2_b = hm[3]; + // String mid_common = hm[4]; + // // Send both pairs off for separate processing. + // LinkedList diffs_a = diff_main(text1_a, text2_a, + // checklines, deadline); + // LinkedList diffs_b = diff_main(text1_b, text2_b, + // checklines, deadline); + // // Merge the results. + // diffs = diffs_a; + // diffs.add(new Diff(Operation.EQUAL, mid_common)); + // diffs.addAll(diffs_b); + // return diffs; + // } + // + // if (checklines && text1.length() > 100 && text2.length() > 100) { + // return diff_lineMode(text1, text2, deadline); + // } + // + // return diff_bisect(text1, text2, deadline); + // } + // + // /** + // * Do a quick line-level diff on both strings, then rediff the parts for + // * greater accuracy. + // * This speedup can produce non-minimal diffs. + // * + // * @param text1 Old string to be diffed. + // * @param text2 New string to be diffed. + // * @param deadline Time when the diff should be complete by. + // * @return Linked List of Diff objects. + // */ + // private LinkedList diff_lineMode(String text1, String text2, + // long deadline) { + // // Scan the text on a line-by-line basis first. + // LinesToCharsResult b = diff_linesToChars(text1, text2); + // text1 = b.chars1; + // text2 = b.chars2; + // List linearray = b.lineArray; + // + // LinkedList diffs = diff_main(text1, text2, false, deadline); + // + // // Convert the diff back to original text. + // diff_charsToLines(diffs, linearray); + // // Eliminate freak matches (e.g. blank lines) + // diff_cleanupSemantic(diffs); + // + // // Rediff any replacement blocks, this time character-by-character. + // // Add a dummy entry at the end. + // diffs.add(new Diff(Operation.EQUAL, "")); + // int count_delete = 0; + // int count_insert = 0; + // String text_delete = ""; + // String text_insert = ""; + // ListIterator pointer = diffs.listIterator(); + // Diff thisDiff = pointer.next(); + // while (thisDiff != null) { + // switch (thisDiff.operation) { + // case INSERT: + // count_insert++; + // text_insert += thisDiff.text; + // break; + // case DELETE: + // count_delete++; + // text_delete += thisDiff.text; + // break; + // case EQUAL: + // // Upon reaching an equality, check for prior redundancies. + // if (count_delete >= 1 && count_insert >= 1) { + // // Delete the offending records and add the merged ones. + // pointer.previous(); + // for (int j = 0; j < count_delete + count_insert; j++) { + // pointer.previous(); + // pointer.remove(); + // } + // for (Diff newDiff : diff_main(text_delete, text_insert, false, + // deadline)) { + // pointer.add(newDiff); + // } + // } + // count_insert = 0; + // count_delete = 0; + // text_delete = ""; + // text_insert = ""; + // break; + // } + // thisDiff = pointer.hasNext() ? pointer.next() : null; + // } + // diffs.removeLast(); // Remove the dummy entry at the end. + // + // return diffs; + // } + // + // /** + // * Find the 'middle snake' of a diff, split the problem in two + // * and return the recursively constructed diff. + // * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. + // * + // * @param text1 Old string to be diffed. + // * @param text2 New string to be diffed. + // * @param deadline Time at which to bail if not yet complete. + // * @return LinkedList of Diff objects. + // */ + // protected LinkedList diff_bisect(String text1, String text2, + // long deadline) { + // // Cache the text lengths to prevent multiple calls. + // int text1_length = text1.length(); + // int text2_length = text2.length(); + // int max_d = (text1_length + text2_length + 1) / 2; + // int v_offset = max_d; + // int v_length = 2 * max_d; + // int[] v1 = new int[v_length]; + // int[] v2 = new int[v_length]; + // for (int x = 0; x < v_length; x++) { + // v1[x] = -1; + // v2[x] = -1; + // } + // v1[v_offset + 1] = 0; + // v2[v_offset + 1] = 0; + // int delta = text1_length - text2_length; + // // If the total number of characters is odd, then the front path will + // // collide with the reverse path. + // boolean front = (delta % 2 != 0); + // // Offsets for start and end of k loop. + // // Prevents mapping of space beyond the grid. + // int k1start = 0; + // int k1end = 0; + // int k2start = 0; + // int k2end = 0; + // for (int d = 0; d < max_d; d++) { + // // Bail out if deadline is reached. + // if (System.currentTimeMillis() > deadline) { + // break; + // } + // + // // Walk the front path one step. + // for (int k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { + // int k1_offset = v_offset + k1; + // int x1; + // if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) { + // x1 = v1[k1_offset + 1]; + // } else { + // x1 = v1[k1_offset - 1] + 1; + // } + // int y1 = x1 - k1; + // while (x1 < text1_length && y1 < text2_length + // && text1.charAt(x1) == text2.charAt(y1)) { + // x1++; + // y1++; + // } + // v1[k1_offset] = x1; + // if (x1 > text1_length) { + // // Ran off the right of the graph. + // k1end += 2; + // } else if (y1 > text2_length) { + // // Ran off the bottom of the graph. + // k1start += 2; + // } else if (front) { + // int k2_offset = v_offset + delta - k1; + // if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) { + // // Mirror x2 onto top-left coordinate system. + // int x2 = text1_length - v2[k2_offset]; + // if (x1 >= x2) { + // // Overlap detected. + // return diff_bisectSplit(text1, text2, x1, y1, deadline); + // } + // } + // } + // } + // + // // Walk the reverse path one step. + // for (int k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { + // int k2_offset = v_offset + k2; + // int x2; + // if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) { + // x2 = v2[k2_offset + 1]; + // } else { + // x2 = v2[k2_offset - 1] + 1; + // } + // int y2 = x2 - k2; + // while (x2 < text1_length && y2 < text2_length + // && text1.charAt(text1_length - x2 - 1) + // == text2.charAt(text2_length - y2 - 1)) { + // x2++; + // y2++; + // } + // v2[k2_offset] = x2; + // if (x2 > text1_length) { + // // Ran off the left of the graph. + // k2end += 2; + // } else if (y2 > text2_length) { + // // Ran off the top of the graph. + // k2start += 2; + // } else if (!front) { + // int k1_offset = v_offset + delta - k2; + // if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) { + // int x1 = v1[k1_offset]; + // int y1 = v_offset + x1 - k1_offset; + // // Mirror x2 onto top-left coordinate system. + // x2 = text1_length - x2; + // if (x1 >= x2) { + // // Overlap detected. + // return diff_bisectSplit(text1, text2, x1, y1, deadline); + // } + // } + // } + // } + // } + // // Diff took too long and hit the deadline or + // // number of diffs equals number of characters, no commonality at all. + // LinkedList diffs = new LinkedList(); + // diffs.add(new Diff(Operation.DELETE, text1)); + // diffs.add(new Diff(Operation.INSERT, text2)); + // return diffs; + // } + // + // /** + // * Given the location of the 'middle snake', split the diff in two parts + // * and recurse. + // * + // * @param text1 Old string to be diffed. + // * @param text2 New string to be diffed. + // * @param x Index of split point in text1. + // * @param y Index of split point in text2. + // * @param deadline Time at which to bail if not yet complete. + // * @return LinkedList of Diff objects. + // */ + // private LinkedList diff_bisectSplit(String text1, String text2, + // int x, int y, long deadline) { + // String text1a = text1.substring(0, x); + // String text2a = text2.substring(0, y); + // String text1b = text1.substring(x); + // String text2b = text2.substring(y); + // + // // Compute both diffs serially. + // LinkedList diffs = diff_main(text1a, text2a, false, deadline); + // LinkedList diffsb = diff_main(text1b, text2b, false, deadline); + // + // diffs.addAll(diffsb); + // return diffs; + // } + // + // /** + // * Split two texts into a list of strings. Reduce the texts to a string of + // * hashes where each Unicode character represents one line. + // * + // * @param text1 First string. + // * @param text2 Second string. + // * @return An object containing the encoded text1, the encoded text2 and + // * the List of unique strings. The zeroth element of the List of + // * unique strings is intentionally blank. + // */ + // protected LinesToCharsResult diff_linesToChars(String text1, String text2) { + // List lineArray = new ArrayList(); + // Map lineHash = new HashMap(); + // // e.g. linearray[4] == "Hello\n" + // // e.g. linehash.get("Hello\n") == 4 + // + // // "\x00" is a valid character, but various debuggers don't like it. + // // So we'll insert a junk entry to avoid generating a null character. + // lineArray.add(""); + // + // String chars1 = diff_linesToCharsMunge(text1, lineArray, lineHash); + // String chars2 = diff_linesToCharsMunge(text2, lineArray, lineHash); + // return new LinesToCharsResult(chars1, chars2, lineArray); + // } + // + // /** + // * Split a text into a list of strings. Reduce the texts to a string of + // * hashes where each Unicode character represents one line. + // * + // * @param text String to encode. + // * @param lineArray List of unique strings. + // * @param lineHash Map of strings to indices. + // * @return Encoded string. + // */ + // private String diff_linesToCharsMunge(String text, List lineArray, + // Map lineHash) { + // int lineStart = 0; + // int lineEnd = -1; + // String line; + // StringBuilder chars = new StringBuilder(); + // // Walk the text, pulling out a substring for each line. + // // text.split('\n') would would temporarily double our memory footprint. + // // Modifying text would create many large strings to garbage collect. + // while (lineEnd < text.length() - 1) { + // lineEnd = text.indexOf('\n', lineStart); + // if (lineEnd == -1) { + // lineEnd = text.length() - 1; + // } + // line = text.substring(lineStart, lineEnd + 1); + // lineStart = lineEnd + 1; + // + // if (lineHash.containsKey(line)) { + // chars.append((char) (int) lineHash.get(line)); + // } else { + // lineArray.add(line); + // lineHash.put(line, lineArray.size() - 1); + // chars.append((char) (lineArray.size() - 1)); + // } + // } + // return chars.toString(); + // } + // + // /** + // * Rehydrate the text in a diff from a string of line hashes to real lines of + // * text. + // * + // * @param diffs LinkedList of Diff objects. + // * @param lineArray List of unique strings. + // */ + // protected void diff_charsToLines(LinkedList diffs, + // List lineArray) { + // StringBuilder text; + // for (Diff diff : diffs) { + // text = new StringBuilder(); + // for (int y = 0; y < diff.text.length(); y++) { + // text.append(lineArray.get(diff.text.charAt(y))); + // } + // diff.text = text.toString(); + // } + // } + // + // /** + // * Determine the common prefix of two strings + // * + // * @param text1 First string. + // * @param text2 Second string. + // * @return The number of characters common to the start of each string. + // */ + // public int diff_commonPrefix(String text1, String text2) { + // // Performance analysis: http://neil.fraser.name/news/2007/10/09/ + // int n = Math.min(text1.length(), text2.length()); + // for (int i = 0; i < n; i++) { + // if (text1.charAt(i) != text2.charAt(i)) { + // return i; + // } + // } + // return n; + // } + // + // /** + // * Determine the common suffix of two strings + // * + // * @param text1 First string. + // * @param text2 Second string. + // * @return The number of characters common to the end of each string. + // */ + // public int diff_commonSuffix(String text1, String text2) { + // // Performance analysis: http://neil.fraser.name/news/2007/10/09/ + // int text1_length = text1.length(); + // int text2_length = text2.length(); + // int n = Math.min(text1_length, text2_length); + // for (int i = 1; i <= n; i++) { + // if (text1.charAt(text1_length - i) != text2.charAt(text2_length - i)) { + // return i - 1; + // } + // } + // return n; + // } + // + // /** + // * Determine if the suffix of one string is the prefix of another. + // * + // * @param text1 First string. + // * @param text2 Second string. + // * @return The number of characters common to the end of the first + // * string and the start of the second string. + // */ + // protected int diff_commonOverlap(String text1, String text2) { + // // Cache the text lengths to prevent multiple calls. + // int text1_length = text1.length(); + // int text2_length = text2.length(); + // // Eliminate the null case. + // if (text1_length == 0 || text2_length == 0) { + // return 0; + // } + // // Truncate the longer string. + // if (text1_length > text2_length) { + // text1 = text1.substring(text1_length - text2_length); + // } else if (text1_length < text2_length) { + // text2 = text2.substring(0, text1_length); + // } + // int text_length = Math.min(text1_length, text2_length); + // // Quick check for the worst case. + // if (text1.equals(text2)) { + // return text_length; + // } + // + // // Start by looking for a single character match + // // and increase length until no match is found. + // // Performance analysis: http://neil.fraser.name/news/2010/11/04/ + // int best = 0; + // int length = 1; + // while (true) { + // String pattern = text1.substring(text_length - length); + // int found = text2.indexOf(pattern); + // if (found == -1) { + // return best; + // } + // length += found; + // if (found == 0 || text1.substring(text_length - length).equals( + // text2.substring(0, length))) { + // best = length; + // length++; + // } + // } + // } + // + // /** + // * Do the two texts share a substring which is at least half the length of + // * the longer text? + // * This speedup can produce non-minimal diffs. + // * + // * @param text1 First string. + // * @param text2 Second string. + // * @return Five element String array, containing the prefix of text1, the + // * suffix of text1, the prefix of text2, the suffix of text2 and the + // * common middle. Or null if there was no match. + // */ + // protected String[] diff_halfMatch(String text1, String text2) { + // if (Diff_Timeout <= 0) { + // // Don't risk returning a non-optimal diff if we have unlimited time. + // return null; + // } + // String longtext = text1.length() > text2.length() ? text1 : text2; + // String shorttext = text1.length() > text2.length() ? text2 : text1; + // if (longtext.length() < 4 || shorttext.length() * 2 < longtext.length()) { + // return null; // Pointless. + // } + // + // // First check if the second quarter is the seed for a half-match. + // String[] hm1 = diff_halfMatchI(longtext, shorttext, + // (longtext.length() + 3) / 4); + // // Check again based on the third quarter. + // String[] hm2 = diff_halfMatchI(longtext, shorttext, + // (longtext.length() + 1) / 2); + // String[] hm; + // if (hm1 == null && hm2 == null) { + // return null; + // } else if (hm2 == null) { + // hm = hm1; + // } else if (hm1 == null) { + // hm = hm2; + // } else { + // // Both matched. Select the longest. + // hm = hm1[4].length() > hm2[4].length() ? hm1 : hm2; + // } + // + // // A half-match was found, sort out the return data. + // if (text1.length() > text2.length()) { + // return hm; + // //return new String[]{hm[0], hm[1], hm[2], hm[3], hm[4]}; + // } else { + // return new String[]{hm[2], hm[3], hm[0], hm[1], hm[4]}; + // } + // } + // + // /** + // * Does a substring of shorttext exist within longtext such that the + // * substring is at least half the length of longtext? + // * + // * @param longtext Longer string. + // * @param shorttext Shorter string. + // * @param i Start index of quarter length substring within longtext. + // * @return Five element String array, containing the prefix of longtext, the + // * suffix of longtext, the prefix of shorttext, the suffix of shorttext + // * and the common middle. Or null if there was no match. + // */ + // private String[] diff_halfMatchI(String longtext, String shorttext, int i) { + // // Start with a 1/4 length substring at position i as a seed. + // String seed = longtext.substring(i, i + longtext.length() / 4); + // int j = -1; + // String best_common = ""; + // String best_longtext_a = "", best_longtext_b = ""; + // String best_shorttext_a = "", best_shorttext_b = ""; + // while ((j = shorttext.indexOf(seed, j + 1)) != -1) { + // int prefixLength = diff_commonPrefix(longtext.substring(i), + // shorttext.substring(j)); + // int suffixLength = diff_commonSuffix(longtext.substring(0, i), + // shorttext.substring(0, j)); + // if (best_common.length() < suffixLength + prefixLength) { + // best_common = shorttext.substring(j - suffixLength, j) + // + shorttext.substring(j, j + prefixLength); + // best_longtext_a = longtext.substring(0, i - suffixLength); + // best_longtext_b = longtext.substring(i + prefixLength); + // best_shorttext_a = shorttext.substring(0, j - suffixLength); + // best_shorttext_b = shorttext.substring(j + prefixLength); + // } + // } + // if (best_common.length() * 2 >= longtext.length()) { + // return new String[]{best_longtext_a, best_longtext_b, + // best_shorttext_a, best_shorttext_b, best_common}; + // } else { + // return null; + // } + // } + // + // /** + // * Reduce the number of edits by eliminating semantically trivial equalities. + // * + // * @param diffs LinkedList of Diff objects. + // */ + // public void diff_cleanupSemantic(LinkedList diffs) { + // if (diffs.isEmpty()) { + // return; + // } + // boolean changes = false; + // Stack equalities = new Stack(); // Stack of qualities. + // String lastequality = null; // Always equal to equalities.lastElement().text + // ListIterator pointer = diffs.listIterator(); + // // Number of characters that changed prior to the equality. + // int length_insertions1 = 0; + // int length_deletions1 = 0; + // // Number of characters that changed after the equality. + // int length_insertions2 = 0; + // int length_deletions2 = 0; + // Diff thisDiff = pointer.next(); + // while (thisDiff != null) { + // if (thisDiff.operation == Operation.EQUAL) { + // // Equality found. + // equalities.push(thisDiff); + // length_insertions1 = length_insertions2; + // length_deletions1 = length_deletions2; + // length_insertions2 = 0; + // length_deletions2 = 0; + // lastequality = thisDiff.text; + // } else { + // // An insertion or deletion. + // if (thisDiff.operation == Operation.INSERT) { + // length_insertions2 += thisDiff.text.length(); + // } else { + // length_deletions2 += thisDiff.text.length(); + // } + // // Eliminate an equality that is smaller or equal to the edits on both + // // sides of it. + // if (lastequality != null && (lastequality.length() + // <= Math.max(length_insertions1, length_deletions1)) + // && (lastequality.length() + // <= Math.max(length_insertions2, length_deletions2))) { + // // Walk back to offending equality. + // while (thisDiff != equalities.lastElement()) { + // thisDiff = pointer.previous(); + // } + // pointer.next(); + // + // // Replace equality with a delete. + // pointer.set(new Diff(Operation.DELETE, lastequality)); + // // Insert a corresponding an insert. + // pointer.add(new Diff(Operation.INSERT, lastequality)); + // + // equalities.pop(); // Throw away the equality we just deleted. + // if (!equalities.empty()) { + // // Throw away the previous equality (it needs to be reevaluated). + // equalities.pop(); + // } + // if (equalities.empty()) { + // // There are no previous equalities, walk back to the start. + // while (pointer.hasPrevious()) { + // pointer.previous(); + // } + // } else { + // // There is a safe equality we can fall back to. + // thisDiff = equalities.lastElement(); + // while (thisDiff != pointer.previous()) { + // // Intentionally empty loop. + // } + // } + // + // length_insertions1 = 0; // Reset the counters. + // length_insertions2 = 0; + // length_deletions1 = 0; + // length_deletions2 = 0; + // lastequality = null; + // changes = true; + // } + // } + // thisDiff = pointer.hasNext() ? pointer.next() : null; + // } + // + // // Normalize the diff. + // if (changes) { + // diff_cleanupMerge(diffs); + // } + // diff_cleanupSemanticLossless(diffs); + // + // // Find any overlaps between deletions and insertions. + // // e.g: abcxxxxxxdef + // // -> abcxxxdef + // // e.g: xxxabcdefxxx + // // -> defxxxabc + // // Only extract an overlap if it is as big as the edit ahead or behind it. + // pointer = diffs.listIterator(); + // Diff prevDiff = null; + // thisDiff = null; + // if (pointer.hasNext()) { + // prevDiff = pointer.next(); + // if (pointer.hasNext()) { + // thisDiff = pointer.next(); + // } + // } + // while (thisDiff != null) { + // if (prevDiff.operation == Operation.DELETE && + // thisDiff.operation == Operation.INSERT) { + // String deletion = prevDiff.text; + // String insertion = thisDiff.text; + // int overlap_length1 = this.diff_commonOverlap(deletion, insertion); + // int overlap_length2 = this.diff_commonOverlap(insertion, deletion); + // if (overlap_length1 >= overlap_length2) { + // if (overlap_length1 >= deletion.length() / 2.0 || + // overlap_length1 >= insertion.length() / 2.0) { + // // Overlap found. Insert an equality and trim the surrounding edits. + // pointer.previous(); + // pointer.add(new Diff(Operation.EQUAL, + // insertion.substring(0, overlap_length1))); + // prevDiff.text = + // deletion.substring(0, deletion.length() - overlap_length1); + // thisDiff.text = insertion.substring(overlap_length1); + // // pointer.add inserts the element before the cursor, so there is + // // no need to step past the new element. + // } + // } else { + // if (overlap_length2 >= deletion.length() / 2.0 || + // overlap_length2 >= insertion.length() / 2.0) { + // // Reverse overlap found. + // // Insert an equality and swap and trim the surrounding edits. + // pointer.previous(); + // pointer.add(new Diff(Operation.EQUAL, + // deletion.substring(0, overlap_length2))); + // prevDiff.operation = Operation.INSERT; + // prevDiff.text = + // insertion.substring(0, insertion.length() - overlap_length2); + // thisDiff.operation = Operation.DELETE; + // thisDiff.text = deletion.substring(overlap_length2); + // // pointer.add inserts the element before the cursor, so there is + // // no need to step past the new element. + // } + // } + // thisDiff = pointer.hasNext() ? pointer.next() : null; + // } + // prevDiff = thisDiff; + // thisDiff = pointer.hasNext() ? pointer.next() : null; + // } + // } + // + // /** + // * Look for single edits surrounded on both sides by equalities + // * which can be shifted sideways to align the edit to a word boundary. + // * e.g: The cat came. -> The cat came. + // * + // * @param diffs LinkedList of Diff objects. + // */ + // public void diff_cleanupSemanticLossless(LinkedList diffs) { + // String equality1, edit, equality2; + // String commonString; + // int commonOffset; + // int score, bestScore; + // String bestEquality1, bestEdit, bestEquality2; + // // Create a new iterator at the start. + // ListIterator pointer = diffs.listIterator(); + // Diff prevDiff = pointer.hasNext() ? pointer.next() : null; + // Diff thisDiff = pointer.hasNext() ? pointer.next() : null; + // Diff nextDiff = pointer.hasNext() ? pointer.next() : null; + // // Intentionally ignore the first and last element (don't need checking). + // while (nextDiff != null) { + // if (prevDiff.operation == Operation.EQUAL && + // nextDiff.operation == Operation.EQUAL) { + // // This is a single edit surrounded by equalities. + // equality1 = prevDiff.text; + // edit = thisDiff.text; + // equality2 = nextDiff.text; + // + // // First, shift the edit as far left as possible. + // commonOffset = diff_commonSuffix(equality1, edit); + // if (commonOffset != 0) { + // commonString = edit.substring(edit.length() - commonOffset); + // equality1 = equality1.substring(0, equality1.length() - commonOffset); + // edit = commonString + edit.substring(0, edit.length() - commonOffset); + // equality2 = commonString + equality2; + // } + // + // // Second, step character by character right, looking for the best fit. + // bestEquality1 = equality1; + // bestEdit = edit; + // bestEquality2 = equality2; + // bestScore = diff_cleanupSemanticScore(equality1, edit) + // + diff_cleanupSemanticScore(edit, equality2); + // while (edit.length() != 0 && equality2.length() != 0 + // && edit.charAt(0) == equality2.charAt(0)) { + // equality1 += edit.charAt(0); + // edit = edit.substring(1) + equality2.charAt(0); + // equality2 = equality2.substring(1); + // score = diff_cleanupSemanticScore(equality1, edit) + // + diff_cleanupSemanticScore(edit, equality2); + // // The >= encourages trailing rather than leading whitespace on edits. + // if (score >= bestScore) { + // bestScore = score; + // bestEquality1 = equality1; + // bestEdit = edit; + // bestEquality2 = equality2; + // } + // } + // + // if (!prevDiff.text.equals(bestEquality1)) { + // // We have an improvement, save it back to the diff. + // if (bestEquality1.length() != 0) { + // prevDiff.text = bestEquality1; + // } else { + // pointer.previous(); // Walk past nextDiff. + // pointer.previous(); // Walk past thisDiff. + // pointer.previous(); // Walk past prevDiff. + // pointer.remove(); // Delete prevDiff. + // pointer.next(); // Walk past thisDiff. + // pointer.next(); // Walk past nextDiff. + // } + // thisDiff.text = bestEdit; + // if (bestEquality2.length() != 0) { + // nextDiff.text = bestEquality2; + // } else { + // pointer.remove(); // Delete nextDiff. + // nextDiff = thisDiff; + // thisDiff = prevDiff; + // } + // } + // } + // prevDiff = thisDiff; + // thisDiff = nextDiff; + // nextDiff = pointer.hasNext() ? pointer.next() : null; + // } + // } + // + // /** + // * Given two strings, compute a score representing whether the internal + // * boundary falls on logical boundaries. + // * Scores range from 6 (best) to 0 (worst). + // * + // * @param one First string. + // * @param two Second string. + // * @return The score. + // */ + // private int diff_cleanupSemanticScore(String one, String two) { + // if (one.length() == 0 || two.length() == 0) { + // // Edges are the best. + // return 6; + // } + // + // // Each port of this function behaves slightly differently due to + // // subtle differences in each language's definition of things like + // // 'whitespace'. Since this function's purpose is largely cosmetic, + // // the choice has been made to use each language's native features + // // rather than force total conformity. + // char char1 = one.charAt(one.length() - 1); + // char char2 = two.charAt(0); + // boolean nonAlphaNumeric1 = !Character.isLetterOrDigit(char1); + // boolean nonAlphaNumeric2 = !Character.isLetterOrDigit(char2); + // boolean whitespace1 = nonAlphaNumeric1 && Character.isWhitespace(char1); + // boolean whitespace2 = nonAlphaNumeric2 && Character.isWhitespace(char2); + // boolean lineBreak1 = whitespace1 + // && Character.getType(char1) == Character.CONTROL; + // boolean lineBreak2 = whitespace2 + // && Character.getType(char2) == Character.CONTROL; + // boolean blankLine1 = lineBreak1 && BLANKLINEEND.matcher(one).find(); + // boolean blankLine2 = lineBreak2 && BLANKLINESTART.matcher(two).find(); + // + // if (blankLine1 || blankLine2) { + // // Five points for blank lines. + // return 5; + // } else if (lineBreak1 || lineBreak2) { + // // Four points for line breaks. + // return 4; + // } else if (nonAlphaNumeric1 && !whitespace1 && whitespace2) { + // // Three points for end of sentences. + // return 3; + // } else if (whitespace1 || whitespace2) { + // // Two points for whitespace. + // return 2; + // } else if (nonAlphaNumeric1 || nonAlphaNumeric2) { + // // One point for non-alphanumeric. + // return 1; + // } + // return 0; + // } + // + // /** + // * Reduce the number of edits by eliminating operationally trivial equalities. + // * + // * @param diffs LinkedList of Diff objects. + // */ + // public void diff_cleanupEfficiency(LinkedList diffs) { + // if (diffs.isEmpty()) { + // return; + // } + // boolean changes = false; + // Stack equalities = new Stack(); // Stack of equalities. + // String lastequality = null; // Always equal to equalities.lastElement().text + // ListIterator pointer = diffs.listIterator(); + // // Is there an insertion operation before the last equality. + // boolean pre_ins = false; + // // Is there a deletion operation before the last equality. + // boolean pre_del = false; + // // Is there an insertion operation after the last equality. + // boolean post_ins = false; + // // Is there a deletion operation after the last equality. + // boolean post_del = false; + // Diff thisDiff = pointer.next(); + // Diff safeDiff = thisDiff; // The last Diff that is known to be unsplitable. + // while (thisDiff != null) { + // if (thisDiff.operation == Operation.EQUAL) { + // // Equality found. + // if (thisDiff.text.length() < Diff_EditCost && (post_ins || post_del)) { + // // Candidate found. + // equalities.push(thisDiff); + // pre_ins = post_ins; + // pre_del = post_del; + // lastequality = thisDiff.text; + // } else { + // // Not a candidate, and can never become one. + // equalities.clear(); + // lastequality = null; + // safeDiff = thisDiff; + // } + // post_ins = post_del = false; + // } else { + // // An insertion or deletion. + // if (thisDiff.operation == Operation.DELETE) { + // post_del = true; + // } else { + // post_ins = true; + // } + // /* + // * Five types to be split: + // * ABXYCD + // * AXCD + // * ABXC + // * AXCD + // * ABXC + // */ + // if (lastequality != null + // && ((pre_ins && pre_del && post_ins && post_del) + // || ((lastequality.length() < Diff_EditCost / 2) + // && ((pre_ins ? 1 : 0) + (pre_del ? 1 : 0) + // + (post_ins ? 1 : 0) + (post_del ? 1 : 0)) == 3))) { + // // Walk back to offending equality. + // while (thisDiff != equalities.lastElement()) { + // thisDiff = pointer.previous(); + // } + // pointer.next(); + // + // // Replace equality with a delete. + // pointer.set(new Diff(Operation.DELETE, lastequality)); + // // Insert a corresponding an insert. + // pointer.add(thisDiff = new Diff(Operation.INSERT, lastequality)); + // + // equalities.pop(); // Throw away the equality we just deleted. + // lastequality = null; + // if (pre_ins && pre_del) { + // // No changes made which could affect previous entry, keep going. + // post_ins = post_del = true; + // equalities.clear(); + // safeDiff = thisDiff; + // } else { + // if (!equalities.empty()) { + // // Throw away the previous equality (it needs to be reevaluated). + // equalities.pop(); + // } + // if (equalities.empty()) { + // // There are no previous questionable equalities, + // // walk back to the last known safe diff. + // thisDiff = safeDiff; + // } else { + // // There is an equality we can fall back to. + // thisDiff = equalities.lastElement(); + // } + // while (thisDiff != pointer.previous()) { + // // Intentionally empty loop. + // } + // post_ins = post_del = false; + // } + // + // changes = true; + // } + // } + // thisDiff = pointer.hasNext() ? pointer.next() : null; + // } + // + // if (changes) { + // diff_cleanupMerge(diffs); + // } + // } + // + // /** + // * Reorder and merge like edit sections. Merge equalities. + // * Any edit section can move as long as it doesn't cross an equality. + // * + // * @param diffs LinkedList of Diff objects. + // */ + // public void diff_cleanupMerge(LinkedList diffs) { + // diffs.add(new Diff(Operation.EQUAL, "")); // Add a dummy entry at the end. + // ListIterator pointer = diffs.listIterator(); + // int count_delete = 0; + // int count_insert = 0; + // String text_delete = ""; + // String text_insert = ""; + // Diff thisDiff = pointer.next(); + // Diff prevEqual = null; + // int commonlength; + // while (thisDiff != null) { + // switch (thisDiff.operation) { + // case INSERT: + // count_insert++; + // text_insert += thisDiff.text; + // prevEqual = null; + // break; + // case DELETE: + // count_delete++; + // text_delete += thisDiff.text; + // prevEqual = null; + // break; + // case EQUAL: + // if (count_delete + count_insert > 1) { + // boolean both_types = count_delete != 0 && count_insert != 0; + // // Delete the offending records. + // pointer.previous(); // Reverse direction. + // while (count_delete-- > 0) { + // pointer.previous(); + // pointer.remove(); + // } + // while (count_insert-- > 0) { + // pointer.previous(); + // pointer.remove(); + // } + // if (both_types) { + // // Factor out any common prefixies. + // commonlength = diff_commonPrefix(text_insert, text_delete); + // if (commonlength != 0) { + // if (pointer.hasPrevious()) { + // thisDiff = pointer.previous(); + // assert thisDiff.operation == Operation.EQUAL + // : "Previous diff should have been an equality."; + // thisDiff.text += text_insert.substring(0, commonlength); + // pointer.next(); + // } else { + // pointer.add(new Diff(Operation.EQUAL, + // text_insert.substring(0, commonlength))); + // } + // text_insert = text_insert.substring(commonlength); + // text_delete = text_delete.substring(commonlength); + // } + // // Factor out any common suffixies. + // commonlength = diff_commonSuffix(text_insert, text_delete); + // if (commonlength != 0) { + // thisDiff = pointer.next(); + // thisDiff.text = text_insert.substring(text_insert.length() + // - commonlength) + thisDiff.text; + // text_insert = text_insert.substring(0, text_insert.length() + // - commonlength); + // text_delete = text_delete.substring(0, text_delete.length() + // - commonlength); + // pointer.previous(); + // } + // } + // // Insert the merged records. + // if (text_delete.length() != 0) { + // pointer.add(new Diff(Operation.DELETE, text_delete)); + // } + // if (text_insert.length() != 0) { + // pointer.add(new Diff(Operation.INSERT, text_insert)); + // } + // // Step forward to the equality. + // thisDiff = pointer.hasNext() ? pointer.next() : null; + // } else if (prevEqual != null) { + // // Merge this equality with the previous one. + // prevEqual.text += thisDiff.text; + // pointer.remove(); + // thisDiff = pointer.previous(); + // pointer.next(); // Forward direction + // } + // count_insert = 0; + // count_delete = 0; + // text_delete = ""; + // text_insert = ""; + // prevEqual = thisDiff; + // break; + // } + // thisDiff = pointer.hasNext() ? pointer.next() : null; + // } + // if (diffs.getLast().text.length() == 0) { + // diffs.removeLast(); // Remove the dummy entry at the end. + // } + // + // /* + // * Second pass: look for single edits surrounded on both sides by equalities + // * which can be shifted sideways to eliminate an equality. + // * e.g: ABAC -> ABAC + // */ + // boolean changes = false; + // // Create a new iterator at the start. + // // (As opposed to walking the current one back.) + // pointer = diffs.listIterator(); + // Diff prevDiff = pointer.hasNext() ? pointer.next() : null; + // thisDiff = pointer.hasNext() ? pointer.next() : null; + // Diff nextDiff = pointer.hasNext() ? pointer.next() : null; + // // Intentionally ignore the first and last element (don't need checking). + // while (nextDiff != null) { + // if (prevDiff.operation == Operation.EQUAL && + // nextDiff.operation == Operation.EQUAL) { + // // This is a single edit surrounded by equalities. + // if (thisDiff.text.endsWith(prevDiff.text)) { + // // Shift the edit over the previous equality. + // thisDiff.text = prevDiff.text + // + thisDiff.text.substring(0, thisDiff.text.length() + // - prevDiff.text.length()); + // nextDiff.text = prevDiff.text + nextDiff.text; + // pointer.previous(); // Walk past nextDiff. + // pointer.previous(); // Walk past thisDiff. + // pointer.previous(); // Walk past prevDiff. + // pointer.remove(); // Delete prevDiff. + // pointer.next(); // Walk past thisDiff. + // thisDiff = pointer.next(); // Walk past nextDiff. + // nextDiff = pointer.hasNext() ? pointer.next() : null; + // changes = true; + // } else if (thisDiff.text.startsWith(nextDiff.text)) { + // // Shift the edit over the next equality. + // prevDiff.text += nextDiff.text; + // thisDiff.text = thisDiff.text.substring(nextDiff.text.length()) + // + nextDiff.text; + // pointer.remove(); // Delete nextDiff. + // nextDiff = pointer.hasNext() ? pointer.next() : null; + // changes = true; + // } + // } + // prevDiff = thisDiff; + // thisDiff = nextDiff; + // nextDiff = pointer.hasNext() ? pointer.next() : null; + // } + // // If shifts were made, the diff needs reordering and another shift sweep. + // if (changes) { + // diff_cleanupMerge(diffs); + // } + // } + // + // /** + // * loc is a location in text1, compute and return the equivalent location in + // * text2. + // * e.g. "The cat" vs "The big cat", 1->1, 5->8 + // * + // * @param diffs LinkedList of Diff objects. + // * @param loc Location within text1. + // * @return Location within text2. + // */ + // public int diff_xIndex(LinkedList diffs, int loc) { + // int chars1 = 0; + // int chars2 = 0; + // int last_chars1 = 0; + // int last_chars2 = 0; + // Diff lastDiff = null; + // for (Diff aDiff : diffs) { + // if (aDiff.operation != Operation.INSERT) { + // // Equality or deletion. + // chars1 += aDiff.text.length(); + // } + // if (aDiff.operation != Operation.DELETE) { + // // Equality or insertion. + // chars2 += aDiff.text.length(); + // } + // if (chars1 > loc) { + // // Overshot the location. + // lastDiff = aDiff; + // break; + // } + // last_chars1 = chars1; + // last_chars2 = chars2; + // } + // if (lastDiff != null && lastDiff.operation == Operation.DELETE) { + // // The location was deleted. + // return last_chars2; + // } + // // Add the remaining character length. + // return last_chars2 + (loc - last_chars1); + // } + // + // /** + // * Convert a Diff list into a pretty HTML report. + // * + // * @param diffs LinkedList of Diff objects. + // * @return HTML representation. + // */ + // public String diff_prettyHtml(LinkedList diffs) { + // StringBuilder html = new StringBuilder(); + // for (Diff aDiff : diffs) { + // String text = aDiff.text.replace("&", "&").replace("<", "<") + // .replace(">", ">").replace("\n", "¶
"); + // switch (aDiff.operation) { + // case INSERT: + // html.append("").append(text) + // .append(""); + // break; + // case DELETE: + // html.append("").append(text) + // .append(""); + // break; + // case EQUAL: + // html.append("").append(text).append(""); + // break; + // } + // } + // return html.toString(); + // } + // + // /** + // * Compute and return the source text (all equalities and deletions). + // * + // * @param diffs LinkedList of Diff objects. + // * @return Source text. + // */ + // public String diff_text1(LinkedList diffs) { + // StringBuilder text = new StringBuilder(); + // for (Diff aDiff : diffs) { + // if (aDiff.operation != Operation.INSERT) { + // text.append(aDiff.text); + // } + // } + // return text.toString(); + // } + // + // /** + // * Compute and return the destination text (all equalities and insertions). + // * + // * @param diffs LinkedList of Diff objects. + // * @return Destination text. + // */ + // public String diff_text2(LinkedList diffs) { + // StringBuilder text = new StringBuilder(); + // for (Diff aDiff : diffs) { + // if (aDiff.operation != Operation.DELETE) { + // text.append(aDiff.text); + // } + // } + // return text.toString(); + // } + // + // /** + // * Compute the Levenshtein distance; the number of inserted, deleted or + // * substituted characters. + // * + // * @param diffs LinkedList of Diff objects. + // * @return Number of changes. + // */ + // public int diff_levenshtein(LinkedList diffs) { + // int levenshtein = 0; + // int insertions = 0; + // int deletions = 0; + // for (Diff aDiff : diffs) { + // switch (aDiff.operation) { + // case INSERT: + // insertions += aDiff.text.length(); + // break; + // case DELETE: + // deletions += aDiff.text.length(); + // break; + // case EQUAL: + // // A deletion and an insertion is one substitution. + // levenshtein += Math.max(insertions, deletions); + // insertions = 0; + // deletions = 0; + // break; + // } + // } + // levenshtein += Math.max(insertions, deletions); + // return levenshtein; + // } + // + // /** + // * Crush the diff into an encoded string which describes the operations + // * required to transform text1 into text2. + // * E.g. =3\t-2\t+ing -> Keep 3 chars, delete 2 chars, insert 'ing'. + // * Operations are tab-separated. Inserted text is escaped using %xx notation. + // * + // * @param diffs Array of Diff objects. + // * @return Delta text. + // */ + // public String diff_toDelta(LinkedList diffs) { + // StringBuilder text = new StringBuilder(); + // for (Diff aDiff : diffs) { + // switch (aDiff.operation) { + // case INSERT: + // text.append("+").append(URLEncoder.encode(aDiff.text, + // StandardCharsets.UTF_8) + // .replace('+', ' ')).append("\t"); + // break; + // case DELETE: + // text.append("-").append(aDiff.text.length()).append("\t"); + // break; + // case EQUAL: + // text.append("=").append(aDiff.text.length()).append("\t"); + // break; + // } + // } + // String delta = text.toString(); + // if (delta.length() != 0) { + // // Strip off trailing tab character. + // delta = delta.substring(0, delta.length() - 1); + // delta = unescapeForEncodeUriCompatability(delta); + // } + // return delta; + // } + // + // /** + // * Given the original text1, and an encoded string which describes the + // * operations required to transform text1 into text2, compute the full diff. + // * + // * @param text1 Source string for the diff. + // * @param delta Delta text. + // * @return Array of Diff objects or null if invalid. + // * @throws IllegalArgumentException If invalid input. + // */ + // public LinkedList diff_fromDelta(String text1, String delta) + // throws IllegalArgumentException { + // LinkedList diffs = new LinkedList(); + // int pointer = 0; // Cursor in text1 + // String[] tokens = delta.split("\t"); + // for (String token : tokens) { + // if (token.length() == 0) { + // // Blank tokens are ok (from a trailing \t). + // continue; + // } + // // Each token begins with a one character parameter which specifies the + // // operation of this token (delete, insert, equality). + // String param = token.substring(1); + // switch (token.charAt(0)) { + // case '+': + // // decode would change all "+" to " " + // param = param.replace("+", "%2B"); + // try { + // param = URLDecoder.decode(param, StandardCharsets.UTF_8); + // } catch (IllegalArgumentException e) { + // // Malformed URI sequence. + // throw new IllegalArgumentException( + // "Illegal escape in diff_fromDelta: " + param, e); + // } + // diffs.add(new Diff(Operation.INSERT, param)); + // break; + // case '-': + // // Fall through. + // case '=': + // int n; + // try { + // n = Integer.parseInt(param); + // } catch (NumberFormatException e) { + // throw new IllegalArgumentException( + // "Invalid number in diff_fromDelta: " + param, e); + // } + // if (n < 0) { + // throw new IllegalArgumentException( + // "Negative number in diff_fromDelta: " + param); + // } + // String text; + // try { + // text = text1.substring(pointer, pointer += n); + // } catch (StringIndexOutOfBoundsException e) { + // throw new IllegalArgumentException("Delta length (" + pointer + // + ") larger than source text length (" + text1.length() + // + ").", e); + // } + // if (token.charAt(0) == '=') { + // diffs.add(new Diff(Operation.EQUAL, text)); + // } else { + // diffs.add(new Diff(Operation.DELETE, text)); + // } + // break; + // default: + // // Anything else is an error. + // throw new IllegalArgumentException( + // "Invalid diff operation in diff_fromDelta: " + token.charAt(0)); + // } + // } + // if (pointer != text1.length()) { + // throw new IllegalArgumentException("Delta length (" + pointer + // + ") smaller than source text length (" + text1.length() + ")."); + // } + // return diffs; + // } + // + // /** + // * Locate the best instance of 'pattern' in 'text' near 'loc'. + // * Returns -1 if no match found. + // * + // * @param text The text to search. + // * @param pattern The pattern to search for. + // * @param loc The location to search around. + // * @return Best match index or -1. + // */ + // public int match_main(String text, String pattern, int loc) { + // // Check for null inputs. + // if (text == null || pattern == null) { + // throw new IllegalArgumentException("Null inputs. (match_main)"); + // } + // + // loc = Math.max(0, Math.min(loc, text.length())); + // if (text.equals(pattern)) { + // // Shortcut (potentially not guaranteed by the algorithm) + // return 0; + // } else if (text.length() == 0) { + // // Nothing to match. + // return -1; + // } else if (loc + pattern.length() <= text.length() + // && text.startsWith(pattern, loc)) { + // // Perfect match at the perfect spot! (Includes case of null pattern) + // return loc; + // } else { + // // Do a fuzzy compare. + // return match_bitap(text, pattern, loc); + // } + // } + // + // + // // MATCH FUNCTIONS + // + // /** + // * Locate the best instance of 'pattern' in 'text' near 'loc' using the + // * Bitap algorithm. Returns -1 if no match found. + // * + // * @param text The text to search. + // * @param pattern The pattern to search for. + // * @param loc The location to search around. + // * @return Best match index or -1. + // */ + // protected int match_bitap(String text, String pattern, int loc) { + // assert (Match_MaxBits == 0 || pattern.length() <= Match_MaxBits) + // : "Pattern too long for this application."; + // + // // Initialise the alphabet. + // Map s = match_alphabet(pattern); + // + // // Highest score beyond which we give up. + // double score_threshold = Match_Threshold; + // // Is there a nearby exact match? (speedup) + // int best_loc = text.indexOf(pattern, loc); + // if (best_loc != -1) { + // score_threshold = Math.min(match_bitapScore(0, best_loc, loc, pattern), + // score_threshold); + // // What about in the other direction? (speedup) + // best_loc = text.lastIndexOf(pattern, loc + pattern.length()); + // if (best_loc != -1) { + // score_threshold = Math.min(match_bitapScore(0, best_loc, loc, pattern), + // score_threshold); + // } + // } + // + // // Initialise the bit arrays. + // int matchmask = 1 << (pattern.length() - 1); + // best_loc = -1; + // + // int bin_min, bin_mid; + // int bin_max = pattern.length() + text.length(); + // // Empty initialization added to appease Java compiler. + // int[] last_rd = new int[0]; + // for (int d = 0; d < pattern.length(); d++) { + // // Scan for the best match; each iteration allows for one more error. + // // Run a binary search to determine how far from 'loc' we can stray at + // // this error level. + // bin_min = 0; + // bin_mid = bin_max; + // while (bin_min < bin_mid) { + // if (match_bitapScore(d, loc + bin_mid, loc, pattern) + // <= score_threshold) { + // bin_min = bin_mid; + // } else { + // bin_max = bin_mid; + // } + // bin_mid = (bin_max - bin_min) / 2 + bin_min; + // } + // // Use the result from this iteration as the maximum for the next. + // bin_max = bin_mid; + // int start = Math.max(1, loc - bin_mid + 1); + // int finish = Math.min(loc + bin_mid, text.length()) + pattern.length(); + // + // int[] rd = new int[finish + 2]; + // rd[finish + 1] = (1 << d) - 1; + // for (int j = finish; j >= start; j--) { + // int charMatch; + // if (text.length() <= j - 1 || !s.containsKey(text.charAt(j - 1))) { + // // Out of range. + // charMatch = 0; + // } else { + // charMatch = s.get(text.charAt(j - 1)); + // } + // if (d == 0) { + // // First pass: exact match. + // rd[j] = ((rd[j + 1] << 1) | 1) & charMatch; + // } else { + // // Subsequent passes: fuzzy match. + // rd[j] = (((rd[j + 1] << 1) | 1) & charMatch) + // | (((last_rd[j + 1] | last_rd[j]) << 1) | 1) | last_rd[j + 1]; + // } + // if ((rd[j] & matchmask) != 0) { + // double score = match_bitapScore(d, j - 1, loc, pattern); + // // This match will almost certainly be better than any existing + // // match. But check anyway. + // if (score <= score_threshold) { + // // Told you so. + // score_threshold = score; + // best_loc = j - 1; + // if (best_loc > loc) { + // // When passing loc, don't exceed our current distance from loc. + // start = Math.max(1, 2 * loc - best_loc); + // } else { + // // Already passed loc, downhill from here on in. + // break; + // } + // } + // } + // } + // if (match_bitapScore(d + 1, loc, loc, pattern) > score_threshold) { + // // No hope for a (better) match at greater error levels. + // break; + // } + // last_rd = rd; + // } + // return best_loc; + // } + // + // /** + // * Compute and return the score for a match with e errors and x location. + // * + // * @param e Number of errors in match. + // * @param x Location of match. + // * @param loc Expected location of match. + // * @param pattern Pattern being sought. + // * @return Overall score for match (0.0 = good, 1.0 = bad). + // */ + // private double match_bitapScore(int e, int x, int loc, String pattern) { + // float accuracy = (float) e / pattern.length(); + // int proximity = Math.abs(loc - x); + // if (Match_Distance == 0) { + // // Dodge divide by zero error. + // return proximity == 0 ? accuracy : 1.0; + // } + // return accuracy + (proximity / (float) Match_Distance); + // } + // + // /** + // * Initialise the alphabet for the Bitap algorithm. + // * + // * @param pattern The text to encode. + // * @return Hash of character locations. + // */ + // protected Map match_alphabet(String pattern) { + // Map s = new HashMap(); + // char[] char_pattern = pattern.toCharArray(); + // for (char c : char_pattern) { + // s.put(c, 0); + // } + // int i = 0; + // for (char c : char_pattern) { + // s.put(c, s.get(c) | (1 << (pattern.length() - i - 1))); + // i++; + // } + // return s; + // } + // + // /** + // * Increase the context until it is unique, + // * but don't let the pattern expand beyond Match_MaxBits. + // * + // * @param patch The patch to grow. + // * @param text Source text. + // */ + // protected void patch_addContext(Patch patch, String text) { + // if (text.length() == 0) { + // return; + // } + // String pattern = text.substring(patch.start2, patch.start2 + patch.length1); + // int padding = 0; + // + // // Look for the first and last matches of pattern in text. If two different + // // matches are found, increase the pattern length. + // while (text.indexOf(pattern) != text.lastIndexOf(pattern) + // && pattern.length() < Match_MaxBits - Patch_Margin - Patch_Margin) { + // padding += Patch_Margin; + // pattern = text.substring(Math.max(0, patch.start2 - padding), + // Math.min(text.length(), patch.start2 + patch.length1 + padding)); + // } + // // Add one chunk for good luck. + // padding += Patch_Margin; + // + // // Add the prefix. + // String prefix = text.substring(Math.max(0, patch.start2 - padding), + // patch.start2); + // if (prefix.length() != 0) { + // patch.diffs.addFirst(new Diff(Operation.EQUAL, prefix)); + // } + // // Add the suffix. + // String suffix = text.substring(patch.start2 + patch.length1, + // Math.min(text.length(), patch.start2 + patch.length1 + padding)); + // if (suffix.length() != 0) { + // patch.diffs.addLast(new Diff(Operation.EQUAL, suffix)); + // } + // + // // Roll back the start points. + // patch.start1 -= prefix.length(); + // patch.start2 -= prefix.length(); + // // Extend the lengths. + // patch.length1 += prefix.length() + suffix.length(); + // patch.length2 += prefix.length() + suffix.length(); + // } + // + // + // // PATCH FUNCTIONS + // + // /** + // * Compute a list of patches to turn text1 into text2. + // * A set of diffs will be computed. + // * + // * @param text1 Old text. + // * @param text2 New text. + // * @return LinkedList of Patch objects. + // */ + // public LinkedList patch_make(String text1, String text2) { + // if (text1 == null || text2 == null) { + // throw new IllegalArgumentException("Null inputs. (patch_make)"); + // } + // // No diffs provided, compute our own. + // LinkedList diffs = diff_main(text1, text2, true); + // if (diffs.size() > 2) { + // diff_cleanupSemantic(diffs); + // diff_cleanupEfficiency(diffs); + // } + // return patch_make(text1, diffs); + // } + // + // /** + // * Compute a list of patches to turn text1 into text2. + // * text1 will be derived from the provided diffs. + // * + // * @param diffs Array of Diff objects for text1 to text2. + // * @return LinkedList of Patch objects. + // */ + // public LinkedList patch_make(LinkedList diffs) { + // if (diffs == null) { + // throw new IllegalArgumentException("Null inputs. (patch_make)"); + // } + // // No origin string provided, compute our own. + // String text1 = diff_text1(diffs); + // return patch_make(text1, diffs); + // } + // + // /** + // * Compute a list of patches to turn text1 into text2. + // * text2 is ignored, diffs are the delta between text1 and text2. + // * + // * @param text1 Old text + // * @param text2 Ignored. + // * @param diffs Array of Diff objects for text1 to text2. + // * @return LinkedList of Patch objects. + // * @deprecated Prefer patch_make(String text1, LinkedList diffs). + // */ + // public LinkedList patch_make(String text1, String text2, + // LinkedList diffs) { + // return patch_make(text1, diffs); + // } + // + // /** + // * Compute a list of patches to turn text1 into text2. + // * text2 is not provided, diffs are the delta between text1 and text2. + // * + // * @param text1 Old text. + // * @param diffs Array of Diff objects for text1 to text2. + // * @return LinkedList of Patch objects. + // */ + // public LinkedList patch_make(String text1, LinkedList diffs) { + // if (text1 == null || diffs == null) { + // throw new IllegalArgumentException("Null inputs. (patch_make)"); + // } + // + // LinkedList patches = new LinkedList(); + // if (diffs.isEmpty()) { + // return patches; // Get rid of the null case. + // } + // Patch patch = new Patch(); + // int char_count1 = 0; // Number of characters into the text1 string. + // int char_count2 = 0; // Number of characters into the text2 string. + // // Start with text1 (prepatch_text) and apply the diffs until we arrive at + // // text2 (postpatch_text). We recreate the patches one by one to determine + // // context info. + // String prepatch_text = text1; + // String postpatch_text = text1; + // for (Diff aDiff : diffs) { + // if (patch.diffs.isEmpty() && aDiff.operation != Operation.EQUAL) { + // // A new patch starts here. + // patch.start1 = char_count1; + // patch.start2 = char_count2; + // } + // + // switch (aDiff.operation) { + // case INSERT: + // patch.diffs.add(aDiff); + // patch.length2 += aDiff.text.length(); + // postpatch_text = postpatch_text.substring(0, char_count2) + // + aDiff.text + postpatch_text.substring(char_count2); + // break; + // case DELETE: + // patch.length1 += aDiff.text.length(); + // patch.diffs.add(aDiff); + // postpatch_text = postpatch_text.substring(0, char_count2) + // + postpatch_text.substring(char_count2 + aDiff.text.length()); + // break; + // case EQUAL: + // if (aDiff.text.length() <= 2 * Patch_Margin + // && !patch.diffs.isEmpty() && aDiff != diffs.getLast()) { + // // Small equality inside a patch. + // patch.diffs.add(aDiff); + // patch.length1 += aDiff.text.length(); + // patch.length2 += aDiff.text.length(); + // } + // + // if (aDiff.text.length() >= 2 * Patch_Margin) { + // // Time for a new patch. + // if (!patch.diffs.isEmpty()) { + // patch_addContext(patch, prepatch_text); + // patches.add(patch); + // patch = new Patch(); + // // Unlike Unidiff, our patch lists have a rolling context. + // // http://code.google.com/p/google-diff-match-patch/wiki/Unidiff + // // Update prepatch text & pos to reflect the application of the + // // just completed patch. + // prepatch_text = postpatch_text; + // char_count1 = char_count2; + // } + // } + // break; + // } + // + // // Update the current character count. + // if (aDiff.operation != Operation.INSERT) { + // char_count1 += aDiff.text.length(); + // } + // if (aDiff.operation != Operation.DELETE) { + // char_count2 += aDiff.text.length(); + // } + // } + // // Pick up the leftover patch if not empty. + // if (!patch.diffs.isEmpty()) { + // patch_addContext(patch, prepatch_text); + // patches.add(patch); + // } + // + // return patches; + // } + // + // /** + // * Given an array of patches, return another array that is identical. + // * + // * @param patches Array of Patch objects. + // * @return Array of Patch objects. + // */ + // public LinkedList patch_deepCopy(LinkedList patches) { + // LinkedList patchesCopy = new LinkedList(); + // for (Patch aPatch : patches) { + // Patch patchCopy = new Patch(); + // for (Diff aDiff : aPatch.diffs) { + // Diff diffCopy = new Diff(aDiff.operation, aDiff.text); + // patchCopy.diffs.add(diffCopy); + // } + // patchCopy.start1 = aPatch.start1; + // patchCopy.start2 = aPatch.start2; + // patchCopy.length1 = aPatch.length1; + // patchCopy.length2 = aPatch.length2; + // patchesCopy.add(patchCopy); + // } + // return patchesCopy; + // } + // + // /** + // * Merge a set of patches onto the text. Return a patched text, as well + // * as an array of true/false values indicating which patches were applied. + // * + // * @param patches Array of Patch objects + // * @param text Old text. + // * @return Two element Object array, containing the new text and an array of + // * boolean values. + // */ + // public Object[] patch_apply(LinkedList patches, String text) { + // if (patches.isEmpty()) { + // return new Object[]{text, new boolean[0]}; + // } + // + // // Deep copy the patches so that no changes are made to originals. + // patches = patch_deepCopy(patches); + // + // String nullPadding = patch_addPadding(patches); + // text = nullPadding + text + nullPadding; + // patch_splitMax(patches); + // + // int x = 0; + // // delta keeps track of the offset between the expected and actual location + // // of the previous patch. If there are patches expected at positions 10 and + // // 20, but the first patch was found at 12, delta is 2 and the second patch + // // has an effective expected position of 22. + // int delta = 0; + // boolean[] results = new boolean[patches.size()]; + // for (Patch aPatch : patches) { + // int expected_loc = aPatch.start2 + delta; + // String text1 = diff_text1(aPatch.diffs); + // int start_loc; + // int end_loc = -1; + // if (text1.length() > this.Match_MaxBits) { + // // patch_splitMax will only provide an oversized pattern in the case of + // // a monster delete. + // start_loc = match_main(text, + // text1.substring(0, this.Match_MaxBits), expected_loc); + // if (start_loc != -1) { + // end_loc = match_main(text, + // text1.substring(text1.length() - this.Match_MaxBits), + // expected_loc + text1.length() - this.Match_MaxBits); + // if (end_loc == -1 || start_loc >= end_loc) { + // // Can't find valid trailing context. Drop this patch. + // start_loc = -1; + // } + // } + // } else { + // start_loc = match_main(text, text1, expected_loc); + // } + // if (start_loc == -1) { + // // No match found. :( + // results[x] = false; + // // Subtract the delta for this failed patch from subsequent patches. + // delta -= aPatch.length2 - aPatch.length1; + // } else { + // // Found a match. :) + // results[x] = true; + // delta = start_loc - expected_loc; + // String text2; + // if (end_loc == -1) { + // text2 = text.substring(start_loc, + // Math.min(start_loc + text1.length(), text.length())); + // } else { + // text2 = text.substring(start_loc, + // Math.min(end_loc + this.Match_MaxBits, text.length())); + // } + // if (text1.equals(text2)) { + // // Perfect match, just shove the replacement text in. + // text = text.substring(0, start_loc) + diff_text2(aPatch.diffs) + // + text.substring(start_loc + text1.length()); + // } else { + // // Imperfect match. Run a diff to get a framework of equivalent + // // indices. + // LinkedList diffs = diff_main(text1, text2, false); + // if (text1.length() > this.Match_MaxBits + // && diff_levenshtein(diffs) / (float) text1.length() + // > this.Patch_DeleteThreshold) { + // // The end points match, but the content is unacceptably bad. + // results[x] = false; + // } else { + // diff_cleanupSemanticLossless(diffs); + // int index1 = 0; + // for (Diff aDiff : aPatch.diffs) { + // if (aDiff.operation != Operation.EQUAL) { + // int index2 = diff_xIndex(diffs, index1); + // if (aDiff.operation == Operation.INSERT) { + // // Insertion + // text = text.substring(0, start_loc + index2) + aDiff.text + // + text.substring(start_loc + index2); + // } else if (aDiff.operation == Operation.DELETE) { + // // Deletion + // text = text.substring(0, start_loc + index2) + // + text.substring(start_loc + diff_xIndex(diffs, + // index1 + aDiff.text.length())); + // } + // } + // if (aDiff.operation != Operation.DELETE) { + // index1 += aDiff.text.length(); + // } + // } + // } + // } + // } + // x++; + // } + // // Strip the padding off. + // text = text.substring(nullPadding.length(), text.length() + // - nullPadding.length()); + // return new Object[]{text, results}; + // } + // + // /** + // * Add some padding on text start and end so that edges can match something. + // * Intended to be called only from within patch_apply. + // * + // * @param patches Array of Patch objects. + // * @return The padding string added to each side. + // */ + // public String patch_addPadding(LinkedList patches) { + // short paddingLength = this.Patch_Margin; + // String nullPadding = ""; + // for (short x = 1; x <= paddingLength; x++) { + // nullPadding += String.valueOf((char) x); + // } + // + // // Bump all the patches forward. + // for (Patch aPatch : patches) { + // aPatch.start1 += paddingLength; + // aPatch.start2 += paddingLength; + // } + // + // // Add some padding on start of first diff. + // Patch patch = patches.getFirst(); + // LinkedList diffs = patch.diffs; + // if (diffs.isEmpty() || diffs.getFirst().operation != Operation.EQUAL) { + // // Add nullPadding equality. + // diffs.addFirst(new Diff(Operation.EQUAL, nullPadding)); + // patch.start1 -= paddingLength; // Should be 0. + // patch.start2 -= paddingLength; // Should be 0. + // patch.length1 += paddingLength; + // patch.length2 += paddingLength; + // } else if (paddingLength > diffs.getFirst().text.length()) { + // // Grow first equality. + // Diff firstDiff = diffs.getFirst(); + // int extraLength = paddingLength - firstDiff.text.length(); + // firstDiff.text = nullPadding.substring(firstDiff.text.length()) + // + firstDiff.text; + // patch.start1 -= extraLength; + // patch.start2 -= extraLength; + // patch.length1 += extraLength; + // patch.length2 += extraLength; + // } + // + // // Add some padding on end of last diff. + // patch = patches.getLast(); + // diffs = patch.diffs; + // if (diffs.isEmpty() || diffs.getLast().operation != Operation.EQUAL) { + // // Add nullPadding equality. + // diffs.addLast(new Diff(Operation.EQUAL, nullPadding)); + // patch.length1 += paddingLength; + // patch.length2 += paddingLength; + // } else if (paddingLength > diffs.getLast().text.length()) { + // // Grow last equality. + // Diff lastDiff = diffs.getLast(); + // int extraLength = paddingLength - lastDiff.text.length(); + // lastDiff.text += nullPadding.substring(0, extraLength); + // patch.length1 += extraLength; + // patch.length2 += extraLength; + // } + // + // return nullPadding; + // } + // + // /** + // * Look through the patches and break up any which are longer than the + // * maximum limit of the match algorithm. + // * Intended to be called only from within patch_apply. + // * + // * @param patches LinkedList of Patch objects. + // */ + // public void patch_splitMax(LinkedList patches) { + // short patch_size = Match_MaxBits; + // String precontext, postcontext; + // Patch patch; + // int start1, start2; + // boolean empty; + // Operation diff_type; + // String diff_text; + // ListIterator pointer = patches.listIterator(); + // Patch bigpatch = pointer.hasNext() ? pointer.next() : null; + // while (bigpatch != null) { + // if (bigpatch.length1 <= Match_MaxBits) { + // bigpatch = pointer.hasNext() ? pointer.next() : null; + // continue; + // } + // // Remove the big old patch. + // pointer.remove(); + // start1 = bigpatch.start1; + // start2 = bigpatch.start2; + // precontext = ""; + // while (!bigpatch.diffs.isEmpty()) { + // // Create one of several smaller patches. + // patch = new Patch(); + // empty = true; + // patch.start1 = start1 - precontext.length(); + // patch.start2 = start2 - precontext.length(); + // if (precontext.length() != 0) { + // patch.length1 = patch.length2 = precontext.length(); + // patch.diffs.add(new Diff(Operation.EQUAL, precontext)); + // } + // while (!bigpatch.diffs.isEmpty() + // && patch.length1 < patch_size - Patch_Margin) { + // diff_type = bigpatch.diffs.getFirst().operation; + // diff_text = bigpatch.diffs.getFirst().text; + // if (diff_type == Operation.INSERT) { + // // Insertions are harmless. + // patch.length2 += diff_text.length(); + // start2 += diff_text.length(); + // patch.diffs.addLast(bigpatch.diffs.removeFirst()); + // empty = false; + // } else if (diff_type == Operation.DELETE && patch.diffs.size() == 1 + // && patch.diffs.getFirst().operation == Operation.EQUAL + // && diff_text.length() > 2 * patch_size) { + // // This is a large deletion. Let it pass in one chunk. + // patch.length1 += diff_text.length(); + // start1 += diff_text.length(); + // empty = false; + // patch.diffs.add(new Diff(diff_type, diff_text)); + // bigpatch.diffs.removeFirst(); + // } else { + // // Deletion or equality. Only take as much as we can stomach. + // diff_text = diff_text.substring(0, Math.min(diff_text.length(), + // patch_size - patch.length1 - Patch_Margin)); + // patch.length1 += diff_text.length(); + // start1 += diff_text.length(); + // if (diff_type == Operation.EQUAL) { + // patch.length2 += diff_text.length(); + // start2 += diff_text.length(); + // } else { + // empty = false; + // } + // patch.diffs.add(new Diff(diff_type, diff_text)); + // if (diff_text.equals(bigpatch.diffs.getFirst().text)) { + // bigpatch.diffs.removeFirst(); + // } else { + // bigpatch.diffs.getFirst().text = bigpatch.diffs.getFirst().text + // .substring(diff_text.length()); + // } + // } + // } + // // Compute the head context for the next patch. + // precontext = diff_text2(patch.diffs); + // precontext = precontext.substring(Math.max(0, precontext.length() + // - Patch_Margin)); + // // Append the end context for this patch. + // if (diff_text1(bigpatch.diffs).length() > Patch_Margin) { + // postcontext = diff_text1(bigpatch.diffs).substring(0, Patch_Margin); + // } else { + // postcontext = diff_text1(bigpatch.diffs); + // } + // if (postcontext.length() != 0) { + // patch.length1 += postcontext.length(); + // patch.length2 += postcontext.length(); + // if (!patch.diffs.isEmpty() + // && patch.diffs.getLast().operation == Operation.EQUAL) { + // patch.diffs.getLast().text += postcontext; + // } else { + // patch.diffs.add(new Diff(Operation.EQUAL, postcontext)); + // } + // } + // if (!empty) { + // pointer.add(patch); + // } + // } + // bigpatch = pointer.hasNext() ? pointer.next() : null; + // } + // } + // + // /** + // * Take a list of patches and return a textual representation. + // * + // * @param patches List of Patch objects. + // * @return Text representation of patches. + // */ + // public String patch_toText(List patches) { + // StringBuilder text = new StringBuilder(); + // for (Patch aPatch : patches) { + // text.append(aPatch); + // } + // return text.toString(); + // } + // + // /** + // * Parse a textual representation of patches and return a List of Patch + // * objects. + // * + // * @param textline Text representation of patches. + // * @return List of Patch objects. + // * @throws IllegalArgumentException If invalid input. + // */ + // public List patch_fromText(String textline) + // throws IllegalArgumentException { + // List patches = new LinkedList(); + // if (textline.length() == 0) { + // return patches; + // } + // List textList = Arrays.asList(textline.split("\n")); + // LinkedList text = new LinkedList(textList); + // Patch patch; + // Pattern patchHeader + // = Pattern.compile("^@@ -(\\d+),?(\\d*) \\+(\\d+),?(\\d*) @@$"); + // Matcher m; + // char sign; + // String line; + // while (!text.isEmpty()) { + // m = patchHeader.matcher(text.getFirst()); + // if (!m.matches()) { + // throw new IllegalArgumentException( + // "Invalid patch string: " + text.getFirst()); + // } + // patch = new Patch(); + // patches.add(patch); + // patch.start1 = Integer.parseInt(m.group(1)); + // if (m.group(2).length() == 0) { + // patch.start1--; + // patch.length1 = 1; + // } else if (m.group(2).equals("0")) { + // patch.length1 = 0; + // } else { + // patch.start1--; + // patch.length1 = Integer.parseInt(m.group(2)); + // } + // + // patch.start2 = Integer.parseInt(m.group(3)); + // if (m.group(4).length() == 0) { + // patch.start2--; + // patch.length2 = 1; + // } else if (m.group(4).equals("0")) { + // patch.length2 = 0; + // } else { + // patch.start2--; + // patch.length2 = Integer.parseInt(m.group(4)); + // } + // text.removeFirst(); + // + // while (!text.isEmpty()) { + // try { + // sign = text.getFirst().charAt(0); + // } catch (IndexOutOfBoundsException e) { + // // Blank line? Whatever. + // text.removeFirst(); + // continue; + // } + // line = text.getFirst().substring(1); + // line = line.replace("+", "%2B"); // decode would change all "+" to " " + // try { + // line = URLDecoder.decode(line, StandardCharsets.UTF_8); + // } catch (IllegalArgumentException e) { + // // Malformed URI sequence. + // throw new IllegalArgumentException( + // "Illegal escape in patch_fromText: " + line, e); + // } + // if (sign == '-') { + // // Deletion. + // patch.diffs.add(new Diff(Operation.DELETE, line)); + // } else if (sign == '+') { + // // Insertion. + // patch.diffs.add(new Diff(Operation.INSERT, line)); + // } else if (sign == ' ') { + // // Minor equality. + // patch.diffs.add(new Diff(Operation.EQUAL, line)); + // } else if (sign == '@') { + // // Start of next patch. + // break; + // } else { + // // WTF? + // throw new IllegalArgumentException( + // "Invalid patch mode '" + sign + "' in: " + line); + // } + // text.removeFirst(); + // } + // } + // return patches; + // } + // + // /** + // * The data structure representing a diff is a Linked list of Diff objects: + // * {Diff(Operation.DELETE, "Hello"), Diff(Operation.INSERT, "Goodbye"), + // * Diff(Operation.EQUAL, " world.")} + // * which means: delete "Hello", add "Goodbye" and keep " world." + // */ + // public enum Operation { + // DELETE, INSERT, EQUAL + // } + // + // /** + // * Internal class for returning results from diff_linesToChars(). + // * Other less paranoid languages just use a three-element array. + // */ + // protected static class LinesToCharsResult { + // protected String chars1; + // protected String chars2; + // protected List lineArray; + // + // protected LinesToCharsResult(String chars1, String chars2, + // List lineArray) { + // this.chars1 = chars1; + // this.chars2 = chars2; + // this.lineArray = lineArray; + // } + // } + // + // /** + // * Class representing one diff operation. + // */ + // public static class Diff { + // /** + // * One of: INSERT, DELETE or EQUAL. + // */ + // public Operation operation; + // /** + // * The text associated with this diff operation. + // */ + // public String text; + // + // /** + // * Constructor. Initializes the diff with the provided values. + // * + // * @param operation One of INSERT, DELETE or EQUAL. + // * @param text The text being applied. + // */ + // public Diff(Operation operation, String text) { + // // Construct a diff with the specified operation and text. + // this.operation = operation; + // this.text = text; + // } + // + // /** + // * Display a human-readable version of this Diff. + // * + // * @return text version. + // */ + // public String toString() { + // String prettyText = this.text.replace('\n', '\u00b6'); + // return "Diff(" + this.operation + ",\"" + prettyText + "\")"; + // } + // + // /** + // * Create a numeric hash value for a Diff. + // * This function is not used by DMP. + // * + // * @return Hash value. + // */ + // @Override + // public int hashCode() { + // final int prime = 31; + // int result = (operation == null) ? 0 : operation.hashCode(); + // result += prime * ((text == null) ? 0 : text.hashCode()); + // return result; + // } + // + // /** + // * Is this Diff equivalent to another Diff? + // * + // * @param obj Another Diff to compare against. + // * @return true or false. + // */ + // @Override + // public boolean equals(Object obj) { + // if (this == obj) { + // return true; + // } + // if (obj == null) { + // return false; + // } + // if (getClass() != obj.getClass()) { + // return false; + // } + // Diff other = (Diff) obj; + // if (operation != other.operation) { + // return false; + // } + // if (text == null) { + // return other.text == null; + // } else return text.equals(other.text); + // } + // } + // + // /** + // * Class representing one patch operation. + // */ + // public static class Patch { + // public LinkedList diffs; + // public int start1; + // public int start2; + // public int length1; + // public int length2; + // + // /** + // * Constructor. Initializes with an empty list of diffs. + // */ + // public Patch() { + // this.diffs = new LinkedList(); + // } + // + // /** + // * Emmulate GNU diff's format. + // * Header: @@ -382,8 +481,9 @@ + // * Indicies are printed as 1-based, not 0-based. + // * + // * @return The GNU diff string. + // */ + // public String toString() { + // String coords1, coords2; + // if (this.length1 == 0) { + // coords1 = this.start1 + ",0"; + // } else if (this.length1 == 1) { + // coords1 = Integer.toString(this.start1 + 1); + // } else { + // coords1 = (this.start1 + 1) + "," + this.length1; + // } + // if (this.length2 == 0) { + // coords2 = this.start2 + ",0"; + // } else if (this.length2 == 1) { + // coords2 = Integer.toString(this.start2 + 1); + // } else { + // coords2 = (this.start2 + 1) + "," + this.length2; + // } + // StringBuilder text = new StringBuilder(); + // text.append("@@ -").append(coords1).append(" +").append(coords2) + // .append(" @@\n"); + // // Escape the body of the patch with %xx notation. + // for (Diff aDiff : this.diffs) { + // switch (aDiff.operation) { + // case INSERT: + // text.append('+'); + // break; + // case DELETE: + // text.append('-'); + // break; + // case EQUAL: + // text.append(' '); + // break; + // } + // text.append(URLEncoder.encode(aDiff.text, StandardCharsets.UTF_8).replace('+', + // ' ')) + // .append("\n"); + // } + // return unescapeForEncodeUriCompatability(text.toString()); + // } + // } +} diff --git a/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/xsw9Test.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/xsw9Test.java new file mode 100644 index 0000000..4036e03 --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/helpers/xsw9Test.java @@ -0,0 +1,49 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2015 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt.samlraider.helpers; + +public class xsw9Test { + // public static void main(String[] args) throws SAXException, IOException { + // String originalAssertion = "http://9ac42dac-7dbd-45e8-b991-6411adcb12c2-idp.idocker.vuln.landhttp://9ac42dac-7dbd-45e8-b991-6411adcb12c2-idp.idocker.vuln.land\n" + + // " \n" + + // " \n" + + // " JY5IOzGUI5V6AKhabmyuuNGhrAI=i0WygaseCAnbUbYsMw5DCfIBOORVjdJDKio2j2YpJnUmJJIRPjko/GrbTZaxYB1vY705qF/uHyaZZ+iz8hdlJ+eIj5XOLACRqHdodNs1DR6fwZpF+2m3ehZWerDW+o0hy0+6dbSOP9RQpS0dpRhTgPXzJyVXt/VhMoL9Rj4hTO0=\n" + + // + // "MIICDDCCAXWgAwIBAgIJAOE4zZWhw6OpMA0GCSqGSIb3DQEBCwUAMB8xCzAJBgNVBAYTAkNIMRAwDgYDVQQDDAdzYW1sdWVsMB4XDTE1MDUyNjIyNTMwMFoXDTI1MDUyNTIyNTMwMFowHzELMAkGA1UEBhMCQ0gxEDAOBgNVBAMMB3NhbWx1ZWwwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALe4eeps2t6HwSsmtYnxlEhdBwIJ2c3+3hEONGVJckrmj6QlMCA9pkYnjk1Afm7JXscFiy2Bhlysanegnn9tjAqSmqe2PT6OhGXr+cmCJRGafby9+gFE0UccjtPW66TyjrcveB90/lJmyA86nc1iASFFwPeQMvJH+YyT9XXQ+ZhTAgMBAAGjUDBOMB0GA1UdDgQWBBRGqV8lPlWbcEikIcuB9vSzR9w6UzAfBgNVHSMEGDAWgBRGqV8lPlWbcEikIcuB9vSzR9w6UzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBABLnTkSJMXlrRz9lDUJc0tBQ5O4cnnITPDuQ3L87+K932aJ7+pVpHSEtBlWxC0Bz3o6wNUsmcvA5lALeRbUec5UR9HL+9oTsX9+7+aMmuLE/edEwrhfJSzwW3wLAVx8LeLk9NlRJInrXy536Mj/fpOQDJA+C9+WgTlugN2OcxE76_c76a5f1d5a33654d73f4a679be9a106bd23f329a24http://9ac42dac-7dbd-45e8-b991-6411adcb12c2-sp.idocker.vuln.landurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordusermemberuser"; + // XSWHelpers.MATCH_AND_REPLACE_MAP.put("user", "admin"); + // + // XMLHelpers xmlHelpers = new XMLHelpers(); + // XSWHelpers xswHelpers = new XSWHelpers(); + // + // Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(originalAssertion); + // xswHelpers.applyXSW("XSW9", document); + // } +} diff --git a/tool/src/main/java/samlraider/model/BurpCertificate.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/model/BurpCertificate.java similarity index 80% rename from tool/src/main/java/samlraider/model/BurpCertificate.java rename to tool/src/main/java/org/zaproxy/addon/migt/samlraider/model/BurpCertificate.java index 1192821..5ff8984 100644 --- a/tool/src/main/java/samlraider/model/BurpCertificate.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/model/BurpCertificate.java @@ -1,36 +1,23 @@ /* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) + * Zed Attack Proxy (ZAP) and its related class files. * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss + * ZAP is an HTTP/HTTPS proxy for assessing web application security. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * Copyright 2015 The ZAP Development Team * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * 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 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ - -package samlraider.model; - -import samlraider.helpers.CertificateHelper; -import org.bouncycastle.asn1.ASN1OctetString; -import org.bouncycastle.asn1.ASN1Primitive; -import org.bouncycastle.asn1.ASN1Sequence; -import org.bouncycastle.asn1.x509.Extension; -import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; +package org.zaproxy.addon.migt.samlraider.model; import java.io.IOException; import java.math.BigInteger; @@ -47,6 +34,12 @@ import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; +import org.zaproxy.addon.migt.samlraider.helpers.CertificateHelper; public class BurpCertificate { @@ -55,8 +48,7 @@ public class BurpCertificate { private PublicKey publicKey; private String source; - public BurpCertificate() { - } + public BurpCertificate() {} public BurpCertificate(X509Certificate certificate) { this.certificate = certificate; @@ -122,11 +114,13 @@ public String getSubject() { } public boolean hasExtensions() { - return (!certificate.getCriticalExtensionOIDs().isEmpty()) || (!certificate.getNonCriticalExtensionOIDs().isEmpty()); + return (!certificate.getCriticalExtensionOIDs().isEmpty()) + || (!certificate.getNonCriticalExtensionOIDs().isEmpty()); } public int getExtensionsCount() { - return certificate.getCriticalExtensionOIDs().size() + certificate.getNonCriticalExtensionOIDs().size(); + return certificate.getCriticalExtensionOIDs().size() + + certificate.getNonCriticalExtensionOIDs().size(); } public String getSignatureAlgorithm() { @@ -134,7 +128,8 @@ public String getSignatureAlgorithm() { } public String getSignature() { - return CertificateHelper.addHexColons(CertificateHelper.byteArrayToHex(certificate.getSignature())); + return CertificateHelper.addHexColons( + CertificateHelper.byteArrayToHex(certificate.getSignature())); } public String getPublicKeyAlgorithm() { @@ -214,7 +209,11 @@ public List getSubjectAlternativeNames() { } for (List i : certificate.getSubjectAlternativeNames()) { - subjectAlternativeNames.add(i.get(1) + " (" + ObjectIdentifier.getSubjectAlternativeNames((Integer) i.get(0)) + ")"); + subjectAlternativeNames.add( + i.get(1) + + " (" + + ObjectIdentifier.getSubjectAlternativeNames((Integer) i.get(0)) + + ")"); } } catch (CertificateParsingException e) { e.printStackTrace(); @@ -232,7 +231,11 @@ public List getIssuerAlternativeNames() { } for (List i : certificate.getIssuerAlternativeNames()) { - issuerAlternativeNames.add(i.get(1) + " (" + ObjectIdentifier.getSubjectAlternativeNames((Integer) i.get(0)) + ")"); + issuerAlternativeNames.add( + i.get(1) + + " (" + + ObjectIdentifier.getSubjectAlternativeNames((Integer) i.get(0)) + + ")"); } } catch (CertificateParsingException e) { e.printStackTrace(); @@ -269,7 +272,8 @@ public String getBasicConstraints() { basicConstraints = "CA: True. No path limit."; break; default: - basicConstraints = "CA: True. Path limit: " + certificate.getBasicConstraints() + "."; + basicConstraints = + "CA: True. Path limit: " + certificate.getBasicConstraints() + "."; } return basicConstraints; @@ -311,7 +315,8 @@ public String getAuthorityKeyIdentifier() { } // Very ugly hack to extract the SHA1 Hash (59 Hex Chars) from the // Extension :( - return CertificateHelper.addHexColons(CertificateHelper.byteArrayToHex(k)).substring(12, k.length * 3 - 1); + return CertificateHelper.addHexColons(CertificateHelper.byteArrayToHex(k)) + .substring(12, k.length * 3 - 1); } public String getSubjectKeyIdentifier() { @@ -339,14 +344,16 @@ public List getAllExtensions() { Set criticalExtensionOIDs = certificate.getCriticalExtensionOIDs(); if (criticalExtensionOIDs != null) { for (String i : criticalExtensionOIDs) { - allExtensions.add(new BurpCertificateExtension(i, true, certificate.getExtensionValue(i))); + allExtensions.add( + new BurpCertificateExtension(i, true, certificate.getExtensionValue(i))); } } Set nonCriticalExtensionOIDs = certificate.getNonCriticalExtensionOIDs(); if (nonCriticalExtensionOIDs != null) { for (String i : nonCriticalExtensionOIDs) { - allExtensions.add(new BurpCertificateExtension(i, false, certificate.getExtensionValue(i))); + allExtensions.add( + new BurpCertificateExtension(i, false, certificate.getExtensionValue(i))); } } @@ -366,5 +373,4 @@ public String toString() { return toString; } - -} \ No newline at end of file +} diff --git a/tool/src/main/java/org/zaproxy/addon/migt/samlraider/model/BurpCertificateExtension.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/model/BurpCertificateExtension.java new file mode 100644 index 0000000..cd89e47 --- /dev/null +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/model/BurpCertificateExtension.java @@ -0,0 +1,52 @@ +/* + * Zed Attack Proxy (ZAP) and its related class files. + * + * ZAP is an HTTP/HTTPS proxy for assessing web application security. + * + * Copyright 2015 The ZAP Development Team + * + * 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. + */ +package org.zaproxy.addon.migt.samlraider.model; + +import org.zaproxy.addon.migt.samlraider.helpers.CertificateHelper; + +public class BurpCertificateExtension { + private final String oid; + private final boolean isCritical; + private final byte[] extensionValue; + + public BurpCertificateExtension(String oid, boolean isCritical, byte[] extensionValue) { + this.oid = oid; + this.isCritical = isCritical; + this.extensionValue = extensionValue; + } + + public String getOid() { + return oid; + } + + public boolean isCritical() { + return isCritical; + } + + public byte[] getExtensionValue() { + return extensionValue; + } + + public String toString() { + return oid + + (isCritical ? " (Critical): " : " (Not critical): ") + + CertificateHelper.byteArrayToHex(extensionValue); + } +} diff --git a/tool/src/main/java/samlraider/model/BurpCertificateStore.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/model/BurpCertificateStore.java similarity index 72% rename from tool/src/main/java/samlraider/model/BurpCertificateStore.java rename to tool/src/main/java/org/zaproxy/addon/migt/samlraider/model/BurpCertificateStore.java index 1da7683..ce7c4e5 100644 --- a/tool/src/main/java/samlraider/model/BurpCertificateStore.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/model/BurpCertificateStore.java @@ -1,36 +1,30 @@ /* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) + * Zed Attack Proxy (ZAP) and its related class files. * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss + * ZAP is an HTTP/HTTPS proxy for assessing web application security. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * Copyright 2015 The ZAP Development Team * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * 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 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ +package org.zaproxy.addon.migt.samlraider.model; -package samlraider.model; - -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.TreeNode; import java.util.Collections; import java.util.Enumeration; import java.util.LinkedList; import java.util.List; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreeNode; public class BurpCertificateStore { @@ -50,8 +44,8 @@ public void addCertificate(BurpCertificate burpCertificate) { } /** - * Adds a complete certificate chain to the store. The top certificate of - * the chain is directly under the root node. + * Adds a complete certificate chain to the store. The top certificate of the chain is directly + * under the root node. * * @param burpCertificateChain to add */ @@ -72,8 +66,7 @@ public void addCertificateChain(List burpCertificateChain) { } /** - * Deletes a certificate from the store. It can be placed anywhere in the - * tree. + * Deletes a certificate from the store. It can be placed anywhere in the tree. * * @param burpCertificate to remove */ @@ -129,4 +122,4 @@ public List getBurpCertificatesWithPrivateKey() { } return certificatesWithPrivateKey; } -} \ No newline at end of file +} diff --git a/tool/src/main/java/samlraider/model/ObjectIdentifier.java b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/model/ObjectIdentifier.java similarity index 86% rename from tool/src/main/java/samlraider/model/ObjectIdentifier.java rename to tool/src/main/java/org/zaproxy/addon/migt/samlraider/model/ObjectIdentifier.java index 3b425da..aec781c 100644 --- a/tool/src/main/java/samlraider/model/ObjectIdentifier.java +++ b/tool/src/main/java/org/zaproxy/addon/migt/samlraider/model/ObjectIdentifier.java @@ -1,36 +1,33 @@ /* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) + * Zed Attack Proxy (ZAP) and its related class files. * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss + * ZAP is an HTTP/HTTPS proxy for assessing web application security. * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: + * Copyright 2015 The ZAP Development Team * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. + * 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 * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * 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. */ +package org.zaproxy.addon.migt.samlraider.model; -package samlraider.model; - +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.KeyPurposeId; import org.bouncycastle.jce.X509KeyUsage; -import java.util.*; - public final class ObjectIdentifier { private static final Map extensionsMap; @@ -152,7 +149,6 @@ public final class ObjectIdentifier { supportedExtensions.add("2.5.29.18"); // IssuerAlternativeName supportedExtensions.add("2.5.29.14"); // SubjectKeyIdentifier supportedExtensions.add("2.5.29.35"); // AuthorityKeyIdentifier - } public static String getExtension(String s) { diff --git a/tool/src/main/java/samlraider/application/SamlTabController.java b/tool/src/main/java/samlraider/application/SamlTabController.java deleted file mode 100644 index 6c256ad..0000000 --- a/tool/src/main/java/samlraider/application/SamlTabController.java +++ /dev/null @@ -1,790 +0,0 @@ -/* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) - * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package samlraider.application; - -import burp.*; -import samlraider.gui.SamlMain; -import samlraider.gui.SamlPanelInfo; -import samlraider.gui.SignatureHelpWindow; -import samlraider.gui.XSWHelpWindow; -import samlraider.helpers.HTTPHelpers; -import samlraider.helpers.XMLHelpers; -import samlraider.helpers.XSWHelpers; -import samlraider.model.BurpCertificate; -import org.w3c.dom.*; -import org.xml.sax.SAXException; - -import javax.xml.crypto.MarshalException; -import javax.xml.crypto.dsig.XMLSignatureException; -import javax.xml.parsers.ParserConfigurationException; -import java.awt.*; -import java.awt.datatransfer.Clipboard; -import java.awt.datatransfer.StringSelection; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.*; -import java.nio.charset.StandardCharsets; -import java.security.NoSuchAlgorithmException; -import java.security.cert.CertificateException; -import java.security.spec.InvalidKeySpecException; -import java.util.HashMap; -import java.util.List; -import java.util.Observable; -import java.util.Observer; -import java.util.zip.DataFormatException; - -public class SamlTabController implements IMessageEditorTab, Observer { - - private static final String XML_CERTIFICATE_NOT_FOUND = "X509 Certificate not found"; - private static final String XSW_ATTACK_APPLIED = "XSW Attack applied"; - private static final String XXE_CONTENT_APPLIED = "XXE content applied"; - private static final String XML_NOT_SUITABLE_FOR_XXE = "This XML Message is not suitable for this particular XXE attack"; - private static final String XSLT_CONTENT_APPLIED = "XSLT content applied"; - private static final String XML_NOT_SUITABLE_FOR_XLST = "This XML Message is not suitable for this particular XLST attack"; - private static final String XML_COULD_NOT_SIGN = "Could not sign XML"; - private static final String XML_COULD_NOT_SERIALIZE = "Could not serialize XML"; - private static final String XML_NOT_WELL_FORMED = "XML isn't well formed or binding is not supported"; - private static final String XML_NOT_SUITABLE_FOR_XSW = "This XML Message is not suitable for this particular XSW, is there a signature?"; - private static final String NO_BROWSER = "Could not open diff in Browser. Path to file was copied to clipboard"; - private static final String NO_DIFF_TEMP_FILE = "Could not create diff temp file."; - - private final IExtensionHelpers helpers; - private final IBurpExtenderCallbacks callbacks; - private final XMLHelpers xmlHelpers; - private final ITextEditor textArea; - private final ITextEditor textEditorInformation; - private final SamlMain samlGUI; - private final boolean editable; - private final CertificateTabController certificateTabController; - private final XSWHelpers xswHelpers; - private final HTTPHelpers httpHelpers; - private byte[] message; - private String orgSAMLMessage; - private String SAMLMessage; - private boolean isInflated = true; - private boolean isGZip = false; - private boolean isWSSUrlEncoded = false; - private boolean isSOAPMessage; - private boolean isWSSMessage; - private boolean isSAMLRequest; // otherwise it's a SAMLResponse - private String httpMethod; // So URI and POST Binding is supported - private boolean isEdited = false; - private boolean isRawMode = false; - - public SamlTabController(IBurpExtenderCallbacks callbacks, boolean editable, - CertificateTabController certificateTabController) { - this.editable = editable; - this.callbacks = callbacks; - this.helpers = callbacks.getHelpers(); - samlGUI = new SamlMain(this); - textArea = samlGUI.getTextEditorAction(); - textArea.setEditable(editable); - textEditorInformation = samlGUI.getTextEditorInformation(); - textEditorInformation.setEditable(false); - xmlHelpers = new XMLHelpers(); - xswHelpers = new XSWHelpers(); - httpHelpers = new HTTPHelpers(); - this.certificateTabController = certificateTabController; - this.certificateTabController.addObserver(this); - } - - public static String removeSignature_edit(String input) { - XMLHelpers xmlHelpers = new XMLHelpers(); - String res = ""; - try { - Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(input); - - int sign = xmlHelpers.removeAllSignatures(document); - if (sign > 0) { - - res = xmlHelpers.getStringOfDocument(document, 2, true); - } else { - //setInfoMessageText("No Signatures available to remove"); - } - - } catch (SAXException e1) { - e1.printStackTrace(); - //setInfoMessageText(XML_NOT_WELL_FORMED); - } catch (IOException e) { - e.printStackTrace(); - //setInfoMessageText(XML_COULD_NOT_SERIALIZE); - } - return res; - } - - public static String resignAssertion_edit(String input, String input_cert) { - XMLHelpers xmlHelpers = new XMLHelpers(); - try { - BurpCertificate original_cert = CertificateTabController.importCertificateFromString_edit(input_cert); - - BurpCertificate cert = CertificateTabController.cloneAndSignCertificate_edit(original_cert); - - if (cert != null) { - Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(input); - NodeList assertions = xmlHelpers.getAssertions(document); - String signAlgorithm = xmlHelpers.getSignatureAlgorithm(assertions.item(0)); - String digestAlgorithm = xmlHelpers.getDigestAlgorithm(assertions.item(0)); - - xmlHelpers.removeAllSignatures(document); - String string = xmlHelpers.getString(document); - Document doc = xmlHelpers.getXMLDocumentOfSAMLMessage(string); - xmlHelpers.removeEmptyTags(doc); - xmlHelpers.signAssertion(doc, signAlgorithm, digestAlgorithm, cert.getCertificate(), - cert.getPrivateKey()); - return xmlHelpers.getStringOfDocument(doc, 2, true); - } - } catch (SAXException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } catch (Exception e) { - e.printStackTrace(); - } - return null; - } - - public static String resignMessage_edit(String input, String input_cert) { - try { - //if (isWSSMessage) { - //setInfoMessageText("Message signing is not possible with WS-Security messages"); - //} else { - - BurpCertificate original_cert = CertificateTabController.importCertificateFromString_edit(input_cert); - - BurpCertificate cert = CertificateTabController.cloneAndSignCertificate_edit(original_cert); - - XMLHelpers xmlHelpers = new XMLHelpers(); - - if (cert != null) { - Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(input); - NodeList responses = xmlHelpers.getResponse(document); - String signAlgorithm = xmlHelpers.getSignatureAlgorithm(responses.item(0)); - String digestAlgorithm = xmlHelpers.getDigestAlgorithm(responses.item(0)); - - xmlHelpers.removeOnlyMessageSignature(document); - xmlHelpers.signMessage(document, signAlgorithm, digestAlgorithm, cert.getCertificate(), - cert.getPrivateKey()); - String res = xmlHelpers.getStringOfDocument(document, 2, true); - return res; - } - //} - } catch (IOException e) { - //setInfoMessageText(XML_COULD_NOT_SERIALIZE); - } catch (SAXException e) { - //setInfoMessageText(XML_NOT_WELL_FORMED); - } catch (CertificateException e) { - //setInfoMessageText(XML_COULD_NOT_SIGN); - } catch (NoSuchAlgorithmException e) { - //setInfoMessageText(XML_COULD_NOT_SIGN + ", no such algorithm"); - } catch (InvalidKeySpecException e) { - //setInfoMessageText(XML_COULD_NOT_SIGN + ", invalid private key"); - } catch (MarshalException e) { - //setInfoMessageText(XML_COULD_NOT_SERIALIZE); - } catch (XMLSignatureException e) { - //setInfoMessageText(XML_COULD_NOT_SIGN); - } - return null; - } - - @Override - public byte[] getMessage() { - byte[] byteMessage = message; - if (isModified()) { - if (isSOAPMessage) { - try { - // TODO Only working with getString for both documents, - // otherwise namespaces and attributes are emptied -.- - IResponseInfo responseInfo = helpers.analyzeResponse(byteMessage); - int bodyOffset = responseInfo.getBodyOffset(); - String HTTPHeader = new String(byteMessage, 0, bodyOffset, StandardCharsets.UTF_8); - - String soapMessage = new String(byteMessage, bodyOffset, byteMessage.length - bodyOffset, StandardCharsets.UTF_8); - Document soapDocument = xmlHelpers.getXMLDocumentOfSAMLMessage(soapMessage); - Element soapBody = xmlHelpers.getSOAPBody(soapDocument); - xmlHelpers.getString(soapDocument); - Document samlDocumentEdited = xmlHelpers.getXMLDocumentOfSAMLMessage(SAMLMessage); - xmlHelpers.getString(samlDocumentEdited); - Element samlResponse = (Element) samlDocumentEdited.getFirstChild(); - soapDocument.adoptNode(samlResponse); - Element soapFirstChildOfBody = (Element) soapBody.getFirstChild(); - soapBody.replaceChild(samlResponse, soapFirstChildOfBody); - String wholeMessage = HTTPHeader + xmlHelpers.getString(soapDocument); - byteMessage = wholeMessage.getBytes(StandardCharsets.UTF_8); - } catch (IOException e) { - } catch (SAXException e) { - setInfoMessageText(XML_NOT_WELL_FORMED); - } - } else { - String textMessage = null; - - if (isRawMode) { - textMessage = new String(textArea.getText()); - } else { - try { - textMessage = xmlHelpers - .getStringOfDocument(xmlHelpers.getXMLDocumentOfSAMLMessage(new String(textArea.getText())), 0, true); - } catch (IOException e) { - setInfoMessageText(XML_COULD_NOT_SERIALIZE); - } catch (SAXException e) { - setInfoMessageText(XML_NOT_WELL_FORMED); - } - } - - String parameterToUpdate; - if (isSAMLRequest) { - parameterToUpdate = certificateTabController.getSamlRequestParameterName(); - } else { - parameterToUpdate = certificateTabController.getSamlResponseParameterName(); - } - - if (isWSSMessage) { - parameterToUpdate = "wresult"; - } - - byte parameterType; - if (httpMethod.equals("GET")) { - parameterType = IParameter.PARAM_URL; - } else { - parameterType = IParameter.PARAM_BODY; - } - IParameter newParameter = helpers.buildParameter(parameterToUpdate, getEncodedSAMLMessage(textMessage), - parameterType); - - byteMessage = helpers.updateParameter(byteMessage, newParameter); - } - } - return byteMessage; - } - - @Override - public byte[] getSelectedData() { - return textArea.getSelectedText(); - } - - @Override - public String getTabCaption() { - return "SAML Raider"; - } - - @Override - public Component getUiComponent() { - return samlGUI; - } - - @Override - public boolean isEnabled(byte[] content, boolean isRequest) { - return isRequest && isSAMLMessage(content); - } - - private boolean isSAMLMessage(byte[] content) { - IRequestInfo info = helpers.analyzeRequest(content); - httpMethod = helpers.analyzeRequest(content).getMethod(); - if (info.getContentType() == IRequestInfo.CONTENT_TYPE_XML) { - isSOAPMessage = true; - try { - IRequestInfo requestInfo = helpers.analyzeRequest(content); - int bodyOffset = requestInfo.getBodyOffset(); - String soapMessage = new String(content, bodyOffset, content.length - bodyOffset, StandardCharsets.UTF_8); - Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(soapMessage); - return xmlHelpers.getAssertions(document).getLength() != 0 - || xmlHelpers.getEncryptedAssertions(document).getLength() != 0; - } catch (SAXException e) { - e.printStackTrace(); - return false; - } - } - // WSS Security - else if (null != helpers.getRequestParameter(content, "wresult")) { - try { - IRequestInfo requestInfo = helpers.analyzeRequest(content); - isWSSUrlEncoded = requestInfo.getContentType() == IRequestInfo.CONTENT_TYPE_URL_ENCODED; - isWSSMessage = true; - IParameter parameter = helpers.getRequestParameter(content, "wresult"); - String wssMessage = getDecodedSAMLMessage(parameter.getValue()); - Document document; - document = xmlHelpers.getXMLDocumentOfSAMLMessage(wssMessage); - return xmlHelpers.getAssertions(document).getLength() != 0 - || xmlHelpers.getEncryptedAssertions(document).getLength() != 0; - } catch (SAXException e) { - e.printStackTrace(); - return false; - } - } else { - isWSSMessage = false; - isSOAPMessage = false; - - IParameter requestParameter; - requestParameter = helpers.getRequestParameter(content, certificateTabController.getSamlResponseParameterName()); - if (requestParameter != null) { - isSAMLRequest = false; - return true; - } - requestParameter = helpers.getRequestParameter(content, certificateTabController.getSamlRequestParameterName()); - if (requestParameter != null) { - isSAMLRequest = true; - return true; - } - } - return false; - } - - @Override - public boolean isModified() { - return textArea.isTextModified() || isEdited; - } - - @Override - public void setMessage(byte[] content, boolean isRequest) { - resetInfoMessageText(); - isEdited = false; - if (content == null) { - textArea.setText(null); - textArea.setEditable(false); - setGUIEditable(false); - resetInformationDisplay(); - } else { - message = content; - try { - if (isSOAPMessage) { - IResponseInfo responseInfo = helpers.analyzeResponse(content); - int bodyOffset = responseInfo.getBodyOffset(); - String soapMessage = new String(content, bodyOffset, content.length - bodyOffset, StandardCharsets.UTF_8); - Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(soapMessage); - Document documentSAML = xmlHelpers.getSAMLResponseOfSOAP(document); - SAMLMessage = xmlHelpers.getStringOfDocument(documentSAML, 0, false); - } else if (isWSSMessage) { - IParameter parameter = helpers.getRequestParameter(content, "wresult"); - SAMLMessage = getDecodedSAMLMessage(parameter.getValue()); - } else { - IParameter parameter; - - if (isSAMLRequest) { - parameter = helpers.getRequestParameter(content, certificateTabController.getSamlRequestParameterName()); - } else { - parameter = helpers.getRequestParameter(content, certificateTabController.getSamlResponseParameterName()); - } - - SAMLMessage = getDecodedSAMLMessage(parameter.getValue()); - } - - } catch (IOException e) { - e.printStackTrace(); - setInfoMessageText(XML_COULD_NOT_SERIALIZE); - } catch (SAXException e) { - e.printStackTrace(); - setInfoMessageText(XML_NOT_WELL_FORMED); - SAMLMessage = "" + XML_NOT_WELL_FORMED + ""; - } catch (ParserConfigurationException e) { - e.printStackTrace(); - } - - setInformationDisplay(); - updateCertificateList(); - updateXSWList(); - orgSAMLMessage = SAMLMessage; - textArea.setText(SAMLMessage.getBytes()); - textArea.setEditable(editable); - - setGUIEditable(editable); - } - } - - private void setInformationDisplay() { - SamlPanelInfo infoPanel = samlGUI.getInfoPanel(); - infoPanel.clearAll(); - - try { - Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(SAMLMessage); - NodeList assertions = xmlHelpers.getAssertions(document); - if (assertions.getLength() > 0) { - Node assertion = assertions.item(0); - infoPanel.setIssuer(xmlHelpers.getIssuer(document)); - infoPanel.setConditionNotBefore(xmlHelpers.getConditionNotBefore(assertion)); - infoPanel.setConditionNotAfter(xmlHelpers.getConditionNotAfter(assertion)); - infoPanel.setSubjectConfNotBefore(xmlHelpers.getSubjectConfNotBefore(assertion)); - infoPanel.setSubjectConfNotAfter(xmlHelpers.getSubjectConfNotAfter(assertion)); - infoPanel.setSignatureAlgorithm(xmlHelpers.getSignatureAlgorithm(assertion)); - infoPanel.setDigestAlgorithm(xmlHelpers.getDigestAlgorithm(assertion)); - textEditorInformation.setText(xmlHelpers.getStringOfDocument(xmlHelpers.getXMLDocumentOfSAMLMessage(SAMLMessage), 2, true).getBytes()); - } else { - assertions = xmlHelpers.getEncryptedAssertions(document); - Node assertion = assertions.item(0); - infoPanel.setEncryptionAlgorithm(xmlHelpers.getEncryptionMethod(assertion)); - } - } catch (SAXException | IOException e) { - setInfoMessageText(XML_NOT_WELL_FORMED); - } - } - - private void resetInformationDisplay() { - SamlPanelInfo infoPanel = samlGUI.getInfoPanel(); - infoPanel.setIssuer(""); - infoPanel.setConditionNotBefore(""); - infoPanel.setConditionNotAfter(""); - infoPanel.setSubjectConfNotBefore(""); - infoPanel.setSubjectConfNotAfter(""); - infoPanel.setSignatureAlgorithm(""); - infoPanel.setDigestAlgorithm(""); - infoPanel.setEncryptionAlgorithm(""); - textEditorInformation.setText("".getBytes()); - } - - public String getEncodedSAMLMessage(String message) { - byte[] byteMessage; - if (isWSSMessage) { - if (isWSSUrlEncoded) { - return URLEncoder.encode(message, StandardCharsets.UTF_8); - } else { - return message; - } - } - byteMessage = message.getBytes(StandardCharsets.UTF_8); - if (isInflated) { - try { - byteMessage = httpHelpers.compress(byteMessage, isGZip); - } catch (IOException e) { - } - } - String base64Encoded = helpers.base64Encode(byteMessage); - return URLEncoder.encode(base64Encoded, StandardCharsets.UTF_8); - } - - public String getDecodedSAMLMessage(String message) { - - if (isWSSMessage) { - if (isWSSUrlEncoded) { - return helpers.urlDecode(message); - } else { - return message; - } - } - - String urlDecoded = helpers.urlDecode(message); - byte[] base64Decoded = helpers.base64Decode(urlDecoded); - - isInflated = true; - isGZip = true; - - // try normal Zip Inflate - try { - byte[] inflated = httpHelpers.decompress(base64Decoded, true); - return new String(inflated, StandardCharsets.UTF_8); - } catch (IOException e) { - } catch (DataFormatException e) { - isGZip = false; - } - - // try Gzip Inflate - try { - byte[] inflated = httpHelpers.decompress(base64Decoded, false); - return new String(inflated, StandardCharsets.UTF_8); - } catch (IOException e) { - } catch (DataFormatException e) { - isInflated = false; - } - - return new String(base64Decoded, StandardCharsets.UTF_8); - } - - public void removeSignature() { - resetInfoMessageText(); - try { - Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(new String(textArea.getText())); - if (xmlHelpers.removeAllSignatures(document) > 0) { - SAMLMessage = xmlHelpers.getStringOfDocument(document, 2, true); - textArea.setText(SAMLMessage.getBytes()); - isEdited = true; - setRawMode(false); - setInfoMessageText("Message signature successful removed"); - } else { - setInfoMessageText("No Signatures available to remove"); - } - } catch (SAXException e1) { - setInfoMessageText(XML_NOT_WELL_FORMED); - } catch (IOException e) { - setInfoMessageText(XML_COULD_NOT_SERIALIZE); - } - } - - public void resetMessage() { - if (isRawMode) { - SAMLMessage = orgSAMLMessage; - } - textArea.setText(SAMLMessage.getBytes()); - isEdited = false; - } - - public void setRawMode(boolean rawModeEnabled) { - isRawMode = rawModeEnabled; - isEdited = true; - samlGUI.getActionPanel().setRawModeEnabled(rawModeEnabled); - } - - public void resignAssertion() { - try { - resetInfoMessageText(); - BurpCertificate cert = samlGUI.getActionPanel().getSelectedCertificate(); - if (cert != null) { - setInfoMessageText("Signing..."); - Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(new String(textArea.getText())); - NodeList assertions = xmlHelpers.getAssertions(document); - String signAlgorithm = xmlHelpers.getSignatureAlgorithm(assertions.item(0)); - String digestAlgorithm = xmlHelpers.getDigestAlgorithm(assertions.item(0)); - - xmlHelpers.removeAllSignatures(document); - String string = xmlHelpers.getString(document); - Document doc = xmlHelpers.getXMLDocumentOfSAMLMessage(string); - xmlHelpers.removeEmptyTags(doc); - xmlHelpers.signAssertion(doc, signAlgorithm, digestAlgorithm, cert.getCertificate(), - cert.getPrivateKey()); - SAMLMessage = xmlHelpers.getStringOfDocument(doc, 2, true); - textArea.setText(SAMLMessage.getBytes()); - isEdited = true; - setRawMode(false); - setInfoMessageText("Assertions successfully signed"); - } else { - setInfoMessageText("no certificate chosen to sign"); - } - } catch (SAXException e) { - setInfoMessageText(XML_NOT_WELL_FORMED); - } catch (IOException e) { - setInfoMessageText(XML_COULD_NOT_SERIALIZE); - } catch (Exception e) { - setInfoMessageText(XML_COULD_NOT_SIGN); - } - } - - public void resignMessage() { - try { - resetInfoMessageText(); - if (isWSSMessage) { - setInfoMessageText("Message signing is not possible with WS-Security messages"); - } else { - setInfoMessageText("Signing..."); - BurpCertificate cert = samlGUI.getActionPanel().getSelectedCertificate(); - if (cert != null) { - Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(new String(textArea.getText())); - NodeList responses = xmlHelpers.getResponse(document); - String signAlgorithm = xmlHelpers.getSignatureAlgorithm(responses.item(0)); - String digestAlgorithm = xmlHelpers.getDigestAlgorithm(responses.item(0)); - - xmlHelpers.removeOnlyMessageSignature(document); - xmlHelpers.signMessage(document, signAlgorithm, digestAlgorithm, cert.getCertificate(), - cert.getPrivateKey()); - SAMLMessage = xmlHelpers.getStringOfDocument(document, 2, true); - textArea.setText(SAMLMessage.getBytes()); - isEdited = true; - setRawMode(false); - setInfoMessageText("Message successfully signed"); - } else { - setInfoMessageText("no certificate chosen to sign"); - } - } - } catch (IOException e) { - setInfoMessageText(XML_COULD_NOT_SERIALIZE); - } catch (SAXException e) { - setInfoMessageText(XML_NOT_WELL_FORMED); - } catch (CertificateException e) { - setInfoMessageText(XML_COULD_NOT_SIGN); - } catch (NoSuchAlgorithmException e) { - setInfoMessageText(XML_COULD_NOT_SIGN + ", no such algorithm"); - } catch (InvalidKeySpecException e) { - setInfoMessageText(XML_COULD_NOT_SIGN + ", invalid private key"); - } catch (MarshalException e) { - setInfoMessageText(XML_COULD_NOT_SERIALIZE); - } catch (XMLSignatureException e) { - setInfoMessageText(XML_COULD_NOT_SIGN); - } - } - - private void setInfoMessageText(String infoMessage) { - samlGUI.getActionPanel().getInfoMessageLabel().setText(infoMessage); - } - - private void resetInfoMessageText() { - samlGUI.getActionPanel().getInfoMessageLabel().setText(""); - } - - private void updateCertificateList() { - List list = certificateTabController.getCertificatesWithPrivateKey(); - samlGUI.getActionPanel().setCertificateList(list); - } - - private void updateXSWList() { - samlGUI.getActionPanel().setXSWList(XSWHelpers.xswTypes); - } - - public void sendToCertificatesTab() { - try { - Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(new String(textArea.getText())); - String cert = xmlHelpers.getCertificate(document.getDocumentElement()); - if (cert != null) { - certificateTabController.importCertificateFromString(cert); - } else { - setInfoMessageText(XML_CERTIFICATE_NOT_FOUND); - } - } catch (SAXException e) { - setInfoMessageText(XML_NOT_WELL_FORMED); - } - } - - public void showXSWPreview() { - try { - Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(orgSAMLMessage); - xswHelpers.applyXSW(samlGUI.getActionPanel().getSelectedXSW(), document); - String after = xmlHelpers.getStringOfDocument(document, 2, true); - String diff = xswHelpers.diffLineMode(orgSAMLMessage, after); - - File file = File.createTempFile("tmp", ".html", null); - FileOutputStream fileOutputStream = new FileOutputStream(file); - file.deleteOnExit(); - fileOutputStream.write(diff.getBytes(StandardCharsets.UTF_8)); - fileOutputStream.flush(); - fileOutputStream.close(); - - URI uri = new URL("file://" + file.getAbsolutePath()).toURI(); - - Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null; - if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) { - desktop.browse(uri); - } else { - StringSelection stringSelection = new StringSelection(uri.toString()); - Clipboard clpbrd = Toolkit.getDefaultToolkit().getSystemClipboard(); - clpbrd.setContents(stringSelection, null); - setInfoMessageText(NO_BROWSER); - } - - } catch (SAXException e) { - setInfoMessageText(XML_NOT_WELL_FORMED); - } catch (DOMException e) { - setInfoMessageText(XML_NOT_SUITABLE_FOR_XSW); - } catch (MalformedURLException e) { - } catch (URISyntaxException e) { - } catch (IOException e) { - setInfoMessageText(NO_DIFF_TEMP_FILE); - } - } - - public void applyXSW() { - Document document; - try { - document = xmlHelpers.getXMLDocumentOfSAMLMessage(orgSAMLMessage); - xswHelpers.applyXSW(samlGUI.getActionPanel().getSelectedXSW(), document); - SAMLMessage = xmlHelpers.getStringOfDocument(document, 2, true); - textArea.setText(SAMLMessage.getBytes()); - isEdited = true; - setRawMode(false); - setInfoMessageText(XSW_ATTACK_APPLIED); - } catch (SAXException e) { - setInfoMessageText(XML_NOT_WELL_FORMED); - } catch (IOException e) { - setInfoMessageText(XML_COULD_NOT_SERIALIZE); - } catch (DOMException | NullPointerException e) { - setInfoMessageText(XML_NOT_SUITABLE_FOR_XSW); - } - } - - public void applyXXE(String collabUrl) { - String xxePayload = " %xxe; ]>\n"; - String[] splitMsg = orgSAMLMessage.split("\\?>"); - if (splitMsg.length == 2) { - SAMLMessage = splitMsg[0] + "?>" + xxePayload + splitMsg[1]; - } else { - String xmlDeclaration = "\n"; - SAMLMessage = xmlDeclaration + xxePayload + orgSAMLMessage; - } - textArea.setText(SAMLMessage.getBytes()); - isEdited = true; - setRawMode(true); - setInfoMessageText(XXE_CONTENT_APPLIED); - } - - public void applyXSLT(String collabUrl) { - String xslt = "\n" + - "\n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - " \n" + - ""; - String transformString = ""; - int index = orgSAMLMessage.indexOf(transformString); - - if (index == -1) { - setInfoMessageText(XML_NOT_SUITABLE_FOR_XLST); - } else { - int substringIndex = index + transformString.length(); - String firstPart = orgSAMLMessage.substring(0, substringIndex); - String secondPart = orgSAMLMessage.substring(substringIndex); - SAMLMessage = firstPart + xslt + secondPart; - textArea.setText(SAMLMessage.getBytes()); - isEdited = true; - setRawMode(true); - setInfoMessageText(XSLT_CONTENT_APPLIED); - } - } - - public synchronized void addMatchAndReplace(String match, String replace) { - XSWHelpers.MATCH_AND_REPLACE_MAP.put(match, replace); - } - - public synchronized HashMap getMatchAndReplaceMap() { - return XSWHelpers.MATCH_AND_REPLACE_MAP; - } - - public void setGUIEditable(boolean editable) { - if (editable) { - samlGUI.getActionPanel().enableControls(); - } else { - samlGUI.getActionPanel().disableControls(); - } - } - - public void showSignatureHelp() { - SignatureHelpWindow window = new SignatureHelpWindow(); - window.setVisible(true); - } - - public void showXSWHelp() { - XSWHelpWindow window = new XSWHelpWindow(); - window.setVisible(true); - } - - @Override - public void update(Observable arg0, Object arg1) { - updateCertificateList(); - } - - public IBurpExtenderCallbacks getCallbacks() { - return callbacks; - } -} diff --git a/tool/src/main/java/samlraider/gui/ImagePanel.java b/tool/src/main/java/samlraider/gui/ImagePanel.java deleted file mode 100644 index 23151be..0000000 --- a/tool/src/main/java/samlraider/gui/ImagePanel.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) - * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package samlraider.gui; - -import javax.imageio.ImageIO; -import javax.swing.*; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; - -public class ImagePanel extends JPanel { - - private static final long serialVersionUID = 1L; - private final int preferredHeight = 1200; - private final int preferredWidth = 800; - private BufferedImage image; - - public ImagePanel(String path) { - try { - image = ImageIO.read(new File(path)); - } catch (IOException ex) { - try { - image = ImageIO.read(getClass().getResourceAsStream("/" + path)); - } catch (IOException e) { - e.printStackTrace(); - } - } - - BufferedImage resized = new BufferedImage(preferredWidth, preferredHeight, image.getType()); - Graphics2D g = resized.createGraphics(); - g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); - g.drawImage(image, 0, 0, preferredWidth, preferredHeight, 0, 0, image.getWidth(), image.getHeight(), null); - g.dispose(); - image = resized; - } - - @Override - protected void paintComponent(Graphics g) { - super.paintComponent(g); - g.drawImage(image, 0, 0, null); - } - - @Override - public Dimension getPreferredSize() { - return new Dimension(preferredWidth, preferredHeight); - - } -} \ No newline at end of file diff --git a/tool/src/main/java/samlraider/gui/SamlMain.java b/tool/src/main/java/samlraider/gui/SamlMain.java deleted file mode 100644 index 8b60c60..0000000 --- a/tool/src/main/java/samlraider/gui/SamlMain.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) - * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package samlraider.gui; - -import samlraider.application.SamlTabController; -import burp.ITextEditor; - -import javax.swing.*; -import java.awt.*; - -public class SamlMain extends javax.swing.JPanel { - - private static final long serialVersionUID = 1L; - private ITextEditor textEditorAction; - private ITextEditor textEditorInformation; - private SamlTabController controller; - private SamlPanelAction panelAction; - private SamlPanelInfo panelInformation; - - public SamlMain() { - super(); - initializeUI(); - } - - public SamlMain(SamlTabController controller) { - super(); - this.controller = controller; - initializeUI(); - } - - private void initializeUI() { - setLayout(new BorderLayout(0, 0)); - - JSplitPane splitPaneAction = new JSplitPane(); - splitPaneAction.setOrientation(JSplitPane.VERTICAL_SPLIT); - splitPaneAction.setDividerSize(5); - add(splitPaneAction, BorderLayout.CENTER); - - JPanel panelActionTop = new JPanel(); - splitPaneAction.setLeftComponent(panelActionTop); - panelActionTop.setLayout(new BorderLayout(0, 0)); - panelAction = new SamlPanelAction(controller); - panelActionTop.add(panelAction); - - JPanel panelActionBottom = new JPanel(); - splitPaneAction.setRightComponent(panelActionBottom); - panelActionBottom.setLayout(new BorderLayout(0, 0)); - textEditorAction = controller.getCallbacks().createTextEditor(); - textEditorAction.setText("".getBytes()); - panelActionBottom.add(textEditorAction.getComponent(), BorderLayout.CENTER); - - JSplitPane splitPaneInformation = new JSplitPane(); - splitPaneInformation.setOrientation(JSplitPane.VERTICAL_SPLIT); - splitPaneAction.setDividerSize(5); - add(splitPaneInformation, BorderLayout.CENTER); - - JPanel panelInformationTop = new JPanel(); - splitPaneInformation.setLeftComponent((panelInformationTop)); - panelInformationTop.setLayout(new BorderLayout(0, 0)); - panelInformation = new SamlPanelInfo(); - panelInformationTop.add(panelInformation); - - JPanel panelInformationBottom = new JPanel(); - splitPaneInformation.setRightComponent(panelInformationBottom); - panelInformationBottom.setLayout(new BorderLayout(0, 0)); - textEditorInformation = controller.getCallbacks().createTextEditor(); - textEditorInformation.setText("".getBytes()); - textEditorAction.setEditable(false); - panelInformationBottom.add(textEditorInformation.getComponent(), BorderLayout.CENTER); - - JTabbedPane tabbedPane = new JTabbedPane(); - add(tabbedPane); - tabbedPane.addTab("SAML Attacks", null, splitPaneAction, "SAML Attacks"); - tabbedPane.addTab("SAML Message Info", null, splitPaneInformation, "SAML Message Info"); - - this.invalidate(); - this.updateUI(); - } - - public ITextEditor getTextEditorAction() { - return textEditorAction; - } - - public ITextEditor getTextEditorInformation() { - return textEditorInformation; - } - - public SamlPanelAction getActionPanel() { - return panelAction; - } - - public SamlPanelInfo getInfoPanel() { - return panelInformation; - } - -} diff --git a/tool/src/main/java/samlraider/gui/SamlPanelAction.java b/tool/src/main/java/samlraider/gui/SamlPanelAction.java deleted file mode 100644 index 9db12d7..0000000 --- a/tool/src/main/java/samlraider/gui/SamlPanelAction.java +++ /dev/null @@ -1,469 +0,0 @@ -/* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) - * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package samlraider.gui; - -import samlraider.application.SamlTabController; -import samlraider.model.BurpCertificate; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.HashMap; -import java.util.List; - -public class SamlPanelAction extends JPanel { - - private static final long serialVersionUID = 1L; - private SamlTabController controller; - private JLabel lblMessage; - private JComboBox cmbboxCertificate; - private JComboBox cmbboxXSW; - private JButton btnXSWHelp; - private JButton btnXSWPreview; - private JButton btnSignatureReset; - private JButton btnXSWApply; - private JButton btnMatchAndReplace; - private JButton btnTestXXE; - private JButton btnTestXSLT; - private JButton btnSignatureHelp; - private JButton btnSignatureRemove; - private JButton btnSignatureReplace; - private JButton btnSendCertificate; - private JButton btnSignatureAdd; - private JTextField txtSearch; - private JCheckBox chkRawMode; - - public SamlPanelAction() { - initialize(); - } - - public SamlPanelAction(SamlTabController controller) { - this.controller = controller; - initialize(); - } - - private void initialize() { - GridBagLayout gridBagLayout = new GridBagLayout(); - gridBagLayout.columnWidths = new int[]{0, 0, 0}; - gridBagLayout.rowHeights = new int[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - gridBagLayout.columnWeights = new double[]{0.0, 0.0, 0.0, Double.MIN_VALUE}; - gridBagLayout.rowWeights = new double[]{0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, Double.MIN_VALUE}; - setLayout(gridBagLayout); - - - JLabel lblSamlMsgTitle = new JLabel("SAML Message"); - GridBagConstraints gbc_lblSamlMsgTitle = new GridBagConstraints(); - gbc_lblSamlMsgTitle.insets = new Insets(10, 5, 5, 0); - gbc_lblSamlMsgTitle.anchor = GridBagConstraints.WEST; - gbc_lblSamlMsgTitle.gridx = 0; - gbc_lblSamlMsgTitle.gridy = 0; - gbc_lblSamlMsgTitle.gridwidth = 3; - add(lblSamlMsgTitle, gbc_lblSamlMsgTitle); - - btnSignatureReset = new JButton("Reset Message"); - GridBagConstraints gbc_btnSignatureReset = new GridBagConstraints(); - gbc_btnSignatureReset.anchor = GridBagConstraints.WEST; - gbc_btnSignatureReset.insets = new Insets(5, 5, 0, 0); - gbc_btnSignatureReset.gridx = 1; - gbc_btnSignatureReset.gridy = 1; - add(btnSignatureReset, gbc_btnSignatureReset); - btnSignatureReset.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - controller.resetMessage(); - } - }); - - chkRawMode = new JCheckBox("Raw Mode (don't parse XML before sending)"); - GridBagConstraints gbc_chkRawMode = new GridBagConstraints(); - gbc_chkRawMode.anchor = GridBagConstraints.WEST; - gbc_chkRawMode.insets = new Insets(5, 5, 0, 0); - gbc_chkRawMode.gridx = 2; - gbc_chkRawMode.gridy = 1; - gbc_chkRawMode.gridwidth = 2; - add(chkRawMode, gbc_chkRawMode); - chkRawMode.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - controller.setRawMode(chkRawMode.isSelected()); - } - }); - - JLabel lblXSWTitle = new JLabel("XSW Attacks"); - GridBagConstraints gbc_lblXSWTitle = new GridBagConstraints(); - gbc_lblXSWTitle.insets = new Insets(10, 5, 5, 0); - gbc_lblXSWTitle.anchor = GridBagConstraints.WEST; - gbc_lblXSWTitle.gridx = 0; - gbc_lblXSWTitle.gridy = 3; - gbc_lblXSWTitle.gridwidth = 3; - add(lblXSWTitle, gbc_lblXSWTitle); - - btnXSWHelp = new JButton("?"); - GridBagConstraints gbc_btnXSWHelp = new GridBagConstraints(); - gbc_btnXSWHelp.insets = new Insets(5, 5, 0, 0); - gbc_btnXSWHelp.gridx = 0; - gbc_btnXSWHelp.gridy = 4; - btnXSWHelp.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - controller.showXSWHelp(); - } - }); - add(btnXSWHelp, gbc_btnXSWHelp); - - cmbboxXSW = new JComboBox(); - GridBagConstraints gbc_cmbboxXSW = new GridBagConstraints(); - gbc_cmbboxXSW.insets = new Insets(5, 5, 0, 0); - gbc_cmbboxXSW.anchor = GridBagConstraints.WEST; - gbc_cmbboxXSW.gridx = 1; - gbc_cmbboxXSW.gridy = 4; - gbc_cmbboxXSW.gridwidth = 3; - add(cmbboxXSW, gbc_cmbboxXSW); - - btnXSWPreview = new JButton("Preview in Browser..."); - GridBagConstraints gbc_btnXSWPreview = new GridBagConstraints(); - gbc_btnXSWPreview.anchor = GridBagConstraints.WEST; - gbc_btnXSWPreview.insets = new Insets(5, 5, 0, 0); - gbc_btnXSWPreview.gridx = 1; - gbc_btnXSWPreview.gridy = 5; - btnXSWPreview.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - controller.showXSWPreview(); - } - }); - add(btnXSWPreview, gbc_btnXSWPreview); - - btnXSWApply = new JButton("Apply XSW"); - GridBagConstraints gbc_btnXSWApply = new GridBagConstraints(); - gbc_btnXSWApply.insets = new Insets(5, 5, 0, 0); - gbc_btnXSWApply.anchor = GridBagConstraints.WEST; - gbc_btnXSWApply.gridx = 3; - gbc_btnXSWApply.gridy = 5; - btnXSWApply.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - controller.applyXSW(); - } - }); - add(btnXSWApply, gbc_btnXSWApply); - - btnMatchAndReplace = new JButton("Match and Replace"); - GridBagConstraints gbc_btnMatchAndReplace = new GridBagConstraints(); - gbc_btnMatchAndReplace.insets = new Insets(5, 5, 0, 0); - gbc_btnMatchAndReplace.anchor = GridBagConstraints.WEST; - gbc_btnMatchAndReplace.gridx = 2; - gbc_btnMatchAndReplace.gridy = 5; - btnMatchAndReplace.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - showMatchAndReplaceDialog(); - } - }); - add(btnMatchAndReplace, gbc_btnMatchAndReplace); - - JLabel lblXmlAttacksTitle = new JLabel("XML Attacks"); - GridBagConstraints gbc_lblXmlAttacksTitle = new GridBagConstraints(); - gbc_lblXmlAttacksTitle.anchor = GridBagConstraints.WEST; - gbc_lblXmlAttacksTitle.insets = new Insets(10, 5, 5, 0); - gbc_lblXmlAttacksTitle.gridx = 0; - gbc_lblXmlAttacksTitle.gridy = 7; - gbc_lblXmlAttacksTitle.gridwidth = 3; - add(lblXmlAttacksTitle, gbc_lblXmlAttacksTitle); - - btnTestXXE = new JButton("Test XXE"); - GridBagConstraints gbc_btnTestXXE = new GridBagConstraints(); - gbc_btnTestXXE.insets = new Insets(5, 5, 0, 0); - gbc_btnTestXXE.anchor = GridBagConstraints.WEST; - gbc_btnTestXXE.gridx = 1; - gbc_btnTestXXE.gridy = 8; - btnTestXXE.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - String collabUrl = JOptionPane.showInputDialog(btnXSWApply, - "Enter Burp Collaborator URL (e.g. https://xyz.burpcollaborator.net)"); - if (collabUrl != null) { - controller.applyXXE(collabUrl); - } - } - }); - add(btnTestXXE, gbc_btnTestXXE); - - btnTestXSLT = new JButton("Test XSLT"); - GridBagConstraints gbc_btnTestXSLT = new GridBagConstraints(); - gbc_btnTestXSLT.insets = new Insets(5, 5, 0, 0); - gbc_btnTestXSLT.anchor = GridBagConstraints.WEST; - gbc_btnTestXSLT.gridx = 2; - gbc_btnTestXSLT.gridy = 8; - btnTestXSLT.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - String collabUrl = JOptionPane.showInputDialog(btnXSWApply, - "Enter Burp Collaborator URL (e.g. https://xyz.burpcollaborator.net)"); - if (collabUrl != null) { - controller.applyXSLT(collabUrl); - } - } - }); - add(btnTestXSLT, gbc_btnTestXSLT); - - JLabel lblSignatureTitle = new JLabel("XML Signature Attacks"); - GridBagConstraints gbc_lblSignatureTitle = new GridBagConstraints(); - gbc_lblSignatureTitle.anchor = GridBagConstraints.WEST; - gbc_lblSignatureTitle.insets = new Insets(10, 5, 5, 0); - gbc_lblSignatureTitle.gridx = 0; - gbc_lblSignatureTitle.gridy = 10; - gbc_lblSignatureTitle.gridwidth = 3; - add(lblSignatureTitle, gbc_lblSignatureTitle); - - btnSignatureHelp = new JButton("?"); - GridBagConstraints gbc_btnSignatureHelp = new GridBagConstraints(); - gbc_btnSignatureHelp.insets = new Insets(5, 5, 0, 0); - gbc_btnSignatureHelp.gridx = 0; - gbc_btnSignatureHelp.gridy = 11; - btnSignatureHelp.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - controller.showSignatureHelp(); - } - }); - add(btnSignatureHelp, gbc_btnSignatureHelp); - - cmbboxCertificate = new JComboBox(); - GridBagConstraints gbc_cmbboxCertificate = new GridBagConstraints(); - gbc_cmbboxCertificate.insets = new Insets(5, 5, 0, 0); - gbc_cmbboxCertificate.anchor = GridBagConstraints.WEST; - gbc_cmbboxCertificate.gridx = 1; - gbc_cmbboxCertificate.gridy = 12; - gbc_cmbboxCertificate.gridwidth = 3; - add(cmbboxCertificate, gbc_cmbboxCertificate); - - btnSignatureRemove = new JButton("Remove Signatures"); - btnSignatureRemove.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - controller.removeSignature(); - } - }); - GridBagConstraints gbc_btnSignatureRemove = new GridBagConstraints(); - gbc_btnSignatureRemove.anchor = GridBagConstraints.WEST; - gbc_btnSignatureRemove.insets = new Insets(5, 5, 0, 0); - gbc_btnSignatureRemove.gridx = 1; - gbc_btnSignatureRemove.gridy = 11; - add(btnSignatureRemove, gbc_btnSignatureRemove); - - btnSignatureReplace = new JButton("(Re-)Sign Assertion"); - btnSignatureReplace.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - controller.resignAssertion(); - } - }); - GridBagConstraints gbc_btnSignatureReplace = new GridBagConstraints(); - gbc_btnSignatureReplace.anchor = GridBagConstraints.WEST; - gbc_btnSignatureReplace.insets = new Insets(5, 5, 0, 0); - gbc_btnSignatureReplace.gridx = 1; - gbc_btnSignatureReplace.gridy = 13; - add(btnSignatureReplace, gbc_btnSignatureReplace); - - - btnSendCertificate = new JButton("Send Certificate to SAML Raider Certificates"); - GridBagConstraints gbc_btnSendCertificate = new GridBagConstraints(); - gbc_btnSendCertificate.anchor = GridBagConstraints.WEST; - gbc_btnSendCertificate.insets = new Insets(5, 5, 0, 0); - gbc_btnSendCertificate.gridx = 2; - gbc_btnSendCertificate.gridy = 11; - gbc_btnSendCertificate.gridwidth = 2; - btnSendCertificate.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - controller.sendToCertificatesTab(); - } - }); - add(btnSendCertificate, gbc_btnSendCertificate); - - btnSignatureAdd = new JButton("(Re-)Sign Message"); - GridBagConstraints gbc_btnSignatureAdd = new GridBagConstraints(); - gbc_btnSignatureAdd.anchor = GridBagConstraints.WEST; - btnSignatureAdd.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - controller.resignMessage(); - } - }); - gbc_btnSignatureAdd.insets = new Insets(5, 5, 0, 0); - gbc_btnSignatureAdd.gridx = 2; - gbc_btnSignatureAdd.gridy = 13; - add(btnSignatureAdd, gbc_btnSignatureAdd); - - - lblMessage = new JLabel(""); - lblMessage.setBackground(new Color(255, 250, 205)); - lblMessage.setForeground(new Color(255, 140, 0)); - GridBagConstraints gbc_lblMessage = new GridBagConstraints(); - gbc_lblMessage.anchor = GridBagConstraints.WEST; - gbc_lblMessage.gridwidth = 3; - gbc_lblMessage.insets = new Insets(20, 5, 10, 0); - gbc_lblMessage.gridx = 0; - gbc_lblMessage.gridy = 14; - add(lblMessage, gbc_lblMessage); - } - - public JLabel getInfoMessageLabel() { - return lblMessage; - } - - public void setCertificateList(List list) { - DefaultComboBoxModel model = new DefaultComboBoxModel(); - - for (BurpCertificate cert : list) { - model.addElement(cert); - } - cmbboxCertificate.setModel(model); - } - - public BurpCertificate getSelectedCertificate() { - return (BurpCertificate) cmbboxCertificate.getSelectedItem(); - } - - public void setXSWList(String[] xswTypes) { - DefaultComboBoxModel model = new DefaultComboBoxModel(xswTypes); - cmbboxXSW.setModel(model); - } - - public String getSelectedXSW() { - return (String) cmbboxXSW.getSelectedItem(); - } - - public String getSearchText() { - return txtSearch.getText(); - } - - public boolean isRawModeEnabled() { - return chkRawMode.isSelected(); - } - - public void setRawModeEnabled(boolean rawModeEnabled) { - chkRawMode.setSelected(rawModeEnabled); - } - - public void disableControls() { - cmbboxCertificate.setEnabled(false); - cmbboxXSW.setEnabled(false); - btnXSWHelp.setEnabled(false); - btnXSWPreview.setEnabled(false); - btnSignatureReset.setEnabled(false); - btnXSWApply.setEnabled(false); - btnSignatureHelp.setEnabled(false); - btnSignatureRemove.setEnabled(false); - btnSignatureReplace.setEnabled(false); - btnSendCertificate.setEnabled(false); - btnSignatureAdd.setEnabled(false); - btnMatchAndReplace.setEnabled(false); - btnTestXXE.setEnabled(false); - btnTestXSLT.setEnabled(false); - chkRawMode.setEnabled(false); - this.revalidate(); - } - - public void enableControls() { - cmbboxCertificate.setEnabled(true); - cmbboxXSW.setEnabled(true); - btnXSWHelp.setEnabled(true); - btnXSWPreview.setEnabled(true); - btnSignatureReset.setEnabled(true); - btnXSWApply.setEnabled(true); - btnSignatureHelp.setEnabled(true); - btnSignatureRemove.setEnabled(true); - btnSignatureReplace.setEnabled(true); - btnSendCertificate.setEnabled(true); - btnSignatureAdd.setEnabled(true); - btnMatchAndReplace.setEnabled(true); - btnTestXXE.setEnabled(true); - btnTestXSLT.setEnabled(true); - chkRawMode.setEnabled(true); - this.revalidate(); - } - - private void showMatchAndReplaceDialog() { - HashMap matchAndReplaceMap = controller.getMatchAndReplaceMap(); - - JPanel dialogPanel = new JPanel(); - dialogPanel.setLayout(new BorderLayout()); - dialogPanel.add(new JLabel("Match and replace rules takes effect after apply XSW"), BorderLayout.NORTH); - - JPanel listPanel = new JPanel(); - JTextField matchInputText = new JTextField(); - JTextField replaceInputText = new JTextField(); - - JButton addEntryButton = new JButton("\u2795"); - addEntryButton.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - if (matchInputText.getText() != "" && replaceInputText.getText() != "") { - matchAndReplaceMap.put(matchInputText.getText(), replaceInputText.getText()); - updateMatchAndReplaceList(listPanel, matchInputText, replaceInputText, addEntryButton); - SwingUtilities.getWindowAncestor((Component) e.getSource()).pack(); - } - } - }); - - updateMatchAndReplaceList(listPanel, matchInputText, replaceInputText, addEntryButton); - JOptionPane.showMessageDialog(this, listPanel, "Apply XSW - Match and Replace", JOptionPane.PLAIN_MESSAGE); - } - - private void updateMatchAndReplaceList(JPanel listPanel, JTextField matchInputText, JTextField replaceInputText, JButton addEntryButton) { - HashMap matchAndReplaceMap = controller.getMatchAndReplaceMap(); - listPanel.setLayout(new GridBagLayout()); - listPanel.removeAll(); - GridBagConstraints c = new GridBagConstraints(); - c.fill = GridBagConstraints.HORIZONTAL; - c.gridx = 0; - c.gridy = 0; - listPanel.add(new JLabel("Match: "), c); - c.gridx = 1; - listPanel.add(new JLabel("Replace: "), c); - c.gridx = 0; - c.gridy = 1; - listPanel.add(matchInputText, c); - c.gridx = 1; - listPanel.add(replaceInputText, c); - c.gridx = 2; - listPanel.add(addEntryButton, c); - - c.gridy = 2; - for (String matchRule : matchAndReplaceMap.keySet()) { - c.gridx = 0; - listPanel.add(new JLabel(matchRule), c); - - c.gridx = 1; - listPanel.add(new JLabel(matchAndReplaceMap.get(matchRule)), c); - JButton deleteEntryBtn = new JButton("\u2796"); - deleteEntryBtn.addActionListener(new ActionListener() { - - @Override - public void actionPerformed(ActionEvent e) { - matchAndReplaceMap.remove(matchRule); - updateMatchAndReplaceList(listPanel, matchInputText, replaceInputText, addEntryButton); - SwingUtilities.getWindowAncestor((Component) e.getSource()).pack(); - } - }); - c.gridx = 2; - listPanel.add(deleteEntryBtn, c); - c.gridy++; - } - listPanel.revalidate(); - } -} diff --git a/tool/src/main/java/samlraider/gui/XSWHelpWindow.java b/tool/src/main/java/samlraider/gui/XSWHelpWindow.java deleted file mode 100644 index 3443038..0000000 --- a/tool/src/main/java/samlraider/gui/XSWHelpWindow.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) - * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package samlraider.gui; - -import javax.swing.*; -import javax.swing.border.EmptyBorder; -import java.awt.*; - -public class XSWHelpWindow extends JFrame { - - private static final long serialVersionUID = 1L; - private final JPanel contentPane; - - public XSWHelpWindow() { - setTitle("XML Signature Wrapping Help"); - setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); - setBounds(100, 100, 600, 400); - setMinimumSize(new Dimension(600, 400)); - contentPane = new JPanel(); - contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); - setContentPane(contentPane); - contentPane.setLayout(new BorderLayout(0, 0)); - - JLabel lblDescription = new JLabel("With xml wrapping attacks you try to trick the xml signature validator into validating an " - + "signature of an element while evaluating an other element. The XSWs in the image are supported." + "
The blue element represents the signature." - + "
The green one represents the original element, which is correctly signed. " - + "
The red one represents the falsly evaluated element, if the validating is not correctly implemented." - + "
Mind that the first two XSWs can be used for signed responses only whereas the other ones can be used for signed assertions only." - + "
These XSW are taken from this paper:
Somorovsky, Juraj, et al. \"On Breaking SAML: Be Whoever You Want to Be.\" USENIX Security Symposium. 2012." - + "
Please check out this paper for further information." + ""); - contentPane.add(lblDescription, BorderLayout.NORTH); - - JScrollPane scrollPane = new JScrollPane(); - scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); - scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); - contentPane.add(scrollPane, BorderLayout.CENTER); - - ImagePanel panel; - String className = getClass().getName().replace('.', '/'); - String classJar = getClass().getResource("/" + className + ".class").toString(); - if (classJar.startsWith("jar:")) { - panel = new ImagePanel("xswlist.png"); - } else { - panel = new ImagePanel("src/main/resources/xswlist.png"); - } - - scrollPane.setViewportView(panel); - } -} diff --git a/tool/src/main/java/samlraider/helpers/Flags.java b/tool/src/main/java/samlraider/helpers/Flags.java deleted file mode 100644 index 8b37c09..0000000 --- a/tool/src/main/java/samlraider/helpers/Flags.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) - * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package samlraider.helpers; - -public class Flags { - - // Write SAMLRaiderDebug.log and load example certificates - public static final boolean DEBUG = false; - -} diff --git a/tool/src/main/java/samlraider/helpers/HTTPHelpers.java b/tool/src/main/java/samlraider/helpers/HTTPHelpers.java deleted file mode 100644 index 024d193..0000000 --- a/tool/src/main/java/samlraider/helpers/HTTPHelpers.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) - * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package samlraider.helpers; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.zip.DataFormatException; -import java.util.zip.Deflater; -import java.util.zip.Inflater; - -public class HTTPHelpers { - - // Source: - // http://qupera.blogspot.ch/2013/02/howto-compress-and-uncompress-java-byte.html - public byte[] decompress(byte[] data, boolean gzip) throws IOException, DataFormatException { - Inflater inflater = new Inflater(true); - inflater.setInput(data); - - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length); - byte[] buffer = new byte[1024]; - while (!inflater.finished()) { - int count = inflater.inflate(buffer); - outputStream.write(buffer, 0, count); - } - outputStream.close(); - byte[] output = outputStream.toByteArray(); - - inflater.end(); - - return output; - } - - // Source: - // http://qupera.blogspot.ch/2013/02/howto-compress-and-uncompress-java-byte.html - public byte[] compress(byte[] data, boolean gzip) throws IOException { - Deflater deflater = new Deflater(5, gzip); - deflater.setInput(data); - - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(data.length); - - deflater.finish(); - byte[] buffer = new byte[1024]; - while (!deflater.finished()) { - int count = deflater.deflate(buffer); - outputStream.write(buffer, 0, count); - } - outputStream.close(); - byte[] output = outputStream.toByteArray(); - - deflater.end(); - - return output; - } - - -} diff --git a/tool/src/main/java/samlraider/helpers/X509KeySelector.java b/tool/src/main/java/samlraider/helpers/X509KeySelector.java deleted file mode 100644 index ad90191..0000000 --- a/tool/src/main/java/samlraider/helpers/X509KeySelector.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) - * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package samlraider.helpers; - -import com.sun.org.apache.xml.internal.security.signature.XMLSignature; - -import javax.xml.crypto.*; -import javax.xml.crypto.dsig.SignatureMethod; -import javax.xml.crypto.dsig.keyinfo.KeyInfo; -import javax.xml.crypto.dsig.keyinfo.X509Data; -import java.security.Key; -import java.security.PublicKey; -import java.security.cert.X509Certificate; -import java.util.Iterator; - -//Source: -/*http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html*/ -//for Validation purposes only -public class X509KeySelector extends KeySelector { - static boolean algEquals(String algURI, String algName) { - return (algName.equalsIgnoreCase("DSA") && - algURI.equalsIgnoreCase(SignatureMethod.DSA_SHA1)) || - (algName.equalsIgnoreCase("RSA") && - algURI.equalsIgnoreCase(SignatureMethod.RSA_SHA1)) || - (algName.equalsIgnoreCase("RSA") && - algURI.equalsIgnoreCase(XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256)); - } - - public KeySelectorResult select(KeyInfo keyInfo, - KeySelector.Purpose purpose, - AlgorithmMethod method, - XMLCryptoContext context) - throws KeySelectorException { - @SuppressWarnings("rawtypes") - Iterator ki = keyInfo.getContent().iterator(); - while (ki.hasNext()) { - XMLStructure info = (XMLStructure) ki.next(); - if (!(info instanceof X509Data)) - continue; - X509Data x509Data = (X509Data) info; - @SuppressWarnings("rawtypes") - Iterator xi = x509Data.getContent().iterator(); - while (xi.hasNext()) { - Object o = xi.next(); - if (!(o instanceof X509Certificate)) - continue; - final PublicKey key = ((X509Certificate) o).getPublicKey(); - // Make sure the algorithm is compatible - // with the method. - if (algEquals(method.getAlgorithm(), key.getAlgorithm())) { - return new KeySelectorResult() { - public Key getKey() { - return key; - } - }; - } - } - } - throw new KeySelectorException("No key found!"); - } -} diff --git a/tool/src/main/java/samlraider/helpers/XMLHelpers.java b/tool/src/main/java/samlraider/helpers/XMLHelpers.java deleted file mode 100644 index a6c2b89..0000000 --- a/tool/src/main/java/samlraider/helpers/XMLHelpers.java +++ /dev/null @@ -1,703 +0,0 @@ -/* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) - * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package samlraider.helpers; - -import org.apache.xml.security.Init; -import org.apache.xml.security.signature.XMLSignature; -import org.apache.xml.security.transforms.Transforms; -import org.w3c.dom.*; -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - -import javax.xml.crypto.MarshalException; -import javax.xml.crypto.XMLStructure; -import javax.xml.crypto.dsig.*; -import javax.xml.crypto.dsig.dom.DOMSignContext; -import javax.xml.crypto.dsig.dom.DOMValidateContext; -import javax.xml.crypto.dsig.keyinfo.KeyInfo; -import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory; -import javax.xml.crypto.dsig.keyinfo.X509Data; -import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import javax.xml.xpath.*; -import java.io.IOException; -import java.io.StringReader; -import java.io.StringWriter; -import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.cert.CertificateException; -import java.security.cert.X509Certificate; -import java.security.spec.InvalidKeySpecException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; - -public class XMLHelpers { - - - /** - * Returns a namespace aware document builder factory. - * - * @return DocumentBuilderFactory NamespaceAware - */ - public DocumentBuilderFactory getDBF() { - try { - DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); - documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false); - documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); - documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); - documentBuilderFactory.setFeature(javax.xml.XMLConstants.FEATURE_SECURE_PROCESSING, true); - documentBuilderFactory.setNamespaceAware(true); - return documentBuilderFactory; - } catch (ParserConfigurationException e) { - e.printStackTrace(); - } - return null; - } - - /** - * Returns a string serialization of a string - * - * @param document document which should be converted to a string - * @return string of document - * @throws IOException If an error in serialization occurred - */ - public String getString(Document document) throws IOException { - return getString(document, false, 0); - } - - - public String getString(Document doc, boolean indenting, int indent) throws IOException { - // https://stackoverflow.com/a/2567428 Taken from here - try { - StringWriter sw = new StringWriter(); - TransformerFactory tf = TransformerFactory.newInstance(); - Transformer transformer = tf.newTransformer(); - transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no"); - transformer.setOutputProperty(OutputKeys.METHOD, "xml"); - transformer.setOutputProperty(OutputKeys.INDENT, "no"); - transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8"); - - transformer.transform(new DOMSource(doc), new StreamResult(sw)); - return sw.toString(); - } catch (Exception ex) { - throw new RuntimeException("Error converting to String", ex); - } - } - - /** - * Returns a string serialization of a string, use indent and linebreaks = - * true to pretty print a document - * - * @param document document which should be converted to a string - * @param indent amount of indent - * @param linebreaks if line breaks should be inserted - * @return string of document, pretty or linearized - * @throws IOException if an Serializer error occures - */ - public String getStringOfDocument(Document document, int indent, boolean linebreaks) throws IOException { - document.normalize(); - removeEmptyTags(document); - return getString(document, linebreaks, indent); - } - - /** - * Converts a string representation of a XML document in a document Object - * - * @param message String representation of a XML document - * @return Document of XML string - * @throws SAXException If any parse errors occur. - */ - public Document getXMLDocumentOfSAMLMessage(String message) throws SAXException { - try { - DocumentBuilderFactory documentBuilderFactory = getDBF(); - DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); - Document document = documentBuilder.parse(new InputSource(new StringReader(message))); - return document; - } catch (ParserConfigurationException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - return null; - } - - /** - * Returns all Signatures of the given Document - * - * @param document document with signatures - * @return NodeList with signatures - */ - public NodeList getSignatures(Document document) { - NodeList nl = document.getElementsByTagNameNS("*", "Signature"); - return nl; - } - - /** - * Removes empty tags, spaces between XML tags - * - * @param document document in which the empty tags should be removed - */ - public void removeEmptyTags(Document document) { - NodeList nl = null; - try { - if (Thread.currentThread().getContextClassLoader() == null) { - Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); - } - XPath xPath = XPathFactory.newInstance().newXPath(); - nl = (NodeList) xPath.evaluate("//text()[normalize-space(.)='']", document, XPathConstants.NODESET); - - for (int i = 0; i < nl.getLength(); ++i) { - Node node = nl.item(i); - node.getParentNode().removeChild(node); - } - - } catch (XPathExpressionException e) { - e.printStackTrace(); - } - } - - /** - * Removes all signatures in a given XML document - * - * @param document document in which the signature should be removed - * @return number of removed signatures - */ - public int removeAllSignatures(Document document) { - NodeList nl = getSignatures(document); - int nrSig = nl.getLength(); - - for (int i = 0; i < nrSig; i++) { - Node parent = nl.item(0).getParentNode(); - parent.removeChild(nl.item(0)); - } - removeEmptyTags(document); - document.normalize(); - return nrSig; - } - - /** - * Removes a signature in a given XML document - * - * @param document document in which the signature should be removed - * @return number of removed signatures - */ - public int removeOnlyMessageSignature(Document document) { - try { - if (Thread.currentThread().getContextClassLoader() == null) { - Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); - } - setIDAttribute(document); - XPath xpath = XPathFactory.newInstance().newXPath(); - XPathExpression expr = xpath.compile("//*[local-name()='Response']/*[local-name()='Signature']"); - NodeList nl = (NodeList) expr.evaluate(document, XPathConstants.NODESET); - - int nrSig = nl.getLength(); - - for (int i = 0; i < nrSig; i++) { - Node parent = nl.item(0).getParentNode(); - parent.removeChild(nl.item(0)); - } - removeEmptyTags(document); - document.normalize(); - return nrSig; - } catch (XPathExpressionException e) { - e.printStackTrace(); - } - return 0; - } - - /** - * Returns a NodeList with assertions of the given XML document - * - * @param document document with the assertions - * @return NodeList with assertions - */ - public NodeList getAssertions(Document document) { - return document.getElementsByTagNameNS("*", "Assertion"); - } - - - /** - * Returns a NodeList with encrypted assertions of the given XML document - * - * @param document document with the encrypted assertions - * @return NodeList with encrypted assertions - */ - public NodeList getEncryptedAssertions(Document document) { - return document.getElementsByTagNameNS("*", "EncryptedAssertion"); - } - - /** - * Returns SOAP Body as an Element - * - * @param document document with SOAP body - * @return Element SOAP Body Element or null if no body found - */ - public Element getSOAPBody(Document document) { - try { - if (Thread.currentThread().getContextClassLoader() == null) { - Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); - } - XPath xpath = XPathFactory.newInstance().newXPath(); - XPathExpression expr = xpath.compile("//*[local-name()='Envelope']/*[local-name()='Body']"); - NodeList elements = (NodeList) expr.evaluate(document, XPathConstants.NODESET); - if (elements.getLength() > 0) { - return (Element) elements.item(0); - } - } catch (XPathExpressionException e) { - e.printStackTrace(); - } - return null; - } - - /** - * Returns SAML Response out of SOAP Body as an Element - * - * @param document document with SOAP envelope - * @return Document SAML Response - */ - public Document getSAMLResponseOfSOAP(Document document) throws ParserConfigurationException { - Element body = getSOAPBody(document); - DocumentBuilderFactory documentBuilderFactory = getDBF(); - DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); - Document documentSAML = documentBuilder.newDocument(); - Element SAMLresponseOld = (Element) body.getFirstChild(); - Element SAMLresponse = (Element) documentSAML.adoptNode(SAMLresponseOld); - documentSAML.appendChild(SAMLresponse); - return documentSAML; - } - - /** - * Returns a NodeList response Element in it - * - * @param document document with the response - * @return NodeList with response element - */ - public NodeList getResponse(Document document) { - return document.getElementsByTagNameNS("*", "Response"); - } - - /** - * Returns the attribute value of a XML tag - * - * @param element DOM element which contains the attribute - * @param attributeName name of the Attribute - * @return attribute if found attribute value otherwise an empty string - */ - private String getAttributeValueByName(Element element, String attributeName) { - if (element == null) { - return ""; - } - Attr attribute = (Attr) element.getAttributes().getNamedItem(attributeName); - if (attribute != null) { - return attribute.getNodeValue(); - } - return ""; - } - - /** - * Returns the issuer of an SAML Message - * - * @param document Document which contains the issuer - * @return Issuer of message / first Assertion if found, else empty string - */ - public String getIssuer(Document document) { - NodeList nl = document.getElementsByTagNameNS("*", "Issuer"); - if (nl.getLength() > 0) { - return nl.item(0).getTextContent(); - } - return ""; - } - - /** - * Returns NotBefore Date Attribute of Condition Element - * - * @param assertion Assertion with Condition tag - * @return NotBefore date Attribute of Condition Element if found, else - * empty string - */ - public String getConditionNotBefore(Node assertion) { - if (assertion == null || !assertion.getLocalName().equals("Assertion")) { - return "no assertion"; - } - Element conditions = (Element) ((Element) assertion).getElementsByTagNameNS("*", "Conditions").item(0); - return getAttributeValueByName(conditions, "NotBefore"); - } - - /** - * Returns NotOnOrAfter Date Attribute of Condition Element - * - * @param assertion Assertion with Condition tag - * @return NotOnOrAfter Date Attribute of Condition Element if found, else - * empty string - */ - public String getConditionNotAfter(Node assertion) { - if (assertion == null || !assertion.getLocalName().equals("Assertion")) { - return "no assertion"; - } - Element conditions = (Element) ((Element) assertion).getElementsByTagNameNS("*", "Conditions").item(0); - return getAttributeValueByName(conditions, "NotOnOrAfter"); - } - - /** - * Returns NotBefore Date Attribute of SubjectConfirmation Element - * - * @param assertion Assertion with SubjectConfirmation tag - * @return NotBefore Date Attribute of SubjectConfirmation Element if found, - * else empty string - */ - public String getSubjectConfNotBefore(Node assertion) { - if (assertion == null || !assertion.getLocalName().equals("Assertion")) { - return "no assertion"; - } - Element subjConfirmation = (Element) ((Element) assertion).getElementsByTagNameNS("*", - "SubjectConfirmationData").item(0); - return getAttributeValueByName(subjConfirmation, "NotBefore"); - } - - /** - * Returns NotOnOrAfter Date Attribute of SubjectConfirmation Element - * - * @param assertion Assertion with SubjectConfirmation tag - * @return NotOnOrAfter Date Attribute of SubjectConfirmation Element if - * found, else empty string - */ - public String getSubjectConfNotAfter(Node assertion) { - if (assertion == null || !assertion.getLocalName().equals("Assertion")) { - return "no assertion"; - } - Element subjConfirmation = (Element) ((Element) assertion).getElementsByTagNameNS("*", - "SubjectConfirmationData").item(0); - return getAttributeValueByName(subjConfirmation, "NotOnOrAfter"); - } - - /** - * Returns Signature Algorithm of Node which is signed - * - * @param node node with Signature - * @return Signature Algorithm of Node which is signed - */ - public String getSignatureAlgorithm(Node node) { - if (node == null) { - return "no element"; - } - Element signatureMethod = (Element) ((Element) node).getElementsByTagNameNS("*", "SignatureMethod").item(0); - return getAttributeValueByName(signatureMethod, "Algorithm"); - } - - /** - * Returns Digest Algorithm of Node which is signed - * - * @param node node with Signature - * @return Digest Algorithm of Node which is signed - */ - public String getDigestAlgorithm(Node node) { - if (node == null) { - return "no element"; - } - Element digestMethod = (Element) ((Element) node).getElementsByTagNameNS("*", "DigestMethod").item(0); - return getAttributeValueByName(digestMethod, "Algorithm"); - } - - /** - * Returns encryption algorithm of encrypted assertion - * - * @param assertion encrypted assertion node - * @return encryption algorithm of encrypted assertion - */ - public String getEncryptionMethod(Node assertion) { - if (assertion == null || !assertion.getLocalName().equals("EncryptedAssertion")) { - return "no encryption"; - } - Element encryptionMethod = (Element) ((Element) assertion).getElementsByTagNameNS("*", "EncryptionMethod") - .item(0); - return getAttributeValueByName(encryptionMethod, "Algorithm"); - } - - /** - * Returns embedded x509 certificate of signature - * - * @param node node with embedded x509 certificate, no matter how deeply nested - * @return first embedded x509 certificate of signature or null if not found - */ - public String getCertificate(Node node) { - NodeList certificates = ((Element) node).getElementsByTagNameNS("*", "X509Certificate"); - - if (certificates.getLength() > 0) { - Element certificate = (Element) certificates.item(0); - return certificate.getTextContent(); - } - return null; - } - - /** - * Set the ID Attribute in an XML Document so that java recognises the ID - * Attribute as a real id - * - * @param document Document to set the ids - */ - public void setIDAttribute(Document document) { - try { - if (Thread.currentThread().getContextClassLoader() == null) { - Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); - } - XPath xpath = XPathFactory.newInstance().newXPath(); - XPathExpression expr = xpath.compile("//*[@ID]"); - NodeList nodeList = (NodeList) expr.evaluate(document, XPathConstants.NODESET); - for (int i = 0; i < nodeList.getLength(); i++) { - Element elem = (Element) nodeList.item(i); - Attr attr = (Attr) elem.getAttributes().getNamedItem("ID"); - elem.setIdAttributeNode(attr, true); - } - } catch (XPathExpressionException e) { - e.printStackTrace(); - } - } - - /** - * Sign assertions in SAML message - * - * @param document Document in assertions should be signed - * @param signAlgorithm Signature algorithm in uri form, default if an unknown - * algorithm is provided: - * http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 - * @param digestAlgorithm Digest algorithm in uri form, default if an unknown algorithm - * is provided: http://www.w3.org/2001/04/xmlenc#sha256 - */ - public void signAssertion(Document document, String signAlgorithm, String digestAlgorithm, X509Certificate cert, PrivateKey key) - throws CertificateException, NoSuchAlgorithmException, InvalidKeySpecException, - MarshalException, XMLSignatureException, IOException { - try { - if (Thread.currentThread().getContextClassLoader() == null) { - Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); - } - setIDAttribute(document); - XPath xpath = XPathFactory.newInstance().newXPath(); - XPathExpression expr = xpath.compile("//*[local-name()='Assertion']/@ID"); - NodeList nlURIs = (NodeList) expr.evaluate(document, XPathConstants.NODESET); - - String[] sigIDs = new String[nlURIs.getLength()]; - - for (int i = 0; i < nlURIs.getLength(); i++) { - sigIDs[i] = nlURIs.item(i).getNodeValue(); - } - - Init.init(); - for (String id : sigIDs) { - signElement(document, id, cert, key, signAlgorithm, digestAlgorithm); - } - } catch (XPathExpressionException e) { - e.printStackTrace(); - } - } - - /** - * Sign whole SAML Message - * - * @param document Document with the response to sign - * @param signAlgorithm Signature algorithm in uri form, default if an unknown - * algorithm is provided: - * http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 - * @param digestAlgorithm Digest algorithm in uri form, default if an unknown algorithm - * is provided: http://www.w3.org/2001/04/xmlenc#sha256 - */ - public void signMessage(Document document, String signAlgorithm, String digestAlgorithm, X509Certificate cert, PrivateKey key) - throws CertificateException, NoSuchAlgorithmException, InvalidKeySpecException, - MarshalException, XMLSignatureException, IOException { - try { - if (Thread.currentThread().getContextClassLoader() == null) { - Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); - } - setIDAttribute(document); - XPath xpath = XPathFactory.newInstance().newXPath(); - XPathExpression expr = xpath.compile("//*[local-name()='Response']/@ID"); - NodeList nlURIs = (NodeList) expr.evaluate(document, XPathConstants.NODESET); - - String[] sigIDs = new String[nlURIs.getLength()]; - - for (int i = 0; i < nlURIs.getLength(); i++) { - sigIDs[i] = nlURIs.item(i).getNodeValue(); - } - - Init.init(); - for (String id : sigIDs) { - signElement(document, id, cert, key, signAlgorithm, digestAlgorithm); - } - } catch (XPathExpressionException e) { - e.printStackTrace(); - } - } - - - /** - * Sign the assertion with the given id - * - * @param doc Document in which the assertion with the given id should be - * signed - * @param id the signature algorithm - * @param key the private key to sign the assertion - * @param cert the certificate which should be included in the assertions - * signed info - * @param signAlgorithm Signature algorithm in uri form, default if an unknown - * algorithm is provided: - * http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 - * @param digestAlgorithm Digest algorithm in uri form, default if an unknown algorithm - * is provided: http://www.w3.org/2001/04/xmlenc#sha256 - */ - private Document signElement(Document doc, String id, X509Certificate cert, PrivateKey key, String signAlgorithm, - String digestAlgorithm) throws MarshalException, XMLSignatureException { - - try { - XMLSignatureFactory xmlSignatureFactory = XMLSignatureFactory.getInstance("DOM", - new org.jcp.xml.dsig.internal.dom.XMLDSigRI()); - List transforms = new ArrayList(); - Transform enveloped = xmlSignatureFactory.newTransform(Transforms.TRANSFORM_ENVELOPED_SIGNATURE, - (XMLStructure) null); - transforms.add(enveloped); - Transform c14n = xmlSignatureFactory.newTransform(Transforms.TRANSFORM_C14N_EXCL_OMIT_COMMENTS, - (XMLStructure) null); - transforms.add(c14n); - - Reference ref; - try { - ref = xmlSignatureFactory.newReference("#" + id, - xmlSignatureFactory.newDigestMethod(digestAlgorithm, null), transforms, null, null); - } catch (NoSuchAlgorithmException e) { - ref = xmlSignatureFactory.newReference("#" + id, - xmlSignatureFactory.newDigestMethod(DigestMethod.SHA256, null), transforms, null, null); - } - - SignedInfo signedInfo; - try { - signedInfo = xmlSignatureFactory.newSignedInfo(xmlSignatureFactory.newCanonicalizationMethod( - CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec) null), xmlSignatureFactory - .newSignatureMethod(signAlgorithm, null), Collections.singletonList(ref)); - } catch (NoSuchAlgorithmException e) { - signedInfo = xmlSignatureFactory.newSignedInfo(xmlSignatureFactory.newCanonicalizationMethod( - CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec) null), xmlSignatureFactory - .newSignatureMethod(XMLSignature.ALGO_ID_SIGNATURE_RSA_SHA256, null), Collections - .singletonList(ref)); - } - - KeyInfoFactory keyInfoFactory = xmlSignatureFactory.getKeyInfoFactory(); - List x509Content = new ArrayList<>(); - x509Content.add(cert); - X509Data x509Data = keyInfoFactory.newX509Data(x509Content); - KeyInfo keyInfo = keyInfoFactory.newKeyInfo(Collections.singletonList(x509Data)); - - Element elementToSign = doc.getElementById(id); - NodeList issuerList = elementToSign.getElementsByTagNameNS("*", "Issuer"); - Element elementBeforeSignature; - - if (issuerList.getLength() > 0) { - elementBeforeSignature = (Element) issuerList.item(0); - } else { - elementBeforeSignature = elementToSign; - } - - // find next sibling node of Element type - Node nextNodeAfterIssuer = elementBeforeSignature.getNextSibling(); - while (nextNodeAfterIssuer != null && nextNodeAfterIssuer.getNodeType() != Node.ELEMENT_NODE) { - nextNodeAfterIssuer = nextNodeAfterIssuer.getNextSibling(); - } - Element nextElementAfterIssuer = (Element) nextNodeAfterIssuer; - - DOMSignContext domSignContext = new DOMSignContext(key, elementToSign); - domSignContext.setDefaultNamespacePrefix("ds"); - domSignContext.setNextSibling(nextElementAfterIssuer); - - javax.xml.crypto.dsig.XMLSignature signature = xmlSignatureFactory.newXMLSignature(signedInfo, keyInfo); - signature.sign(domSignContext); - - return doc; - } catch (InvalidAlgorithmParameterException e) { - e.printStackTrace(); - } catch (NoSuchAlgorithmException e1) { - e1.printStackTrace(); - } - return null; - } - - /*------------ - //Source: http://www.oracle.com/technetwork/articles/javase/dig-signature-api-140772.html - ------------*/ - - /** - * Validates if the first XML Signature of the given document is valid - * Only used for test purposes - * - * @param document Document with signature to validate - * @return true if valid, else false - */ - public boolean validateSignature(Document document) throws Exception { - - setIDAttribute(document); - XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM"); - - // Find Signature element. - NodeList nl = document.getElementsByTagNameNS(javax.xml.crypto.dsig.XMLSignature.XMLNS, "Signature"); - if (nl.getLength() == 0) { - throw new Exception("Cannot find Signature element"); - } - - // Create a DOMValidateContext and specify a KeySelector - // and document context. - DOMValidateContext valContext = new DOMValidateContext(new X509KeySelector(), nl.item(0)); - - // Unmarshal the XMLSignature - javax.xml.crypto.dsig.XMLSignature signature = fac.unmarshalXMLSignature(valContext); - - // Validate the XMLSignature. - boolean coreValidity = signature.validate(valContext); - - // Check core validation status. - if (coreValidity == false) { - boolean sv = signature.getSignatureValue().validate(valContext); - if (sv == false) { - if (Flags.DEBUG) { - // Check the validation status of each Reference. - @SuppressWarnings("rawtypes") - Iterator i = signature.getSignedInfo().getReferences().iterator(); - for (int j = 0; i.hasNext(); j++) { - boolean refValid = ((Reference) i.next()).validate(valContext); - System.out.println("ref[" + j + "] validity status: " + refValid); - } - } - } - } - return coreValidity; - } -} diff --git a/tool/src/main/java/samlraider/helpers/XSWHelpers.java b/tool/src/main/java/samlraider/helpers/XSWHelpers.java deleted file mode 100644 index 7256060..0000000 --- a/tool/src/main/java/samlraider/helpers/XSWHelpers.java +++ /dev/null @@ -1,297 +0,0 @@ -/* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) - * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package samlraider.helpers; - -import samlraider.application.BurpCertificateBuilder; -import samlraider.model.BurpCertificate; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.SAXException; - -import javax.xml.crypto.MarshalException; -import javax.xml.crypto.dsig.XMLSignatureException; -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.SignatureException; -import java.security.cert.CertificateException; -import java.security.spec.InvalidKeySpecException; -import java.util.HashMap; -import java.util.LinkedList; - -public class XSWHelpers { - - // XSW9 was removed b/c it does not work. Code is still there if you want to have a look :) - public final static String[] xswTypes = {"XSW1", "XSW2", "XSW3", "XSW4", "XSW5", "XSW6", "XSW7", "XSW8"}; - - public static final HashMap MATCH_AND_REPLACE_MAP = new HashMap<>(); - - /* - * Following are the 8 common XML Signature Wrapping attacks implemented, which - * were found in a paper called "On Breaking SAML: Be Whoever You Want to Be" We - * have also documented these attacks in our product documentation for further - * information - * - */ - - public void applyXSW(String xswType, Document document) { - switch (xswType) { - case "XSW1": - applyXSW1(document); - break; - case "XSW2": - applyXSW2(document); - break; - case "XSW3": - applyXSW3(document); - break; - case "XSW4": - applyXSW4(document); - break; - case "XSW5": - applyXSW5(document); - break; - case "XSW6": - applyXSW6(document); - break; - case "XSW7": - applyXSW7(document); - break; - case "XSW8": - applyXSW8(document); - break; - case "XSW9": - applyXSW9(document); - break; - } - } - - // Fixed - clonedSignature was not found in clonedResponse - public void applyXSW1(Document document) { - Element response = (Element) document.getElementsByTagNameNS("*", "Response").item(0); - Element clonedResponse = (Element) response.cloneNode(true); - // The Original response will be the evil one - applyMatchAndReplaceValues(response); - Element clonedSignature = (Element) clonedResponse.getElementsByTagNameNS("*", "Signature").item(0); - clonedSignature.getParentNode().removeChild(clonedSignature); - Element signature = (Element) response.getElementsByTagNameNS("*", "Signature").item(0); - signature.appendChild(clonedResponse); - response.setAttribute("ID", "_evil_response_ID"); - } - - // Fixed - clonedSignature was not found in clonedResponse - public void applyXSW2(Document document) { - Element response = (Element) document.getElementsByTagNameNS("*", "Response").item(0); - Element clonedResponse = (Element) response.cloneNode(true); - // The Original response will be the evil one - applyMatchAndReplaceValues(response); - Element clonedSignature = (Element) clonedResponse.getElementsByTagNameNS("*", "Signature").item(0); - clonedSignature.getParentNode().removeChild(clonedSignature); - Element signature = (Element) response.getElementsByTagNameNS("*", "Signature").item(0); - signature.getParentNode().insertBefore(clonedResponse, signature); - response.setAttribute("ID", "_evil_response_ID"); - } - - public void applyXSW3(Document document) { - Element assertion = (Element) document.getElementsByTagNameNS("*", "Assertion").item(0); - Element evilAssertion = (Element) assertion.cloneNode(true); - applyMatchAndReplaceValues(evilAssertion); - Element copiedSignature = (Element) evilAssertion.getElementsByTagNameNS("*", "Signature").item(0); - evilAssertion.setAttribute("ID", "_evil_assertion_ID"); - evilAssertion.removeChild(copiedSignature); - document.getDocumentElement().insertBefore(evilAssertion, assertion); - } - - public void applyXSW4(Document document) { - Element assertion = (Element) document.getElementsByTagNameNS("*", "Assertion").item(0); - Element evilAssertion = (Element) assertion.cloneNode(true); - applyMatchAndReplaceValues(evilAssertion); - Element copiedSignature = (Element) evilAssertion.getElementsByTagNameNS("*", "Signature").item(0); - evilAssertion.setAttribute("ID", "_evil_assertion_ID"); - evilAssertion.removeChild(copiedSignature); - document.getDocumentElement().appendChild(evilAssertion); - evilAssertion.appendChild(assertion); - } - - public void applyXSW5(Document document) { - Element evilAssertion = (Element) document.getElementsByTagNameNS("*", "Assertion").item(0); - Element assertion = (Element) evilAssertion.cloneNode(true); - applyMatchAndReplaceValues(evilAssertion); - Element copiedSignature = (Element) assertion.getElementsByTagNameNS("*", "Signature").item(0); - assertion.removeChild(copiedSignature); - document.getDocumentElement().appendChild(assertion); - evilAssertion.setAttribute("ID", "_evil_assertion_ID"); - } - - public void applyXSW6(Document document) { - Element evilAssertion = (Element) document.getElementsByTagNameNS("*", "Assertion").item(0); - Element originalSignature = (Element) evilAssertion.getElementsByTagNameNS("*", "Signature").item(0); - Element assertion = (Element) evilAssertion.cloneNode(true); - applyMatchAndReplaceValues(evilAssertion); - Element copiedSignature = (Element) assertion.getElementsByTagNameNS("*", "Signature").item(0); - assertion.removeChild(copiedSignature); - originalSignature.appendChild(assertion); - evilAssertion.setAttribute("ID", "_evil_assertion_ID"); - } - - public void applyXSW7(Document document) { - Element assertion = (Element) document.getElementsByTagNameNS("*", "Assertion").item(0); - Element extensions = document.createElement("Extensions"); - document.getDocumentElement().insertBefore(extensions, assertion); - Element evilAssertion = (Element) assertion.cloneNode(true); - applyMatchAndReplaceValues(evilAssertion); - Element copiedSignature = (Element) evilAssertion.getElementsByTagNameNS("*", "Signature").item(0); - evilAssertion.removeChild(copiedSignature); - extensions.appendChild(evilAssertion); - } - - public void applyXSW8(Document document) { - Element evilAssertion = (Element) document.getElementsByTagNameNS("*", "Assertion").item(0); - Element originalSignature = (Element) evilAssertion.getElementsByTagNameNS("*", "Signature").item(0); - Element assertion = (Element) evilAssertion.cloneNode(true); - applyMatchAndReplaceValues(evilAssertion); - Element copiedSignature = (Element) assertion.getElementsByTagNameNS("*", "Signature").item(0); - assertion.removeChild(copiedSignature); - Element object = document.createElement("Object"); - originalSignature.appendChild(object); - object.appendChild(assertion); - } - - /* - * CVE-2019-3465 - * XMLSecLibs <= 31.4.2, 2.1.0 & 3.0.3 - * SimpleSAMLphp <= 1.17.6 - * Duo Authentication Gateway (DAG) <= 1.5.9 (https://duo.com/labs/psa/duo-psa-2019-002) - * Moodle auth_saml2 < 2019110701 (https://twitter.com/Catalyst_IT_AU/status/1192353402753208320) - * Advisory: https://simplesamlphp.org/security/201911-01 - * Vuln. discovery and write up by Hackmanit: - * https://www.hackmanit.de/en/blog-en/82-xml-signature-validation-bypass-in-simplesamlphp-and-xmlseclibs - * Tweet by @jurajsomorovsky: https://twitter.com/jurajsomorovsky/status/1192452032835325952 - * - * TODO: This does not yet work :/ - */ - public void applyXSW9(Document document) { - - try { - XMLHelpers xmlHelpers = new XMLHelpers(); - - // Calculate new digest by signing the document - Document documentToSign = xmlHelpers.getXMLDocumentOfSAMLMessage(xmlHelpers.getStringOfDocument(document, 2, true)); - Element evilAssertion = (Element) documentToSign.getElementsByTagNameNS("*", "Assertion").item(0); - evilAssertion.setAttribute("ID", "_evil_assertion_ID"); - applyMatchAndReplaceValues(evilAssertion); - Document documentNewDigest = selfSignAssertion(documentToSign); - - // Remove new SignatureValue and KeyInfo - Element newAssertion = (Element) documentNewDigest.getElementsByTagNameNS("*", "Assertion").item(0); - Element signatureToModify = (Element) newAssertion.getElementsByTagNameNS("*", "Signature").item(0); - Element signatureValueToRemove = (Element) newAssertion.getElementsByTagNameNS("*", "SignatureValue").item(0); - signatureToModify.removeChild(signatureValueToRemove); - Element keyInfoToRemove = (Element) newAssertion.getElementsByTagNameNS("*", "KeyInfo").item(0); - signatureToModify.removeChild(keyInfoToRemove); - - // Read original SignatureValue, KeyInfo & SignedInfo - Element originalAssertion = (Element) document.getElementsByTagNameNS("*", "Assertion").item(0); - Element originalSignatureValue = (Element) originalAssertion.getElementsByTagNameNS("*", "SignatureValue").item(0); - Element originalKeyInfo = (Element) originalAssertion.getElementsByTagNameNS("*", "KeyInfo").item(0); - Element originalSignedInfo = (Element) originalAssertion.getElementsByTagNameNS("*", "SignedInfo").item(0); - - // Add original SignatureValue and KeyInfo to new document - Element fakeSignedInfo = (Element) newAssertion.getElementsByTagNameNS("*", "SignedInfo").item(0); - signatureToModify.insertBefore(documentNewDigest.adoptNode(originalSignedInfo.cloneNode(true)), fakeSignedInfo); - signatureToModify.insertBefore(documentNewDigest.adoptNode(originalSignatureValue.cloneNode(true)), fakeSignedInfo); - // signatureToModify.insertBefore(documentNewDigest.adoptNode(originalKeyInfo.cloneNode(true)), fakeSignedInfo); - - // Add wrapper element and original assertion to the end - Element wrapper = documentNewDigest.createElement("Wrapper"); - newAssertion.getParentNode().appendChild(wrapper); - wrapper.appendChild(documentNewDigest.adoptNode(originalAssertion.cloneNode(true))); - - // Print for testing... - - - } catch (IOException | SAXException e) { - e.printStackTrace(); - } - } - - // Used for XSW9 - private Document selfSignAssertion(Document document) { - - try { - BurpCertificateBuilder burpCertificateBuilder = new BurpCertificateBuilder("CN=samlraider-temporary-cert.example.net"); - BurpCertificate burpCertificate = burpCertificateBuilder.generateSelfSignedCertificate(); - XMLHelpers xmlHelpers = new XMLHelpers(); - - NodeList assertions = xmlHelpers.getAssertions(document); - String signAlgorithm = xmlHelpers.getSignatureAlgorithm(assertions.item(0)); - String digestAlgorithm = xmlHelpers.getDigestAlgorithm(assertions.item(0)); - xmlHelpers.removeAllSignatures(document); - - String string = xmlHelpers.getString(document); - Document documentToSign = xmlHelpers.getXMLDocumentOfSAMLMessage(string); - xmlHelpers.removeEmptyTags(documentToSign); - xmlHelpers.signAssertion(documentToSign, signAlgorithm, digestAlgorithm, burpCertificate.getCertificate(), burpCertificate.getPrivateKey()); - return documentToSign; - - } catch (InvalidKeyException | NoSuchAlgorithmException | SignatureException | NoSuchProviderException | - InvalidKeySpecException | IOException | CertificateException | XMLSignatureException | SAXException | - MarshalException e) { - e.printStackTrace(); - } - return null; - } - - public String diffLineMode(String text1, String text2) { - diff_match_patch differ = new diff_match_patch(); - differ.Diff_Timeout = 5; - diff_match_patch.LinesToCharsResult result = differ.diff_linesToChars(text1, text2); - - LinkedList diffs = differ.diff_main(result.chars1, result.chars2, false); - differ.diff_charsToLines(diffs, result.lineArray); - return differ.diff_prettyHtml(diffs); - } - - public void applyMatchAndReplaceValues(Node elem) { - for (int i = 0; i < elem.getChildNodes().getLength(); i++) { - Node currentNode = elem.getChildNodes().item(i); - if (currentNode.getNodeType() == Node.ELEMENT_NODE) { - applyMatchAndReplaceValues(currentNode); - } else { - if (!currentNode.getNodeValue().trim().equals("")) { - for (String matchString : MATCH_AND_REPLACE_MAP.keySet()) { - if (currentNode.getNodeValue().equals(matchString)) { - currentNode.setNodeValue(MATCH_AND_REPLACE_MAP.get(matchString)); - } - } - } - } - } - } -} \ No newline at end of file diff --git a/tool/src/main/java/samlraider/helpers/diff_match_patch.java b/tool/src/main/java/samlraider/helpers/diff_match_patch.java deleted file mode 100644 index dbe1322..0000000 --- a/tool/src/main/java/samlraider/helpers/diff_match_patch.java +++ /dev/null @@ -1,2506 +0,0 @@ -/* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) - * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -/* - * Diff Match and Patch - * - * Copyright 2006 Google Inc. - * http://code.google.com/p/google-diff-match-patch/ - * - * 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. - */ - -package samlraider.helpers; - -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/* - * Functions for diff, match and patch. - * Computes the difference between two texts to create a patch. - * Applies the patch onto another text, allowing for errors. - * - * @author fraser@google.com (Neil Fraser) - */ - -/** - * Class containing the diff, match and patch methods. - * Also contains the behaviour settings. - */ -public class diff_match_patch { - - // Defaults. - // Set these on your diff_match_patch instance to override the defaults. - - /** - * The number of bits in an int. - */ - private final short Match_MaxBits = 32; - // Define some regex patterns for matching boundaries. - private final Pattern BLANKLINEEND - = Pattern.compile("\\n\\r?\\n\\Z", Pattern.DOTALL); - private final Pattern BLANKLINESTART - = Pattern.compile("\\A\\r?\\n\\r?\\n", Pattern.DOTALL); - /** - * Number of seconds to map a diff before giving up (0 for infinity). - */ - public float Diff_Timeout = 1.0f; - /** - * Cost of an empty edit operation in terms of edit characters. - */ - public short Diff_EditCost = 4; - /** - * At what point is no match declared (0.0 = perfection, 1.0 = very loose). - */ - public float Match_Threshold = 0.5f; - /** - * How far to search for a match (0 = exact location, 1000+ = broad match). - * A match this many characters away from the expected location will add - * 1.0 to the score (0.0 is a perfect match). - */ - public int Match_Distance = 1000; - /** - * When deleting a large block of text (over ~64 characters), how close do - * the contents have to be to match the expected contents. (0.0 = perfection, - * 1.0 = very loose). Note that Match_Threshold controls how closely the - * end points of a delete need to match. - */ - public float Patch_DeleteThreshold = 0.5f; - - - // DIFF FUNCTIONS - /** - * Chunk size for context length. - */ - public short Patch_Margin = 4; - - /** - * Unescape selected chars for compatability with JavaScript's encodeURI. - * In speed critical applications this could be dropped since the - * receiving application will certainly decode these fine. - * Note that this function is case-sensitive. Thus "%3f" would not be - * unescaped. But this is ok because it is only called with the output of - * URLEncoder.encode which returns uppercase hex. - *

- * Example: "%3F" -> "?", "%24" -> "$", etc. - * - * @param str The string to escape. - * @return The escaped string. - */ - private static String unescapeForEncodeUriCompatability(String str) { - return str.replace("%21", "!").replace("%7E", "~") - .replace("%27", "'").replace("%28", "(").replace("%29", ")") - .replace("%3B", ";").replace("%2F", "/").replace("%3F", "?") - .replace("%3A", ":").replace("%40", "@").replace("%26", "&") - .replace("%3D", "=").replace("%2B", "+").replace("%24", "$") - .replace("%2C", ",").replace("%23", "#"); - } - - /** - * Find the differences between two texts. - * Run a faster, slightly less optimal diff. - * This method allows the 'checklines' of diff_main() to be optional. - * Most of the time checklines is wanted, so default to true. - * - * @param text1 Old string to be diffed. - * @param text2 New string to be diffed. - * @return Linked List of Diff objects. - */ - public LinkedList diff_main(String text1, String text2) { - return diff_main(text1, text2, true); - } - - /** - * Find the differences between two texts. - * - * @param text1 Old string to be diffed. - * @param text2 New string to be diffed. - * @param checklines Speedup flag. If false, then don't run a - * line-level diff first to identify the changed areas. - * If true, then run a faster slightly less optimal diff. - * @return Linked List of Diff objects. - */ - public LinkedList diff_main(String text1, String text2, - boolean checklines) { - // Set a deadline by which time the diff must be complete. - long deadline; - if (Diff_Timeout <= 0) { - deadline = Long.MAX_VALUE; - } else { - deadline = System.currentTimeMillis() + (long) (Diff_Timeout * 1000); - } - return diff_main(text1, text2, checklines, deadline); - } - - /** - * Find the differences between two texts. Simplifies the problem by - * stripping any common prefix or suffix off the texts before diffing. - * - * @param text1 Old string to be diffed. - * @param text2 New string to be diffed. - * @param checklines Speedup flag. If false, then don't run a - * line-level diff first to identify the changed areas. - * If true, then run a faster slightly less optimal diff. - * @param deadline Time when the diff should be complete by. Used - * internally for recursive calls. Users should set DiffTimeout instead. - * @return Linked List of Diff objects. - */ - private LinkedList diff_main(String text1, String text2, - boolean checklines, long deadline) { - // Check for null inputs. - if (text1 == null || text2 == null) { - throw new IllegalArgumentException("Null inputs. (diff_main)"); - } - - // Check for equality (speedup). - LinkedList diffs; - if (text1.equals(text2)) { - diffs = new LinkedList(); - if (text1.length() != 0) { - diffs.add(new Diff(Operation.EQUAL, text1)); - } - return diffs; - } - - // Trim off common prefix (speedup). - int commonlength = diff_commonPrefix(text1, text2); - String commonprefix = text1.substring(0, commonlength); - text1 = text1.substring(commonlength); - text2 = text2.substring(commonlength); - - // Trim off common suffix (speedup). - commonlength = diff_commonSuffix(text1, text2); - String commonsuffix = text1.substring(text1.length() - commonlength); - text1 = text1.substring(0, text1.length() - commonlength); - text2 = text2.substring(0, text2.length() - commonlength); - - // Compute the diff on the middle block. - diffs = diff_compute(text1, text2, checklines, deadline); - - // Restore the prefix and suffix. - if (commonprefix.length() != 0) { - diffs.addFirst(new Diff(Operation.EQUAL, commonprefix)); - } - if (commonsuffix.length() != 0) { - diffs.addLast(new Diff(Operation.EQUAL, commonsuffix)); - } - - diff_cleanupMerge(diffs); - return diffs; - } - - /** - * Find the differences between two texts. Assumes that the texts do not - * have any common prefix or suffix. - * - * @param text1 Old string to be diffed. - * @param text2 New string to be diffed. - * @param checklines Speedup flag. If false, then don't run a - * line-level diff first to identify the changed areas. - * If true, then run a faster slightly less optimal diff. - * @param deadline Time when the diff should be complete by. - * @return Linked List of Diff objects. - */ - private LinkedList diff_compute(String text1, String text2, - boolean checklines, long deadline) { - LinkedList diffs = new LinkedList(); - - if (text1.length() == 0) { - // Just add some text (speedup). - diffs.add(new Diff(Operation.INSERT, text2)); - return diffs; - } - - if (text2.length() == 0) { - // Just delete some text (speedup). - diffs.add(new Diff(Operation.DELETE, text1)); - return diffs; - } - - String longtext = text1.length() > text2.length() ? text1 : text2; - String shorttext = text1.length() > text2.length() ? text2 : text1; - int i = longtext.indexOf(shorttext); - if (i != -1) { - // Shorter text is inside the longer text (speedup). - Operation op = (text1.length() > text2.length()) ? - Operation.DELETE : Operation.INSERT; - diffs.add(new Diff(op, longtext.substring(0, i))); - diffs.add(new Diff(Operation.EQUAL, shorttext)); - diffs.add(new Diff(op, longtext.substring(i + shorttext.length()))); - return diffs; - } - - if (shorttext.length() == 1) { - // Single character string. - // After the previous speedup, the character can't be an equality. - diffs.add(new Diff(Operation.DELETE, text1)); - diffs.add(new Diff(Operation.INSERT, text2)); - return diffs; - } - - // Check to see if the problem can be split in two. - String[] hm = diff_halfMatch(text1, text2); - if (hm != null) { - // A half-match was found, sort out the return data. - String text1_a = hm[0]; - String text1_b = hm[1]; - String text2_a = hm[2]; - String text2_b = hm[3]; - String mid_common = hm[4]; - // Send both pairs off for separate processing. - LinkedList diffs_a = diff_main(text1_a, text2_a, - checklines, deadline); - LinkedList diffs_b = diff_main(text1_b, text2_b, - checklines, deadline); - // Merge the results. - diffs = diffs_a; - diffs.add(new Diff(Operation.EQUAL, mid_common)); - diffs.addAll(diffs_b); - return diffs; - } - - if (checklines && text1.length() > 100 && text2.length() > 100) { - return diff_lineMode(text1, text2, deadline); - } - - return diff_bisect(text1, text2, deadline); - } - - /** - * Do a quick line-level diff on both strings, then rediff the parts for - * greater accuracy. - * This speedup can produce non-minimal diffs. - * - * @param text1 Old string to be diffed. - * @param text2 New string to be diffed. - * @param deadline Time when the diff should be complete by. - * @return Linked List of Diff objects. - */ - private LinkedList diff_lineMode(String text1, String text2, - long deadline) { - // Scan the text on a line-by-line basis first. - LinesToCharsResult b = diff_linesToChars(text1, text2); - text1 = b.chars1; - text2 = b.chars2; - List linearray = b.lineArray; - - LinkedList diffs = diff_main(text1, text2, false, deadline); - - // Convert the diff back to original text. - diff_charsToLines(diffs, linearray); - // Eliminate freak matches (e.g. blank lines) - diff_cleanupSemantic(diffs); - - // Rediff any replacement blocks, this time character-by-character. - // Add a dummy entry at the end. - diffs.add(new Diff(Operation.EQUAL, "")); - int count_delete = 0; - int count_insert = 0; - String text_delete = ""; - String text_insert = ""; - ListIterator pointer = diffs.listIterator(); - Diff thisDiff = pointer.next(); - while (thisDiff != null) { - switch (thisDiff.operation) { - case INSERT: - count_insert++; - text_insert += thisDiff.text; - break; - case DELETE: - count_delete++; - text_delete += thisDiff.text; - break; - case EQUAL: - // Upon reaching an equality, check for prior redundancies. - if (count_delete >= 1 && count_insert >= 1) { - // Delete the offending records and add the merged ones. - pointer.previous(); - for (int j = 0; j < count_delete + count_insert; j++) { - pointer.previous(); - pointer.remove(); - } - for (Diff newDiff : diff_main(text_delete, text_insert, false, - deadline)) { - pointer.add(newDiff); - } - } - count_insert = 0; - count_delete = 0; - text_delete = ""; - text_insert = ""; - break; - } - thisDiff = pointer.hasNext() ? pointer.next() : null; - } - diffs.removeLast(); // Remove the dummy entry at the end. - - return diffs; - } - - /** - * Find the 'middle snake' of a diff, split the problem in two - * and return the recursively constructed diff. - * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations. - * - * @param text1 Old string to be diffed. - * @param text2 New string to be diffed. - * @param deadline Time at which to bail if not yet complete. - * @return LinkedList of Diff objects. - */ - protected LinkedList diff_bisect(String text1, String text2, - long deadline) { - // Cache the text lengths to prevent multiple calls. - int text1_length = text1.length(); - int text2_length = text2.length(); - int max_d = (text1_length + text2_length + 1) / 2; - int v_offset = max_d; - int v_length = 2 * max_d; - int[] v1 = new int[v_length]; - int[] v2 = new int[v_length]; - for (int x = 0; x < v_length; x++) { - v1[x] = -1; - v2[x] = -1; - } - v1[v_offset + 1] = 0; - v2[v_offset + 1] = 0; - int delta = text1_length - text2_length; - // If the total number of characters is odd, then the front path will - // collide with the reverse path. - boolean front = (delta % 2 != 0); - // Offsets for start and end of k loop. - // Prevents mapping of space beyond the grid. - int k1start = 0; - int k1end = 0; - int k2start = 0; - int k2end = 0; - for (int d = 0; d < max_d; d++) { - // Bail out if deadline is reached. - if (System.currentTimeMillis() > deadline) { - break; - } - - // Walk the front path one step. - for (int k1 = -d + k1start; k1 <= d - k1end; k1 += 2) { - int k1_offset = v_offset + k1; - int x1; - if (k1 == -d || (k1 != d && v1[k1_offset - 1] < v1[k1_offset + 1])) { - x1 = v1[k1_offset + 1]; - } else { - x1 = v1[k1_offset - 1] + 1; - } - int y1 = x1 - k1; - while (x1 < text1_length && y1 < text2_length - && text1.charAt(x1) == text2.charAt(y1)) { - x1++; - y1++; - } - v1[k1_offset] = x1; - if (x1 > text1_length) { - // Ran off the right of the graph. - k1end += 2; - } else if (y1 > text2_length) { - // Ran off the bottom of the graph. - k1start += 2; - } else if (front) { - int k2_offset = v_offset + delta - k1; - if (k2_offset >= 0 && k2_offset < v_length && v2[k2_offset] != -1) { - // Mirror x2 onto top-left coordinate system. - int x2 = text1_length - v2[k2_offset]; - if (x1 >= x2) { - // Overlap detected. - return diff_bisectSplit(text1, text2, x1, y1, deadline); - } - } - } - } - - // Walk the reverse path one step. - for (int k2 = -d + k2start; k2 <= d - k2end; k2 += 2) { - int k2_offset = v_offset + k2; - int x2; - if (k2 == -d || (k2 != d && v2[k2_offset - 1] < v2[k2_offset + 1])) { - x2 = v2[k2_offset + 1]; - } else { - x2 = v2[k2_offset - 1] + 1; - } - int y2 = x2 - k2; - while (x2 < text1_length && y2 < text2_length - && text1.charAt(text1_length - x2 - 1) - == text2.charAt(text2_length - y2 - 1)) { - x2++; - y2++; - } - v2[k2_offset] = x2; - if (x2 > text1_length) { - // Ran off the left of the graph. - k2end += 2; - } else if (y2 > text2_length) { - // Ran off the top of the graph. - k2start += 2; - } else if (!front) { - int k1_offset = v_offset + delta - k2; - if (k1_offset >= 0 && k1_offset < v_length && v1[k1_offset] != -1) { - int x1 = v1[k1_offset]; - int y1 = v_offset + x1 - k1_offset; - // Mirror x2 onto top-left coordinate system. - x2 = text1_length - x2; - if (x1 >= x2) { - // Overlap detected. - return diff_bisectSplit(text1, text2, x1, y1, deadline); - } - } - } - } - } - // Diff took too long and hit the deadline or - // number of diffs equals number of characters, no commonality at all. - LinkedList diffs = new LinkedList(); - diffs.add(new Diff(Operation.DELETE, text1)); - diffs.add(new Diff(Operation.INSERT, text2)); - return diffs; - } - - /** - * Given the location of the 'middle snake', split the diff in two parts - * and recurse. - * - * @param text1 Old string to be diffed. - * @param text2 New string to be diffed. - * @param x Index of split point in text1. - * @param y Index of split point in text2. - * @param deadline Time at which to bail if not yet complete. - * @return LinkedList of Diff objects. - */ - private LinkedList diff_bisectSplit(String text1, String text2, - int x, int y, long deadline) { - String text1a = text1.substring(0, x); - String text2a = text2.substring(0, y); - String text1b = text1.substring(x); - String text2b = text2.substring(y); - - // Compute both diffs serially. - LinkedList diffs = diff_main(text1a, text2a, false, deadline); - LinkedList diffsb = diff_main(text1b, text2b, false, deadline); - - diffs.addAll(diffsb); - return diffs; - } - - /** - * Split two texts into a list of strings. Reduce the texts to a string of - * hashes where each Unicode character represents one line. - * - * @param text1 First string. - * @param text2 Second string. - * @return An object containing the encoded text1, the encoded text2 and - * the List of unique strings. The zeroth element of the List of - * unique strings is intentionally blank. - */ - protected LinesToCharsResult diff_linesToChars(String text1, String text2) { - List lineArray = new ArrayList(); - Map lineHash = new HashMap(); - // e.g. linearray[4] == "Hello\n" - // e.g. linehash.get("Hello\n") == 4 - - // "\x00" is a valid character, but various debuggers don't like it. - // So we'll insert a junk entry to avoid generating a null character. - lineArray.add(""); - - String chars1 = diff_linesToCharsMunge(text1, lineArray, lineHash); - String chars2 = diff_linesToCharsMunge(text2, lineArray, lineHash); - return new LinesToCharsResult(chars1, chars2, lineArray); - } - - /** - * Split a text into a list of strings. Reduce the texts to a string of - * hashes where each Unicode character represents one line. - * - * @param text String to encode. - * @param lineArray List of unique strings. - * @param lineHash Map of strings to indices. - * @return Encoded string. - */ - private String diff_linesToCharsMunge(String text, List lineArray, - Map lineHash) { - int lineStart = 0; - int lineEnd = -1; - String line; - StringBuilder chars = new StringBuilder(); - // Walk the text, pulling out a substring for each line. - // text.split('\n') would would temporarily double our memory footprint. - // Modifying text would create many large strings to garbage collect. - while (lineEnd < text.length() - 1) { - lineEnd = text.indexOf('\n', lineStart); - if (lineEnd == -1) { - lineEnd = text.length() - 1; - } - line = text.substring(lineStart, lineEnd + 1); - lineStart = lineEnd + 1; - - if (lineHash.containsKey(line)) { - chars.append((char) (int) lineHash.get(line)); - } else { - lineArray.add(line); - lineHash.put(line, lineArray.size() - 1); - chars.append((char) (lineArray.size() - 1)); - } - } - return chars.toString(); - } - - /** - * Rehydrate the text in a diff from a string of line hashes to real lines of - * text. - * - * @param diffs LinkedList of Diff objects. - * @param lineArray List of unique strings. - */ - protected void diff_charsToLines(LinkedList diffs, - List lineArray) { - StringBuilder text; - for (Diff diff : diffs) { - text = new StringBuilder(); - for (int y = 0; y < diff.text.length(); y++) { - text.append(lineArray.get(diff.text.charAt(y))); - } - diff.text = text.toString(); - } - } - - /** - * Determine the common prefix of two strings - * - * @param text1 First string. - * @param text2 Second string. - * @return The number of characters common to the start of each string. - */ - public int diff_commonPrefix(String text1, String text2) { - // Performance analysis: http://neil.fraser.name/news/2007/10/09/ - int n = Math.min(text1.length(), text2.length()); - for (int i = 0; i < n; i++) { - if (text1.charAt(i) != text2.charAt(i)) { - return i; - } - } - return n; - } - - /** - * Determine the common suffix of two strings - * - * @param text1 First string. - * @param text2 Second string. - * @return The number of characters common to the end of each string. - */ - public int diff_commonSuffix(String text1, String text2) { - // Performance analysis: http://neil.fraser.name/news/2007/10/09/ - int text1_length = text1.length(); - int text2_length = text2.length(); - int n = Math.min(text1_length, text2_length); - for (int i = 1; i <= n; i++) { - if (text1.charAt(text1_length - i) != text2.charAt(text2_length - i)) { - return i - 1; - } - } - return n; - } - - /** - * Determine if the suffix of one string is the prefix of another. - * - * @param text1 First string. - * @param text2 Second string. - * @return The number of characters common to the end of the first - * string and the start of the second string. - */ - protected int diff_commonOverlap(String text1, String text2) { - // Cache the text lengths to prevent multiple calls. - int text1_length = text1.length(); - int text2_length = text2.length(); - // Eliminate the null case. - if (text1_length == 0 || text2_length == 0) { - return 0; - } - // Truncate the longer string. - if (text1_length > text2_length) { - text1 = text1.substring(text1_length - text2_length); - } else if (text1_length < text2_length) { - text2 = text2.substring(0, text1_length); - } - int text_length = Math.min(text1_length, text2_length); - // Quick check for the worst case. - if (text1.equals(text2)) { - return text_length; - } - - // Start by looking for a single character match - // and increase length until no match is found. - // Performance analysis: http://neil.fraser.name/news/2010/11/04/ - int best = 0; - int length = 1; - while (true) { - String pattern = text1.substring(text_length - length); - int found = text2.indexOf(pattern); - if (found == -1) { - return best; - } - length += found; - if (found == 0 || text1.substring(text_length - length).equals( - text2.substring(0, length))) { - best = length; - length++; - } - } - } - - /** - * Do the two texts share a substring which is at least half the length of - * the longer text? - * This speedup can produce non-minimal diffs. - * - * @param text1 First string. - * @param text2 Second string. - * @return Five element String array, containing the prefix of text1, the - * suffix of text1, the prefix of text2, the suffix of text2 and the - * common middle. Or null if there was no match. - */ - protected String[] diff_halfMatch(String text1, String text2) { - if (Diff_Timeout <= 0) { - // Don't risk returning a non-optimal diff if we have unlimited time. - return null; - } - String longtext = text1.length() > text2.length() ? text1 : text2; - String shorttext = text1.length() > text2.length() ? text2 : text1; - if (longtext.length() < 4 || shorttext.length() * 2 < longtext.length()) { - return null; // Pointless. - } - - // First check if the second quarter is the seed for a half-match. - String[] hm1 = diff_halfMatchI(longtext, shorttext, - (longtext.length() + 3) / 4); - // Check again based on the third quarter. - String[] hm2 = diff_halfMatchI(longtext, shorttext, - (longtext.length() + 1) / 2); - String[] hm; - if (hm1 == null && hm2 == null) { - return null; - } else if (hm2 == null) { - hm = hm1; - } else if (hm1 == null) { - hm = hm2; - } else { - // Both matched. Select the longest. - hm = hm1[4].length() > hm2[4].length() ? hm1 : hm2; - } - - // A half-match was found, sort out the return data. - if (text1.length() > text2.length()) { - return hm; - //return new String[]{hm[0], hm[1], hm[2], hm[3], hm[4]}; - } else { - return new String[]{hm[2], hm[3], hm[0], hm[1], hm[4]}; - } - } - - /** - * Does a substring of shorttext exist within longtext such that the - * substring is at least half the length of longtext? - * - * @param longtext Longer string. - * @param shorttext Shorter string. - * @param i Start index of quarter length substring within longtext. - * @return Five element String array, containing the prefix of longtext, the - * suffix of longtext, the prefix of shorttext, the suffix of shorttext - * and the common middle. Or null if there was no match. - */ - private String[] diff_halfMatchI(String longtext, String shorttext, int i) { - // Start with a 1/4 length substring at position i as a seed. - String seed = longtext.substring(i, i + longtext.length() / 4); - int j = -1; - String best_common = ""; - String best_longtext_a = "", best_longtext_b = ""; - String best_shorttext_a = "", best_shorttext_b = ""; - while ((j = shorttext.indexOf(seed, j + 1)) != -1) { - int prefixLength = diff_commonPrefix(longtext.substring(i), - shorttext.substring(j)); - int suffixLength = diff_commonSuffix(longtext.substring(0, i), - shorttext.substring(0, j)); - if (best_common.length() < suffixLength + prefixLength) { - best_common = shorttext.substring(j - suffixLength, j) - + shorttext.substring(j, j + prefixLength); - best_longtext_a = longtext.substring(0, i - suffixLength); - best_longtext_b = longtext.substring(i + prefixLength); - best_shorttext_a = shorttext.substring(0, j - suffixLength); - best_shorttext_b = shorttext.substring(j + prefixLength); - } - } - if (best_common.length() * 2 >= longtext.length()) { - return new String[]{best_longtext_a, best_longtext_b, - best_shorttext_a, best_shorttext_b, best_common}; - } else { - return null; - } - } - - /** - * Reduce the number of edits by eliminating semantically trivial equalities. - * - * @param diffs LinkedList of Diff objects. - */ - public void diff_cleanupSemantic(LinkedList diffs) { - if (diffs.isEmpty()) { - return; - } - boolean changes = false; - Stack equalities = new Stack(); // Stack of qualities. - String lastequality = null; // Always equal to equalities.lastElement().text - ListIterator pointer = diffs.listIterator(); - // Number of characters that changed prior to the equality. - int length_insertions1 = 0; - int length_deletions1 = 0; - // Number of characters that changed after the equality. - int length_insertions2 = 0; - int length_deletions2 = 0; - Diff thisDiff = pointer.next(); - while (thisDiff != null) { - if (thisDiff.operation == Operation.EQUAL) { - // Equality found. - equalities.push(thisDiff); - length_insertions1 = length_insertions2; - length_deletions1 = length_deletions2; - length_insertions2 = 0; - length_deletions2 = 0; - lastequality = thisDiff.text; - } else { - // An insertion or deletion. - if (thisDiff.operation == Operation.INSERT) { - length_insertions2 += thisDiff.text.length(); - } else { - length_deletions2 += thisDiff.text.length(); - } - // Eliminate an equality that is smaller or equal to the edits on both - // sides of it. - if (lastequality != null && (lastequality.length() - <= Math.max(length_insertions1, length_deletions1)) - && (lastequality.length() - <= Math.max(length_insertions2, length_deletions2))) { - // Walk back to offending equality. - while (thisDiff != equalities.lastElement()) { - thisDiff = pointer.previous(); - } - pointer.next(); - - // Replace equality with a delete. - pointer.set(new Diff(Operation.DELETE, lastequality)); - // Insert a corresponding an insert. - pointer.add(new Diff(Operation.INSERT, lastequality)); - - equalities.pop(); // Throw away the equality we just deleted. - if (!equalities.empty()) { - // Throw away the previous equality (it needs to be reevaluated). - equalities.pop(); - } - if (equalities.empty()) { - // There are no previous equalities, walk back to the start. - while (pointer.hasPrevious()) { - pointer.previous(); - } - } else { - // There is a safe equality we can fall back to. - thisDiff = equalities.lastElement(); - while (thisDiff != pointer.previous()) { - // Intentionally empty loop. - } - } - - length_insertions1 = 0; // Reset the counters. - length_insertions2 = 0; - length_deletions1 = 0; - length_deletions2 = 0; - lastequality = null; - changes = true; - } - } - thisDiff = pointer.hasNext() ? pointer.next() : null; - } - - // Normalize the diff. - if (changes) { - diff_cleanupMerge(diffs); - } - diff_cleanupSemanticLossless(diffs); - - // Find any overlaps between deletions and insertions. - // e.g: abcxxxxxxdef - // -> abcxxxdef - // e.g: xxxabcdefxxx - // -> defxxxabc - // Only extract an overlap if it is as big as the edit ahead or behind it. - pointer = diffs.listIterator(); - Diff prevDiff = null; - thisDiff = null; - if (pointer.hasNext()) { - prevDiff = pointer.next(); - if (pointer.hasNext()) { - thisDiff = pointer.next(); - } - } - while (thisDiff != null) { - if (prevDiff.operation == Operation.DELETE && - thisDiff.operation == Operation.INSERT) { - String deletion = prevDiff.text; - String insertion = thisDiff.text; - int overlap_length1 = this.diff_commonOverlap(deletion, insertion); - int overlap_length2 = this.diff_commonOverlap(insertion, deletion); - if (overlap_length1 >= overlap_length2) { - if (overlap_length1 >= deletion.length() / 2.0 || - overlap_length1 >= insertion.length() / 2.0) { - // Overlap found. Insert an equality and trim the surrounding edits. - pointer.previous(); - pointer.add(new Diff(Operation.EQUAL, - insertion.substring(0, overlap_length1))); - prevDiff.text = - deletion.substring(0, deletion.length() - overlap_length1); - thisDiff.text = insertion.substring(overlap_length1); - // pointer.add inserts the element before the cursor, so there is - // no need to step past the new element. - } - } else { - if (overlap_length2 >= deletion.length() / 2.0 || - overlap_length2 >= insertion.length() / 2.0) { - // Reverse overlap found. - // Insert an equality and swap and trim the surrounding edits. - pointer.previous(); - pointer.add(new Diff(Operation.EQUAL, - deletion.substring(0, overlap_length2))); - prevDiff.operation = Operation.INSERT; - prevDiff.text = - insertion.substring(0, insertion.length() - overlap_length2); - thisDiff.operation = Operation.DELETE; - thisDiff.text = deletion.substring(overlap_length2); - // pointer.add inserts the element before the cursor, so there is - // no need to step past the new element. - } - } - thisDiff = pointer.hasNext() ? pointer.next() : null; - } - prevDiff = thisDiff; - thisDiff = pointer.hasNext() ? pointer.next() : null; - } - } - - /** - * Look for single edits surrounded on both sides by equalities - * which can be shifted sideways to align the edit to a word boundary. - * e.g: The cat came. -> The cat came. - * - * @param diffs LinkedList of Diff objects. - */ - public void diff_cleanupSemanticLossless(LinkedList diffs) { - String equality1, edit, equality2; - String commonString; - int commonOffset; - int score, bestScore; - String bestEquality1, bestEdit, bestEquality2; - // Create a new iterator at the start. - ListIterator pointer = diffs.listIterator(); - Diff prevDiff = pointer.hasNext() ? pointer.next() : null; - Diff thisDiff = pointer.hasNext() ? pointer.next() : null; - Diff nextDiff = pointer.hasNext() ? pointer.next() : null; - // Intentionally ignore the first and last element (don't need checking). - while (nextDiff != null) { - if (prevDiff.operation == Operation.EQUAL && - nextDiff.operation == Operation.EQUAL) { - // This is a single edit surrounded by equalities. - equality1 = prevDiff.text; - edit = thisDiff.text; - equality2 = nextDiff.text; - - // First, shift the edit as far left as possible. - commonOffset = diff_commonSuffix(equality1, edit); - if (commonOffset != 0) { - commonString = edit.substring(edit.length() - commonOffset); - equality1 = equality1.substring(0, equality1.length() - commonOffset); - edit = commonString + edit.substring(0, edit.length() - commonOffset); - equality2 = commonString + equality2; - } - - // Second, step character by character right, looking for the best fit. - bestEquality1 = equality1; - bestEdit = edit; - bestEquality2 = equality2; - bestScore = diff_cleanupSemanticScore(equality1, edit) - + diff_cleanupSemanticScore(edit, equality2); - while (edit.length() != 0 && equality2.length() != 0 - && edit.charAt(0) == equality2.charAt(0)) { - equality1 += edit.charAt(0); - edit = edit.substring(1) + equality2.charAt(0); - equality2 = equality2.substring(1); - score = diff_cleanupSemanticScore(equality1, edit) - + diff_cleanupSemanticScore(edit, equality2); - // The >= encourages trailing rather than leading whitespace on edits. - if (score >= bestScore) { - bestScore = score; - bestEquality1 = equality1; - bestEdit = edit; - bestEquality2 = equality2; - } - } - - if (!prevDiff.text.equals(bestEquality1)) { - // We have an improvement, save it back to the diff. - if (bestEquality1.length() != 0) { - prevDiff.text = bestEquality1; - } else { - pointer.previous(); // Walk past nextDiff. - pointer.previous(); // Walk past thisDiff. - pointer.previous(); // Walk past prevDiff. - pointer.remove(); // Delete prevDiff. - pointer.next(); // Walk past thisDiff. - pointer.next(); // Walk past nextDiff. - } - thisDiff.text = bestEdit; - if (bestEquality2.length() != 0) { - nextDiff.text = bestEquality2; - } else { - pointer.remove(); // Delete nextDiff. - nextDiff = thisDiff; - thisDiff = prevDiff; - } - } - } - prevDiff = thisDiff; - thisDiff = nextDiff; - nextDiff = pointer.hasNext() ? pointer.next() : null; - } - } - - /** - * Given two strings, compute a score representing whether the internal - * boundary falls on logical boundaries. - * Scores range from 6 (best) to 0 (worst). - * - * @param one First string. - * @param two Second string. - * @return The score. - */ - private int diff_cleanupSemanticScore(String one, String two) { - if (one.length() == 0 || two.length() == 0) { - // Edges are the best. - return 6; - } - - // Each port of this function behaves slightly differently due to - // subtle differences in each language's definition of things like - // 'whitespace'. Since this function's purpose is largely cosmetic, - // the choice has been made to use each language's native features - // rather than force total conformity. - char char1 = one.charAt(one.length() - 1); - char char2 = two.charAt(0); - boolean nonAlphaNumeric1 = !Character.isLetterOrDigit(char1); - boolean nonAlphaNumeric2 = !Character.isLetterOrDigit(char2); - boolean whitespace1 = nonAlphaNumeric1 && Character.isWhitespace(char1); - boolean whitespace2 = nonAlphaNumeric2 && Character.isWhitespace(char2); - boolean lineBreak1 = whitespace1 - && Character.getType(char1) == Character.CONTROL; - boolean lineBreak2 = whitespace2 - && Character.getType(char2) == Character.CONTROL; - boolean blankLine1 = lineBreak1 && BLANKLINEEND.matcher(one).find(); - boolean blankLine2 = lineBreak2 && BLANKLINESTART.matcher(two).find(); - - if (blankLine1 || blankLine2) { - // Five points for blank lines. - return 5; - } else if (lineBreak1 || lineBreak2) { - // Four points for line breaks. - return 4; - } else if (nonAlphaNumeric1 && !whitespace1 && whitespace2) { - // Three points for end of sentences. - return 3; - } else if (whitespace1 || whitespace2) { - // Two points for whitespace. - return 2; - } else if (nonAlphaNumeric1 || nonAlphaNumeric2) { - // One point for non-alphanumeric. - return 1; - } - return 0; - } - - /** - * Reduce the number of edits by eliminating operationally trivial equalities. - * - * @param diffs LinkedList of Diff objects. - */ - public void diff_cleanupEfficiency(LinkedList diffs) { - if (diffs.isEmpty()) { - return; - } - boolean changes = false; - Stack equalities = new Stack(); // Stack of equalities. - String lastequality = null; // Always equal to equalities.lastElement().text - ListIterator pointer = diffs.listIterator(); - // Is there an insertion operation before the last equality. - boolean pre_ins = false; - // Is there a deletion operation before the last equality. - boolean pre_del = false; - // Is there an insertion operation after the last equality. - boolean post_ins = false; - // Is there a deletion operation after the last equality. - boolean post_del = false; - Diff thisDiff = pointer.next(); - Diff safeDiff = thisDiff; // The last Diff that is known to be unsplitable. - while (thisDiff != null) { - if (thisDiff.operation == Operation.EQUAL) { - // Equality found. - if (thisDiff.text.length() < Diff_EditCost && (post_ins || post_del)) { - // Candidate found. - equalities.push(thisDiff); - pre_ins = post_ins; - pre_del = post_del; - lastequality = thisDiff.text; - } else { - // Not a candidate, and can never become one. - equalities.clear(); - lastequality = null; - safeDiff = thisDiff; - } - post_ins = post_del = false; - } else { - // An insertion or deletion. - if (thisDiff.operation == Operation.DELETE) { - post_del = true; - } else { - post_ins = true; - } - /* - * Five types to be split: - * ABXYCD - * AXCD - * ABXC - * AXCD - * ABXC - */ - if (lastequality != null - && ((pre_ins && pre_del && post_ins && post_del) - || ((lastequality.length() < Diff_EditCost / 2) - && ((pre_ins ? 1 : 0) + (pre_del ? 1 : 0) - + (post_ins ? 1 : 0) + (post_del ? 1 : 0)) == 3))) { - // Walk back to offending equality. - while (thisDiff != equalities.lastElement()) { - thisDiff = pointer.previous(); - } - pointer.next(); - - // Replace equality with a delete. - pointer.set(new Diff(Operation.DELETE, lastequality)); - // Insert a corresponding an insert. - pointer.add(thisDiff = new Diff(Operation.INSERT, lastequality)); - - equalities.pop(); // Throw away the equality we just deleted. - lastequality = null; - if (pre_ins && pre_del) { - // No changes made which could affect previous entry, keep going. - post_ins = post_del = true; - equalities.clear(); - safeDiff = thisDiff; - } else { - if (!equalities.empty()) { - // Throw away the previous equality (it needs to be reevaluated). - equalities.pop(); - } - if (equalities.empty()) { - // There are no previous questionable equalities, - // walk back to the last known safe diff. - thisDiff = safeDiff; - } else { - // There is an equality we can fall back to. - thisDiff = equalities.lastElement(); - } - while (thisDiff != pointer.previous()) { - // Intentionally empty loop. - } - post_ins = post_del = false; - } - - changes = true; - } - } - thisDiff = pointer.hasNext() ? pointer.next() : null; - } - - if (changes) { - diff_cleanupMerge(diffs); - } - } - - /** - * Reorder and merge like edit sections. Merge equalities. - * Any edit section can move as long as it doesn't cross an equality. - * - * @param diffs LinkedList of Diff objects. - */ - public void diff_cleanupMerge(LinkedList diffs) { - diffs.add(new Diff(Operation.EQUAL, "")); // Add a dummy entry at the end. - ListIterator pointer = diffs.listIterator(); - int count_delete = 0; - int count_insert = 0; - String text_delete = ""; - String text_insert = ""; - Diff thisDiff = pointer.next(); - Diff prevEqual = null; - int commonlength; - while (thisDiff != null) { - switch (thisDiff.operation) { - case INSERT: - count_insert++; - text_insert += thisDiff.text; - prevEqual = null; - break; - case DELETE: - count_delete++; - text_delete += thisDiff.text; - prevEqual = null; - break; - case EQUAL: - if (count_delete + count_insert > 1) { - boolean both_types = count_delete != 0 && count_insert != 0; - // Delete the offending records. - pointer.previous(); // Reverse direction. - while (count_delete-- > 0) { - pointer.previous(); - pointer.remove(); - } - while (count_insert-- > 0) { - pointer.previous(); - pointer.remove(); - } - if (both_types) { - // Factor out any common prefixies. - commonlength = diff_commonPrefix(text_insert, text_delete); - if (commonlength != 0) { - if (pointer.hasPrevious()) { - thisDiff = pointer.previous(); - assert thisDiff.operation == Operation.EQUAL - : "Previous diff should have been an equality."; - thisDiff.text += text_insert.substring(0, commonlength); - pointer.next(); - } else { - pointer.add(new Diff(Operation.EQUAL, - text_insert.substring(0, commonlength))); - } - text_insert = text_insert.substring(commonlength); - text_delete = text_delete.substring(commonlength); - } - // Factor out any common suffixies. - commonlength = diff_commonSuffix(text_insert, text_delete); - if (commonlength != 0) { - thisDiff = pointer.next(); - thisDiff.text = text_insert.substring(text_insert.length() - - commonlength) + thisDiff.text; - text_insert = text_insert.substring(0, text_insert.length() - - commonlength); - text_delete = text_delete.substring(0, text_delete.length() - - commonlength); - pointer.previous(); - } - } - // Insert the merged records. - if (text_delete.length() != 0) { - pointer.add(new Diff(Operation.DELETE, text_delete)); - } - if (text_insert.length() != 0) { - pointer.add(new Diff(Operation.INSERT, text_insert)); - } - // Step forward to the equality. - thisDiff = pointer.hasNext() ? pointer.next() : null; - } else if (prevEqual != null) { - // Merge this equality with the previous one. - prevEqual.text += thisDiff.text; - pointer.remove(); - thisDiff = pointer.previous(); - pointer.next(); // Forward direction - } - count_insert = 0; - count_delete = 0; - text_delete = ""; - text_insert = ""; - prevEqual = thisDiff; - break; - } - thisDiff = pointer.hasNext() ? pointer.next() : null; - } - if (diffs.getLast().text.length() == 0) { - diffs.removeLast(); // Remove the dummy entry at the end. - } - - /* - * Second pass: look for single edits surrounded on both sides by equalities - * which can be shifted sideways to eliminate an equality. - * e.g: ABAC -> ABAC - */ - boolean changes = false; - // Create a new iterator at the start. - // (As opposed to walking the current one back.) - pointer = diffs.listIterator(); - Diff prevDiff = pointer.hasNext() ? pointer.next() : null; - thisDiff = pointer.hasNext() ? pointer.next() : null; - Diff nextDiff = pointer.hasNext() ? pointer.next() : null; - // Intentionally ignore the first and last element (don't need checking). - while (nextDiff != null) { - if (prevDiff.operation == Operation.EQUAL && - nextDiff.operation == Operation.EQUAL) { - // This is a single edit surrounded by equalities. - if (thisDiff.text.endsWith(prevDiff.text)) { - // Shift the edit over the previous equality. - thisDiff.text = prevDiff.text - + thisDiff.text.substring(0, thisDiff.text.length() - - prevDiff.text.length()); - nextDiff.text = prevDiff.text + nextDiff.text; - pointer.previous(); // Walk past nextDiff. - pointer.previous(); // Walk past thisDiff. - pointer.previous(); // Walk past prevDiff. - pointer.remove(); // Delete prevDiff. - pointer.next(); // Walk past thisDiff. - thisDiff = pointer.next(); // Walk past nextDiff. - nextDiff = pointer.hasNext() ? pointer.next() : null; - changes = true; - } else if (thisDiff.text.startsWith(nextDiff.text)) { - // Shift the edit over the next equality. - prevDiff.text += nextDiff.text; - thisDiff.text = thisDiff.text.substring(nextDiff.text.length()) - + nextDiff.text; - pointer.remove(); // Delete nextDiff. - nextDiff = pointer.hasNext() ? pointer.next() : null; - changes = true; - } - } - prevDiff = thisDiff; - thisDiff = nextDiff; - nextDiff = pointer.hasNext() ? pointer.next() : null; - } - // If shifts were made, the diff needs reordering and another shift sweep. - if (changes) { - diff_cleanupMerge(diffs); - } - } - - /** - * loc is a location in text1, compute and return the equivalent location in - * text2. - * e.g. "The cat" vs "The big cat", 1->1, 5->8 - * - * @param diffs LinkedList of Diff objects. - * @param loc Location within text1. - * @return Location within text2. - */ - public int diff_xIndex(LinkedList diffs, int loc) { - int chars1 = 0; - int chars2 = 0; - int last_chars1 = 0; - int last_chars2 = 0; - Diff lastDiff = null; - for (Diff aDiff : diffs) { - if (aDiff.operation != Operation.INSERT) { - // Equality or deletion. - chars1 += aDiff.text.length(); - } - if (aDiff.operation != Operation.DELETE) { - // Equality or insertion. - chars2 += aDiff.text.length(); - } - if (chars1 > loc) { - // Overshot the location. - lastDiff = aDiff; - break; - } - last_chars1 = chars1; - last_chars2 = chars2; - } - if (lastDiff != null && lastDiff.operation == Operation.DELETE) { - // The location was deleted. - return last_chars2; - } - // Add the remaining character length. - return last_chars2 + (loc - last_chars1); - } - - /** - * Convert a Diff list into a pretty HTML report. - * - * @param diffs LinkedList of Diff objects. - * @return HTML representation. - */ - public String diff_prettyHtml(LinkedList diffs) { - StringBuilder html = new StringBuilder(); - for (Diff aDiff : diffs) { - String text = aDiff.text.replace("&", "&").replace("<", "<") - .replace(">", ">").replace("\n", "¶
"); - switch (aDiff.operation) { - case INSERT: - html.append("").append(text) - .append(""); - break; - case DELETE: - html.append("").append(text) - .append(""); - break; - case EQUAL: - html.append("").append(text).append(""); - break; - } - } - return html.toString(); - } - - /** - * Compute and return the source text (all equalities and deletions). - * - * @param diffs LinkedList of Diff objects. - * @return Source text. - */ - public String diff_text1(LinkedList diffs) { - StringBuilder text = new StringBuilder(); - for (Diff aDiff : diffs) { - if (aDiff.operation != Operation.INSERT) { - text.append(aDiff.text); - } - } - return text.toString(); - } - - /** - * Compute and return the destination text (all equalities and insertions). - * - * @param diffs LinkedList of Diff objects. - * @return Destination text. - */ - public String diff_text2(LinkedList diffs) { - StringBuilder text = new StringBuilder(); - for (Diff aDiff : diffs) { - if (aDiff.operation != Operation.DELETE) { - text.append(aDiff.text); - } - } - return text.toString(); - } - - /** - * Compute the Levenshtein distance; the number of inserted, deleted or - * substituted characters. - * - * @param diffs LinkedList of Diff objects. - * @return Number of changes. - */ - public int diff_levenshtein(LinkedList diffs) { - int levenshtein = 0; - int insertions = 0; - int deletions = 0; - for (Diff aDiff : diffs) { - switch (aDiff.operation) { - case INSERT: - insertions += aDiff.text.length(); - break; - case DELETE: - deletions += aDiff.text.length(); - break; - case EQUAL: - // A deletion and an insertion is one substitution. - levenshtein += Math.max(insertions, deletions); - insertions = 0; - deletions = 0; - break; - } - } - levenshtein += Math.max(insertions, deletions); - return levenshtein; - } - - /** - * Crush the diff into an encoded string which describes the operations - * required to transform text1 into text2. - * E.g. =3\t-2\t+ing -> Keep 3 chars, delete 2 chars, insert 'ing'. - * Operations are tab-separated. Inserted text is escaped using %xx notation. - * - * @param diffs Array of Diff objects. - * @return Delta text. - */ - public String diff_toDelta(LinkedList diffs) { - StringBuilder text = new StringBuilder(); - for (Diff aDiff : diffs) { - switch (aDiff.operation) { - case INSERT: - text.append("+").append(URLEncoder.encode(aDiff.text, StandardCharsets.UTF_8) - .replace('+', ' ')).append("\t"); - break; - case DELETE: - text.append("-").append(aDiff.text.length()).append("\t"); - break; - case EQUAL: - text.append("=").append(aDiff.text.length()).append("\t"); - break; - } - } - String delta = text.toString(); - if (delta.length() != 0) { - // Strip off trailing tab character. - delta = delta.substring(0, delta.length() - 1); - delta = unescapeForEncodeUriCompatability(delta); - } - return delta; - } - - /** - * Given the original text1, and an encoded string which describes the - * operations required to transform text1 into text2, compute the full diff. - * - * @param text1 Source string for the diff. - * @param delta Delta text. - * @return Array of Diff objects or null if invalid. - * @throws IllegalArgumentException If invalid input. - */ - public LinkedList diff_fromDelta(String text1, String delta) - throws IllegalArgumentException { - LinkedList diffs = new LinkedList(); - int pointer = 0; // Cursor in text1 - String[] tokens = delta.split("\t"); - for (String token : tokens) { - if (token.length() == 0) { - // Blank tokens are ok (from a trailing \t). - continue; - } - // Each token begins with a one character parameter which specifies the - // operation of this token (delete, insert, equality). - String param = token.substring(1); - switch (token.charAt(0)) { - case '+': - // decode would change all "+" to " " - param = param.replace("+", "%2B"); - try { - param = URLDecoder.decode(param, StandardCharsets.UTF_8); - } catch (IllegalArgumentException e) { - // Malformed URI sequence. - throw new IllegalArgumentException( - "Illegal escape in diff_fromDelta: " + param, e); - } - diffs.add(new Diff(Operation.INSERT, param)); - break; - case '-': - // Fall through. - case '=': - int n; - try { - n = Integer.parseInt(param); - } catch (NumberFormatException e) { - throw new IllegalArgumentException( - "Invalid number in diff_fromDelta: " + param, e); - } - if (n < 0) { - throw new IllegalArgumentException( - "Negative number in diff_fromDelta: " + param); - } - String text; - try { - text = text1.substring(pointer, pointer += n); - } catch (StringIndexOutOfBoundsException e) { - throw new IllegalArgumentException("Delta length (" + pointer - + ") larger than source text length (" + text1.length() - + ").", e); - } - if (token.charAt(0) == '=') { - diffs.add(new Diff(Operation.EQUAL, text)); - } else { - diffs.add(new Diff(Operation.DELETE, text)); - } - break; - default: - // Anything else is an error. - throw new IllegalArgumentException( - "Invalid diff operation in diff_fromDelta: " + token.charAt(0)); - } - } - if (pointer != text1.length()) { - throw new IllegalArgumentException("Delta length (" + pointer - + ") smaller than source text length (" + text1.length() + ")."); - } - return diffs; - } - - /** - * Locate the best instance of 'pattern' in 'text' near 'loc'. - * Returns -1 if no match found. - * - * @param text The text to search. - * @param pattern The pattern to search for. - * @param loc The location to search around. - * @return Best match index or -1. - */ - public int match_main(String text, String pattern, int loc) { - // Check for null inputs. - if (text == null || pattern == null) { - throw new IllegalArgumentException("Null inputs. (match_main)"); - } - - loc = Math.max(0, Math.min(loc, text.length())); - if (text.equals(pattern)) { - // Shortcut (potentially not guaranteed by the algorithm) - return 0; - } else if (text.length() == 0) { - // Nothing to match. - return -1; - } else if (loc + pattern.length() <= text.length() - && text.startsWith(pattern, loc)) { - // Perfect match at the perfect spot! (Includes case of null pattern) - return loc; - } else { - // Do a fuzzy compare. - return match_bitap(text, pattern, loc); - } - } - - - // MATCH FUNCTIONS - - /** - * Locate the best instance of 'pattern' in 'text' near 'loc' using the - * Bitap algorithm. Returns -1 if no match found. - * - * @param text The text to search. - * @param pattern The pattern to search for. - * @param loc The location to search around. - * @return Best match index or -1. - */ - protected int match_bitap(String text, String pattern, int loc) { - assert (Match_MaxBits == 0 || pattern.length() <= Match_MaxBits) - : "Pattern too long for this application."; - - // Initialise the alphabet. - Map s = match_alphabet(pattern); - - // Highest score beyond which we give up. - double score_threshold = Match_Threshold; - // Is there a nearby exact match? (speedup) - int best_loc = text.indexOf(pattern, loc); - if (best_loc != -1) { - score_threshold = Math.min(match_bitapScore(0, best_loc, loc, pattern), - score_threshold); - // What about in the other direction? (speedup) - best_loc = text.lastIndexOf(pattern, loc + pattern.length()); - if (best_loc != -1) { - score_threshold = Math.min(match_bitapScore(0, best_loc, loc, pattern), - score_threshold); - } - } - - // Initialise the bit arrays. - int matchmask = 1 << (pattern.length() - 1); - best_loc = -1; - - int bin_min, bin_mid; - int bin_max = pattern.length() + text.length(); - // Empty initialization added to appease Java compiler. - int[] last_rd = new int[0]; - for (int d = 0; d < pattern.length(); d++) { - // Scan for the best match; each iteration allows for one more error. - // Run a binary search to determine how far from 'loc' we can stray at - // this error level. - bin_min = 0; - bin_mid = bin_max; - while (bin_min < bin_mid) { - if (match_bitapScore(d, loc + bin_mid, loc, pattern) - <= score_threshold) { - bin_min = bin_mid; - } else { - bin_max = bin_mid; - } - bin_mid = (bin_max - bin_min) / 2 + bin_min; - } - // Use the result from this iteration as the maximum for the next. - bin_max = bin_mid; - int start = Math.max(1, loc - bin_mid + 1); - int finish = Math.min(loc + bin_mid, text.length()) + pattern.length(); - - int[] rd = new int[finish + 2]; - rd[finish + 1] = (1 << d) - 1; - for (int j = finish; j >= start; j--) { - int charMatch; - if (text.length() <= j - 1 || !s.containsKey(text.charAt(j - 1))) { - // Out of range. - charMatch = 0; - } else { - charMatch = s.get(text.charAt(j - 1)); - } - if (d == 0) { - // First pass: exact match. - rd[j] = ((rd[j + 1] << 1) | 1) & charMatch; - } else { - // Subsequent passes: fuzzy match. - rd[j] = (((rd[j + 1] << 1) | 1) & charMatch) - | (((last_rd[j + 1] | last_rd[j]) << 1) | 1) | last_rd[j + 1]; - } - if ((rd[j] & matchmask) != 0) { - double score = match_bitapScore(d, j - 1, loc, pattern); - // This match will almost certainly be better than any existing - // match. But check anyway. - if (score <= score_threshold) { - // Told you so. - score_threshold = score; - best_loc = j - 1; - if (best_loc > loc) { - // When passing loc, don't exceed our current distance from loc. - start = Math.max(1, 2 * loc - best_loc); - } else { - // Already passed loc, downhill from here on in. - break; - } - } - } - } - if (match_bitapScore(d + 1, loc, loc, pattern) > score_threshold) { - // No hope for a (better) match at greater error levels. - break; - } - last_rd = rd; - } - return best_loc; - } - - /** - * Compute and return the score for a match with e errors and x location. - * - * @param e Number of errors in match. - * @param x Location of match. - * @param loc Expected location of match. - * @param pattern Pattern being sought. - * @return Overall score for match (0.0 = good, 1.0 = bad). - */ - private double match_bitapScore(int e, int x, int loc, String pattern) { - float accuracy = (float) e / pattern.length(); - int proximity = Math.abs(loc - x); - if (Match_Distance == 0) { - // Dodge divide by zero error. - return proximity == 0 ? accuracy : 1.0; - } - return accuracy + (proximity / (float) Match_Distance); - } - - /** - * Initialise the alphabet for the Bitap algorithm. - * - * @param pattern The text to encode. - * @return Hash of character locations. - */ - protected Map match_alphabet(String pattern) { - Map s = new HashMap(); - char[] char_pattern = pattern.toCharArray(); - for (char c : char_pattern) { - s.put(c, 0); - } - int i = 0; - for (char c : char_pattern) { - s.put(c, s.get(c) | (1 << (pattern.length() - i - 1))); - i++; - } - return s; - } - - /** - * Increase the context until it is unique, - * but don't let the pattern expand beyond Match_MaxBits. - * - * @param patch The patch to grow. - * @param text Source text. - */ - protected void patch_addContext(Patch patch, String text) { - if (text.length() == 0) { - return; - } - String pattern = text.substring(patch.start2, patch.start2 + patch.length1); - int padding = 0; - - // Look for the first and last matches of pattern in text. If two different - // matches are found, increase the pattern length. - while (text.indexOf(pattern) != text.lastIndexOf(pattern) - && pattern.length() < Match_MaxBits - Patch_Margin - Patch_Margin) { - padding += Patch_Margin; - pattern = text.substring(Math.max(0, patch.start2 - padding), - Math.min(text.length(), patch.start2 + patch.length1 + padding)); - } - // Add one chunk for good luck. - padding += Patch_Margin; - - // Add the prefix. - String prefix = text.substring(Math.max(0, patch.start2 - padding), - patch.start2); - if (prefix.length() != 0) { - patch.diffs.addFirst(new Diff(Operation.EQUAL, prefix)); - } - // Add the suffix. - String suffix = text.substring(patch.start2 + patch.length1, - Math.min(text.length(), patch.start2 + patch.length1 + padding)); - if (suffix.length() != 0) { - patch.diffs.addLast(new Diff(Operation.EQUAL, suffix)); - } - - // Roll back the start points. - patch.start1 -= prefix.length(); - patch.start2 -= prefix.length(); - // Extend the lengths. - patch.length1 += prefix.length() + suffix.length(); - patch.length2 += prefix.length() + suffix.length(); - } - - - // PATCH FUNCTIONS - - /** - * Compute a list of patches to turn text1 into text2. - * A set of diffs will be computed. - * - * @param text1 Old text. - * @param text2 New text. - * @return LinkedList of Patch objects. - */ - public LinkedList patch_make(String text1, String text2) { - if (text1 == null || text2 == null) { - throw new IllegalArgumentException("Null inputs. (patch_make)"); - } - // No diffs provided, compute our own. - LinkedList diffs = diff_main(text1, text2, true); - if (diffs.size() > 2) { - diff_cleanupSemantic(diffs); - diff_cleanupEfficiency(diffs); - } - return patch_make(text1, diffs); - } - - /** - * Compute a list of patches to turn text1 into text2. - * text1 will be derived from the provided diffs. - * - * @param diffs Array of Diff objects for text1 to text2. - * @return LinkedList of Patch objects. - */ - public LinkedList patch_make(LinkedList diffs) { - if (diffs == null) { - throw new IllegalArgumentException("Null inputs. (patch_make)"); - } - // No origin string provided, compute our own. - String text1 = diff_text1(diffs); - return patch_make(text1, diffs); - } - - /** - * Compute a list of patches to turn text1 into text2. - * text2 is ignored, diffs are the delta between text1 and text2. - * - * @param text1 Old text - * @param text2 Ignored. - * @param diffs Array of Diff objects for text1 to text2. - * @return LinkedList of Patch objects. - * @deprecated Prefer patch_make(String text1, LinkedList diffs). - */ - public LinkedList patch_make(String text1, String text2, - LinkedList diffs) { - return patch_make(text1, diffs); - } - - /** - * Compute a list of patches to turn text1 into text2. - * text2 is not provided, diffs are the delta between text1 and text2. - * - * @param text1 Old text. - * @param diffs Array of Diff objects for text1 to text2. - * @return LinkedList of Patch objects. - */ - public LinkedList patch_make(String text1, LinkedList diffs) { - if (text1 == null || diffs == null) { - throw new IllegalArgumentException("Null inputs. (patch_make)"); - } - - LinkedList patches = new LinkedList(); - if (diffs.isEmpty()) { - return patches; // Get rid of the null case. - } - Patch patch = new Patch(); - int char_count1 = 0; // Number of characters into the text1 string. - int char_count2 = 0; // Number of characters into the text2 string. - // Start with text1 (prepatch_text) and apply the diffs until we arrive at - // text2 (postpatch_text). We recreate the patches one by one to determine - // context info. - String prepatch_text = text1; - String postpatch_text = text1; - for (Diff aDiff : diffs) { - if (patch.diffs.isEmpty() && aDiff.operation != Operation.EQUAL) { - // A new patch starts here. - patch.start1 = char_count1; - patch.start2 = char_count2; - } - - switch (aDiff.operation) { - case INSERT: - patch.diffs.add(aDiff); - patch.length2 += aDiff.text.length(); - postpatch_text = postpatch_text.substring(0, char_count2) - + aDiff.text + postpatch_text.substring(char_count2); - break; - case DELETE: - patch.length1 += aDiff.text.length(); - patch.diffs.add(aDiff); - postpatch_text = postpatch_text.substring(0, char_count2) - + postpatch_text.substring(char_count2 + aDiff.text.length()); - break; - case EQUAL: - if (aDiff.text.length() <= 2 * Patch_Margin - && !patch.diffs.isEmpty() && aDiff != diffs.getLast()) { - // Small equality inside a patch. - patch.diffs.add(aDiff); - patch.length1 += aDiff.text.length(); - patch.length2 += aDiff.text.length(); - } - - if (aDiff.text.length() >= 2 * Patch_Margin) { - // Time for a new patch. - if (!patch.diffs.isEmpty()) { - patch_addContext(patch, prepatch_text); - patches.add(patch); - patch = new Patch(); - // Unlike Unidiff, our patch lists have a rolling context. - // http://code.google.com/p/google-diff-match-patch/wiki/Unidiff - // Update prepatch text & pos to reflect the application of the - // just completed patch. - prepatch_text = postpatch_text; - char_count1 = char_count2; - } - } - break; - } - - // Update the current character count. - if (aDiff.operation != Operation.INSERT) { - char_count1 += aDiff.text.length(); - } - if (aDiff.operation != Operation.DELETE) { - char_count2 += aDiff.text.length(); - } - } - // Pick up the leftover patch if not empty. - if (!patch.diffs.isEmpty()) { - patch_addContext(patch, prepatch_text); - patches.add(patch); - } - - return patches; - } - - /** - * Given an array of patches, return another array that is identical. - * - * @param patches Array of Patch objects. - * @return Array of Patch objects. - */ - public LinkedList patch_deepCopy(LinkedList patches) { - LinkedList patchesCopy = new LinkedList(); - for (Patch aPatch : patches) { - Patch patchCopy = new Patch(); - for (Diff aDiff : aPatch.diffs) { - Diff diffCopy = new Diff(aDiff.operation, aDiff.text); - patchCopy.diffs.add(diffCopy); - } - patchCopy.start1 = aPatch.start1; - patchCopy.start2 = aPatch.start2; - patchCopy.length1 = aPatch.length1; - patchCopy.length2 = aPatch.length2; - patchesCopy.add(patchCopy); - } - return patchesCopy; - } - - /** - * Merge a set of patches onto the text. Return a patched text, as well - * as an array of true/false values indicating which patches were applied. - * - * @param patches Array of Patch objects - * @param text Old text. - * @return Two element Object array, containing the new text and an array of - * boolean values. - */ - public Object[] patch_apply(LinkedList patches, String text) { - if (patches.isEmpty()) { - return new Object[]{text, new boolean[0]}; - } - - // Deep copy the patches so that no changes are made to originals. - patches = patch_deepCopy(patches); - - String nullPadding = patch_addPadding(patches); - text = nullPadding + text + nullPadding; - patch_splitMax(patches); - - int x = 0; - // delta keeps track of the offset between the expected and actual location - // of the previous patch. If there are patches expected at positions 10 and - // 20, but the first patch was found at 12, delta is 2 and the second patch - // has an effective expected position of 22. - int delta = 0; - boolean[] results = new boolean[patches.size()]; - for (Patch aPatch : patches) { - int expected_loc = aPatch.start2 + delta; - String text1 = diff_text1(aPatch.diffs); - int start_loc; - int end_loc = -1; - if (text1.length() > this.Match_MaxBits) { - // patch_splitMax will only provide an oversized pattern in the case of - // a monster delete. - start_loc = match_main(text, - text1.substring(0, this.Match_MaxBits), expected_loc); - if (start_loc != -1) { - end_loc = match_main(text, - text1.substring(text1.length() - this.Match_MaxBits), - expected_loc + text1.length() - this.Match_MaxBits); - if (end_loc == -1 || start_loc >= end_loc) { - // Can't find valid trailing context. Drop this patch. - start_loc = -1; - } - } - } else { - start_loc = match_main(text, text1, expected_loc); - } - if (start_loc == -1) { - // No match found. :( - results[x] = false; - // Subtract the delta for this failed patch from subsequent patches. - delta -= aPatch.length2 - aPatch.length1; - } else { - // Found a match. :) - results[x] = true; - delta = start_loc - expected_loc; - String text2; - if (end_loc == -1) { - text2 = text.substring(start_loc, - Math.min(start_loc + text1.length(), text.length())); - } else { - text2 = text.substring(start_loc, - Math.min(end_loc + this.Match_MaxBits, text.length())); - } - if (text1.equals(text2)) { - // Perfect match, just shove the replacement text in. - text = text.substring(0, start_loc) + diff_text2(aPatch.diffs) - + text.substring(start_loc + text1.length()); - } else { - // Imperfect match. Run a diff to get a framework of equivalent - // indices. - LinkedList diffs = diff_main(text1, text2, false); - if (text1.length() > this.Match_MaxBits - && diff_levenshtein(diffs) / (float) text1.length() - > this.Patch_DeleteThreshold) { - // The end points match, but the content is unacceptably bad. - results[x] = false; - } else { - diff_cleanupSemanticLossless(diffs); - int index1 = 0; - for (Diff aDiff : aPatch.diffs) { - if (aDiff.operation != Operation.EQUAL) { - int index2 = diff_xIndex(diffs, index1); - if (aDiff.operation == Operation.INSERT) { - // Insertion - text = text.substring(0, start_loc + index2) + aDiff.text - + text.substring(start_loc + index2); - } else if (aDiff.operation == Operation.DELETE) { - // Deletion - text = text.substring(0, start_loc + index2) - + text.substring(start_loc + diff_xIndex(diffs, - index1 + aDiff.text.length())); - } - } - if (aDiff.operation != Operation.DELETE) { - index1 += aDiff.text.length(); - } - } - } - } - } - x++; - } - // Strip the padding off. - text = text.substring(nullPadding.length(), text.length() - - nullPadding.length()); - return new Object[]{text, results}; - } - - /** - * Add some padding on text start and end so that edges can match something. - * Intended to be called only from within patch_apply. - * - * @param patches Array of Patch objects. - * @return The padding string added to each side. - */ - public String patch_addPadding(LinkedList patches) { - short paddingLength = this.Patch_Margin; - String nullPadding = ""; - for (short x = 1; x <= paddingLength; x++) { - nullPadding += String.valueOf((char) x); - } - - // Bump all the patches forward. - for (Patch aPatch : patches) { - aPatch.start1 += paddingLength; - aPatch.start2 += paddingLength; - } - - // Add some padding on start of first diff. - Patch patch = patches.getFirst(); - LinkedList diffs = patch.diffs; - if (diffs.isEmpty() || diffs.getFirst().operation != Operation.EQUAL) { - // Add nullPadding equality. - diffs.addFirst(new Diff(Operation.EQUAL, nullPadding)); - patch.start1 -= paddingLength; // Should be 0. - patch.start2 -= paddingLength; // Should be 0. - patch.length1 += paddingLength; - patch.length2 += paddingLength; - } else if (paddingLength > diffs.getFirst().text.length()) { - // Grow first equality. - Diff firstDiff = diffs.getFirst(); - int extraLength = paddingLength - firstDiff.text.length(); - firstDiff.text = nullPadding.substring(firstDiff.text.length()) - + firstDiff.text; - patch.start1 -= extraLength; - patch.start2 -= extraLength; - patch.length1 += extraLength; - patch.length2 += extraLength; - } - - // Add some padding on end of last diff. - patch = patches.getLast(); - diffs = patch.diffs; - if (diffs.isEmpty() || diffs.getLast().operation != Operation.EQUAL) { - // Add nullPadding equality. - diffs.addLast(new Diff(Operation.EQUAL, nullPadding)); - patch.length1 += paddingLength; - patch.length2 += paddingLength; - } else if (paddingLength > diffs.getLast().text.length()) { - // Grow last equality. - Diff lastDiff = diffs.getLast(); - int extraLength = paddingLength - lastDiff.text.length(); - lastDiff.text += nullPadding.substring(0, extraLength); - patch.length1 += extraLength; - patch.length2 += extraLength; - } - - return nullPadding; - } - - /** - * Look through the patches and break up any which are longer than the - * maximum limit of the match algorithm. - * Intended to be called only from within patch_apply. - * - * @param patches LinkedList of Patch objects. - */ - public void patch_splitMax(LinkedList patches) { - short patch_size = Match_MaxBits; - String precontext, postcontext; - Patch patch; - int start1, start2; - boolean empty; - Operation diff_type; - String diff_text; - ListIterator pointer = patches.listIterator(); - Patch bigpatch = pointer.hasNext() ? pointer.next() : null; - while (bigpatch != null) { - if (bigpatch.length1 <= Match_MaxBits) { - bigpatch = pointer.hasNext() ? pointer.next() : null; - continue; - } - // Remove the big old patch. - pointer.remove(); - start1 = bigpatch.start1; - start2 = bigpatch.start2; - precontext = ""; - while (!bigpatch.diffs.isEmpty()) { - // Create one of several smaller patches. - patch = new Patch(); - empty = true; - patch.start1 = start1 - precontext.length(); - patch.start2 = start2 - precontext.length(); - if (precontext.length() != 0) { - patch.length1 = patch.length2 = precontext.length(); - patch.diffs.add(new Diff(Operation.EQUAL, precontext)); - } - while (!bigpatch.diffs.isEmpty() - && patch.length1 < patch_size - Patch_Margin) { - diff_type = bigpatch.diffs.getFirst().operation; - diff_text = bigpatch.diffs.getFirst().text; - if (diff_type == Operation.INSERT) { - // Insertions are harmless. - patch.length2 += diff_text.length(); - start2 += diff_text.length(); - patch.diffs.addLast(bigpatch.diffs.removeFirst()); - empty = false; - } else if (diff_type == Operation.DELETE && patch.diffs.size() == 1 - && patch.diffs.getFirst().operation == Operation.EQUAL - && diff_text.length() > 2 * patch_size) { - // This is a large deletion. Let it pass in one chunk. - patch.length1 += diff_text.length(); - start1 += diff_text.length(); - empty = false; - patch.diffs.add(new Diff(diff_type, diff_text)); - bigpatch.diffs.removeFirst(); - } else { - // Deletion or equality. Only take as much as we can stomach. - diff_text = diff_text.substring(0, Math.min(diff_text.length(), - patch_size - patch.length1 - Patch_Margin)); - patch.length1 += diff_text.length(); - start1 += diff_text.length(); - if (diff_type == Operation.EQUAL) { - patch.length2 += diff_text.length(); - start2 += diff_text.length(); - } else { - empty = false; - } - patch.diffs.add(new Diff(diff_type, diff_text)); - if (diff_text.equals(bigpatch.diffs.getFirst().text)) { - bigpatch.diffs.removeFirst(); - } else { - bigpatch.diffs.getFirst().text = bigpatch.diffs.getFirst().text - .substring(diff_text.length()); - } - } - } - // Compute the head context for the next patch. - precontext = diff_text2(patch.diffs); - precontext = precontext.substring(Math.max(0, precontext.length() - - Patch_Margin)); - // Append the end context for this patch. - if (diff_text1(bigpatch.diffs).length() > Patch_Margin) { - postcontext = diff_text1(bigpatch.diffs).substring(0, Patch_Margin); - } else { - postcontext = diff_text1(bigpatch.diffs); - } - if (postcontext.length() != 0) { - patch.length1 += postcontext.length(); - patch.length2 += postcontext.length(); - if (!patch.diffs.isEmpty() - && patch.diffs.getLast().operation == Operation.EQUAL) { - patch.diffs.getLast().text += postcontext; - } else { - patch.diffs.add(new Diff(Operation.EQUAL, postcontext)); - } - } - if (!empty) { - pointer.add(patch); - } - } - bigpatch = pointer.hasNext() ? pointer.next() : null; - } - } - - /** - * Take a list of patches and return a textual representation. - * - * @param patches List of Patch objects. - * @return Text representation of patches. - */ - public String patch_toText(List patches) { - StringBuilder text = new StringBuilder(); - for (Patch aPatch : patches) { - text.append(aPatch); - } - return text.toString(); - } - - /** - * Parse a textual representation of patches and return a List of Patch - * objects. - * - * @param textline Text representation of patches. - * @return List of Patch objects. - * @throws IllegalArgumentException If invalid input. - */ - public List patch_fromText(String textline) - throws IllegalArgumentException { - List patches = new LinkedList(); - if (textline.length() == 0) { - return patches; - } - List textList = Arrays.asList(textline.split("\n")); - LinkedList text = new LinkedList(textList); - Patch patch; - Pattern patchHeader - = Pattern.compile("^@@ -(\\d+),?(\\d*) \\+(\\d+),?(\\d*) @@$"); - Matcher m; - char sign; - String line; - while (!text.isEmpty()) { - m = patchHeader.matcher(text.getFirst()); - if (!m.matches()) { - throw new IllegalArgumentException( - "Invalid patch string: " + text.getFirst()); - } - patch = new Patch(); - patches.add(patch); - patch.start1 = Integer.parseInt(m.group(1)); - if (m.group(2).length() == 0) { - patch.start1--; - patch.length1 = 1; - } else if (m.group(2).equals("0")) { - patch.length1 = 0; - } else { - patch.start1--; - patch.length1 = Integer.parseInt(m.group(2)); - } - - patch.start2 = Integer.parseInt(m.group(3)); - if (m.group(4).length() == 0) { - patch.start2--; - patch.length2 = 1; - } else if (m.group(4).equals("0")) { - patch.length2 = 0; - } else { - patch.start2--; - patch.length2 = Integer.parseInt(m.group(4)); - } - text.removeFirst(); - - while (!text.isEmpty()) { - try { - sign = text.getFirst().charAt(0); - } catch (IndexOutOfBoundsException e) { - // Blank line? Whatever. - text.removeFirst(); - continue; - } - line = text.getFirst().substring(1); - line = line.replace("+", "%2B"); // decode would change all "+" to " " - try { - line = URLDecoder.decode(line, StandardCharsets.UTF_8); - } catch (IllegalArgumentException e) { - // Malformed URI sequence. - throw new IllegalArgumentException( - "Illegal escape in patch_fromText: " + line, e); - } - if (sign == '-') { - // Deletion. - patch.diffs.add(new Diff(Operation.DELETE, line)); - } else if (sign == '+') { - // Insertion. - patch.diffs.add(new Diff(Operation.INSERT, line)); - } else if (sign == ' ') { - // Minor equality. - patch.diffs.add(new Diff(Operation.EQUAL, line)); - } else if (sign == '@') { - // Start of next patch. - break; - } else { - // WTF? - throw new IllegalArgumentException( - "Invalid patch mode '" + sign + "' in: " + line); - } - text.removeFirst(); - } - } - return patches; - } - - /** - * The data structure representing a diff is a Linked list of Diff objects: - * {Diff(Operation.DELETE, "Hello"), Diff(Operation.INSERT, "Goodbye"), - * Diff(Operation.EQUAL, " world.")} - * which means: delete "Hello", add "Goodbye" and keep " world." - */ - public enum Operation { - DELETE, INSERT, EQUAL - } - - /** - * Internal class for returning results from diff_linesToChars(). - * Other less paranoid languages just use a three-element array. - */ - protected static class LinesToCharsResult { - protected String chars1; - protected String chars2; - protected List lineArray; - - protected LinesToCharsResult(String chars1, String chars2, - List lineArray) { - this.chars1 = chars1; - this.chars2 = chars2; - this.lineArray = lineArray; - } - } - - /** - * Class representing one diff operation. - */ - public static class Diff { - /** - * One of: INSERT, DELETE or EQUAL. - */ - public Operation operation; - /** - * The text associated with this diff operation. - */ - public String text; - - /** - * Constructor. Initializes the diff with the provided values. - * - * @param operation One of INSERT, DELETE or EQUAL. - * @param text The text being applied. - */ - public Diff(Operation operation, String text) { - // Construct a diff with the specified operation and text. - this.operation = operation; - this.text = text; - } - - /** - * Display a human-readable version of this Diff. - * - * @return text version. - */ - public String toString() { - String prettyText = this.text.replace('\n', '\u00b6'); - return "Diff(" + this.operation + ",\"" + prettyText + "\")"; - } - - /** - * Create a numeric hash value for a Diff. - * This function is not used by DMP. - * - * @return Hash value. - */ - @Override - public int hashCode() { - final int prime = 31; - int result = (operation == null) ? 0 : operation.hashCode(); - result += prime * ((text == null) ? 0 : text.hashCode()); - return result; - } - - /** - * Is this Diff equivalent to another Diff? - * - * @param obj Another Diff to compare against. - * @return true or false. - */ - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - Diff other = (Diff) obj; - if (operation != other.operation) { - return false; - } - if (text == null) { - return other.text == null; - } else return text.equals(other.text); - } - } - - /** - * Class representing one patch operation. - */ - public static class Patch { - public LinkedList diffs; - public int start1; - public int start2; - public int length1; - public int length2; - - /** - * Constructor. Initializes with an empty list of diffs. - */ - public Patch() { - this.diffs = new LinkedList(); - } - - /** - * Emmulate GNU diff's format. - * Header: @@ -382,8 +481,9 @@ - * Indicies are printed as 1-based, not 0-based. - * - * @return The GNU diff string. - */ - public String toString() { - String coords1, coords2; - if (this.length1 == 0) { - coords1 = this.start1 + ",0"; - } else if (this.length1 == 1) { - coords1 = Integer.toString(this.start1 + 1); - } else { - coords1 = (this.start1 + 1) + "," + this.length1; - } - if (this.length2 == 0) { - coords2 = this.start2 + ",0"; - } else if (this.length2 == 1) { - coords2 = Integer.toString(this.start2 + 1); - } else { - coords2 = (this.start2 + 1) + "," + this.length2; - } - StringBuilder text = new StringBuilder(); - text.append("@@ -").append(coords1).append(" +").append(coords2) - .append(" @@\n"); - // Escape the body of the patch with %xx notation. - for (Diff aDiff : this.diffs) { - switch (aDiff.operation) { - case INSERT: - text.append('+'); - break; - case DELETE: - text.append('-'); - break; - case EQUAL: - text.append(' '); - break; - } - text.append(URLEncoder.encode(aDiff.text, StandardCharsets.UTF_8).replace('+', ' ')) - .append("\n"); - } - return unescapeForEncodeUriCompatability(text.toString()); - } - } -} diff --git a/tool/src/main/java/samlraider/helpers/xsw9Test.java b/tool/src/main/java/samlraider/helpers/xsw9Test.java deleted file mode 100644 index d383d89..0000000 --- a/tool/src/main/java/samlraider/helpers/xsw9Test.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) - * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package samlraider.helpers; - -import org.w3c.dom.Document; -import org.xml.sax.SAXException; - -import java.io.IOException; - -public class xsw9Test { - public static void main(String[] args) throws SAXException, IOException { - String originalAssertion = "http://9ac42dac-7dbd-45e8-b991-6411adcb12c2-idp.idocker.vuln.landhttp://9ac42dac-7dbd-45e8-b991-6411adcb12c2-idp.idocker.vuln.land\n" + - " \n" + - " \n" + - " JY5IOzGUI5V6AKhabmyuuNGhrAI=i0WygaseCAnbUbYsMw5DCfIBOORVjdJDKio2j2YpJnUmJJIRPjko/GrbTZaxYB1vY705qF/uHyaZZ+iz8hdlJ+eIj5XOLACRqHdodNs1DR6fwZpF+2m3ehZWerDW+o0hy0+6dbSOP9RQpS0dpRhTgPXzJyVXt/VhMoL9Rj4hTO0=\n" + - "MIICDDCCAXWgAwIBAgIJAOE4zZWhw6OpMA0GCSqGSIb3DQEBCwUAMB8xCzAJBgNVBAYTAkNIMRAwDgYDVQQDDAdzYW1sdWVsMB4XDTE1MDUyNjIyNTMwMFoXDTI1MDUyNTIyNTMwMFowHzELMAkGA1UEBhMCQ0gxEDAOBgNVBAMMB3NhbWx1ZWwwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALe4eeps2t6HwSsmtYnxlEhdBwIJ2c3+3hEONGVJckrmj6QlMCA9pkYnjk1Afm7JXscFiy2Bhlysanegnn9tjAqSmqe2PT6OhGXr+cmCJRGafby9+gFE0UccjtPW66TyjrcveB90/lJmyA86nc1iASFFwPeQMvJH+YyT9XXQ+ZhTAgMBAAGjUDBOMB0GA1UdDgQWBBRGqV8lPlWbcEikIcuB9vSzR9w6UzAfBgNVHSMEGDAWgBRGqV8lPlWbcEikIcuB9vSzR9w6UzAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBABLnTkSJMXlrRz9lDUJc0tBQ5O4cnnITPDuQ3L87+K932aJ7+pVpHSEtBlWxC0Bz3o6wNUsmcvA5lALeRbUec5UR9HL+9oTsX9+7+aMmuLE/edEwrhfJSzwW3wLAVx8LeLk9NlRJInrXy536Mj/fpOQDJA+C9+WgTlugN2OcxE76_c76a5f1d5a33654d73f4a679be9a106bd23f329a24http://9ac42dac-7dbd-45e8-b991-6411adcb12c2-sp.idocker.vuln.landurn:oasis:names:tc:SAML:2.0:ac:classes:Passwordusermemberuser"; - XSWHelpers.MATCH_AND_REPLACE_MAP.put("user", "admin"); - - XMLHelpers xmlHelpers = new XMLHelpers(); - XSWHelpers xswHelpers = new XSWHelpers(); - - Document document = xmlHelpers.getXMLDocumentOfSAMLMessage(originalAssertion); - xswHelpers.applyXSW("XSW9", document); - } -} diff --git a/tool/src/main/java/samlraider/model/BurpCertificateExtension.java b/tool/src/main/java/samlraider/model/BurpCertificateExtension.java deleted file mode 100644 index 5b036f8..0000000 --- a/tool/src/main/java/samlraider/model/BurpCertificateExtension.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * https://github.com/CompassSecurity/SAMLRaider - * The MIT License (MIT) - * - * Copyright (c) 2015 Roland Bischofberger and Emanuel Duss - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -package samlraider.model; - -import samlraider.helpers.CertificateHelper; - -public class BurpCertificateExtension { - private final String oid; - private final boolean isCritical; - private final byte[] extensionValue; - - public BurpCertificateExtension(String oid, boolean isCritical, byte[] extensionValue) { - this.oid = oid; - this.isCritical = isCritical; - this.extensionValue = extensionValue; - } - - public String getOid() { - return oid; - } - - public boolean isCritical() { - return isCritical; - } - - public byte[] getExtensionValue() { - return extensionValue; - } - - public String toString() { - return oid + (isCritical ? " (Critical): " : " (Not critical): ") + CertificateHelper.byteArrayToHex(extensionValue); - } -} diff --git a/tool/src/test/java/At_Hash_Test.java b/tool/src/test/java/At_Hash_Test.java deleted file mode 100644 index 8fee173..0000000 --- a/tool/src/test/java/At_Hash_Test.java +++ /dev/null @@ -1,59 +0,0 @@ -import migt.At_Hash_check; -import migt.HTTPReqRes; -import migt.Operation_API; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class At_Hash_Test { - - public HTTPReqRes init_message_token_resp() { - String raw = "HTTP/1.1 200 OK\r\n" + - "Date: Fri, 22 Dec 2023 13:12:13 GMT\r\n" + - "Server: WSGIServer/0.2 CPython/3.10.13\r\n" + - "Content-Type: application/json\r\n" + - "X-Frame-Options: DENY\r\n" + - "Content-Length: 2037\r\n" + - "X-Content-Type-Options: nosniff\r\n" + - "Referrer-Policy: same-origin\r\n" + - "Cross-Origin-Opener-Policy: same-origin\r\n" + - "\r\n"; - - - List headers = new ArrayList<>(); - - Collections.addAll(headers, raw.split("\r\n")); - - int body_offset = raw.length(); - - raw += "{\"access_token\": \"eyJ0eXAiOiJhdCtqd3QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlBkMk45LVRael9BV1MzR0ZDa29ZZFJhWFhsczhZUGh4X2RfRXo3SndqUUkifQ.eyJpc3MiOiJodHRwOi8vY2llLXByb3ZpZGVyLm9yZzo4MDAyL29pZGMvb3AiLCJzdWIiOiIyMmE4M2FhZmRlOWUyYzhkZmU2MzM1NTk3ZDk1MTNlMzYzMDdhOWI0NjI1NjVkYTg4MzM5ZTQzMDEyOGE0ODlhIiwiYXVkIjpbImh0dHA6Ly9jaWUtcHJvdmlkZXIub3JnOjgwMDIvb2lkYy9vcCIsIi9vaWRjL29wL3VzZXJpbmZvIl0sImNsaWVudF9pZCI6Imh0dHA6Ly9yZWx5aW5nLXBhcnR5Lm9yZzo4MDAxIiwic2NvcGUiOiJvcGVuaWQgb2ZmbGluZV9hY2Nlc3MiLCJqdGkiOiJmMTA0MWMxYi1hNDYyLTQ3ZWYtOTJjNi0zYWU0ZDNkZTgzMjIiLCJleHAiOjE3MDMyNTI3MTMsImlhdCI6MTcwMzI1MDczM30.bSlYNNyB8zdvE4M-9aiMEeI9NAsd12w47BCb_5ywZqLZMEJ06NYSHzhnJKzonh2TW32I9VFeB8ZxlyTmiFpaLuTm4onN2FfHIWeWYgwwAo-0JgUdjNGS07Vy4EkqZeFChDJCcI4uUriIFEG4u2dnTILNjJC1qcjA3CIlPn7kz9RkDfGw4zAFlOQZ9oVJj5LFUHfB7oDem2z0uJehw5gXHEVBi0hcA1Lj10i8rVuTqhRfCoOdxZwBuTq7eH6z6jCSIplyPIhVqY-dhGQrDvR_tMY4Ulz7Xd0EVjvs09H9QT1tDz9e8WNTF_UbQV8nEaTkbOzN9AC9C0JdJ76O0kH_yg\", \"id_token\": \"eyJhbGciOiJSUzI1NiIsImtpZCI6IlBkMk45LVRael9BV1MzR0ZDa29ZZFJhWFhsczhZUGh4X2RfRXo3SndqUUkifQ.eyJzdWIiOiIyMmE4M2FhZmRlOWUyYzhkZmU2MzM1NTk3ZDk1MTNlMzYzMDdhOWI0NjI1NjVkYTg4MzM5ZTQzMDEyOGE0ODlhIiwibm9uY2UiOiJFNzdKenN0NDNNdjNsUmNyZ2lSRW01U3lRNjNCMTd4VyIsImF0X2hhc2giOiJhVDVmd21tNmFaZmdoTUpYNGZ0Q0N3IiwiY19oYXNoIjoiZm8wVHB3cDRMVm03MmhwcFNsVVpLUSIsImF1ZCI6WyJodHRwOi8vcmVseWluZy1wYXJ0eS5vcmc6ODAwMSJdLCJpc3MiOiJodHRwOi8vY2llLXByb3ZpZGVyLm9yZzo4MDAyL29pZGMvb3AiLCJhY3IiOiJodHRwczovL3d3dy5zcGlkLmdvdi5pdC9TcGlkTDIiLCJqdGkiOiJiYjE5ZGUwYi0xNGQ3LTQzNGEtOTVmNS1jMWRlNWNhNzhkNWQiLCJmYW1pbHlfbmFtZSI6Im1hcmFkb25hIiwiZ2l2ZW5fbmFtZSI6InBlcHBlIiwiZXhwIjoxNzAzMjUyNzEzLCJpYXQiOjE3MDMyNTA3MzN9.h6cUctsaauPpwgPlL_S5A1v_FxWsgAPh1lppizSOwmw0k0r8XlA8EhxXF7NuTG0fyF-TvIR-XzjOovZyv7nfp2uTExzwjBK36S9qixRlLdZeAP5keMTzBGQRAvRzf3xCfdbo9Hz3wdFVAaZL4owbFZJrEf79j6bkJ7EYWPpaxgT2iWoaBDLNBh9cNhRAisBjpKF7Pg9Qjcgh06JBWTdcaGXxt7RITNhh03_kXNmHXc1tNyHqBMhUfPpdDgn0qzbhRm4lRPKg93sMvLTpyETsPsTiVrlBZqZpR-0DpSDRWibCw40vcPS0fbRgHZUWmrxPiyqh9e7hr3MM2Gbejg8THg\", \"token_type\": \"Bearer\", \"expires_in\": 1980, \"scope\": \"openid offline_access\"}"; - byte[] raw_b = raw.getBytes(StandardCharsets.UTF_8); - - HTTPReqRes message = new HTTPReqRes(null, raw_b); - message.body_offset_resp = body_offset; - message.setHeaders(false, headers); - message.isResponse = true; - message.isRequest = false; - - return message; - } - - @Test - public void test_At_Hash() throws NoSuchAlgorithmException { - HTTPReqRes test_message = init_message_token_resp(); - - At_Hash_check ah = new At_Hash_check(); - - Operation_API o = new Operation_API(test_message, false); - - ah.loader(o); - ah.execute(); - assertTrue(ah.getResult()); - } -} diff --git a/tool/src/test/java/Checks_Test.java b/tool/src/test/java/Checks_Test.java deleted file mode 100644 index 4a9076c..0000000 --- a/tool/src/test/java/Checks_Test.java +++ /dev/null @@ -1,440 +0,0 @@ -import migt.*; -import org.json.JSONArray; -import org.json.JSONObject; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - -public class Checks_Test { - - public Check initCheck_json(String check_str) throws ParsingException { - String input = "{\n" + - " \"pageInfo\": {\n" + - " \"pageName\": \"abc\",\n" + - " \"pagePic\": \"http://example.com/content.jpg\",\n" + - " \"entry\": [123, \"abc\",\"cde\"],\n" + - " \"imaninteger\": 123,\n" + - " \"imafloat\": 123.321,\n" + - " },\n" + - " \"posts\": [\n" + - " {\n" + - " \"post_id\": \"123456789012_123456789012\",\n" + - " \"actor_id\": \"1234567890\",\n" + - " \"picOfPersonWhoPosted\": \"http://example.com/photo.jpg\",\n" + - " \"nameOfPersonWhoPosted\": \"Jane Doe\",\n" + - " \"message\": \"Sounds cool. Can't wait to see it!\",\n" + - " \"likesCount\": \"2\",\n" + - " \"comments\": [\"abc\",\"cde\"],\n" + - " \"timeOfPost\": \"1234567890\"\n" + - " }\n" + - " ]\n" + - "}"; - - Check c = new Check(new JSONObject(check_str)); - - DecodeOperation_API dopapi = new DecodeOperation_API(); - dopapi.type = DecodeOperation.DecodeOpType.JWT; - dopapi.jwt.header = input; - c.loader(dopapi); - - return c; - } - - @Test - @DisplayName("check") - void test_check() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.pageName\",\n" + - " \"is\": \"abc\"\n" + - "}"; - - Check c = initCheck_json(check_str); - c.execute(new ArrayList()); - assertTrue(c.getResult()); - } - - @Test - @DisplayName("check") - void test_check_json_is_not() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.pageName\",\n" + - " \"is\": \"notabc\"\n" + - "}"; - - Check c = initCheck_json(check_str); - c.execute(new ArrayList()); - assertFalse(c.getResult()); - } - - @Test - @DisplayName("check") - void test_check_json_contains() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.pageName\",\n" + - " \"contains\": \"abc\"\n" + - "}"; - - Check c = initCheck_json(check_str); - c.execute(new ArrayList()); - assertTrue(c.getResult()); - } - - @Test - @DisplayName("check") - void test_check_json_not_contains() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.pageName\",\n" + - " \"not contains\": \"abc\"\n" + - "}"; - - Check c = initCheck_json(check_str); - c.execute(new ArrayList()); - assertFalse(c.getResult()); - } - - @Test - @DisplayName("check") - void test_check_json_not_found() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.notexisting\",\n" + - " \"is\": \"abc\"\n" + - "}"; - - Check c = initCheck_json(check_str); - c.execute(new ArrayList()); - assertFalse(c.getResult()); - } - - @Test - @DisplayName("check") - void test_check_json_not_present() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.notexisting\",\n" + - " \"is present\": \"false\"\n" + - "}"; - - Check c = initCheck_json(check_str); - c.execute(new ArrayList()); - assertTrue(c.getResult()); - } - - @Test - @DisplayName("check") - void test_check_json_not_present_wrong() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.notexisting\",\n" + - " \"is present\": \"true\"\n" + - "}"; - - Check c = initCheck_json(check_str); - c.execute(new ArrayList()); - assertFalse(c.getResult()); - } - - @Test - @DisplayName("check") - void test_check_json_is_inside_list() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.posts[0].actor_id\",\n" + - " \"is\": \"1234567890\"\n" + - "}"; - - Check c = initCheck_json(check_str); - c.execute(new ArrayList()); - assertTrue(c.getResult()); - } - - @Test - @DisplayName("check") - void test_check_json_set_result() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.posts[0].actor_id\",\n" + - " \"is\": \"1234567890\"\n" + - "}"; - - Check c = initCheck_json(check_str); - c.execute(new ArrayList()); - - DecodeOperation dop = new DecodeOperation(); - dop.setApplicable(true); - dop.setResult(c); - - assertTrue(dop.getResult()); - } - - @Test - @DisplayName("check") - void test_check_use_variable() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"use variable\": true,\n" + - " \"check\": \"$.pageInfo.pageName\",\n" + - " \"is\": \"variablename\"\n" + - "}"; - - List vars = new ArrayList(); - vars.add(new Var("variablename", "abc")); - - Check c = initCheck_json(check_str); - c.execute(vars); - assertTrue(c.getResult()); - } - - @Test - @DisplayName("check") - void test_check_use_variable_wrong() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"use variable\": true,\n" + - " \"check\": \"$.pageInfo.pageName\",\n" + - " \"is\": \"variablename\"\n" + - "}"; - - List vars = new ArrayList(); - vars.add(new Var("variablename", "ac")); - - Check c = initCheck_json(check_str); - c.execute(vars); - assertFalse(c.getResult()); - } - - @Test - @DisplayName("check") - void test_check_json_string_contains_elem() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.entry\",\n" + - " \"contains\": [\"123\"]\n" + - "}"; - - Check c = initCheck_json(check_str); - c.execute(new ArrayList()); - assertTrue(c.getResult()); - } - - @Test - @DisplayName("check") - void test_check_json_string_not_contains_elem_wrong() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.entry\",\n" + - " \"not contains\": [\"123\"]\n" + - "}"; - - Check c = initCheck_json(check_str); - c.execute(new ArrayList()); - assertFalse(c.getResult()); - } - - @Test - @DisplayName("check") - void test_check_json_string_not_contains_elem() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.entry\",\n" + - " \"not contains\": [\"aaa\"]\n" + - "}"; - - Check c = initCheck_json(check_str); - c.execute(new ArrayList()); - assertTrue(c.getResult()); - } - - @Test - @DisplayName("check") - void test_check_json_array_is_subset_of_ok() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.entry\",\n" + - " \"is subset of\": [\"123\", \"abc\",\"cde\", \"altro\"]\n" + - "}"; - - Check c = initCheck_json(check_str); - c.execute(new ArrayList()); - assertTrue(c.getResult()); - } - - @Test - @DisplayName("check") - void test_check_json_array_is_subset_of_variable_ok() throws ParsingException { - Var v = new Var("var1", new JSONArray("[\"123\", \"abc\",\"cde\", \"altro\"]").toList().toArray()); - - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.entry\",\n" + - " \"is subset of\": \"var1\",\n" + - " \"use variable\": true\n" + - "}"; - - Check c = initCheck_json(check_str); - - List vars = new ArrayList(); - vars.add(v); - - c.execute(vars); - assertTrue(c.getResult()); - } - - @Test - @DisplayName("check") - void test_check_json_array_is_subset_of_variable_not_ok() throws ParsingException { - Var v = new Var("var1", new JSONArray("[\"123\", \"abc\",\"fgh\", \"altro\"]").toList().toArray()); - - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.entry\",\n" + - " \"is subset of\": \"var1\",\n" + - " \"use variable\": true\n" + - "}"; - - Check c = initCheck_json(check_str); - - List vars = new ArrayList(); - vars.add(v); - - c.execute(vars); - assertFalse(c.getResult()); - } - - @Test - @DisplayName("check") - void test_check_json_array_is_subset_of_wrong() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.entry\",\n" + - " \"is subset of\": [\"123\", \"abc\",\"aaa\", \"altro\"]\n" + - "}"; - - Check c = initCheck_json(check_str); - c.execute(new ArrayList()); - assertFalse(c.getResult()); - } - - @Test - @DisplayName("check") - void test_check_json_matches_regex() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.pagePic\",\n" + - " \"matches regex\": \"example\"\n" + - "}"; - - Check c = initCheck_json(check_str); - c.execute(new ArrayList()); - assertTrue(c.getResult()); - } - - @Test - @DisplayName("check") - void test_check_json_matches_regex_wrong() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.pagePic\",\n" + - " \"matches regex\": \"exampsle\"\n" + - "}"; - - Check c = initCheck_json(check_str); - c.execute(new ArrayList()); - assertFalse(c.getResult()); - } - - @Test - @DisplayName("check") - void test_check_json_not_matches_regex() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.pagePic\",\n" + - " \"not matches regex\": \"exampsle\"\n" + - "}"; - - Check c = initCheck_json(check_str); - c.execute(new ArrayList()); - assertTrue(c.getResult()); - } - - @Test - @DisplayName("check") - void test_check_json_not_matches_regex_wrong() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.pagePic\",\n" + - " \"not matches regex\": \"example\"\n" + - "}"; - - Check c = initCheck_json(check_str); - c.execute(new ArrayList()); - assertFalse(c.getResult()); - } - - @Test - void test_check_json_schema_validation_ok() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.imaninteger\",\n" + - " \"json schema compliant\": \"{\\\"type\\\":\\\"integer\\\"}\" " + - "}"; - - Check c = initCheck_json(check_str); - - c.execute(new ArrayList<>()); - assertTrue(c.getResult()); - } - - @Test - void test_check_json_schema_validation_wrong() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.imaninteger\",\n" + - " \"json schema compliant\": \"{\\\"type\\\":\\\"string\\\"}\" " + - "}"; - - Check c = initCheck_json(check_str); - - c.execute(new ArrayList<>()); - assertFalse(c.getResult()); - } - - @Test - void test_check_json_schema_validation_wrong_schema() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.imaninteger\",\n" + - " \"json schema compliant\": \"wrongschema\" " + - "}"; - - Check c = initCheck_json(check_str); - try { - c.execute(new ArrayList<>()); - } catch (RuntimeException | ParsingException e) { - assertEquals(1, 1); - return; - } - assertEquals(1, 0); - } - - @Test - void test_print_extended() throws ParsingException { - String check_str = "{\n" + - " \"in\": \"header\",\n" + - " \"check\": \"$.pageInfo.imaninteger\",\n" + - " \"json schema compliant\": \"wrongschema\" " + - "}"; - - Check c = initCheck_json(check_str); - - System.out.println(c.toStringExtended()); - } -} diff --git a/tool/src/test/java/DecodeOperation_Test.java b/tool/src/test/java/DecodeOperation_Test.java deleted file mode 100644 index f003894..0000000 --- a/tool/src/test/java/DecodeOperation_Test.java +++ /dev/null @@ -1,149 +0,0 @@ -import migt.*; -import org.json.JSONObject; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class DecodeOperation_Test { - String input = "{\n" + - " \"from\": \"url\",\n" + - " \"type\": \"jwt\",\n" + - " \"decode param\": \"asd\",\n" + - " \"decode operations\": [\n" + - " {\n" + - " \"from\": \"jwt header\",\n" + - " \"type\": \"jwt\",\n" + - " \"decode param\": \"$.something\"\n" + - " }\n" + - " ]\n" + - " }"; - - String input_w_checks = "{\n" + - " \"from\": \"body\",\n" + - " \"decode param\": \"(?<=authz_request_object=)[^$\\n& ]*\",\n" + - " \"type\": \"jwt\",\n" + - " \"checks\": [\n" + - " {\n" + - " \"in\": \"payload\",\n" + - " \"check\": \"$.scope\",\n" + - " \"is\": \"openid\"\n" + - " }\n" + - " ]\n" + - " }"; - - String input_w_edits = "{\n" + - " \"from\": \"body\",\n" + - " \"decode param\": \"(?<=authz_request_object=)[^$\\n& ]*\",\n" + - " \"type\": \"jwt\",\n" + - " \"edits\": [\n" + - " {\n" + - " \"jwt from\": \"payload\",\n" + - " \"jwt edit\": \"$.scope\",\n" + - " \"value\": \"qualcosaltro\"\n" + - " }\n" + - " ]\n" + - " }"; - - String input_w_edits_save = "{\n" + - " \"from\": \"body\",\n" + - " \"decode param\": \"(?<=authz_request_object=)[^$\\n& ]*\",\n" + - " \"type\": \"jwt\",\n" + - " \"edits\": [\n" + - " {\n" + - " \"jwt from\": \"payload\",\n" + - " \"jwt save\": \"$.scope\",\n" + - " \"as\": \"varname\"\n" + - " }\n" + - " ]\n" + - " }"; - - public static HTTPReqRes get_test_message_with_jwt() { - String req = "GET /.well-known/openid-federation HTTP/1.1\n" + - "Host: trust-anchor.org:8000\n" + - "Accept: */*\n" + - "Accept-Encoding: gzip, deflate\n" + - "User-Agent: Python/3.10 aiohttp/3.9.3\n" + - "Connection: close\n" + - "\n"; - - String resp = "HTTP/1.1 200 OK\n" + - "Date: Wed, 13 Mar 2024 10:31:06 GMT\n" + - "Server: WSGIServer/0.2 CPython/3.10.13\n" + - "Content-Type: application/entity-statement+jwt\n" + - "X-Frame-Options: DENY\n" + - "Content-Length: 2464\n" + - "X-Content-Type-Options: nosniff\n" + - "Referrer-Policy: same-origin\n" + - "Cross-Origin-Opener-Policy: same-origin\n" + - "\n" + - "eyJ0eXAiOiJlbnRpdHktc3RhdGVtZW50K2p3dCIsImFsZyI6IlJTMjU2Iiwia2lkIjoiQlh2ZnJsbmhBTXVIUjA3YWpVbUFjQlJRY1N6bXcwY19SQWdKbnBTLTlXUSJ9.eyJleHAiOjE3MTAzMjc4NDYsImlhdCI6MTcxMDMyNTg2NiwiaXNzIjoiaHR0cDovL3RydXN0LWFuY2hvci5vcmc6ODAwMCIsInN1YiI6Imh0dHA6Ly90cnVzdC1hbmNob3Iub3JnOjgwMDAiLCJqd2tzIjp7ImtleXMiOlt7Imt0eSI6IlJTQSIsIm4iOiJvOElvbFJqWmxremN0LTQ4cmhyVmxUbllVMXBrTWJWSkQtRFUwNW9NUzlSVkdyc0Z5cGc5OG0tS3c0SDRxTlB5UVZ4Mk9RT1JpLXhTaGdrN0hVLWdLXzJwVmd1WWt2MDZGYWpMX2VkRUFxcXNxdF83NFFmMldMUkM1cGZKR196OU9Qelk4Skd5ay16M1NiZUhOX0JYS0k4R1k1RTRXVTJTc3RtUTlmeUw0Q3h0UmZqVWlhOGxpbVRDXzNNT3BUM3ppNW5yMDNqZmJqcG5qZ2E1MXFYdXJ4bmx6YzNhX3hqazVSQUFwS3hVdk53aEoyNzVNMENtQjk5RGpQd0Y2Qkx2VWdKcWd5Q3BVT24zNkxPaEk0RnF1VnFocWhpd0tsTW1pTWUzeXkweU5RN0ZYQld4anpoZXhicHljM1Z1N3pGSUhQQWNDNFV5SVFoYzN3YUVqMnZpWHciLCJlIjoiQVFBQiIsImtpZCI6IkJYdmZybG5oQU11SFIwN2FqVW1BY0JSUWNTem13MGNfUkFnSm5wUy05V1EifV19LCJtZXRhZGF0YSI6eyJmZWRlcmF0aW9uX2VudGl0eSI6eyJjb250YWN0cyI6WyJvcHNAbG9jYWxob3N0Il0sImZlZGVyYXRpb25fZmV0Y2hfZW5kcG9pbnQiOiJodHRwOi8vdHJ1c3QtYW5jaG9yLm9yZzo4MDAwL2ZldGNoIiwiZmVkZXJhdGlvbl9yZXNvbHZlX2VuZHBvaW50IjoiaHR0cDovL3RydXN0LWFuY2hvci5vcmc6ODAwMC9yZXNvbHZlIiwiZmVkZXJhdGlvbl90cnVzdF9tYXJrX3N0YXR1c19lbmRwb2ludCI6Imh0dHA6Ly90cnVzdC1hbmNob3Iub3JnOjgwMDAvdHJ1c3RfbWFya19zdGF0dXMiLCJob21lcGFnZV91cmkiOiJodHRwOi8vdHJ1c3QtYW5jaG9yLm9yZzo4MDAwIiwib3JnYW5pemF0aW9uX25hbWUiOiJleGFtcGxlIFRBIiwicG9saWN5X3VyaSI6Imh0dHA6Ly90cnVzdC1hbmNob3Iub3JnOjgwMDAvZW4vd2Vic2l0ZS9sZWdhbC1pbmZvcm1hdGlvbiIsImxvZ29fdXJpIjoiaHR0cDovL3RydXN0LWFuY2hvci5vcmc6ODAwMC9zdGF0aWMvc3ZnL3NwaWQtbG9nby1jLWxiLnN2ZyIsImZlZGVyYXRpb25fbGlzdF9lbmRwb2ludCI6Imh0dHA6Ly90cnVzdC1hbmNob3Iub3JnOjgwMDAvbGlzdCJ9fSwidHJ1c3RfbWFya19pc3N1ZXJzIjp7Imh0dHBzOi8vd3d3LnNwaWQuZ292Lml0L2NlcnRpZmljYXRpb24vcnAvcHVibGljIjpbImh0dHBzOi8vcmVnaXN0cnkuc3BpZC5hZ2lkLmdvdi5pdCIsImh0dHBzOi8vcHVibGljLmludGVybWVkaWFyeS5zcGlkLml0Il0sImh0dHBzOi8vd3d3LnNwaWQuZ292Lml0L2NlcnRpZmljYXRpb24vcnAvcHJpdmF0ZSI6WyJodHRwczovL3JlZ2lzdHJ5LnNwaWQuYWdpZC5nb3YuaXQiLCJodHRwczovL3ByaXZhdGUub3RoZXIuaW50ZXJtZWRpYXJ5Lml0Il0sImh0dHBzOi8vc2dkLmFhLml0L29uYm9hcmRpbmciOlsiaHR0cHM6Ly9zZ2QuYWEuaXQiXX0sImNvbnN0cmFpbnRzIjp7Im1heF9wYXRoX2xlbmd0aCI6MX19.XfNyESWn60pKcxd-g_fThaV0ig59GZgPkULIrKedEq1MBXpTchwrlSrpI490bSWIsgSARqtugXRMl6fqNhJfty82patlWGTPaYHvCd_6xOo-juRZC-WYn9zSIEw320SLA8FzDeGKIx_ny_vyv6Q2xVQt0BVE5ICngrFgxsLNQ1KOLo4l3EdorEJt8E18zGrB_bPwkMwIHg8smV5MAmWo9mc-4g4ZDRG2-BB2F2tVyavYa_eFPC-lhQSkmKMgtOh4m0QgslANfTKbT8Ce0dsDDrsjujUsiLCwXfwSVR_r9lbAm4tC-TQitpLregoGJYsD5SkEpbSTyWUGTGBvEcXBtg"; - - HTTPReqRes res = new HTTPReqRes(req.getBytes(), resp.getBytes()); - res.body_offset_resp = 283; - - return res; - } - - @Test - void test_parse() throws ParsingException { - DecodeOperation dop = new DecodeOperation(new JSONObject(input)); - - assertEquals(DecodeOperation.DecodeOperationFrom.URL, dop.from); - assertEquals("asd", dop.decode_target); - assertEquals(1, dop.decodeOperations.size()); - - DecodeOperation child_dop = dop.decodeOperations.get(0); - assertEquals(DecodeOperation.DecodeOperationFrom.JWT_HEADER, child_dop.from); - assertEquals("$.something", child_dop.decode_target); - } - - @Test - void test_parse_w_checks() throws ParsingException { - DecodeOperation dop = new DecodeOperation(new JSONObject(input_w_checks)); - - assertEquals(DecodeOperation.DecodeOperationFrom.BODY, dop.from); - assertEquals(1, dop.checks.size()); - - } - - @Test - void test_parse_w_edits() throws ParsingException { - DecodeOperation dop = new DecodeOperation(new JSONObject(input_w_edits)); - - assertEquals(1, dop.editOperations.size()); - } - - @Test - void test_parse_w_edits_save() throws ParsingException { - DecodeOperation dop = new DecodeOperation(new JSONObject(input_w_edits_save)); - - assertEquals(1, dop.editOperations.size()); - } - - @Test - void test_print_extended() throws ParsingException { - DecodeOperation dop = new DecodeOperation(new JSONObject(input_w_edits_save)); - System.out.println(dop.toStringExtended()); - } - - @Test - void test_decode_jwt() throws ParsingException { - String in = "{\n" + - " \"from\": \"body\",\n" + - " \"type\": \"jwt\",\n" + - " \"decode regex\": \"[^\\\\n\\\\r]*\"\n" + - "}"; - DecodeOperation dop = new DecodeOperation(new JSONObject(in)); - - Operation_API op_api = new Operation_API(null); - op_api.message = get_test_message_with_jwt(); - - dop.loader(op_api); - dop.execute(null); - assertTrue(dop.getResult()); - DecodeOperation_API dop_api = dop.getAPI(); - assertEquals("{\"exp\":1710327846,\"iat\":1710325866,\"iss\":\"http://trust-anchor.org:8000\",\"sub\":\"http://trust-anchor.org:8000\",\"jwks\":{\"keys\":[{\"kty\":\"RSA\",\"n\":\"o8IolRjZlkzct-48rhrVlTnYU1pkMbVJD-DU05oMS9RVGrsFypg98m-Kw4H4qNPyQVx2OQORi-xShgk7HU-gK_2pVguYkv06FajL_edEAqqsqt_74Qf2WLRC5pfJG_z9OPzY8JGyk-z3SbeHN_BXKI8GY5E4WU2SstmQ9fyL4CxtRfjUia8limTC_3MOpT3zi5nr03jfbjpnjga51qXurxnlzc3a_xjk5RAApKxUvNwhJ275M0CmB99DjPwF6BLvUgJqgyCpUOn36LOhI4FquVqhqhiwKlMmiMe3yy0yNQ7FXBWxjzhexbpyc3Vu7zFIHPAcC4UyIQhc3waEj2viXw\",\"e\":\"AQAB\",\"kid\":\"BXvfrlnhAMuHR07ajUmAcBRQcSzmw0c_RAgJnpS-9WQ\"}]},\"metadata\":{\"federation_entity\":{\"contacts\":[\"ops@localhost\"],\"federation_fetch_endpoint\":\"http://trust-anchor.org:8000/fetch\",\"federation_resolve_endpoint\":\"http://trust-anchor.org:8000/resolve\",\"federation_trust_mark_status_endpoint\":\"http://trust-anchor.org:8000/trust_mark_status\",\"homepage_uri\":\"http://trust-anchor.org:8000\",\"organization_name\":\"example TA\",\"policy_uri\":\"http://trust-anchor.org:8000/en/website/legal-information\",\"logo_uri\":\"http://trust-anchor.org:8000/static/svg/spid-logo-c-lb.svg\",\"federation_list_endpoint\":\"http://trust-anchor.org:8000/list\"}},\"trust_mark_issuers\":{\"https://www.spid.gov.it/certification/rp/public\":[\"https://registry.spid.agid.gov.it\",\"https://public.intermediary.spid.it\"],\"https://www.spid.gov.it/certification/rp/private\":[\"https://registry.spid.agid.gov.it\",\"https://private.other.intermediary.it\"],\"https://sgd.aa.it/onboarding\":[\"https://sgd.aa.it\"]},\"constraints\":{\"max_path_length\":1}}", dop_api.jwt.payload); - assertEquals("{\"kid\":\"BXvfrlnhAMuHR07ajUmAcBRQcSzmw0c_RAgJnpS-9WQ\",\"typ\":\"entity-statement+jwt\",\"alg\":\"RS256\"}", dop_api.jwt.header); - } -} diff --git a/tool/src/test/java/EditOperation_test.java b/tool/src/test/java/EditOperation_test.java deleted file mode 100644 index 260f5b4..0000000 --- a/tool/src/test/java/EditOperation_test.java +++ /dev/null @@ -1,205 +0,0 @@ -import migt.*; -import org.json.JSONObject; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class EditOperation_test { - HTTPReqRes message = HTTPReqRes_Test.initMessage_ok(); - HTTPReqRes message_w_body; - - @Test - public void test_encode_url_param() throws ParsingException { - String input = "{\"from\": \"url\", \"encode\": \"format\"," + "\"encodings\": [\"base64\"]}"; - EditOperation eop = new EditOperation(new JSONObject(input)); - Operation_API api = new Operation_API(message, true); - - eop.setAPI(api); - eop.execute(null); - assertTrue(eop.getResult()); - Operation_API res = (Operation_API) eop.exporter(); - assertEquals("anNvbg==", res.message.getUrlParam("format")); - } - - @Test - public void test_encode_head_param() throws ParsingException { - String input = "{\"from\": \"head\", \"encode\": \"Host\"," + "\"encodings\": [\"base64\"]}"; - EditOperation eop = new EditOperation(new JSONObject(input)); - Operation_API api = new Operation_API(message, true); - - eop.setAPI(api); - eop.execute(null); - assertTrue(eop.getResult()); - Operation_API res = (Operation_API) eop.exporter(); - assertEquals("cGxheS5nb29nbGUuY29t", res.message.getHeadParam(true, "Host")); - } - - @Test - public void test_encode_body_param() throws ParsingException { - String input = "{\"from\": \"body\", \"encode\": \".*\"," + "\"encodings\": [\"base64\"]}"; - EditOperation eop = new EditOperation(new JSONObject(input)); - Operation_API api = new Operation_API(message, true); - - eop.setAPI(api); - eop.execute(null); - assertTrue(eop.getResult()); - Operation_API res = (Operation_API) eop.exporter(); - assertEquals("Ym9keWNvbnRlbnQ=", new String(res.message.getBody(true))); - } - - @Test - public void test_edit_url_regex() throws ParsingException { - String input = "{\"from\": \"url\", \"edit regex\": \"format=json\"," + "\"value\": \"test=testone\"}"; - EditOperation eop = new EditOperation(new JSONObject(input)); - Operation_API api = new Operation_API(message, true); - - eop.setAPI(api); - eop.execute(null); - assertTrue(eop.getResult()); - Operation_API res = (Operation_API) eop.exporter(); - assertEquals("testone", res.message.getUrlParam("test")); - } - - @Test - public void test_edit_head_regex() throws ParsingException { - String input = "{\"from\": \"head\", \"edit regex\": \"Host:\"," + "\"value\": \"Hosted:\"}"; - EditOperation eop = new EditOperation(new JSONObject(input)); - Operation_API api = new Operation_API(message, true); - - eop.setAPI(api); - eop.execute(null); - assertTrue(eop.getResult()); - Operation_API res = (Operation_API) eop.exporter(); - assertEquals("play.google.com", res.message.getHeadParam(true, "Hosted")); - } - - @Test - public void test_edit_body_regex() throws ParsingException { - String input = "{\"from\": \"body\", \"edit regex\": \"ent\"," + "\"value\": \"123\"}"; - EditOperation eop = new EditOperation(new JSONObject(input)); - Operation_API api = new Operation_API(message, true); - - eop.setAPI(api); - eop.execute(null); - assertTrue(eop.getResult()); - Operation_API res = (Operation_API) eop.exporter(); - assertEquals("bodycont123", new String(res.message.getBody(true))); - } - - @Test - public void test_add_url_param() throws ParsingException { - String input = "{\"from\": \"url\", \"add\": \"codechallenge\"," + "\"value\": \"12345\"}"; - EditOperation eop = new EditOperation(new JSONObject(input)); - Operation_API api = new Operation_API(message, true); - - eop.setAPI(api); - eop.execute(null); - assertTrue(eop.getResult()); - Operation_API res = (Operation_API) eop.exporter(); - assertEquals("12345", res.message.getUrlParam("codechallenge")); - } - - @Test - public void test_add_url_param_already_present() throws ParsingException { - String input = "{\"from\": \"url\", \"add\": \"authuser\"," + "\"value\": \"1\"}"; - EditOperation eop = new EditOperation(new JSONObject(input)); - Operation_API api = new Operation_API(message, true); - - eop.setAPI(api); - eop.execute(null); - assertTrue(eop.getResult()); - Operation_API res = (Operation_API) eop.exporter(); - assertEquals("01", res.message.getUrlParam("authuser")); - } - - @Test - public void test_add_head_param() throws ParsingException { - String input = "{\"from\": \"head\", \"add\": \"Magicheader\"," + "\"value\": \"123123\"}"; - EditOperation eop = new EditOperation(new JSONObject(input)); - Operation_API api = new Operation_API(message, true); - - eop.setAPI(api); - eop.execute(null); - assertTrue(eop.getResult()); - Operation_API res = (Operation_API) eop.exporter(); - assertEquals("123123", res.message.getHeadParam(true, "Magicheader")); - } - - @Test - public void test_add_head_param_already_present() throws ParsingException { - String input = "{\"from\": \"head\", \"add\": \"Accept\"," + "\"value\": \"1\"}"; - EditOperation eop = new EditOperation(new JSONObject(input)); - Operation_API api = new Operation_API(message, true); - - eop.setAPI(api); - eop.execute(null); - assertTrue(eop.getResult()); - Operation_API res = (Operation_API) eop.exporter(); - assertEquals("*/*1", res.message.getHeadParam(true, "Accept")); - } - - @Test - public void test_add_body() throws ParsingException { - String input = "{\"from\": \"body\", \"add\": \"anything\"," + "\"value\": \"&appended\"}"; - EditOperation eop = new EditOperation(new JSONObject(input)); - Operation_API api = new Operation_API(message, true); - - eop.setAPI(api); - eop.execute(null); - assertTrue(eop.getResult()); - Operation_API res = (Operation_API) eop.exporter(); - assertEquals("bodycontent&appended", - new String(res.message.getBody(true))); - } - - @Test - public void test_add_json_jwt() throws ParsingException { - String input = "{\n" + - " \"from\": \"body\",\n" + - " \"type\": \"jwt\",\n" + - " \"decode regex\": \"[^\\\\n\\\\r]*\",\n" + - " \"edits\": [\n" + - " {\n" + - " \"jwt from\": \"payload\",\n" + - " \"jwt add\": \"$\",\n" + - " \"key\": \"new\",\n" + - " \"value\": \"valuenew\"\n" + - " }\n" + - " ]\n" + - "}"; - - DecodeOperation dop = new DecodeOperation(new JSONObject(input)); - dop.loader(new Operation_API(DecodeOperation_Test.get_test_message_with_jwt(), false)); - - dop.execute(null); - assertTrue(dop.editOperations.get(0).getResult()); - - assertEquals("{\"exp\":1710327846,\"iat\":1710325866,\"iss\":\"http://trust-anchor.org:8000\",\"sub\":\"http://trust-anchor.org:8000\",\"jwks\":{\"keys\":[{\"kty\":\"RSA\",\"n\":\"o8IolRjZlkzct-48rhrVlTnYU1pkMbVJD-DU05oMS9RVGrsFypg98m-Kw4H4qNPyQVx2OQORi-xShgk7HU-gK_2pVguYkv06FajL_edEAqqsqt_74Qf2WLRC5pfJG_z9OPzY8JGyk-z3SbeHN_BXKI8GY5E4WU2SstmQ9fyL4CxtRfjUia8limTC_3MOpT3zi5nr03jfbjpnjga51qXurxnlzc3a_xjk5RAApKxUvNwhJ275M0CmB99DjPwF6BLvUgJqgyCpUOn36LOhI4FquVqhqhiwKlMmiMe3yy0yNQ7FXBWxjzhexbpyc3Vu7zFIHPAcC4UyIQhc3waEj2viXw\",\"e\":\"AQAB\",\"kid\":\"BXvfrlnhAMuHR07ajUmAcBRQcSzmw0c_RAgJnpS-9WQ\"}]},\"metadata\":{\"federation_entity\":{\"contacts\":[\"ops@localhost\"],\"federation_fetch_endpoint\":\"http://trust-anchor.org:8000/fetch\",\"federation_resolve_endpoint\":\"http://trust-anchor.org:8000/resolve\",\"federation_trust_mark_status_endpoint\":\"http://trust-anchor.org:8000/trust_mark_status\",\"homepage_uri\":\"http://trust-anchor.org:8000\",\"organization_name\":\"example TA\",\"policy_uri\":\"http://trust-anchor.org:8000/en/website/legal-information\",\"logo_uri\":\"http://trust-anchor.org:8000/static/svg/spid-logo-c-lb.svg\",\"federation_list_endpoint\":\"http://trust-anchor.org:8000/list\"}},\"trust_mark_issuers\":{\"https://www.spid.gov.it/certification/rp/public\":[\"https://registry.spid.agid.gov.it\",\"https://public.intermediary.spid.it\"],\"https://www.spid.gov.it/certification/rp/private\":[\"https://registry.spid.agid.gov.it\",\"https://private.other.intermediary.it\"],\"https://sgd.aa.it/onboarding\":[\"https://sgd.aa.it\"]},\"constraints\":{\"max_path_length\":1},\"new\":\"valuenew\"}", dop.getAPI().jwt.payload); - } - - @Test - public void test_add_json_jwt_null() throws ParsingException { - String input = "{\n" + - " \"from\": \"body\",\n" + - " \"type\": \"jwt\",\n" + - " \"decode regex\": \"[^\\\\n\\\\r]*\",\n" + - " \"edits\": [\n" + - " {\n" + - " \"jwt from\": \"payload\",\n" + - " \"jwt add\": \"$\",\n" + - " \"key\": \"new\",\n" + - " \"value\": \"\"\n" + - " }\n" + - " ]\n" + - "}"; - - DecodeOperation dop = new DecodeOperation(new JSONObject(input)); - dop.loader(new Operation_API(DecodeOperation_Test.get_test_message_with_jwt(), false)); - - dop.execute(null); - assertTrue(dop.editOperations.get(0).getResult()); - - assertEquals("{\"exp\":1710327846,\"iat\":1710325866,\"iss\":\"http://trust-anchor.org:8000\",\"sub\":\"http://trust-anchor.org:8000\",\"jwks\":{\"keys\":[{\"kty\":\"RSA\",\"n\":\"o8IolRjZlkzct-48rhrVlTnYU1pkMbVJD-DU05oMS9RVGrsFypg98m-Kw4H4qNPyQVx2OQORi-xShgk7HU-gK_2pVguYkv06FajL_edEAqqsqt_74Qf2WLRC5pfJG_z9OPzY8JGyk-z3SbeHN_BXKI8GY5E4WU2SstmQ9fyL4CxtRfjUia8limTC_3MOpT3zi5nr03jfbjpnjga51qXurxnlzc3a_xjk5RAApKxUvNwhJ275M0CmB99DjPwF6BLvUgJqgyCpUOn36LOhI4FquVqhqhiwKlMmiMe3yy0yNQ7FXBWxjzhexbpyc3Vu7zFIHPAcC4UyIQhc3waEj2viXw\",\"e\":\"AQAB\",\"kid\":\"BXvfrlnhAMuHR07ajUmAcBRQcSzmw0c_RAgJnpS-9WQ\"}]},\"metadata\":{\"federation_entity\":{\"contacts\":[\"ops@localhost\"],\"federation_fetch_endpoint\":\"http://trust-anchor.org:8000/fetch\",\"federation_resolve_endpoint\":\"http://trust-anchor.org:8000/resolve\",\"federation_trust_mark_status_endpoint\":\"http://trust-anchor.org:8000/trust_mark_status\",\"homepage_uri\":\"http://trust-anchor.org:8000\",\"organization_name\":\"example TA\",\"policy_uri\":\"http://trust-anchor.org:8000/en/website/legal-information\",\"logo_uri\":\"http://trust-anchor.org:8000/static/svg/spid-logo-c-lb.svg\",\"federation_list_endpoint\":\"http://trust-anchor.org:8000/list\"}},\"trust_mark_issuers\":{\"https://www.spid.gov.it/certification/rp/public\":[\"https://registry.spid.agid.gov.it\",\"https://public.intermediary.spid.it\"],\"https://www.spid.gov.it/certification/rp/private\":[\"https://registry.spid.agid.gov.it\",\"https://private.other.intermediary.it\"],\"https://sgd.aa.it/onboarding\":[\"https://sgd.aa.it\"]},\"constraints\":{\"max_path_length\":1},\"new\":null}", dop.getAPI().jwt.payload); - } -} diff --git a/tool/src/test/java/HTTPReqRes_Test.java b/tool/src/test/java/HTTPReqRes_Test.java deleted file mode 100644 index a3c591e..0000000 --- a/tool/src/test/java/HTTPReqRes_Test.java +++ /dev/null @@ -1,363 +0,0 @@ -import migt.HTTPReqRes; -import migt.ParsingException; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - -public class HTTPReqRes_Test { - - public static HTTPReqRes initMessage_ok() { - String raw = "POST /log?format=json&hasfast=true&authuser=0¶mwithspace=first+last HTTPS/2\r\n" + - "Host: play.google.com\r\n" + - "Cookie: CONSENT=PENDING+392; SOCS=CAISHAgCEhJnd3NfMjAyMzAyMjgtMF9SQzIaAml0IAEaBgiA2pSgBg; AEC=AUEFqZdSS4hmP6dNNRrldXefJFuHK2ldiLrZLJG24hUqaFA2L0jJxZwSBA; NID=511=SPj3DZBbWBMVstxl414okznEMUOaUHRzxZehEHxoaTi0Fr_X9RQ6UmFDBvI6wWn1Iivh7lzi_q7Ktri2q8hHc9nVY3XNgQP-IQ4AHNz7lCKra72IjxzhBvEBQFdXy7lEaIVC3wK5TfPIXLX3TWhKwrZAVEg77UkqV2oHYohcSXg\r\n" + - "Content-Length: 11\r\n" + - "Sec-Ch-Ua: \"Chromium\";v=\"111\", \"Not(A:Brand\";v=\"8\"\r\n" + - "Content-Type: application/x-www-form-urlencoded;charset=UTF-8\r\n" + - "X-Goog-Authuser: 0\r\n" + - "Sec-Ch-Ua-Mobile: ?0\r\n" + - "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.65 Safari/537.36\r\n" + - "Sec-Ch-Ua-Platform: \"Linux\"\r\n" + - "Accept: */*\r\n" + - "Origin: https://www.google.com\r\n" + - "X-Client-Data: CNiKywE=\r\n" + - "Sec-Fetch-Site: same-site\r\n" + - "Sec-Fetch-Mode: cors\r\n" + - "Sec-Fetch-Dest: empty\r\n" + - "Referer: https://www.google.com/\r\n" + - "Accept-Encoding: gzip, deflate\r\n" + - "Accept-Language: en-US,en;q=0.9\r\n" + - "\r\n"; - - List headers = new ArrayList<>(); - - Collections.addAll(headers, raw.split("\r\n")); - - int body_offset = raw.length(); - - raw += "bodycontent"; - - byte[] raw_b = raw.getBytes(StandardCharsets.UTF_8); - - HTTPReqRes message = new HTTPReqRes(raw_b, null); - - message.body_offset_req = body_offset; - message.setHeaders(true, headers); - message.isRequest = true; - message.isResponse = false; - message.setRequest_url("https://play.google.com/log?format=json&hasfast=true&authuser=0¶mwithspace=first+last"); - - return message; - } - - public HTTPReqRes init_message_no_body() { - String raw = "POST /log?format=json&hasfast=true&authuser=0¶mwithspace=first+last HTTPS/2\r\n" + - "Host: play.google.com\r\n" + - "Cookie: CONSENT=PENDING+392; SOCS=CAISHAgCEhJnd3NfMjAyMzAyMjgtMF9SQzIaAml0IAEaBgiA2pSgBg; AEC=AUEFqZdSS4hmP6dNNRrldXefJFuHK2ldiLrZLJG24hUqaFA2L0jJxZwSBA; NID=511=SPj3DZBbWBMVstxl414okznEMUOaUHRzxZehEHxoaTi0Fr_X9RQ6UmFDBvI6wWn1Iivh7lzi_q7Ktri2q8hHc9nVY3XNgQP-IQ4AHNz7lCKra72IjxzhBvEBQFdXy7lEaIVC3wK5TfPIXLX3TWhKwrZAVEg77UkqV2oHYohcSXg\r\n" + - "Content-Length: 11\r\n" + - "Sec-Ch-Ua: \"Chromium\";v=\"111\", \"Not(A:Brand\";v=\"8\"\r\n" + - "Content-Type: application/x-www-form-urlencoded;charset=UTF-8\r\n" + - "X-Goog-Authuser: 0\r\n" + - "Sec-Ch-Ua-Mobile: ?0\r\n" + - "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.65 Safari/537.36\r\n" + - "Sec-Ch-Ua-Platform: \"Linux\"\r\n" + - "Accept: */*\r\n" + - "Origin: https://www.google.com\r\n" + - "X-Client-Data: CNiKywE=\r\n" + - "Sec-Fetch-Site: same-site\r\n" + - "Sec-Fetch-Mode: cors\r\n" + - "Sec-Fetch-Dest: empty\r\n" + - "Referer: https://www.google.com/\r\n" + - "Accept-Encoding: gzip, deflate\r\n" + - "Accept-Language: en-US,en;q=0.9\r\n" + - "\r\n"; - - List headers = new ArrayList<>(); - - Collections.addAll(headers, raw.split("\r\n")); - - byte[] raw_b = raw.getBytes(StandardCharsets.UTF_8); - - HTTPReqRes message = new HTTPReqRes(raw_b, null); - - message.body_offset_req = 0; - message.setHeaders(true, headers); - message.isRequest = true; - message.isResponse = false; - message.setRequest_url("https://play.google.com/log?format=json&hasfast=true&authuser=0¶mwithspace=first+last"); - - return message; - } - - @Test - @DisplayName("") - public void test_build() { - String raw = "POST /log?format=json&hasfast=true&authuser=0¶mwithspace=first+last HTTP/2\r\n" + - "Host: play.google.com\r\n" + - "Cookie: CONSENT=PENDING+392; SOCS=CAISHAgCEhJnd3NfMjAyMzAyMjgtMF9SQzIaAml0IAEaBgiA2pSgBg; AEC=AUEFqZdSS4hmP6dNNRrldXefJFuHK2ldiLrZLJG24hUqaFA2L0jJxZwSBA; NID=511=SPj3DZBbWBMVstxl414okznEMUOaUHRzxZehEHxoaTi0Fr_X9RQ6UmFDBvI6wWn1Iivh7lzi_q7Ktri2q8hHc9nVY3XNgQP-IQ4AHNz7lCKra72IjxzhBvEBQFdXy7lEaIVC3wK5TfPIXLX3TWhKwrZAVEg77UkqV2oHYohcSXg\r\n" + - "Content-Length: 11\r\n" + - "Sec-Ch-Ua: \"Chromium\";v=\"111\", \"Not(A:Brand\";v=\"8\"\r\n" + - "Content-Type: application/x-www-form-urlencoded;charset=UTF-8\r\n" + - "X-Goog-Authuser: 0\r\n" + - "Sec-Ch-Ua-Mobile: ?0\r\n" + - "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.65 Safari/537.36\r\n" + - "Sec-Ch-Ua-Platform: \"Linux\"\r\n" + - "Accept: */*\r\n" + - "Origin: https://www.google.com\r\n" + - "X-Client-Data: CNiKywE=\r\n" + - "Sec-Fetch-Site: same-site\r\n" + - "Sec-Fetch-Mode: cors\r\n" + - "Sec-Fetch-Dest: empty\r\n" + - "Referer: https://www.google.com/\r\n" + - "Accept-Encoding: gzip, deflate\r\n" + - "Accept-Language: en-US,en;q=0.9\r\n" + - "\r\n"; - - List headers = List.of(raw.split("\r\n")); - - int body_offset = raw.length(); - - raw += "bodycontent"; - - byte[] raw_b = raw.getBytes(StandardCharsets.UTF_8); - - HTTPReqRes message = new HTTPReqRes(raw_b, null); - message.body_offset_req = body_offset; - message.setHeaders(true, headers); - - byte[] builded = message.build_message(message.isRequest); - String builded_str = new String(builded, StandardCharsets.UTF_8); - assertEquals(raw, builded_str); - assertArrayEquals(builded, raw_b); - } - - @Test - @DisplayName("") - public void test_build_no_body() { - String raw = "POST /log?format=json&hasfast=true&authuser=0¶mwithspace=first+last HTTP/2\r\n" + - "Host: play.google.com\r\n" + - "Cookie: CONSENT=PENDING+392; SOCS=CAISHAgCEhJnd3NfMjAyMzAyMjgtMF9SQzIaAml0IAEaBgiA2pSgBg; AEC=AUEFqZdSS4hmP6dNNRrldXefJFuHK2ldiLrZLJG24hUqaFA2L0jJxZwSBA; NID=511=SPj3DZBbWBMVstxl414okznEMUOaUHRzxZehEHxoaTi0Fr_X9RQ6UmFDBvI6wWn1Iivh7lzi_q7Ktri2q8hHc9nVY3XNgQP-IQ4AHNz7lCKra72IjxzhBvEBQFdXy7lEaIVC3wK5TfPIXLX3TWhKwrZAVEg77UkqV2oHYohcSXg\r\n" + - "Sec-Ch-Ua: \"Chromium\";v=\"111\", \"Not(A:Brand\";v=\"8\"\r\n" + - "X-Goog-Authuser: 0\r\n" + - "Sec-Ch-Ua-Mobile: ?0\r\n" + - "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.65 Safari/537.36\r\n" + - "Sec-Ch-Ua-Platform: \"Linux\"\r\n" + - "Accept: */*\r\n" + - "Origin: https://www.google.com\r\n" + - "X-Client-Data: CNiKywE=\r\n" + - "Sec-Fetch-Site: same-site\r\n" + - "Sec-Fetch-Mode: cors\r\n" + - "Sec-Fetch-Dest: empty\r\n" + - "Referer: https://www.google.com/\r\n" + - "Accept-Encoding: gzip, deflate\r\n" + - "Accept-Language: en-US,en;q=0.9\r\n" + - "\r\n"; - - List headers = List.of(raw.split("\r\n")); - - int body_offset = raw.length(); - - byte[] raw_b = raw.getBytes(StandardCharsets.UTF_8); - - HTTPReqRes message = new HTTPReqRes(raw_b, null); - message.body_offset_req = body_offset; - message.setHeaders(true, headers); - - byte[] builded = message.build_message(message.isRequest); - String builded_str = new String(builded, StandardCharsets.UTF_8); - assertEquals(raw, builded_str); - assertArrayEquals(builded, raw_b); - } - - @Test - @DisplayName("") - public void test_build_no_body_with_content_len() { - String raw = "POST /log?format=json&hasfast=true&authuser=0¶mwithspace=first+last HTTP/2\r\n" + - "Host: play.google.com\r\n" + - "Cookie: CONSENT=PENDING+392; SOCS=CAISHAgCEhJnd3NfMjAyMzAyMjgtMF9SQzIaAml0IAEaBgiA2pSgBg; AEC=AUEFqZdSS4hmP6dNNRrldXefJFuHK2ldiLrZLJG24hUqaFA2L0jJxZwSBA; NID=511=SPj3DZBbWBMVstxl414okznEMUOaUHRzxZehEHxoaTi0Fr_X9RQ6UmFDBvI6wWn1Iivh7lzi_q7Ktri2q8hHc9nVY3XNgQP-IQ4AHNz7lCKra72IjxzhBvEBQFdXy7lEaIVC3wK5TfPIXLX3TWhKwrZAVEg77UkqV2oHYohcSXg\r\n" + - "Sec-Ch-Ua: \"Chromium\";v=\"111\", \"Not(A:Brand\";v=\"8\"\r\n" + - "Content-Length: 11\r\n" + - "X-Goog-Authuser: 0\r\n" + - "Sec-Ch-Ua-Mobile: ?0\r\n" + - "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/111.0.5563.65 Safari/537.36\r\n" + - "Sec-Ch-Ua-Platform: \"Linux\"\r\n" + - "Accept: */*\r\n" + - "Origin: https://www.google.com\r\n" + - "X-Client-Data: CNiKywE=\r\n" + - "Sec-Fetch-Site: same-site\r\n" + - "Sec-Fetch-Mode: cors\r\n" + - "Sec-Fetch-Dest: empty\r\n" + - "Referer: https://www.google.com/\r\n" + - "Accept-Encoding: gzip, deflate\r\n" + - "Accept-Language: en-US,en;q=0.9\r\n" + - "\r\n"; - - List headers = List.of(raw.split("\r\n")); - - int body_offset = raw.length(); - - byte[] raw_b = raw.getBytes(StandardCharsets.UTF_8); - - HTTPReqRes message = new HTTPReqRes(raw_b, null); - message.body_offset_req = body_offset; - message.setHeaders(true, headers); - - byte[] builded = message.build_message(message.isRequest); - String builded_str = new String(builded, StandardCharsets.UTF_8); - - raw = raw.replace("Content-Length: 11\r\n", ""); - raw_b = raw.getBytes(StandardCharsets.UTF_8); - - assertEquals(raw, builded_str); - assertArrayEquals(builded, raw_b); - } - - @Test - @DisplayName("") - public void test_getUrlHeader() { - HTTPReqRes message = initMessage_ok(); - String header_0 = message.getUrlHeader(); - - assertEquals("POST /log?format=json&hasfast=true&authuser=0¶mwithspace=first+last HTTPS/2", header_0); - } - - @Test - @DisplayName("") - public void test_getUrlParam() { - HTTPReqRes message = initMessage_ok(); - String value = message.getUrlParam("format"); - assertEquals("json", value); - value = message.getUrlParam("hasfast"); - assertEquals("true", value); - value = message.getUrlParam("authuser"); - assertEquals("0", value); - } - - @Test - @DisplayName("") - public void test_editUrlParam() throws ParsingException { - HTTPReqRes message = initMessage_ok(); - message.editUrlParam("format", "new"); - assertEquals("https://play.google.com/log?format=new&hasfast=true&authuser=0¶mwithspace=first+last", message.getUrl()); - message.setRequest_url("https://play.google.com:8080/log?format=new&hasfast=true&authuser=0#123123123"); - message.editUrlParam("format", "newnew"); - assertEquals("https://play.google.com:8080/log?format=newnew&hasfast=true&authuser=0#123123123", message.getUrl()); - assertEquals("POST /log?format=newnew&hasfast=true&authuser=0 HTTPS/2", message.getUrlHeader()); - } - - @Test - public void test_url_update_header_0() { - HTTPReqRes message = initMessage_ok(); - message.setRequest_url("https://play.google.com:8080/log?format=newnew&hasfast=true&authuser=0#123123123"); - assertEquals("https://play.google.com:8080/log?format=newnew&hasfast=true&authuser=0#123123123", message.getUrl()); - assertEquals("POST /log?format=newnew&hasfast=true&authuser=0 HTTPS/2", message.getUrlHeader()); - } - - @Test - public void test_editUrlHeaders() throws ParsingException { - HTTPReqRes message = initMessage_ok(); - message.updateHeadersWHurl(); - assertEquals("POST /log?format=json&hasfast=true&authuser=0¶mwithspace=first+last HTTPS/2", message.getHeaders(true).get(0)); - message.editUrlParam("format", "new"); - assertEquals("POST /log?format=new&hasfast=true&authuser=0¶mwithspace=first+last HTTPS/2", message.getHeaders(true).get(0)); - message.removeUrlParam("hasfast"); - assertEquals("POST /log?format=new&authuser=0¶mwithspace=first+last HTTPS/2", message.getHeaders(true).get(0)); - message.addUrlParam("prova", "provona"); - assertEquals("POST /log?format=new&authuser=0¶mwithspace=first+last&prova=provona HTTPS/2", message.getHeaders(true).get(0)); - } - - @Test - @DisplayName("") - public void test_removeUrlParam() throws ParsingException { - HTTPReqRes message = initMessage_ok(); - message.removeUrlParam("format"); - assertEquals("https://play.google.com/log?hasfast=true&authuser=0¶mwithspace=first+last", message.getUrl()); - } - - @Test - @DisplayName("") - public void test_addUrlParam() throws ParsingException { - HTTPReqRes message = initMessage_ok(); - message.addUrlParam("test", "test"); - assertEquals("https://play.google.com/log?format=json&hasfast=true&authuser=0¶mwithspace=first+last&test=test", message.getUrl()); - } - - @Test - @DisplayName("") - public void test_getHeadParam() { - HTTPReqRes message = initMessage_ok(); - String value = message.getHeadParam(true, "Origin"); - assertEquals("https://www.google.com", value); - } - - @Test - @DisplayName("") - public void test_editHeadParam() { - HTTPReqRes message = initMessage_ok(); - message.editHeadParam(true, "Origin", "www.another.com"); - String value = message.getHeadParam(true, "Origin"); - assertEquals("www.another.com", value); - } - - - @Test - @DisplayName("") - public void test_addHeadParameter() { - HTTPReqRes message = initMessage_ok(); - message.addHeadParameter(true, "Test", "valuetest"); - String value = message.getHeadParam(true, "Test"); - assertEquals("valuetest", value); - } - - - @Test - @DisplayName("") - public void test_removeHeadParameter() { - HTTPReqRes message = initMessage_ok(); - message.removeHeadParameter(true, "Origin"); - String value = message.getHeadParam(true, "Origin"); - assertEquals("", value); - } - - @Test - public void test_add_body() { - // add body to a message that does not have it - HTTPReqRes message = init_message_no_body(); - assertFalse(message.hasBody(true)); - - message.addBody(true, "testbodycontent"); - - assertTrue(message.hasBody(true)); - assertEquals("testbodycontent", new String(message.getBody(true))); - - message.addBody(true, "1"); - assertEquals("testbodycontent1", new String(message.getBody(true))); - } - - @Test - public void test_edit_body_regex() { - HTTPReqRes message = initMessage_ok(); - - message.editBodyRegex(true, "conte", "1234"); - assertEquals("body1234nt", new String(message.getBody(true))); - } - - @Test - public void test_get_url_param_no_urlencode() { - HTTPReqRes msg = initMessage_ok(); - - // with url encode (default) - String value = msg.getUrlParam("paramwithspace"); - assertEquals("first last", value); - - // without url encode - value = msg.getUrlParam("paramwithspace", true); - assertEquals("first+last", value); - } - -} diff --git a/tool/src/test/java/JWT_Test.java b/tool/src/test/java/JWT_Test.java deleted file mode 100644 index 24f3379..0000000 --- a/tool/src/test/java/JWT_Test.java +++ /dev/null @@ -1,449 +0,0 @@ -import com.nimbusds.jose.*; -import com.nimbusds.jose.crypto.RSAEncrypter; -import com.nimbusds.jose.crypto.RSASSASigner; -import com.nimbusds.jose.jwk.JWK; -import com.nimbusds.jwt.JWTClaimsSet; -import com.nimbusds.jwt.SignedJWT; -import migt.JWT; -import migt.ParsingException; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.Date; - -import static org.junit.jupiter.api.Assertions.*; - -public class JWT_Test { - JWT j; - String raw_jwt = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ"; - - String raw_header = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9"; - String raw_payload = "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0"; - String raw_signature = "NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ"; - - String public_pem_ed = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAPq3ZL01cG1DHZ4iZLiRlRJIlupb5MGfHipSBq1hG2Jo=\n-----END PUBLIC KEY-----\n"; - String private_pem_ed = "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIFlqmiu8kHunEywNZhbZjdZcT1YGTUCoOlh9aHF+43UE\n-----END PRIVATE KEY-----\n"; - - String private_pem_rsa = "-----BEGIN RSA PRIVATE KEY-----\n" + - "MIIJKQIBAAKCAgEAlD5LtoIK0+dFO2bEaGWRdK3yO4BXOty05yv61WTJO8l8gl1X\n" + - "LoQZS35bXYmsrh/4a58Wr+d2KrFx71ZzBrx0hJsJ/5+Ia+9q7zUCAmyuv+A73e/k\n" + - "kf/CIRSyg2tq++etsFoyUtx8AEw7IrGcLzhvy4R4h2vmgtqaln7Nw55FoLKJ8QiE\n" + - "Tq9UPz9KiV3OhA6ks07y6Brj63fCv6G9sX7uxDoflxVTiH7CaimlLv0h0hrA8s1O\n" + - "D3VfZjsKIGP+bTtsGzeBzGhxPCkg5DRGTRM/ST1OBMe3Swf9/kZ0ZsbcM2RliEVi\n" + - "OigVm4gYoVyIHbt+/Sig438qcrc581w1jdOzvmrPjXRlj60iKBjXnhPwT8UGZn4O\n" + - "WFu9NypVSfMjHgAIdEs+rOVp0YDxPhRU41DbRXci7TefZmCguaoir+7S7em5vPVO\n" + - "73fnYFrTEKTGdhlsYo43t5I9NkWlRZigg3UsdSREDZCdBlDcPx7UQDiBs9u3Uh4+\n" + - "2RT7R/Cdp4aPihq2lI4+iWVUk51xdWRJHeI4vbUYj3bwn4OrkXsb0PgzSz2Ss84f\n" + - "VIsFd6oLprxQn2OOj5Ra6P3ZpoosSvyD4J8zVZGUhi4sHzKDL3B7/wHTjXvHYbRW\n" + - "kWnvde7YV7aHMY+RlT+4LscDozIYRp5fPoh/DLNWfy8zTwGDWiiZGrB9RrcCAwEA\n" + - "AQKCAgA0R2PcETBQWpcHw84wIuGRDGcIpNIeaAdEHzZuWwS8mOnX76L3PI7PGNiP\n" + - "vCWxooSxL4GIt0/s7ncHuK0ICx3sReDYzSIHLn+/rCnxQPK/qAx00E0DT/beQ7ZQ\n" + - "smkgPSv7rVNh9W+lizyvl4NFA9opI6Z924eHTiCGQmG+Quq7KTuMTTyboylKxL88\n" + - "gmB6Ic/jjEwNnq4SNEHx4tBK8ECz4uuRFGxJDqrxVY5za8GpntW8yrpkqTfjjZ6c\n" + - "nab0TqhpUMHtnEeSt85prCW+uLLw2TXSabwyMbdZHO+f7zFozlcgH5fseoZkOzK0\n" + - "dTVrhtvZ26IhmI8XtZYyRKp+QdJ5IwF3XpXWt7zafUdYCu8Z45HZhEe+aTpNIDvv\n" + - "z2nkqiJ0zAMH188YqbgawCdWow6D3/vqoyr5IlZcj9qE2wKunT5eqr248rNQsLA5\n" + - "2nOwz4fPcN4RHyiBygD8G4867di7Q+qRBdMAfRx5DrG0ottL2oNrr5G9Wv94Dsu8\n" + - "+A0q8EpwAvwYsxCNVnrjd2ypfJhyS30Pktz/LWj/MWbee1BP83C3zveqwBQ+zxTd\n" + - "03surmBlNS7tZOZvThMbBlWIO66wQWCzkZwi6zbrJwHCf4B30jTepERsiLSv2jyI\n" + - "oHNGOa84i2IEdWtXGVVyAET9E+pzNVQjFHsembIS6H1W4GTjwQKCAQEAzkB4lLkx\n" + - "6gm5o5IhRC2ZAL9RaZGSrT9sXJThtq7qVqQiLItvNRa633eqfxzlkgBgmH2dGgc+\n" + - "OqZMOHE2u0PSWBUinkrzatFTyjn99H6jYtG70rxLMZcS9GD5padP2mFklrM9+WaN\n" + - "f7fuvZjsiIanVxhykyEqB7y43JSR5vUSab9tltbnG2+V2ZMmh+8GWEDwozhyOZW9\n" + - "cjhu+eI+7FqyU1JBydArO3Ds8ZpWrjfZvxmS7LzoJEkJ4hdfMmeAWFNv+tLhYGAT\n" + - "Et7d6atoxPL6bId+F+ncqgCF9SVmYpq9faoXsvOr7yxR3n6MLY5Fvz2fX0tvBeSv\n" + - "skn/a1mei1yqHQKCAQEAt//yZzEjCgi49QeT01CHPbkogbtkG+tVuB5V2zpLArj1\n" + - "AXXKSNZn96+UjgScEY/DTWm8ljjDP8UYZXmYgkBm+oEdYnyLa60u/ZSdP4E1ICJT\n" + - "32XHrbkt1Z9SdMDFwhitKuA9uyUIuLf2OfIvZQQDU6BPmr18OX82ODxZXqCMdGJH\n" + - "3rml16joa5u9KDPNLfei2NEEKc9L7szvtBwQYf2DKd4jgpZMOg8EFQBd3+szl7UD\n" + - "gTPRT7sZX035mNYfZkFTjj0z4vqEu1ARCkKz0fp05uEQnQLOTUGEgECDTh7jZnfn\n" + - "PDDio4/vW8qEPEfUcfpetbNr3i8hhAeA5xHyBij74wKCAQEAvPo1gY9uPJJMlaL+\n" + - "+AkPd6/UWHYZfsPt9aY0ab462MfqyAW6D1qUPszWW0GO1wehehceKwsX6YUVsWGK\n" + - "VGr//9Tds0vZXLYPn+si1TJzYcfp4FzGSNmzdFamZzG16NHz6GCzGCDu5WcSSIYl\n" + - "s7ItAZBU6pooeI5ikzlNteA2zs2nC948Qtcq5f/9/e70UUivM940Sq74tf8fL7Yt\n" + - "EULIwa9MuC0Ub5I4h+ZyJY7m5EH6bQ9pZFXHyHDBuN08q7FHmPo/pp5g25l4mvGD\n" + - "PXGkImzDDAYrOVjhZIywEwjVNp7yt/SsRKjHGqW4qsUBAwjjTd1ADJZMpX9HmIS0\n" + - "z9xHwQKCAQEAp3O0LHuIcupLQRvbSZXg7qhil+ZtjgcXZM+evTwI5fpjZyfGp5EQ\n" + - "31YYcUL6sfTO/dW7vk78Sj3aHQeTZv6reVEl5+qGi8D5oeetUA0LxynWgNnE5nI/\n" + - "p0kupniFwUXp2rpnE7j5ffpViJjCz0Desi2UJLRLqJwAQR+TCc485PJIjAcSSfk7\n" + - "RCtg84RpN2tF9eIK0u4IIdS6VYSw2Cz6QJEcagzUZIYj5eUGifEoa+ldvijlVZVl\n" + - "2tlAzPoZa1sKasmCPhBV2Y5dY6QeuHsiBrhPAUV7cM2ug3WyydbMhwWaGKo4qDgm\n" + - "0re0rpOEYRJFPUGDapoj+19EzYYEZ9zGlwKCAQBdOy3ctg82hG8y2GR2S8abgVI1\n" + - "b9S4s8FOYxxujzM6nkB+m8J1el4Sk8n4HuYDY9kivfI9/sbR7wUX9fV0QJHlr70t\n" + - "z8aTKNQVxzGzO+OLEGs66ieAI8uCOByCEbyqPUZZAg0YN7BWtfFsqdAwOdEdYCMO\n" + - "CR0axKnVhkIV7Oj9JQ1+mucB8gL5N8miSxc9lsWuOz2tGEM8rcTAoGSmvwMkgcfK\n" + - "P34FFHdGk4fZnRYA6pGfgVZD3ZyRYF9cWnNgw823JlsYhzGGX4pQbFFGZ4rGuQ9z\n" + - "ursu/oNFRzKSZrb1FHOHvv5DfkINWXPVL8EgQG2HN2AF+LSZllSrStef9Urw\n" + - "-----END RSA PRIVATE KEY-----"; - - String public_pem_rsa = "-----BEGIN PUBLIC KEY-----\n" + - "MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAlD5LtoIK0+dFO2bEaGWR\n" + - "dK3yO4BXOty05yv61WTJO8l8gl1XLoQZS35bXYmsrh/4a58Wr+d2KrFx71ZzBrx0\n" + - "hJsJ/5+Ia+9q7zUCAmyuv+A73e/kkf/CIRSyg2tq++etsFoyUtx8AEw7IrGcLzhv\n" + - "y4R4h2vmgtqaln7Nw55FoLKJ8QiETq9UPz9KiV3OhA6ks07y6Brj63fCv6G9sX7u\n" + - "xDoflxVTiH7CaimlLv0h0hrA8s1OD3VfZjsKIGP+bTtsGzeBzGhxPCkg5DRGTRM/\n" + - "ST1OBMe3Swf9/kZ0ZsbcM2RliEViOigVm4gYoVyIHbt+/Sig438qcrc581w1jdOz\n" + - "vmrPjXRlj60iKBjXnhPwT8UGZn4OWFu9NypVSfMjHgAIdEs+rOVp0YDxPhRU41Db\n" + - "RXci7TefZmCguaoir+7S7em5vPVO73fnYFrTEKTGdhlsYo43t5I9NkWlRZigg3Us\n" + - "dSREDZCdBlDcPx7UQDiBs9u3Uh4+2RT7R/Cdp4aPihq2lI4+iWVUk51xdWRJHeI4\n" + - "vbUYj3bwn4OrkXsb0PgzSz2Ss84fVIsFd6oLprxQn2OOj5Ra6P3ZpoosSvyD4J8z\n" + - "VZGUhi4sHzKDL3B7/wHTjXvHYbRWkWnvde7YV7aHMY+RlT+4LscDozIYRp5fPoh/\n" + - "DLNWfy8zTwGDWiiZGrB9RrcCAwEAAQ==\n" + - "-----END PUBLIC KEY-----"; - - String raw_jwe = "eyJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiUlNBLU9BRVAtMjU2In0.df14swiSheMDVazjpF60-Vi1Qk46HBJtVhlBOhnAeouJGu0Qf5q_-ENHNVC0ur7pza3n0o3dUodgx_vTNcJq5irnfOiBk4Vq0D9r8uh9eRQCgZVTCX8fNDhGl0gLlcvOIhL9JTn6bj4KGh4MrYsEHRfI7XD-PZyDIDMliZlwesrw3D4wQaHEs3QYAYn7VpKkOXZ0uYOPcDEZKJZgfQx1bSCjEYOxrBp0-9N7Vb4iwUaDzXDQtYbzGdAfvaU9LuEci0l8PkXDjk2z28xVOEXR1biWMhci3FZoqY47CaSjHEeJStAVOm5mEWy76ype4aBJ6e1f1JNr5xvOPdQMLeGzlg.QAK60NWaZVLHOWGd.MAPO8dBIwdnCxQLPeHvPBhMISbxHcLlmX6SsUyznnTA_rL9oaX-lbwUaMnJG8JEsOQhXEpRDL8R8wGqSf_euZUFPxjMVtuJXhlnYBulNju-Ce20nq72xPq5TsPsvUUjwms_Z9VRcJiyq9t88KU-SbWY4D7A8zlIj_QYu7UE7jwP-5rJ9ZJ_9BiLQ4nxoueQARTs8IbOy-W38YU8gCpXJqkIc9FIA5sEiQE2YTppisXrUajeffARk7-0wGQTxodWTOoMSEQBVnBtYr0AglnzYOl89PmDjNhf_5ViJJV_AxbKCrTXjd_akDVkhPDAad6qtJFGJVNNdXfnd2Q3UwKGp2vTp52xY6q66t3aELZKHIM861Qry0tQZaagBXJDg2JKZCOG6ylj0a3ZVtKyworBLdbj6tvrS6lJ1LqivaahCCyXAGoWq203VR55l9k7BPW325AUTto8eUQ2qOGSB3h-nQF9rnbKdIp5VQj4o0ub7QARuhoZ7cyfLPWEs013xQnuXwdLTFAN4dV42WxTo6ZWAMwFWRSRllRRhBHCtWd1XU-y0P6rZ1AvuJDGvX5YlSwcVnkmczueEcIxlucOmwRo9JkkZ31Lck6WKVnLx_yS-7zXKOcMNdPR5dEWZGENFLiRqns3q4654zygPt9N0WZbVcPZmyVqO0EYiVor_wfij9HUyZzjVb4YiYCNAKM_QJb1c0P6xYgiCqxSaWw-BUqYPrUZXEimxo9XWrIpWBBBNXtCMZqrTNKFXmOODQT-sDLVDj4C1X1bbtgr6D02G7gzvy2lpE3u4yB7PdL4peltuP0dvGqgg19oUw3JaJK-n-mzNXy5w_gwDr2YCcZc9lpvLqZX5QnIsk6_WIhgRBC5T5jurefnj_rvTQCEdCC2hDQxFhsXOlPMXbrYzQ7MpmxnN1JJFa5W3hzMhG8SXTNdlvI9IuqF1tDA_NY6AGUkzegmdiyARa-GXq3ZsWW9idfLYYWxc5yHfiDJzjqBBRlQZKjwNopfbOiFoNe_7KfB98Kt7cf2s41eM6LeSPtynyw.yN7-uTC9ihTAVuQLA5rJUg"; - - String jwe_private_key_pem = "-----BEGIN PRIVATE KEY-----\n" + - "MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCZ92bxCHSFfY1V\n" + - "f7RPsj6sdvUhFsgOBjPMSuFxmpIk9L5+ps7dKpuoVgID5rfT6GT3l/ze3RQ16orp\n" + - "Oy0+iwuVCTFJlFs/L3i2dmWfvVyyiwOWp+9/MMvIVNUT7wDKGJLC9onoqAhnKEqI\n" + - "ERv080cD5NUns7BoUNytuEgWK+J3fRCsHGfeu+Vdl3C0hIE0grOerpWTYAZQQf3H\n" + - "vX/Q7PeGbDCKS+/1eOSBqJ67YiqSn66Qs4g/A89GzMMKuGZ6Lh33x/1kGvT5P8fk\n" + - "FzbqTJWPwN+tu9npw06MFM70/rabztNg1I6uMK/s/H85NjO4DB6SzrVcQu2mhNRG\n" + - "AncNuYLNAgMBAAECggEAAhUlpQYM6fxWQOphUm8a3FSrJB68J2yXR9IfBYAdrf+c\n" + - "I2Y0LbdEk8nzT+Q+G4s1tvcEUN+wc5dubItvwk2/UZwDVgvAKQmGUxfe2LcM5Q5d\n" + - "RsQ1/QFswsd4QEkpT9KBreGqmxya7zhHdaB8WJ5kE1DuVgBB7mbuJw3cAiA4s5n/\n" + - "QxyY3w0Lh89CTJx8IqlNihbjzY/OBanEK8IJe03twdGuGp4fiBnkPMvIzcBhdeXP\n" + - "+8a4NR//KaNvLGC+FcONLEtR0T2/bkjvUrMyBY2gngxYgSCldVXguuU5jGhezkmu\n" + - "+7gP0ipSwyRdR8G36twrpCv5fGruRoGdKqCr+viecQKBgQDINf+NGQpeoFf82UjU\n" + - "b27xz/4YP1td1iIyfNtTdBodC7h6o7Ce/+QNt2b0rVltXtXQ8Ci3uorBih9bMirX\n" + - "F6MBOj4cgdhKySDMaaex5zFr4LOYCJ2nK18sa7QobvkfN3htUcimD3/B6yiEHbpI\n" + - "9Tf71Ix0HOYKL1k7/JsM4TaYHQKBgQDE3o/LmeUM95PJCFAWbB+ZKM3A3x8klZ6m\n" + - "mntSzAFB8yLjXtN0Y34E6+tyXJvW20AE4EWdvjduC/JaxF51sBulnaZbZwDU4Q2G\n" + - "lJkXx7l/iHUaHedxQDvGzhtiiStd7kzh5XLckC8dvsR306z/035fic35VQhTAANv\n" + - "1vzFGx92cQKBgGgzWm7YMoJvV3v8pqAR4x8tjmSWTPo4oZG/U/NKQPEPEZOasCkA\n" + - "q3PMGWSM+DcpHYViCP8esmrqdUlkgdFytt7DrmHt3mGF7nEVKDc6SYmI6E/fZBYG\n" + - "R8F5yMkmgLgTibTz1MdA19BYkLy6MCMapWmHBRbFl6CDZiEHZrc8W8qtAoGBAMQi\n" + - "7GYvO8lQe3dFBe1g6ZZA1cS7Rp6/ReG8dPNHdlVLM84NMmR5nxquJNO6OjS0GTMC\n" + - "cbk3wqer1Vfi3i0oOFMnHo9frq9oTH5xW5kajc/mlqxfcK8fDK8DtrrT6FXbzdMd\n" + - "MvNV3usmnTy4slnqTrRGaeRneDShBcuOCCUj4ZOxAoGBAMGlZqGPjaMZKA4Ub/1y\n" + - "T9wwy40H2DjBfkvOd0+GGYNZkpPlMf6+OR4eaXIhR94g/jDB5rNWMOi3G54J/qsa\n" + - "4iqeWVRpP8kmb3NBJ/Wu0n6JaE2oMOygaMQdpSggPDU6kh9o2Q6Xm8Kc4XP4vR9f\n" + - "cQuZkb1AlVAWiCUHSL8mpDFC\n" + - "-----END PRIVATE KEY-----"; - - String jwe_public_key_pem = "-----BEGIN PUBLIC KEY-----\n" + - "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmfdm8Qh0hX2NVX+0T7I+\n" + - "rHb1IRbIDgYzzErhcZqSJPS+fqbO3SqbqFYCA+a30+hk95f83t0UNeqK6TstPosL\n" + - "lQkxSZRbPy94tnZln71csosDlqfvfzDLyFTVE+8AyhiSwvaJ6KgIZyhKiBEb9PNH\n" + - "A+TVJ7OwaFDcrbhIFivid30QrBxn3rvlXZdwtISBNIKznq6Vk2AGUEH9x71/0Oz3\n" + - "hmwwikvv9Xjkgaieu2Iqkp+ukLOIPwPPRszDCrhmei4d98f9ZBr0+T/H5Bc26kyV\n" + - "j8DfrbvZ6cNOjBTO9P62m87TYNSOrjCv7Px/OTYzuAweks61XELtpoTURgJ3DbmC\n" + - "zQIDAQAB\n" + - "-----END PUBLIC KEY-----"; - - @BeforeEach - void setUp() { - j = new JWT(); - } - - @Test - @DisplayName("Testing default values") - void testDefaultValues() { - JWT j = new JWT(); - assertEquals("", j.raw); - assertEquals("", j.signature); - assertEquals("", j.header); - assertEquals("", j.payload); - } - - @Test - @DisplayName("Testing jwt decode and encode") - void testJWTParse_build() { - JWT j = new JWT(); - boolean errors = false; - try { - j.parse(raw_jwt); - - String out = j.build(); - assertEquals(raw_jwt, out); - String[] splitted = out.split("\\."); - - assertEquals(3, splitted.length); - - assertEquals(raw_header, splitted[0]); - assertEquals(raw_payload, splitted[1]); - assertEquals(raw_signature, splitted[2]); - - //assertEquals(raw_header, j.header); - //assertEquals(raw_payload, j.payload); - //assertEquals(raw_signature, j.signature); - } catch (ParsingException e) { - errors = true; - } - assertFalse(errors); - } - - @Test - @DisplayName("Testing jwt signing and verify") - void testJWTSign_and_verify() { - JWT j = new JWT(); - boolean errors = false; - try { - j.parse(raw_jwt); - j.sign = true; - j.private_key_pem = private_pem_rsa; - - String out = j.build(); - assertNotEquals(raw_jwt, out); - String[] splitted = out.split("\\."); - - assertEquals(3, splitted.length); - - assertEquals(raw_header, splitted[0]); - assertEquals(raw_payload, splitted[1]); - assertNotEquals(raw_signature, splitted[2]); - - JWT j2 = new JWT(); - j2.parse(out); - j2.public_key_pem = public_pem_rsa; - assertTrue(j2.check_sig()); - } catch (ParsingException e) { - errors = true; - } - assertFalse(errors); - } - - @Test - @DisplayName("Testing jwt decode and encode") - void test_jwt_wrong_signature() { - JWT j = new JWT(); - boolean errors = false; - try { - j.parse(raw_jwt); - j.public_key_pem = public_pem_rsa; - assertFalse(j.check_sig()); - } catch (ParsingException e) { - errors = true; - } - assertFalse(errors); - } - - @Test - @DisplayName("Testing jwt decode and encode") - void test_decrypt_encrypt_jwe() { - JWT j = new JWT(); - boolean errors = false; - try { - j.decrypt = true; - j.private_key_pem_enc = jwe_private_key_pem; - j.public_key_pem_enc = jwe_public_key_pem; - j.parse(raw_jwe); - - String parsed_head = j.header; - String parsed_payload = j.payload; - String parsed_signature = j.signature; - - String out = j.build(); - - JWT j2 = new JWT(); - j2.decrypt = true; - j2.private_key_pem_enc = jwe_private_key_pem; - j2.public_key_pem_enc = jwe_public_key_pem; - j2.parse(out); - - assertEquals(parsed_head, j2.header); - assertEquals(parsed_payload, j2.payload); - assertEquals(parsed_signature, j2.signature); - } catch (ParsingException e) { - errors = true; - } - assertFalse(errors); - } - - @Test - void test_check_key() throws JOSEException { - - JWK senderJWK = JWK.parseFromPEMEncodedObjects(private_pem_rsa); - JWK recipientPublicJWK = JWK.parseFromPEMEncodedObjects(jwe_public_key_pem); - - // Create JWT - SignedJWT signedJWT = new SignedJWT( - new JWSHeader.Builder(JWSAlgorithm.RS256).keyID(senderJWK.getKeyID()).build(), - new JWTClaimsSet.Builder() - .subject("alice") - .issueTime(new Date()) - .issuer("https://c2id.com") - .build()); - - // Sign the JWT - signedJWT.sign(new RSASSASigner(senderJWK.toRSAKey())); - - // Create JWE object with signed JWT as payload - JWEObject jweObject = new JWEObject( - new JWEHeader.Builder(JWEAlgorithm.RSA_OAEP_256, EncryptionMethod.A256GCM) - .contentType("JWT") // required to indicate nested JWT - .build(), - new Payload(signedJWT)); - - // Encrypt with the recipient's public key - jweObject.encrypt(new RSAEncrypter(recipientPublicJWK.toRSAKey())); - - // Serialise to JWE compact form - String jweString = jweObject.serialize(); - System.out.println(jweString); - } - - /** - * @Test void test_complete() throws NoSuchAlgorithmException, ParsingException { - * String public_pem = "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAPq3ZL01cG1DHZ4iZLiRlRJIlupb5MGfHipSBq1hG2Jo=\n-----END PUBLIC KEY-----\n"; - * String private_pem = "-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIFlqmiu8kHunEywNZhbZjdZcT1YGTUCoOlh9aHF+43UE\n-----END PRIVATE KEY-----\n"; - *

- * //KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA"); - * //keyGen.initialize(2048); - * //KeyPair pair = keyGen.generateKeyPair(); - * //PrivateKey sk = pair.getPrivate(); - * //PublicKey pk = pair.getPublic(); - *

- * String jws = Jwts.builder() - * .setSubject("Bob") - * .signWith(sk) - * .compact(); - *

- * assertFalse(jws.equals("")); - *

- * JWT j = new JWT(); - * j.check_sig = true; - * j.public_key = "pk_string"; - * j.parse(raw_jwt); - * } - * @Test - * @DisplayName("Testing jwt remove claim") - * void testJWTRemoveClaim() { - * boolean errors = false; - * try { - * JWT j = new JWT(); - * j.parse(raw_jwt); - * j.removeClaim(Utils.Jwt_section.HEADER, "typ"); - * String out = j.build(); - * assertEquals("eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ", out); - *

- * j = new JWT(); - * j.parse(raw_jwt); - * j.removeClaim(Utils.Jwt_section.PAYLOAD, "name"); - * out = j.build(); - * assertEquals("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ", out); - *

- * String[] splitted = out.split("\\."); - *

- * } catch (ParsingException e) { - * errors = true; - * } - * assertFalse(errors); - * } - * @Test - * @DisplayName("Testing jwt edit claim") - * void testJWTEditClaim() { - * boolean errors = false; - * try { - * JWT j = new JWT(); - * j.parse(raw_jwt); - * j.editAddClaim(Utils.Jwt_section.HEADER, "typ", "asdasd"); - * String out = j.build(); - * assertEquals("eyJhbGciOiJSUzI1NiIsInR5cCI6ImFzZGFzZCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ", out); - *

- * j = new JWT(); - * j.parse(raw_jwt); - * j.editAddClaim(Utils.Jwt_section.PAYLOAD, "name", "peppe"); - * out = j.build(); - * assertEquals("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6InBlcHBlIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ", out); - *

- * j = new JWT(); - * j.parse(raw_jwt); - * j.editAddClaim(Utils.Jwt_section.SIGNATURE, "", "peppe"); - * out = j.build(); - * assertEquals("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.peppe", out); - *

- * j = new JWT(); - * j.parse(raw_jwt); - * j.editAddClaim(Utils.Jwt_section.HEADER, "prova", "provona"); - * out = j.build(); - * assertEquals("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsInByb3ZhIjoicHJvdm9uYSJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ", out); - *

- * j = new JWT(); - * j.parse(raw_jwt); - * j.editAddClaim(Utils.Jwt_section.PAYLOAD, "prova", "provona"); - * out = j.build(); - * assertEquals("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMiwicHJvdmEiOiJwcm92b25hIn0.NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ", out); - * } catch (ParsingException e) { - * errors = true; - * } - * assertFalse(errors); - * } - * @Test - * @DisplayName("Claims edit") - * void test_claimEdit() { - * String in = "eyJhbGciOiJSUzI1NiIsImtpZCI6IllodUlKVTZvMTVFVUN5cUEwTEhFcUpkLXhWUEpnb3lXNXdaMW80cGFkV3MifQ.eyJzY29wZSI6Im9wZW5pZCIsInJlZGlyZWN0X3VyaSI6Imh0dHA6Ly9yZWx5aW5nLXBhcnR5Lm9yZzo4MDAxL29pZGMvcnAvY2FsbGJhY2siLCJyZXNwb25zZV90eXBlIjoiY29kZSIsIm5vbmNlIjoidUNhQkJ6RDNPa3VPbEVVenZUSGJOcWFoOHVZdTRVa3UiLCJzdGF0ZSI6IjZFY3JwdzlYNThZaFVXMVlYSHF4bEVEVUhvbXczNUlxIiwiY2xpZW50X2lkIjoiaHR0cDovL3JlbHlpbmctcGFydHkub3JnOjgwMDEvIiwiZW5kcG9pbnQiOiJodHRwOi8vY2llLXByb3ZpZGVyLm9yZzo4MDAyL29pZGMvb3AvYXV0aG9yaXphdGlvbiIsImFjcl92YWx1ZXMiOiJodHRwczovL3d3dy5zcGlkLmdvdi5pdC9TcGlkTDIiLCJpYXQiOjE2NTM5ODM4NTksImF1ZCI6WyJodHRwOi8vY2llLXByb3ZpZGVyLm9yZzo4MDAyL29pZGMvb3AvIiwiaHR0cDovL2NpZS1wcm92aWRlci5vcmc6ODAwMi9vaWRjL29wL2F1dGhvcml6YXRpb24iXSwiY2xhaW1zIjp7ImlkX3Rva2VuIjp7ImZhbWlseV9uYW1lIjp7ImVzc2VudGlhbCI6dHJ1ZX0sImVtYWlsIjp7ImVzc2VudGlhbCI6dHJ1ZX19LCJ1c2VyaW5mbyI6eyJnaXZlbl9uYW1lIjpudWxsLCJmYW1pbHlfbmFtZSI6bnVsbCwiZW1haWwiOm51bGwsImZpc2NhbF9udW1iZXIiOm51bGx9fSwicHJvbXB0IjoiY29uc2VudCBsb2dpbiIsImNvZGVfY2hhbGxlbmdlIjoiU2hOX0t0U3ZhMEtwS1pZUFZ2MEhVd0lFM1lHclhZeHBuVS1Vb1BGTEluZyIsImNvZGVfY2hhbGxlbmdlX21ldGhvZCI6IlMyNTYiLCJpc3MiOiJodHRwOi8vcmVseWluZy1wYXJ0eS5vcmc6ODAwMS8iLCJzdWIiOiJodHRwOi8vcmVseWluZy1wYXJ0eS5vcmc6ODAwMS8ifQ.mETftfWL9MYrf3BVnahWOilFYItkBSaTw3nhKu0UzfiAI5lFy1orNGatNIR-Dg4hgsFCXgaY9rJSi2TVRSqIsHAJPe0HC5sKfXJ-mka0_w4koGDjbmYRZVN3yI05QWsLpENlsuCk2JEgZfz5BvAuX_MgxytIQHhUgy7DsdoJW-6Bk2DPDUiG_bDrBBjdFYgVocaQrxW49NmVIwtVz3dbhdslGA6g0uX7Dp9lQ9HqyWr1YnHtxUdyfuM2wdwPf11fhZNI8Nu_tpgVUxUMQgyEFA1nAscos2FuvLhpNovuciyh0BAlrYTpbXpZ-hjBv5rbfIrv5wytRNhlK2VxP7DA2g"; - * boolean errors = false; - * try { - * j = new JWT(); - * j.parse(in); - *

- * boolean a = j.jwt.getBody().containsKey("family_name"); - * String s = (String) j.jwt.getBody().get("family_name"); - * if (!a) { - * Object c = j.jwt.getBody().get("claims"); - * } - * } catch (ParsingException e) { - * errors = true; - * } - * assertFalse(errors); - * } - * @Test - * @DisplayName("Decode raw jwt") - * void test_decodeRawJwt() { - * String in = "eyJhbGciOiJSUzI1NiIsImtpZCI6IllodUlKVTZvMTVFVUN5cUEwTEhFcUpkLXhWUEpnb3lXNXdaMW80cGFkV3MifQ.eyJzY29wZSI6Im9wZW5pZCIsInJlZGlyZWN0X3VyaSI6Imh0dHA6Ly9yZWx5aW5nLXBhcnR5Lm9yZzo4MDAxL29pZGMvcnAvY2FsbGJhY2siLCJyZXNwb25zZV90eXBlIjoiY29kZSIsIm5vbmNlIjoiOUZKcWczZDBBS0FYTWpEcDRVRnpkbGJUdG5kazgxanUiLCJzdGF0ZSI6ImhURHVRS0t1YUY4dnVxRk1XSVN4NWlxaTBlOXlmRGJiIiwiY2xpZW50X2lkIjoiaHR0cDovL3JlbHlpbmctcGFydHkub3JnOjgwMDEvIiwiZW5kcG9pbnQiOiJodHRwOi8vY2llLXByb3ZpZGVyLm9yZzo4MDAyL29pZGMvb3AvYXV0aG9yaXphdGlvbiIsImFjcl92YWx1ZXMiOiJodHRwczovL3d3dy5zcGlkLmdvdi5pdC9TcGlkTDIiLCJpYXQiOjE2NTY0MDMxNzEsImF1ZCI6WyJodHRwOi8vY2llLXByb3ZpZGVyLm9yZzo4MDAyL29pZGMvb3AvIiwiaHR0cDovL2NpZS1wcm92aWRlci5vcmc6ODAwMi9vaWRjL29wL2F1dGhvcml6YXRpb24iXSwiY2xhaW1zIjp7ImlkX3Rva2VuIjp7ImZhbWlseV9uYW1lIjp7ImVzc2VudGlhbCI6dHJ1ZX0sImVtYWlsIjp7ImVzc2VudGlhbCI6dHJ1ZX19LCJ1c2VyaW5mbyI6eyJnaXZlbl9uYW1lIjpudWxsLCJmYW1pbHlfbmFtZSI6bnVsbCwiZW1haWwiOm51bGwsImZpc2NhbF9udW1iZXIiOm51bGx9fSwicHJvbXB0IjoiY29uc2VudCBsb2dpbiIsImNvZGVfY2hhbGxlbmdlIjoiLXJQSkJfNDFPaUVzUmtXSTNQeDJmNkdaVjdpdWNOQkVReTZXVzRaenVTOCIsImNvZGVfY2hhbGxlbmdlX21ldGhvZCI6IlMyNTYiLCJpc3MiOiJodHRwOi8vcmVseWluZy1wYXJ0eS5vcmc6ODAwMS8ifQ.hZQdNZoZeLNJrIezuXQIV0C5a9ZOubiYTOUYdtmbsR4F_NFZFKDZccbjYk-ntYa2O7_DgcwQ083kAv5dutwU6nhiHBh3K__W4zct2yxcsLspE2pvBbmMjvq7IqmEYgIR2NEBwtCz9RrV6srnjzygm3XHb7kpfu-Z2eVPzxRTqi1C5l-ZX-xPDr2YFFdpHVB17G3lXTEj_Mm6zr6uNeJkS8Ytscq6SXyni3OTj_bRLTLONjoypLRO-qw8z2d8lY7bYgx9mZCAuUtgS75yRlrHuGu4zsE3Bg3UigfnCO_Pqouq-HZOGEZ_7_Hra0S5V8BPek_fRhRH6K534rFWlApRMQ"; - *

- * String out = JWT.decode_raw_jwt(in); - *

- * assertEquals("{" + - * "\"alg\":\"RS256\"," + - * "\"kid\":\"YhuIJU6o15EUCyqA0LHEqJd-xVPJgoyW5wZ1o4padWs\"" + - * "}.{\"scope\":\"openid\",\"redirect_uri\":\"http://relying-party.org:8001/oidc/rp/callback\",\"response_type\":\"code\",\"nonce\":\"9FJqg3d0AKAXMjDp4UFzdlbTtndk81ju\",\"state\":\"hTDuQKKuaF8vuqFMWISx5iqi0e9yfDbb\",\"client_id\":\"http://relying-party.org:8001/\",\"endpoint\":\"http://cie-provider.org:8002/oidc/op/authorization\",\"acr_values\":\"https://www.spid.gov.it/SpidL2\",\"iat\":1656403171,\"aud\":[\"http://cie-provider.org:8002/oidc/op/\",\"http://cie-provider.org:8002/oidc/op/authorization\"],\"claims\":{\"id_token\":{\"family_name\":{\"essential\":true},\"email\":{\"essential\":true}},\"userinfo\":{\"given_name\":null,\"family_name\":null,\"email\":null,\"fiscal_number\":null}},\"prompt\":\"consent login\",\"code_challenge\":\"-rPJB_41OiEsRkWI3Px2f6GZV7iucNBEQy6WW4ZzuS8\",\"code_challenge_method\":\"S256\",\"iss\":\"http://relying-party.org:8001/\"}" + - * ".hZQdNZoZeLNJrIezuXQIV0C5a9ZOubiYTOUYdtmbsR4F_NFZFKDZccbjYk-ntYa2O7_DgcwQ083kAv5dutwU6nhiHBh3K__W4zct2yxcsLspE2pvBbmMjvq7IqmEYgIR2NEBwtCz9RrV6srnjzygm3XHb7kpfu-Z2eVPzxRTqi1C5l-ZX-xPDr2YFFdpHVB17G3lXTEj_Mm6zr6uNeJkS8Ytscq6SXyni3OTj_bRLTLONjoypLRO-qw8z2d8lY7bYgx9mZCAuUtgS75yRlrHuGu4zsE3Bg3UigfnCO_Pqouq-HZOGEZ_7_Hra0S5V8BPek_fRhRH6K534rFWlApRMQ", - * out); - * } - */ - - @Test - @DisplayName("Testing jwt decode and encode") - void test_jwt_parse_change_order() { - JWT j = new JWT(); - boolean errors = false; - try { - j.parse(raw_jwt); - - j.header = "{\"alg\":\"RS256\", \"typ\":\"JWT\"}"; // invert header keys - - String out = j.build(); - assertEquals(raw_jwt, out); - String[] splitted = out.split("\\."); - - assertEquals(3, splitted.length); - - assertEquals(raw_header, splitted[0]); - assertEquals(raw_payload, splitted[1]); - assertEquals(raw_signature, splitted[2]); - - //assertEquals(raw_header, j.header); - //assertEquals(raw_payload, j.payload); - //assertEquals(raw_signature, j.signature); - } catch (ParsingException e) { - errors = true; - } - assertFalse(errors); - } - - @Test - void test_jwt_with_string_escaped() throws ParsingException { - JWT j = new JWT(); - String input_jwt = "eyJraWQiOiJjbGllbiIsImFsZyI6IlJTMjU2In0.eyJpc3MiOiJodHRwOlwvXC9hYWMtcHJvdmlkZXIub3JnOjgwODBcL2F1dGhcL29wZW5pZGZlZFwvbWV0YWRhdGFcLzcwZTYyYzNiLTgwMDYtNDdjYy1hMTg2LWVhMzdiZGEwMDRhOSIsInJlc3BvbnNlX3R5cGUiOiJjb2RlIiwiY29kZV9jaGFsbGVuZ2VfbWV0aG9kIjoiUzI1NiIsIm5vbmNlIjoieEc2NEExa3ZzNC1CM29XRXpidlVUclRUanE0RHNoOTVsMDlYckotbG1sRSIsImNsaWVudF9pZCI6Imh0dHA6XC9cL2FhYy1wcm92aWRlci5vcmc6ODA4MFwvYXV0aFwvb3BlbmlkZmVkXC9tZXRhZGF0YVwvNzBlNjJjM2ItODAwNi00N2NjLWExODYtZWEzN2JkYTAwNGE5IiwiYXVkIjoiaHR0cDpcL1wvdHJ1c3QtYW5jaG9yLm9yZzo4MDAwXC9vaWRjXC9vcCIsInNjb3BlIjoib3BlbmlkIG9mZmxpbmVfYWNjZXNzIiwiYWNyX3ZhbHVlcyI6Imh0dHBzOlwvXC93d3cuc3BpZC5nb3YuaXRcL1NwaWRMMSIsImNsYWltcyI6eyJ1c2VyaW5mbyI6eyJodHRwczpcL1wvYXR0cmlidXRlcy5laWQuZ292Lml0XC9maXNjYWxfbnVtYmVyIjp7ImVzc2VudGlhbCI6dHJ1ZX0sImh0dHBzOlwvXC9hdHRyaWJ1dGVzLmVpZC5nb3YuaXRcL2VfZGVsaXZlcnlfc2VydmljZSI6eyJlc3NlbnRpYWwiOnRydWV9LCJkb2N1bWVudF9kZXRhaWxzIjp7ImVzc2VudGlhbCI6dHJ1ZX0sImRhdGVfb2ZfaXNzdWFuY2UiOnsiZXNzZW50aWFsIjp0cnVlfSwiYmlydGhkYXRlIjp7ImVzc2VudGlhbCI6dHJ1ZX0sImVtYWlsX3ZlcmlmaWVkIjp7ImVzc2VudGlhbCI6dHJ1ZX0sImFkZHJlc3MiOnsiZXNzZW50aWFsIjp0cnVlfSwiZ2VuZGVyIjp7ImVzc2VudGlhbCI6dHJ1ZX0sInBob25lX251bWJlcl92ZXJpZmllZCI6eyJlc3NlbnRpYWwiOnRydWV9LCJnaXZlbl9uYW1lIjp7ImVzc2VudGlhbCI6dHJ1ZX0sInBsYWNlX29mX2JpcnRoIjp7ImVzc2VudGlhbCI6dHJ1ZX0sInBob25lX251bWJlciI6eyJlc3NlbnRpYWwiOnRydWV9LCJmYW1pbHlfbmFtZSI6eyJlc3NlbnRpYWwiOnRydWV9LCJlbWFpbCI6eyJlc3NlbnRpYWwiOnRydWV9LCJ1c2VybmFtZSI6eyJlc3NlbnRpYWwiOnRydWV9fX0sInJlZGlyZWN0X3VyaSI6Imh0dHA6XC9cL2FhYy1wcm92aWRlci5vcmc6ODA4MFwvYXV0aFwvb3BlbmlkZmVkXC9sb2dpblwvNzBlNjJjM2ItODAwNi00N2NjLWExODYtZWEzN2JkYTAwNGE5Iiwic3RhdGUiOiIzd0o1cDV5U1lEVkwzWXptMVlRcWw1RDlzdWoxYS1LTlU1WEJHWXpYcmpBPSIsImV4cCI6MTcwNDg5ODY1MiwiaWF0IjoxNzA0ODk4MzUyLCJwcm9tcHQiOiJjb25zZW50IiwianRpIjoiZWExZGE0MmQtMmM0OC00ZjlhLWJiNzAtMzVkODJmY2Q2ZjU0IiwiY29kZV9jaGFsbGVuZ2UiOiJyVUcyalNpMDc2NnlQX044ai13ZUVDUmZPbWcyZ1NjZ1lNS20xS2JvMUpnIn0.ZVJDsU5xi0qUDPcUPV9Gi9u54DPp2HGLzwgPbAdZA6IX3Thnu3sBhTBSJts0L8RBfe4xXxlTOrkOL-OM7bWG4ZxITwIixFRVhsjRgF6QgPYmis3Nv464KgK_LePlmlXmRNNC6cGTxwiAD_PLPyb2Earcp23FWNaQe5XGfil2gbVQimtSAlHjl3zUCnndAWGQbgtb6jXGdxzEDZlOdZH_WNxgFqpSHZmS-BCfc4yLukZS43iGDBYHpbTv2J4mMg5RFYBPMcS09zBtFuI6lmZcayczMpsdZfnCre44zi19xPtf2y4vSnf_I66k8AudumAGmLJFazzfGhp8MCy3nomstA"; - - j.parse(input_jwt); - String built_jwt = j.build(); - - assertEquals(input_jwt, built_jwt); - } - -} diff --git a/tool/src/test/java/MessageOeration_Test.java b/tool/src/test/java/MessageOeration_Test.java deleted file mode 100644 index e9fc990..0000000 --- a/tool/src/test/java/MessageOeration_Test.java +++ /dev/null @@ -1,130 +0,0 @@ -import migt.HTTPReqRes; -import migt.MessageOperation; -import migt.Operation_API; -import migt.ParsingException; -import org.json.JSONObject; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -public class MessageOeration_Test { - - @Test - void test_save_head_param() throws ParsingException { - HTTPReqRes msg = HTTPReqRes_Test.initMessage_ok(); - - String msg_op_txt = - "{\n" + - " \"from\": \"head\",\n" + - " \"save\": \"Host\",\n" + - " \"as\": \"var_name\"\n" + - " }"; - - MessageOperation mop = new MessageOperation(new JSONObject(msg_op_txt)); - Operation_API op_api = new Operation_API(msg, true); - mop.loader(op_api); - mop.execute(); - op_api = mop.exporter(); - assertTrue(mop.getResult()); - assertEquals("play.google.com", op_api.vars.get(0).value); - } - - @Test - void test_save_head_regex() throws ParsingException { - HTTPReqRes msg = HTTPReqRes_Test.initMessage_ok(); - - String msg_op_txt = - "{\n" + - " \"from\": \"head\",\n" + - " \"save match\": \"Host:[^\\n\\r]*\",\n" + - " \"as\": \"var_name\"\n" + - " }"; - - MessageOperation mop = new MessageOperation(new JSONObject(msg_op_txt)); - Operation_API op_api = new Operation_API(msg, true); - mop.loader(op_api); - mop.execute(); - op_api = mop.exporter(); - assertTrue(mop.getResult()); - assertEquals("Host: play.google.com", op_api.vars.get(0).value); - } - - @Test - void test_save_url_param() throws ParsingException { - HTTPReqRes msg = HTTPReqRes_Test.initMessage_ok(); - String msg_op_txt = - "{\n" + - " \"from\": \"url\",\n" + - " \"save\": \"format\",\n" + - " \"as\": \"var_name\"\n" + - " }"; - - MessageOperation mop = new MessageOperation(new JSONObject(msg_op_txt)); - Operation_API op_api = new Operation_API(msg, true); - mop.loader(op_api); - mop.execute(); - op_api = mop.exporter(); - assertTrue(mop.getResult()); - assertEquals("json", op_api.vars.get(0).value); - } - - @Test - void test_save_url_param_no_decode() throws ParsingException { - HTTPReqRes msg = HTTPReqRes_Test.initMessage_ok(); - String msg_op_txt = - "{\n" + - " \"from\": \"url\",\n" + - " \"save\": \"paramwithspace\",\n" + - " \"as\": \"var_name\",\n" + - " \"url decode\": false" + - " }"; - - MessageOperation mop = new MessageOperation(new JSONObject(msg_op_txt)); - Operation_API op_api = new Operation_API(msg, true); - mop.loader(op_api); - mop.execute(); - op_api = mop.exporter(); - assertTrue(mop.getResult()); - assertEquals("first+last", op_api.vars.get(0).value); - } - - @Test - void test_save_url_regex() throws ParsingException { - HTTPReqRes msg = HTTPReqRes_Test.initMessage_ok(); - String msg_op_txt = - "{\n" + - " \"from\": \"url\",\n" + - " \"save match\": \"format=[^&\\n\\r]*\",\n" + - " \"as\": \"var_name\"\n" + - " }"; - - MessageOperation mop = new MessageOperation(new JSONObject(msg_op_txt)); - Operation_API op_api = new Operation_API(msg, true); - mop.loader(op_api); - mop.execute(); - op_api = mop.exporter(); - assertTrue(mop.getResult()); - assertEquals("format=json", op_api.vars.get(0).value); - } - - @Test - void test_save_body_regex() throws ParsingException { - HTTPReqRes msg = HTTPReqRes_Test.initMessage_ok(); - msg.setBody(true, "asdasdasd"); - String msg_op_txt = - "{\n" + - " \"from\": \"body\",\n" + - " \"save\": \"asdasdasd\"," + - " \"as\": \"var_name\"\n" + - " }"; - - MessageOperation mop = new MessageOperation(new JSONObject(msg_op_txt)); - Operation_API op_api = new Operation_API(msg, true); - mop.loader(op_api); - mop.execute(); - op_api = mop.exporter(); - assertTrue(mop.getResult()); - assertEquals("asdasdasd", op_api.vars.get(0).value); - } -} diff --git a/tool/src/test/java/SessionOperation_Test.java b/tool/src/test/java/SessionOperation_Test.java deleted file mode 100644 index 86ede08..0000000 --- a/tool/src/test/java/SessionOperation_Test.java +++ /dev/null @@ -1,29 +0,0 @@ -import migt.ParsingException; -import migt.SessionOperation; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - -public class SessionOperation_Test { - - @Test - @DisplayName("ParsingRawSessionAction test") - void test_parseRawSessionAction() throws ParsingException { - List l = SessionOperation.parseRange("[something, prova2]"); - - assertTrue((boolean) l.get(0)); - assertTrue((boolean) l.get(1)); - assertEquals(l.get(2), "something"); - assertEquals(l.get(3), "prova2"); - - l = SessionOperation.parseRange("( something, prova2)"); - - assertFalse((boolean) l.get(0)); - assertFalse((boolean) l.get(1)); - assertEquals(l.get(2), "something"); - assertEquals(l.get(3), "prova2"); - } -} \ No newline at end of file diff --git a/tool/src/test/java/SessionTrackAction_Test.java b/tool/src/test/java/SessionTrackAction_Test.java deleted file mode 100644 index f879f38..0000000 --- a/tool/src/test/java/SessionTrackAction_Test.java +++ /dev/null @@ -1,74 +0,0 @@ -import migt.ParsingException; -import migt.SessionOperation; -import migt.SessionTrackAction; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class SessionTrackAction_Test { - - @Test - @DisplayName("ParsingRawSessionAction test") - void test_parseRawSessionAction() { - SessionTrackAction s = new SessionTrackAction(); - - try { - s.parse_raw_action("open | https://www.facebook.com/ |"); - assertEquals(SessionOperation.SessAction.OPEN, s.action); - assertEquals("https://www.facebook.com/", s.elem); - assertEquals("open | https://www.facebook.com/ |", s.toString()); - - s = new SessionTrackAction("click | xpath=/html/body/div[1]/div[3]/div/div[5]/div[1]/span[1] |"); - assertEquals(SessionOperation.SessAction.CLICK, s.action); - assertEquals("xpath=/html/body/div[1]/div[3]/div/div[5]/div[1]/span[1]", s.elem); - assertEquals("xpath", s.elem_type); - assertEquals("/html/body/div[1]/div[3]/div/div[5]/div[1]/span[1]", s.elem_source); - assertEquals("click | xpath=/html/body/div[1]/div[3]/div/div[5]/div[1]/span[1] |", s.toString()); - - s = new SessionTrackAction("type | id=login | folafo9046@eoscast.com"); - assertEquals(SessionOperation.SessAction.TYPE, s.action); - assertEquals("id=login", s.elem); - assertEquals("id", s.elem_type); - assertEquals("login", s.elem_source); - assertEquals("folafo9046@eoscast.com", s.content); - assertEquals("type | id=login | folafo9046@eoscast.com", s.toString()); - - s = new SessionTrackAction("wait | 3000 |"); - assertEquals("wait | 3000 |", s.toString()); - - s = new SessionTrackAction("equals | xpath=/html/body/div[1]/div[3]/div/div[5]/div[1]/span[1] |"); - assertEquals(SessionOperation.SessAction.EQUALS, s.action); - assertEquals("equals | xpath=/html/body/div[1]/div[3]/div/div[5]/div[1]/span[1] |", s.toString()); - } catch (ParsingException e) { - assertEquals(1, 0); - } - } - - @Test - @DisplayName("Test Action assert") - void testActionAsserts() throws ParsingException { - SessionTrackAction s = new SessionTrackAction(); - String in = ""; - - in = "assert clickable | xpath=/html/body/div[1]/div[2]/div/div[2]/div/div/div/div[2]/div[2]/form/div[6]/div/div[3]/div/div[2]/div[1]/a |"; - s.parse_raw_action(in); - assertEquals(s.action, SessionOperation.SessAction.ASSERT_CLICKABLE); - assertEquals(in, s.toString()); - - s = new SessionTrackAction(); - in = "assert not clickable | xpath=/html/body/div[1]/div[2]/div/div[2]/div/div/div/div[2]/div[2]/form/div[6]/div/div[3]/div/div[2]/div[1]/a |"; - s.parse_raw_action(in); - assertEquals(s.toString(), in); - - s = new SessionTrackAction(); - in = "assert visible | xpath=/html/body/div[1]/div[2]/div/div[2]/div/div/div/div[2]/div[2]/form/div[6]/div/div[3]/div/div[2]/div[1]/a |"; - s.parse_raw_action(in); - assertEquals(s.toString(), in); - - s = new SessionTrackAction(); - in = "assert not visible | xpath=/html/body/div[1]/div[2]/div/div[2]/div/div/div/div[2]/div[2]/form/div[6]/div/div[3]/div/div[2]/div[1]/a |"; - s.parse_raw_action(in); - assertEquals(s.toString(), in); - } -} diff --git a/tool/src/test/java/Track_Test.java b/tool/src/test/java/Track_Test.java deleted file mode 100644 index 9efb116..0000000 --- a/tool/src/test/java/Track_Test.java +++ /dev/null @@ -1,157 +0,0 @@ -import migt.Marker; -import migt.ParsingException; -import migt.SessionTrackAction; -import migt.Track; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotEquals; - -public class Track_Test { - - @Test - @DisplayName("Test indexes") - void testIndexes() throws ParsingException { - Track t = new Track("open | https://www.google.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "wait | 3000"); - - int indx_0 = t.indexOfStaFromMarker("M0", false); - int indx_L = t.indexOfStaFromMarker("ML", true); - - assertEquals(0, indx_0); - assertEquals(2, indx_L); - - t = new Track(""); - indx_0 = t.indexOfStaFromMarker("M0", false); - indx_L = t.indexOfStaFromMarker("ML", true); - - assertEquals(-1, indx_0); - assertEquals(-1, indx_L); - } - - @Test - @DisplayName("Test first and last indexes") - void testFirstAndLAstIndexes() throws ParsingException { - Track t = new Track("open | https://www.google.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "wait | 3000"); - - int indx_0 = t.indexOfStaFromMarker("M0", false); - int indx_L = t.indexOfStaFromMarker("ML", true); - - assertEquals(t.getTrack().get(0), t.getStasFromMarkers("M0", "M0", true, true).get(0)); - assertEquals(t.getTrack().get(indx_L), t.getStasFromMarkers("ML", "ML", true, true).get(0)); - assertEquals(t.getTrack(), t.getStasFromMarkers("M0", "ML", true, true)); - } - - @Test - @DisplayName("Test insert") - void testInsert() throws ParsingException { - Track t = new Track("open | https://www.google.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "wait | 3000"); - - t.insert(new Marker("M0"), "wait | 3000"); - - assertEquals(t.getStasFromMarkers("M0", "M0", true, true).get(0).toString(), - new SessionTrackAction("wait | 3000").toString()); - - List l = new ArrayList<>(); - t.mark(t.getTrack().get(1), "M1"); - l.add(t.getTrack().get(1)); - t.mark(t.getTrack().get(2), "M2"); - l.add(t.getTrack().get(2)); - assertEquals(l, t.getStasFromMarkers("M1", "M2", true, true)); - - t = new Track(""); - t.insert(new Marker("M0"), "wait | 3000"); - t.insert(new Marker("M0"), "wait | 4000"); - t.insert(new Marker("ML"), "wait | 5000"); - - t = new Track(""); - String in = "open | https://www.google.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "wait | 3000 |"; - - t.insert(new Marker("M0"), in); - - assertEquals("open | https://www.google.com/ |\nopen | https://www.youtube.com/ |\nwait | 3000 |\n", - t.toString()); - } - - @Test - @DisplayName("Track equals") - void testTrackEquals() throws ParsingException { - Track t1 = new Track("open | https://www.google.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "wait | 3000"); - Track t2 = new Track("open | https://www.google.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "wait | 3000"); - - assertEquals(t1, t2); - assertEquals(t2, t1); - - t2 = new Track("open | https://www.google.com/ |\n" + - "open | https://www.youtube.com/ |\n"); - - assertNotEquals(t1, t2); - assertNotEquals(t2, t1); - } - - @Test - @DisplayName("Track remove test") - void testTrackRemove() throws ParsingException { - Track t = new Track("open | https://www.google.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "wait | 3000"); - - t.remove(new Marker("ML")); - assertEquals("open | https://www.google.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "open | https://www.youtube.com/ |\n", t.toString()); - - t.remove(new Marker("M0")); - assertEquals("open | https://www.youtube.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "open | https://www.youtube.com/ |\n", t.toString()); - } - - @Test - @DisplayName("Test range") - void rangeTest() throws ParsingException { - Track t = new Track("open | https://www.google.com/ |\n" + - "open | https://www.youtube.com/ |\n" + - "wait | 3000"); - List sta = t.getStasFromMarkers("M0", "ML", false, true); - assertEquals(t.getTrack().get(1), sta.get(0)); - assertEquals(t.getTrack().get(2), sta.get(1)); - - sta = t.getStasFromMarkers("M0", "ML", true, false); - assertEquals(2, sta.size()); - assertEquals(t.getTrack().get(0), sta.get(0)); - assertEquals(t.getTrack().get(1), sta.get(1)); - - sta = t.getStasFromMarkers("M0", "M0", true, true); - assertEquals(1, sta.size()); - assertEquals(t.getTrack().get(0), sta.get(0)); - } -} \ No newline at end of file diff --git a/tool/src/test/java/Utils_Test.java b/tool/src/test/java/Utils_Test.java deleted file mode 100644 index b1fa20b..0000000 --- a/tool/src/test/java/Utils_Test.java +++ /dev/null @@ -1,193 +0,0 @@ -import migt.*; -import org.json.JSONObject; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - -public class Utils_Test { - - @Test - @DisplayName("Testing build string with vars") - void testBuildStringWithVars() throws ParsingException { - List vars = new ArrayList<>(); - Var v = new Var("questo", "provona"); - vars.add(v); - - Var v2 = new Var("qualcosaltro", "prova"); - vars.add(v2); - - String s = "test da aggiungere $questo$ e poi $qualcosaltro$"; - String res = ""; - try { - res = Tools.buildStringWithVars(vars, s); - } catch (ParsingException e) { - e.printStackTrace(); - } - assertEquals("test da aggiungere provona e poi prova", res); - - s = "open | https://$var3$$var4$ |"; - Var v3 = new Var("var3", "www.youtube.com"); - vars.add(v3); - - Var v4 = new Var("var4", "/link/a/caso"); - vars.add(v4); - - res = Tools.buildStringWithVars(vars, s); - assertEquals("open | https://www.youtube.com/link/a/caso |", res); - } - - @Test - @DisplayName("Test find parent div") - void testFindParentDiv() throws ParsingException { - String in = "xpath=/html/body/div[3]/div[2]/div/div/div/div/div[3]/*[2]"; - String res = Tools.findParentDiv(in); - assertEquals("xpath=/html/body/div[3]/div[2]/div/div/div/div/div", res); - - assertThrows(ParsingException.class, () -> { - Tools.findParentDiv("https://www.facebook.com/"); - }); - - assertThrows(ParsingException.class, () -> { - Tools.findParentDiv("https://www.ted.com/settings/account"); - }); - - in = "xpath=/html/body/div[2]/div/div[2]/form/div/span/span/button"; - res = Tools.findParentDiv(in); - assertEquals("xpath=/html/body/div[2]/div/div[2]/form/div", res); - - in = "id=email"; - res = Tools.findParentDiv(in); - assertEquals("id=email", res); - } - - /* - @Test - @DisplayName("Test execute session ops") - void testExecuteSessionOps() throws ParsingException { - migt.Test t = new migt.Test(); - Operation op = new Operation(); - List vars = new ArrayList<>(); - - t.sessions.add(new Session()); - - SessionOperation s = new SessionOperation(); - s.action = Utils.SessOperationAction.SAVE; - s.target = Utils.SessOperationTarget.LAST_CLICK; - s.from_session = "s1"; - - op.session_operations.add(s); - - t.last_click = new SessionTrackAction("click | xpath=qualcosa |"); - - Object[] res = Utils.executeSessionOps(t, op, vars); - op = (Operation) res[0]; - vars = (List) res[1]; - - assertEquals("click | xpath=qualcosa |", vars.get(1).value); - } - */ - - @Test - @DisplayName("Test execute session Ops") - void executeSessioOps_test() throws ParsingException { - migt.Test t = new migt.Test(); - Session s = new Session("s1"); - - try { - s.setTrackFromString("set var | idp_usr | provaprovona99@outlook.it\n" + - "set var | idp_pw | Xflo98!@ops\n" + - "open | https://auth.fandom.com/signin |\n" + - "click | xpath=/html/body/div[2]/div/div/div[2]/div[2] |\n" + - "type | xpath=/html/body/div/main/div/div[2]/div/form[1]/section/div[1]/div/input | provaprovona\n" + - "wait | 500\n" + - "type | xpath=/html/body/div/main/div/div[2]/div/form[1]/section/div[2]/div[1]/div/input | Asddasdda123!\n" + - "wait | 300\n" + - "click | xpath=/html/body/div/main/div/div[2]/div/form[1]/section/div[3]/button |\n" + - "open | https://auth.fandom.com/auth/settings |\n" + - "click | xpath=/html/body/div[1]/main/div/div[2]/form/section[2]/div[2]/button[1] |\n" + - "click | xpath=/html/body/div[3]/div[2]/div/div/div/div/div[3]/button[2] |\n" + - "click | id=email |\n" + - "type | id=email | provaprovona99@outlook.it\n" + - "click | id=pass |\n" + - "type | id=pass | Xflo98!@ops\n" + - "click | id=loginbutton |\n" + - "click | xpath=/html/body/div[1]/div/div/div/div/div/div/div/div[1]/div/div/div[2]/div[2]/div[1]/div/div |\n" + - "assert open | https://auth.fandom.com/auth/settings |\n" + - "assert element content has | xpath=/html/body/div[1]/main/div/div[2]/form/section[2]/div[2]/button[1]/span[2] | Connect\n" + - "open | https://auth.fandom.com/auth/settings |\n" + - "click | xpath=/html/body/div[1]/main/div/div[2]/form/section[2]/div[2]/button[1] |"); - } catch (ParsingException e) { - assertEquals(1, 0); - } - t.sessions.add(s); - Operation op = new Operation(); - JSONObject sop_json = new JSONObject("{\"session operations\":[\n" + - " {\n" + - " \"session\": \"s1\",\n" + - " \"mark\": \"all_assert\",\n" + - " \"name\": \"A\"\n" + - " },\n" + - " {\n" + - " \"session\": \"s1\",\n" + - " \"remove\": \"track\",\n" + - " \"range\": \"[A,ML]\"\n" + - " } \n" + - "]}"); - try { - op.session_operations = SessionOperation.parseFromJson(sop_json); - } catch (ParsingException e) { - assertEquals(1, 0); - } - - List vars = new ArrayList<>(); - try { - op.executeSessionOps(t, vars); - } catch (ParsingException e) { - assertEquals(1, 0); - } - - Session right_output = new Session("s1"); - try { - right_output.setTrackFromString("set var | idp_usr | provaprovona99@outlook.it\n" + - "set var | idp_pw | Xflo98!@ops\n" + - "open | https://auth.fandom.com/signin |\n" + - "click | xpath=/html/body/div[2]/div/div/div[2]/div[2] |\n" + - "type | xpath=/html/body/div/main/div/div[2]/div/form[1]/section/div[1]/div/input | provaprovona\n" + - "wait | 500\n" + - "type | xpath=/html/body/div/main/div/div[2]/div/form[1]/section/div[2]/div[1]/div/input | Asddasdda123!\n" + - "wait | 300\n" + - "click | xpath=/html/body/div/main/div/div[2]/div/form[1]/section/div[3]/button |\n" + - "open | https://auth.fandom.com/auth/settings |\n" + - "click | xpath=/html/body/div[1]/main/div/div[2]/form/section[2]/div[2]/button[1] |\n" + - "click | xpath=/html/body/div[3]/div[2]/div/div/div/div/div[3]/button[2] |\n" + - "click | id=email |\n" + - "type | id=email | provaprovona99@outlook.it\n" + - "click | id=pass |\n" + - "type | id=pass | Xflo98!@ops\n" + - "click | id=loginbutton |\n" + - "click | xpath=/html/body/div[1]/div/div/div/div/div/div/div/div[1]/div/div/div[2]/div[2]/div[1]/div/div |\n"); - } catch (ParsingException e) { - assertEquals(1, 0); - } - - try { - assertEquals(t.getSession("s1").getTrack().toString(), right_output.getTrack().toString()); - } catch (ParsingException e) { - assertEquals(1, 0); - } - } - - @Test - void test_check_json_strings_equals() { - boolean res = Tools.check_json_strings_equals( - "{a : {a : 2}, b : 2}", - "{b : 2, a : {a : 2}}" - ); - - assertTrue(res); - } -} diff --git a/tool/src/test/java/Var_Test.java b/tool/src/test/java/Var_Test.java deleted file mode 100644 index 9e05e31..0000000 --- a/tool/src/test/java/Var_Test.java +++ /dev/null @@ -1,44 +0,0 @@ -import migt.HTTPReqRes; -import migt.ParsingException; -import migt.Var; -import org.json.JSONArray; -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class Var_Test { - - @Test - void test_var_string() throws ParsingException { - Var v = new Var("prova", "provona"); - - assertEquals(v.name, "prova"); - assertEquals(v.value, "provona"); - assertEquals(v.getType(), Var.VarType.STRING); - } - - @Test - void test_var_message() throws ParsingException { - - HTTPReqRes message = HTTPReqRes_Test.initMessage_ok(); - - Var v = new Var("prova", message.getRequest()); - - assertEquals(v.name, "prova"); - assertEquals(v.value, message.getRequest()); - assertEquals(v.get_value_message(), message.getRequest()); - assertEquals(v.getType(), Var.VarType.MESSAGE); - } - - @Test - void test_var_json_array() { - JSONArray ja = new JSONArray("[\"first\", \"second\"]"); - - Var v = new Var("var1", ja.toList().toArray()); - - assertEquals(v.name, "var1"); - assertEquals(((String[]) v.value)[0], ja.get(0)); - assertEquals(((String[]) v.value)[1], ja.get(1)); - assertEquals(v.getType(), Var.VarType.STRING_ARRAY); - } -} diff --git a/tool/src/test/java/main_Test.java b/tool/src/test/java/main_Test.java deleted file mode 100644 index 09a4f25..0000000 --- a/tool/src/test/java/main_Test.java +++ /dev/null @@ -1,42 +0,0 @@ -import migt.ParsingException; -import migt.Session; -import migt.Tools; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class main_Test { - - @Test - @DisplayName("ParsingRawSessionAction test") - void test_batchPassivesFromSession() throws ParsingException { - List tests = new ArrayList<>(); - - for (int i = 0; i < 8; i++) { - migt.Test t1 = new migt.Test(); - t1.sessions.add(new Session()); - tests.add(t1); - } - - tests.get(0).sessions.get(0).name = "1"; - tests.get(1).sessions.get(0).name = "2"; - tests.get(2).sessions.get(0).name = "2"; - tests.get(3).sessions.get(0).name = "3"; - tests.get(4).sessions.get(0).name = "1"; - tests.get(5).sessions.get(0).name = "3"; - tests.get(6).sessions.get(0).name = "4"; - tests.get(7).sessions.get(0).name = "1"; - - HashMap> hm = Tools.batchPassivesFromSession(tests); - - assertEquals(3, hm.get("1").size()); - assertEquals(2, hm.get("2").size()); - assertEquals(2, hm.get("3").size()); - assertEquals(1, hm.get("4").size()); - } -} \ No newline at end of file